7
|
1 /**
|
|
2 * wgsdk - Winamp plugin for Discord's GameSDK
|
|
3 **/
|
0
|
4 #include <assert.h>
|
|
5 #include <stdio.h>
|
|
6 #include <stdlib.h>
|
|
7 #include <Winamp/wa_ipc.h>
|
|
8 #include "discord_game_sdk.h"
|
|
9 #include "timer.h"
|
|
10 #include "config.h"
|
1
|
11 #include "resource.h"
|
7
|
12 #include "utils.h"
|
0
|
13 #ifndef WIN32_LEAN_AND_MEAN
|
|
14 # define WIN32_LEAN_AND_MEAN
|
|
15 #endif
|
|
16 #include <windows.h>
|
1
|
17 #include <windowsx.h>
|
0
|
18
|
|
19 #define CLIENT_ID 969367220599803955
|
|
20 #define DISCORD_REQUIRE(x) \
|
|
21 assert(x == DiscordResult_Ok)
|
|
22
|
|
23 #define GPPHDR_VER 0x10
|
|
24
|
7
|
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 = {
|
1
|
41 GPPHDR_VER, // version of the plugin, defined in "gen_myplugin.h"
|
|
42 "Discord GameSDK", // name/title of the plugin, defined in "gen_myplugin.h"
|
|
43 init, // function name which will be executed on init event
|
|
44 conf, // function name which will be executed on config event
|
|
45 quit, // function name which will be executed on quit event
|
|
46 0, // handle to Winamp main window, loaded by winamp when this dll is loaded
|
|
47 0 // hinstance to this dll, loaded by winamp when this dll is loaded
|
0
|
48 };
|
|
49
|
7
|
50 /* Discord stuff */
|
0
|
51
|
|
52 struct DiscordActivity activity = {
|
|
53 .application_id = CLIENT_ID,
|
|
54 .name = "Winamp",
|
|
55 .instance = 0
|
|
56 };
|
|
57
|
|
58 struct IDiscordActivityEvents activities_events;
|
|
59
|
7
|
60 struct application {
|
|
61 struct IDiscordCore* core;
|
|
62 struct IDiscordUsers* users;
|
|
63 struct IDiscordActivityManager* activities;
|
|
64 } app;
|
|
65
|
|
66 /* Now we get to our built-in stuff */
|
|
67
|
|
68 struct timer timer_callbacks = { .interval = 16 };
|
0
|
69
|
7
|
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) {
|
1
|
81 DISCORD_REQUIRE(result);
|
0
|
82 }
|
|
83
|
7
|
84 void report_current_song_status(int playback_state) {
|
|
85 assert(playback_state != 0);
|
|
86 strcpy(activity.state, playback_state == 1 ? "(Playing)" : "(Paused)");
|
0
|
87
|
7
|
88 activity.timestamps.start = (playback_state == 1 && config.show_elapsed_time)
|
|
89 ? get_system_time_in_milliseconds() - SendMessage(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GETOUTPUTTIME) / 1000
|
|
90 : 0;
|
1
|
91
|
|
92 if (config.display_title) {
|
0
|
93 wchar_t* title = (wchar_t*)SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_PLAYING_TITLE);
|
7
|
94 assert(WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, title, -1, activity.details, 256, NULL, NULL));
|
1
|
95 }
|
0
|
96
|
|
97 app.activities->update_activity(app.activities, &activity, &app, update_activity_callback);
|
|
98 }
|
|
99
|
|
100 void report_idle_status(void)
|
|
101 {
|
|
102 activity.timestamps.start = 0;
|
|
103 strcpy(activity.state, "(Idle)");
|
|
104
|
|
105 app.activities->update_activity(app.activities, &activity, &app, update_activity_callback);
|
|
106 }
|
|
107
|
|
108 void update_rich_presence_details(void)
|
|
109 {
|
7
|
110 LONG is_playing = SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING);
|
0
|
111
|
7
|
112 switch (is_playing) {
|
0
|
113 case 0:
|
|
114 report_idle_status();
|
|
115 break;
|
4
|
116 case 1:
|
|
117 case 3:
|
7
|
118 report_current_song_status(is_playing);
|
4
|
119 default:
|
|
120 break;
|
0
|
121 }
|
|
122 }
|
|
123
|
7
|
124 void CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD) {
|
|
125 DISCORD_REQUIRE(app.core->run_callbacks(app.core));
|
|
126 }
|
|
127
|
|
128 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
0
|
129 if (message == WM_WA_IPC && lParam == IPC_CB_MISC && wParam == IPC_CB_MISC_STATUS)
|
|
130 update_rich_presence_details();
|
|
131
|
7
|
132 return CallWindowProc(_old_wnd_proc, hwnd, message, wParam, lParam);
|
0
|
133 }
|
|
134
|
7
|
135 #define set_wnd_long(x) \
|
|
136 (WNDPROC)SetWindowLong##x(g_plugin.hwndParent, GWLP_WNDPROC, (LONG)WndProc)
|
0
|
137 int init() {
|
|
138 memset(&app, 0, sizeof(app));
|
|
139 memset(&activity, 0, sizeof(activity));
|
1
|
140 memset(&activities_events, 0, sizeof(activities_events));
|
0
|
141
|
7
|
142 if (IsWindowUnicode(g_plugin.hwndParent))
|
|
143 _old_wnd_proc = set_wnd_long(W);
|
|
144 else
|
|
145 _old_wnd_proc = set_wnd_long(A);
|
|
146
|
0
|
147 struct DiscordCreateParams params;
|
|
148 DiscordCreateParamsSetDefault(¶ms);
|
|
149 params.client_id = CLIENT_ID;
|
|
150 params.flags = DiscordCreateFlags_Default;
|
|
151 params.event_data = &app;
|
|
152 params.activity_events = &activities_events;
|
|
153
|
|
154 DISCORD_REQUIRE(DiscordCreate(DISCORD_VERSION, ¶ms, &app.core));
|
|
155
|
|
156 app.activities = app.core->get_activity_manager(app.core);
|
|
157
|
|
158 timer_init(&timer_callbacks, g_plugin.hwndParent, TimerProc);
|
|
159 timer_set(&timer_callbacks, g_plugin.hwndParent);
|
|
160
|
|
161 cfg_load(&config);
|
|
162
|
|
163 strcpy(activity.assets.large_image, "winamp-logo");
|
|
164
|
|
165 update_rich_presence_details();
|
|
166
|
|
167 return 0;
|
|
168 }
|
7
|
169 #undef set_wnd_long
|
0
|
170
|
|
171 void quit() {
|
|
172 assert(!cfg_save(config));
|
|
173 app.activities->clear_activity(app.activities, &app, update_activity_callback);
|
|
174 }
|
|
175
|
1
|
176 void conf() {
|
4
|
177 DialogBoxW(g_plugin.hDllInstance, (LPWSTR)DIALOG_CONFIG, g_plugin.hwndParent, (DLGPROC)cfg_win_proc);
|
1
|
178 }
|
|
179
|
7
|
180 __declspec(dllexport) struct winamp_gpp* winampGetGeneralPurposePlugin() {
|
0
|
181 return &g_plugin;
|
|
182 }
|