Mercurial > wgsdk
view src/plugin.c @ 12:dd427b7cc459 default tip
json: replace with nxjson library
more lightweight, reduces the binary size by about 40 kb
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Fri, 15 Mar 2024 20:46:18 -0400 |
parents | e6a594f16403 |
children |
line wrap: on
line source
#include "plugin.h" #include "deezer.h" #include "timer.h" #include "config.h" #include "resource.h" #include "utils.h" #include "dialog/dlg_config.h" /* ugh. */ #ifndef _MSC_VER #define _MSC_VER 1201 #define WGSDK_UGLY_MSC_HACK 1 #endif #include <Winamp/wa_ipc.h> #ifdef WGSDK_UGLY_MSC_HACK #undef _MSC_VER #endif #include "discord_game_sdk.h" #include <assert.h> #define CLIENT_ID (969367220599803955) #define GPPHDR_VER (0x10) #define METADATA_ITEM_SIZE (256) int init(); void conf(); void quit(); struct winamp_gpp g_plugin = { GPPHDR_VER, // version of the plugin, DO NOT change "Discord GameSDK", // name of the plugin init, // function pointer, executed on init event conf, // function pointer, executed on config event quit, // function pointer, executed on quit event NULL, // Winamp main window HWND, loaded by Winamp NULL // HINSTANCE to this DLL, loaded by Winamp }; /* Discord stuff */ struct application { struct IDiscordCore* core; struct IDiscordActivityManager* activities; }; static struct application app = {0}; /* Now we get to our built-in stuff */ struct timer timer_callbacks = {0}; WNDPROC _winamp_proc = NULL; /* ------------------------------------ */ void DISCORD_CALLBACK update_activity_callback(void* data, enum EDiscordResult result) { /* no-op */ } /* a bunch of spaghetti... */ void report_current_song_status(int playback_state) { /* struct to be filled by us */ struct DiscordActivity activity = {0}; assert(playback_state != 0); activity.application_id = CLIENT_ID; strcpy(activity.name, "Winamp"); if (config.show_elapsed_time && playback_state == 1) { /* don't even bother trying to get the elapsed time. */ activity.timestamps.start = get_system_time_in_milliseconds(); /* this is how we can get the end timestamp if we want to: * * LRESULT track_length = SendMessage(g_plugin.hwndParent, WM_WA_IPC, 2, IPC_GETOUTPUTTIME); * activity.timestamps.end = get_system_time_in_milliseconds() + track_length; */ } LPCWSTR filename = (LPCWSTR)SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_PLAYING_FILENAME); WCHAR title[METADATA_ITEM_SIZE + 1] = {L'\0'}; extendedFileInfoStructW file_info = {filename, L"title", title, METADATA_ITEM_SIZE}; /* please excuse the spaghetti here, I swear it wasn't this bad before */ int have_metadata = SendMessageW(g_plugin.hwndParent, WM_WA_IPC, (WPARAM)&file_info, IPC_GET_EXTENDED_FILE_INFOW); if (have_metadata) { WCHAR album[METADATA_ITEM_SIZE + 1] = {L'\0'}; WCHAR artist[METADATA_ITEM_SIZE + 1] = {L'\0'}; /* grab artist info */ file_info.metadata = L"artist"; file_info.ret = artist; SendMessageW(g_plugin.hwndParent, WM_WA_IPC, (WPARAM)&file_info, IPC_GET_EXTENDED_FILE_INFOW); if (artist[0] == '\0') { /* fallback to album artist */ file_info.metadata = L"album artist"; SendMessageW(g_plugin.hwndParent, WM_WA_IPC, (WPARAM)&file_info, IPC_GET_EXTENDED_FILE_INFOW); } /* grab album info */ file_info.metadata = L"album"; file_info.ret = album; SendMessageW(g_plugin.hwndParent, WM_WA_IPC, (WPARAM)&file_info, IPC_GET_EXTENDED_FILE_INFOW); /* get thumbnail URL */ if (config.display_song_info && config.display_album_art) { char* image_url = deezer_get_thumbnail(artist, album); strncpy(activity.assets.large_image, image_url ? image_url : "winamp-logo", ARRAYSIZE(activity.assets.large_image) - 1); free(image_url); /* freeing NULL is a no-op */ } /* do NOT use something like ARRAYSIZE here, we need the size of the array *in bytes* */ do { size_t activity_details_offset = 0; if (artist[0] && config.display_song_info && config.display_artist_name) { size_t off = append_wstr_to_utf8(artist, activity.details, activity_details_offset, sizeof(activity.details)); if (!off) break; else activity_details_offset += off; } if (artist[0] && album[0] && config.display_song_info && config.display_artist_name && config.display_album_name) { LPCWSTR delimiter = L" - "; size_t off = append_wstr_to_utf8(delimiter, activity.details, activity_details_offset, sizeof(activity.details)); if (!off) break; else activity_details_offset += off; } if (album[0] && config.display_song_info && config.display_album_name) { size_t off = append_wstr_to_utf8(album, activity.details, activity_details_offset, sizeof(activity.details)); if (!off) break; else activity_details_offset += off; } } while (0); } else { /* fallback to basic info */ wchar_t* winamp_title = (wchar_t*)SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_PLAYING_TITLE); wcsncpy(title, winamp_title, METADATA_ITEM_SIZE); } size_t activity_state_offset = 0; do { size_t off = 0; if (title[0] && config.display_song_info && config.display_title) { off = append_wstr_to_utf8(title, activity.state, activity_state_offset, sizeof(activity.state)); if (!off) break; else activity_state_offset += off; LPCWSTR title_delimiter = L" "; off = append_wstr_to_utf8(title_delimiter, activity.state, activity_state_offset, sizeof(activity.state)); if (!off) break; else activity_state_offset += off; } LPCWSTR title_playing = (playback_state == 1) ? L"(Playing)" : L"(Stopped)"; off = append_wstr_to_utf8(title_playing, activity.state, activity_state_offset, sizeof(activity.state)); if (!off) break; else activity_state_offset += off; } while (0); app.activities->update_activity(app.activities, &activity, &app, update_activity_callback); } void report_idle_status(void) { struct DiscordActivity activity = {0}; activity.application_id = CLIENT_ID; strcpy(activity.name, "Winamp"); strcpy(activity.state, "(Idle)"); strcpy(activity.assets.large_image, "winamp-logo"); app.activities->update_activity(app.activities, &activity, &app, update_activity_callback); } void update_rich_presence_details(void) { LONG is_playing = SendMessageW(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING); switch (is_playing) { case 0: report_idle_status(); break; case 1: case 3: report_current_song_status(is_playing); break; default: break; } } void CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD) { app.core->run_callbacks(app.core); } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_WA_IPC && lParam == IPC_CB_MISC && wParam == IPC_CB_MISC_STATUS) update_rich_presence_details(); return CallWindowProc(_winamp_proc, hwnd, message, wParam, lParam); } int init() { struct DiscordCreateParams params = {0}; DiscordCreateParamsSetDefault(¶ms); params.client_id = CLIENT_ID; params.flags = DiscordCreateFlags_Default; params.event_data = &app; if (DiscordCreate(DISCORD_VERSION, ¶ms, &app.core) != DiscordResult_Ok) return -1; /* don't do this if we don't have discord */ _winamp_proc = (IsWindowUnicode(g_plugin.hwndParent)) ? (WNDPROC)SetWindowLongPtrW(g_plugin.hwndParent, GWLP_WNDPROC, (LONG_PTR)WndProc) : (WNDPROC)SetWindowLongPtrA(g_plugin.hwndParent, GWLP_WNDPROC, (LONG_PTR)WndProc); app.activities = app.core->get_activity_manager(app.core); timer_init(&timer_callbacks, 16, TimerProc); timer_set(&timer_callbacks); cfg_load(&config); update_rich_presence_details(); return 0; } void quit() { assert(!cfg_save(&config)); app.activities->clear_activity(app.activities, &app, update_activity_callback); timer_stop(&timer_callbacks); close_open_http_handles(); } void conf() { DialogBoxW(g_plugin.hDllInstance, (LPWSTR)DIALOG_CONFIG, g_plugin.hwndParent, (DLGPROC)cfg_win_proc); } __declspec(dllexport) struct winamp_gpp* winampGetGeneralPurposePlugin() { return &g_plugin; }