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(&params); 148 DiscordCreateParamsSetDefault(&params);
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 }