Mercurial > wgsdk
comparison src/main.c @ 7:be4835547dd0
clean up code, convert git files to hg, etc.
| author | Paper |
|---|---|
| date | Fri, 16 Dec 2022 20:35:06 -0500 |
| parents | 59bf702b2b21 |
| children | 42ac054c0231 |
comparison
equal
deleted
inserted
replaced
| 6:8ce85aee15c0 | 7:be4835547dd0 |
|---|---|
| 1 /** | |
| 2 * wgsdk - Winamp plugin for Discord's GameSDK | |
| 3 **/ | |
| 1 #include <assert.h> | 4 #include <assert.h> |
| 2 #include <stdio.h> | 5 #include <stdio.h> |
| 3 #include <stdlib.h> | 6 #include <stdlib.h> |
| 4 #include <Winamp/wa_ipc.h> | 7 #include <Winamp/wa_ipc.h> |
| 5 #include "main.h" | |
| 6 #include "discord_game_sdk.h" | 8 #include "discord_game_sdk.h" |
| 7 #include "timer.h" | 9 #include "timer.h" |
| 8 #include "config.h" | 10 #include "config.h" |
| 9 #include "resource.h" | 11 #include "resource.h" |
| 12 #include "utils.h" | |
| 10 #ifndef WIN32_LEAN_AND_MEAN | 13 #ifndef WIN32_LEAN_AND_MEAN |
| 11 # define WIN32_LEAN_AND_MEAN | 14 # define WIN32_LEAN_AND_MEAN |
| 12 #endif | 15 #endif |
| 13 #include <windows.h> | 16 #include <windows.h> |
| 14 #include <windowsx.h> | 17 #include <windowsx.h> |
| 17 #define DISCORD_REQUIRE(x) \ | 20 #define DISCORD_REQUIRE(x) \ |
| 18 assert(x == DiscordResult_Ok) | 21 assert(x == DiscordResult_Ok) |
| 19 | 22 |
| 20 #define GPPHDR_VER 0x10 | 23 #define GPPHDR_VER 0x10 |
| 21 | 24 |
| 22 winamp_general_purpose_plugin g_plugin = { | 25 int init(); |
| 26 void conf(); | |
| 27 void quit(); | |
| 28 | |
| 29 /* Winamp-specific stuff */ | |
| 30 struct winamp_gpp { | |
| 31 int version; // version of the plugin structure | |
| 32 char *description; // name/title of the plugin | |
| 33 int(*init)(); // function which will be executed on init event | |
| 34 void(*conf)(); // function which will be executed on config event | |
| 35 void(*quit)(); // function which will be executed on quit event | |
| 36 HWND hwndParent; // hwnd of the Winamp client main window (stored by Winamp when dll is loaded) | |
| 37 HINSTANCE hDllInstance; // hinstance of this plugin DLL. (stored by Winamp when dll is loaded) | |
| 38 }; | |
| 39 | |
| 40 struct winamp_gpp g_plugin = { | |
| 23 GPPHDR_VER, // version of the plugin, defined in "gen_myplugin.h" | 41 GPPHDR_VER, // version of the plugin, defined in "gen_myplugin.h" |
| 24 "Discord GameSDK", // name/title of the plugin, defined in "gen_myplugin.h" | 42 "Discord GameSDK", // name/title of the plugin, defined in "gen_myplugin.h" |
| 25 init, // function name which will be executed on init event | 43 init, // function name which will be executed on init event |
| 26 conf, // function name which will be executed on config event | 44 conf, // function name which will be executed on config event |
| 27 quit, // function name which will be executed on quit event | 45 quit, // function name which will be executed on quit event |
| 28 0, // handle to Winamp main window, loaded by winamp when this dll is loaded | 46 0, // handle to Winamp main window, loaded by winamp when this dll is loaded |
| 29 0 // hinstance to this dll, loaded by winamp when this dll is loaded | 47 0 // hinstance to this dll, loaded by winamp when this dll is loaded |
| 30 }; | 48 }; |
| 31 | 49 |
| 32 WNDPROC g_lpWndProcOld = 0; | 50 /* Discord stuff */ |
| 33 | |
| 34 struct timer_t timer_callbacks = { .interval = 16 }; | |
| 35 struct config config = { | |
| 36 .display_title = 1, | |
| 37 .show_elapsed_time = 1 | |
| 38 }; | |
| 39 | 51 |
| 40 struct DiscordActivity activity = { | 52 struct DiscordActivity activity = { |
| 41 .application_id = CLIENT_ID, | 53 .application_id = CLIENT_ID, |
| 42 .name = "Winamp", | 54 .name = "Winamp", |
| 43 .instance = 0 | 55 .instance = 0 |
| 44 }; | 56 }; |
| 45 | 57 |
| 46 struct IDiscordActivityEvents activities_events; | 58 struct IDiscordActivityEvents activities_events; |
| 47 | 59 |
| 48 struct app_t app; | 60 struct application { |
| 61 struct IDiscordCore* core; | |
| 62 struct IDiscordUsers* users; | |
| 63 struct IDiscordActivityManager* activities; | |
| 64 } app; | |
| 49 | 65 |
| 50 void update_activity_callback(void* data, enum EDiscordResult result) | 66 /* Now we get to our built-in stuff */ |
| 51 { | 67 |
| 68 struct timer timer_callbacks = { .interval = 16 }; | |
| 69 | |
| 70 struct config config = { | |
| 71 .display_title = 1, | |
| 72 .show_elapsed_time = 1 | |
| 73 }; | |
| 74 | |
| 75 /* CallWindowProc is the *only* function that ever needs this. */ | |
| 76 WNDPROC _old_wnd_proc = 0; | |
| 77 | |
| 78 /* ------------------------------------ */ | |
| 79 | |
| 80 void DISCORD_CALLBACK update_activity_callback(void* data, enum EDiscordResult result) { | |
| 52 DISCORD_REQUIRE(result); | 81 DISCORD_REQUIRE(result); |
| 53 } | 82 } |
| 54 | 83 |
| 55 void report_current_song_status(int playbackState) | 84 void report_current_song_status(int playback_state) { |
| 56 { | 85 assert(playback_state != 0); |
| 57 assert(playbackState != 0); | 86 strcpy(activity.state, playback_state == 1 ? "(Playing)" : "(Paused)"); |
| 58 activity.timestamps.start = 0; | |
| 59 strcpy(activity.state, playbackState == 1 ? "(Playing)" : "(Paused)"); | |
| 60 | 87 |
| 61 if (playbackState == 1 && config.show_elapsed_time) { | 88 activity.timestamps.start = (playback_state == 1 && config.show_elapsed_time) |
| 62 FILETIME ft; | 89 ? get_system_time_in_milliseconds() - SendMessage(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GETOUTPUTTIME) / 1000 |
| 63 GetSystemTimeAsFileTime(&ft); | 90 : 0; |
| 64 ULARGE_INTEGER ul; | |
| 65 ul.LowPart = ft.dwLowDateTime; | |
| 66 ul.HighPart = ft.dwHighDateTime; | |
| 67 long long dtn = ((ul.QuadPart - 116444736000000000ULL)/10000000); | |
| 68 | 91 |
| 69 activity.timestamps.start = dtn - SendMessage(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GETOUTPUTTIME) / 1000; | |
| 70 } else { | |
| 71 activity.timestamps.start = 0; | |
| 72 } | |
| 73 | |
| 74 char* details_message = calloc(256, sizeof(char)); | |
| 75 if (config.display_title) { | 92 if (config.display_title) { |
| 76 wchar_t* title = (wchar_t*)SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_PLAYING_TITLE); | 93 wchar_t* title = (wchar_t*)SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_PLAYING_TITLE); |
| 77 assert(WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, title, -1, details_message, 256, NULL, NULL)); | 94 assert(WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, title, -1, activity.details, 256, NULL, NULL)); |
| 78 free(title); | |
| 79 } | 95 } |
| 80 strcpy(activity.details, details_message); | |
| 81 free(details_message); | |
| 82 | 96 |
| 83 app.activities->update_activity(app.activities, &activity, &app, update_activity_callback); | 97 app.activities->update_activity(app.activities, &activity, &app, update_activity_callback); |
| 84 } | 98 } |
| 85 | 99 |
| 86 void report_idle_status(void) | 100 void report_idle_status(void) |
| 91 app.activities->update_activity(app.activities, &activity, &app, update_activity_callback); | 105 app.activities->update_activity(app.activities, &activity, &app, update_activity_callback); |
| 92 } | 106 } |
| 93 | 107 |
| 94 void update_rich_presence_details(void) | 108 void update_rich_presence_details(void) |
| 95 { | 109 { |
| 96 LONG isPlayingResult = SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING); | 110 LONG is_playing = SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING); |
| 97 | 111 |
| 98 switch (isPlayingResult) { | 112 switch (is_playing) { |
| 99 case 0: | 113 case 0: |
| 100 report_idle_status(); | 114 report_idle_status(); |
| 101 break; | 115 break; |
| 102 case 1: | 116 case 1: |
| 103 case 3: | 117 case 3: |
| 104 report_current_song_status(isPlayingResult); | 118 report_current_song_status(is_playing); |
| 105 default: | 119 default: |
| 106 break; | 120 break; |
| 107 } | 121 } |
| 108 } | 122 } |
| 109 | 123 |
| 110 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | 124 void CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD) { |
| 111 { | 125 DISCORD_REQUIRE(app.core->run_callbacks(app.core)); |
| 112 if (message == WM_WA_IPC && lParam == IPC_CB_MISC && wParam == IPC_CB_MISC_STATUS) | |
| 113 { | |
| 114 update_rich_presence_details(); | |
| 115 } | |
| 116 | |
| 117 return CallWindowProc(g_lpWndProcOld, hwnd, message, wParam, lParam); | |
| 118 } | 126 } |
| 119 | 127 |
| 128 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { | |
| 129 if (message == WM_WA_IPC && lParam == IPC_CB_MISC && wParam == IPC_CB_MISC_STATUS) | |
| 130 update_rich_presence_details(); | |
| 131 | |
| 132 return CallWindowProc(_old_wnd_proc, hwnd, message, wParam, lParam); | |
| 133 } | |
| 134 | |
| 135 #define set_wnd_long(x) \ | |
| 136 (WNDPROC)SetWindowLong##x(g_plugin.hwndParent, GWLP_WNDPROC, (LONG)WndProc) | |
| 120 int init() { | 137 int init() { |
| 121 memset(&app, 0, sizeof(app)); | 138 memset(&app, 0, sizeof(app)); |
| 122 if (IsWindowUnicode(g_plugin.hwndParent)) { | |
| 123 g_lpWndProcOld = (WNDPROC)SetWindowLongW(g_plugin.hwndParent, -4, (LONG)WndProc); | |
| 124 } else { | |
| 125 g_lpWndProcOld = (WNDPROC)SetWindowLongA(g_plugin.hwndParent, -4, (LONG)WndProc); | |
| 126 } | |
| 127 | |
| 128 memset(&activity, 0, sizeof(activity)); | 139 memset(&activity, 0, sizeof(activity)); |
| 129 memset(&activities_events, 0, sizeof(activities_events)); | 140 memset(&activities_events, 0, sizeof(activities_events)); |
| 141 | |
| 142 if (IsWindowUnicode(g_plugin.hwndParent)) | |
| 143 _old_wnd_proc = set_wnd_long(W); | |
| 144 else | |
| 145 _old_wnd_proc = set_wnd_long(A); | |
| 130 | 146 |
| 131 struct DiscordCreateParams params; | 147 struct DiscordCreateParams params; |
| 132 DiscordCreateParamsSetDefault(¶ms); | 148 DiscordCreateParamsSetDefault(¶ms); |
| 133 params.client_id = CLIENT_ID; | 149 params.client_id = CLIENT_ID; |
| 134 params.flags = DiscordCreateFlags_Default; | 150 params.flags = DiscordCreateFlags_Default; |
| 148 | 164 |
| 149 update_rich_presence_details(); | 165 update_rich_presence_details(); |
| 150 | 166 |
| 151 return 0; | 167 return 0; |
| 152 } | 168 } |
| 169 #undef set_wnd_long | |
| 153 | 170 |
| 154 void quit() { | 171 void quit() { |
| 155 assert(!cfg_save(config)); | 172 assert(!cfg_save(config)); |
| 156 app.activities->clear_activity(app.activities, &app, update_activity_callback); | 173 app.activities->clear_activity(app.activities, &app, update_activity_callback); |
| 157 } | 174 } |
| 158 | 175 |
| 159 void CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD) { | |
| 160 DISCORD_REQUIRE(app.core->run_callbacks(app.core)); | |
| 161 } | |
| 162 | |
| 163 void conf() { | 176 void conf() { |
| 164 DialogBoxW(g_plugin.hDllInstance, (LPWSTR)DIALOG_CONFIG, g_plugin.hwndParent, (DLGPROC)cfg_win_proc); | 177 DialogBoxW(g_plugin.hDllInstance, (LPWSTR)DIALOG_CONFIG, g_plugin.hwndParent, (DLGPROC)cfg_win_proc); |
| 165 } | 178 } |
| 166 | 179 |
| 167 __declspec(dllexport) winamp_general_purpose_plugin* winampGetGeneralPurposePlugin() { | 180 __declspec(dllexport) struct winamp_gpp* winampGetGeneralPurposePlugin() { |
| 168 return &g_plugin; | 181 return &g_plugin; |
| 169 } | 182 } |
