0
|
1 #include <assert.h>
|
|
2 #include <stdio.h>
|
|
3 #include <stdint.h>
|
|
4 #include <stdlib.h>
|
|
5 #include <time.h>
|
|
6 #include <Winamp/wa_ipc.h>
|
|
7 #include "main.h"
|
|
8 #include "discord_game_sdk.h"
|
|
9 #include "timer.h"
|
|
10 #include "config.h"
|
|
11 #include "dirtools.h"
|
1
|
12 #include "resource.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
|
|
25 winamp_general_purpose_plugin g_plugin = {
|
1
|
26 GPPHDR_VER, // version of the plugin, defined in "gen_myplugin.h"
|
|
27 "Discord GameSDK", // name/title of the plugin, defined in "gen_myplugin.h"
|
|
28 init, // function name which will be executed on init event
|
|
29 conf, // function name which will be executed on config event
|
|
30 quit, // function name which will be executed on quit event
|
|
31 0, // handle to Winamp main window, loaded by winamp when this dll is loaded
|
|
32 0 // hinstance to this dll, loaded by winamp when this dll is loaded
|
0
|
33 };
|
|
34
|
|
35 WNDPROC g_lpWndProcOld = 0;
|
|
36
|
|
37 struct timer_t timer_callbacks = { .interval = 16 };
|
|
38 struct config_t config = {
|
|
39 .display_title = 1,
|
|
40 .show_elapsed_time = 1
|
|
41 };
|
|
42
|
|
43 struct DiscordActivity activity = {
|
|
44 .application_id = CLIENT_ID,
|
|
45 .name = "Winamp",
|
|
46 .instance = 0
|
|
47 };
|
|
48
|
|
49 struct IDiscordActivityEvents activities_events;
|
|
50
|
|
51 struct app_t app;
|
|
52
|
|
53 void update_activity_callback(void* data, enum EDiscordResult result)
|
|
54 {
|
1
|
55 DISCORD_REQUIRE(result);
|
0
|
56 }
|
|
57
|
|
58 void report_current_song_status(int playbackState)
|
|
59 {
|
|
60 assert(playbackState != 0);
|
|
61 activity.timestamps.start = 0;
|
|
62 strcpy(activity.state, playbackState == 1 ? "(Playing)" : "(Paused)");
|
|
63
|
1
|
64 if (playbackState == 1 && config.show_elapsed_time) {
|
0
|
65 FILETIME ft;
|
|
66 GetSystemTimeAsFileTime(&ft);
|
|
67 ULARGE_INTEGER ul;
|
|
68 ul.LowPart = ft.dwLowDateTime;
|
|
69 ul.HighPart = ft.dwHighDateTime;
|
|
70 long long dtn = ((ul.QuadPart - 116444736000000000ULL)/10000000);
|
1
|
71
|
0
|
72 activity.timestamps.start = dtn - SendMessage(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GETOUTPUTTIME) / 1000;
|
|
73 } else {
|
|
74 activity.timestamps.start = 0;
|
|
75 }
|
|
76
|
|
77 char* detailsMessage = calloc(256, sizeof(char));
|
1
|
78 if (config.display_title) {
|
0
|
79 wchar_t* title = (wchar_t*)SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_PLAYING_TITLE);
|
|
80 assert(WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, title, -1, detailsMessage, 256, NULL, NULL));
|
1
|
81 } else {
|
|
82 strcpy(activity.details, "");
|
|
83 }
|
0
|
84 strcpy(activity.details, detailsMessage);
|
|
85 free(detailsMessage);
|
|
86
|
|
87 app.activities->update_activity(app.activities, &activity, &app, update_activity_callback);
|
|
88 }
|
|
89
|
|
90 void report_idle_status(void)
|
|
91 {
|
|
92 activity.timestamps.start = 0;
|
|
93 strcpy(activity.state, "(Idle)");
|
|
94
|
|
95 app.activities->update_activity(app.activities, &activity, &app, update_activity_callback);
|
|
96 }
|
|
97
|
|
98 void update_rich_presence_details(void)
|
|
99 {
|
|
100 LONG isPlayingResult = SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING);
|
|
101
|
|
102 switch (isPlayingResult) {
|
|
103 case 1:
|
|
104 report_current_song_status(1);
|
|
105 break;
|
|
106 case 3:
|
|
107 report_current_song_status(3);
|
|
108 break;
|
|
109 case 0:
|
|
110 report_idle_status();
|
|
111 break;
|
|
112 default: break;
|
|
113 }
|
|
114 }
|
|
115
|
|
116 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
117 {
|
|
118 if (message == WM_WA_IPC && lParam == IPC_CB_MISC && wParam == IPC_CB_MISC_STATUS)
|
|
119 {
|
|
120 update_rich_presence_details();
|
|
121 }
|
|
122
|
|
123 return CallWindowProc(g_lpWndProcOld, hwnd, message, wParam, lParam);
|
|
124 }
|
|
125
|
|
126 int init() {
|
|
127 char* path = dirtools_concat_paths(getenv("APPDATA"), "Winamp\\Plugins\\wgsdk");
|
|
128 printf("%s", path);
|
|
129 memset(&app, 0, sizeof(app));
|
|
130 if (IsWindowUnicode(g_plugin.hwndParent)) {
|
|
131 g_lpWndProcOld = (WNDPROC)SetWindowLongW(g_plugin.hwndParent, -4, (LONG)WndProc);
|
1
|
132 } else {
|
0
|
133 g_lpWndProcOld = (WNDPROC)SetWindowLongA(g_plugin.hwndParent, -4, (LONG)WndProc);
|
|
134 }
|
|
135
|
|
136 memset(&activity, 0, sizeof(activity));
|
1
|
137 memset(&activities_events, 0, sizeof(activities_events));
|
0
|
138
|
|
139 struct DiscordCreateParams params;
|
|
140 DiscordCreateParamsSetDefault(¶ms);
|
|
141 params.client_id = CLIENT_ID;
|
|
142 params.flags = DiscordCreateFlags_Default;
|
|
143 params.event_data = &app;
|
|
144 params.activity_events = &activities_events;
|
|
145
|
|
146 DISCORD_REQUIRE(DiscordCreate(DISCORD_VERSION, ¶ms, &app.core));
|
|
147
|
|
148 app.activities = app.core->get_activity_manager(app.core);
|
|
149
|
|
150 timer_init(&timer_callbacks, g_plugin.hwndParent, TimerProc);
|
|
151 timer_set(&timer_callbacks, g_plugin.hwndParent);
|
|
152
|
|
153 cfg_load(&config);
|
|
154
|
|
155 strcpy(activity.assets.large_image, "winamp-logo");
|
|
156
|
|
157 update_rich_presence_details();
|
|
158
|
|
159 return 0;
|
|
160 }
|
|
161
|
|
162 void quit() {
|
|
163 assert(!cfg_save(config));
|
|
164 app.activities->clear_activity(app.activities, &app, update_activity_callback);
|
|
165 }
|
|
166
|
|
167 void CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD) {
|
|
168 DISCORD_REQUIRE(app.core->run_callbacks(app.core));
|
|
169 }
|
|
170
|
1
|
171 void conf() {
|
|
172 DialogBoxW(g_plugin.hDllInstance, (LPWSTR)DIALOG_CONFIG, g_plugin.hwndParent, &cfg_win_proc);
|
|
173 }
|
|
174
|
0
|
175 __declspec(dllexport) winamp_general_purpose_plugin* winampGetGeneralPurposePlugin() {
|
|
176 return &g_plugin;
|
|
177 }
|