From 634f6ea9f5e0909c288c1c59104428a5ed4b6f8d Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Fri, 11 Apr 2025 23:27:40 +0200 Subject: [PATCH] discord-rpc: Formatting --- 3rdparty/discord-rpc/CMakeLists.txt | 42 +++++++++- .../{src/backoff.h => discord_backoff.h} | 0 .../connection.h => discord_connection.h} | 2 +- ...n_unix.cpp => discord_connection_unix.cpp} | 34 ++++++-- ...ion_win.cpp => discord_connection_win.cpp} | 26 ++++-- .../{src/msg_queue.h => discord_msg_queue.h} | 0 .../{include => }/discord_register.h | 1 - .../{src => }/discord_register_linux.cpp | 24 ++---- 3rdparty/discord-rpc/discord_register_osx.m | 76 +++++++++++++++++ .../{src => }/discord_register_win.cpp | 78 +++++------------ .../discord-rpc/{src => }/discord_rpc.cpp | 84 ++++++++++--------- .../discord-rpc/{include => }/discord_rpc.h | 11 +-- ...nection.cpp => discord_rpc_connection.cpp} | 23 +++-- ..._connection.h => discord_rpc_connection.h} | 7 +- ...lization.cpp => discord_serialization.cpp} | 29 +++++-- ...erialization.h => discord_serialization.h} | 19 +++-- 3rdparty/discord-rpc/src/CMakeLists.txt | 41 --------- .../discord-rpc/src/discord_register_osx.m | 80 ------------------ CMakeLists.txt | 2 +- src/discord/richpresence.cpp | 2 +- 20 files changed, 291 insertions(+), 290 deletions(-) rename 3rdparty/discord-rpc/{src/backoff.h => discord_backoff.h} (100%) rename 3rdparty/discord-rpc/{src/connection.h => discord_connection.h} (94%) rename 3rdparty/discord-rpc/{src/connection_unix.cpp => discord_connection_unix.cpp} (82%) rename 3rdparty/discord-rpc/{src/connection_win.cpp => discord_connection_win.cpp} (89%) rename 3rdparty/discord-rpc/{src/msg_queue.h => discord_msg_queue.h} (100%) rename 3rdparty/discord-rpc/{include => }/discord_register.h (65%) rename 3rdparty/discord-rpc/{src => }/discord_register_linux.cpp (77%) create mode 100644 3rdparty/discord-rpc/discord_register_osx.m rename 3rdparty/discord-rpc/{src => }/discord_register_win.cpp (63%) rename 3rdparty/discord-rpc/{src => }/discord_rpc.cpp (89%) rename 3rdparty/discord-rpc/{include => }/discord_rpc.h (86%) rename 3rdparty/discord-rpc/{src/rpc_connection.cpp => discord_rpc_connection.cpp} (93%) rename 3rdparty/discord-rpc/{src/rpc_connection.h => discord_rpc_connection.h} (92%) rename 3rdparty/discord-rpc/{src/serialization.cpp => discord_serialization.cpp} (93%) rename 3rdparty/discord-rpc/{src/serialization.h => discord_serialization.h} (92%) delete mode 100644 3rdparty/discord-rpc/src/CMakeLists.txt delete mode 100644 3rdparty/discord-rpc/src/discord_register_osx.m diff --git a/3rdparty/discord-rpc/CMakeLists.txt b/3rdparty/discord-rpc/CMakeLists.txt index febd4f0ab..b86973762 100644 --- a/3rdparty/discord-rpc/CMakeLists.txt +++ b/3rdparty/discord-rpc/CMakeLists.txt @@ -1 +1,41 @@ -add_subdirectory(src) +set(DISCORD_RPC_SOURCES + discord_rpc.h + discord_register.h + discord_rpc.cpp + discord_rpc_connection.h + discord_rpc_connection.cpp + discord_serialization.h + discord_serialization.cpp + discord_connection.h + discord_backoff.h + discord_msg_queue.h +) + +if(UNIX) + list(APPEND DISCORD_RPC_SOURCES discord_connection_unix.cpp) + if(APPLE) + list(APPEND DISCORD_RPC_SOURCES discord_register_osx.m) + add_definitions(-DDISCORD_OSX) + else() + list(APPEND DISCORD_RPC_SOURCES discord_register_linux.cpp) + add_definitions(-DDISCORD_LINUX) + endif() +endif() + +if(WIN32) + list(APPEND DISCORD_RPC_SOURCES discord_connection_win.cpp discord_register_win.cpp) + add_definitions(-DDISCORD_WINDOWS) +endif() + +add_library(discord-rpc STATIC ${DISCORD_RPC_SOURCES}) + +if(APPLE) + target_link_libraries(discord-rpc PRIVATE "-framework AppKit") +endif() + +if(WIN32) + target_link_libraries(discord-rpc PRIVATE psapi advapi32) +endif() + +target_include_directories(discord-rpc SYSTEM PRIVATE ${RapidJSON_INCLUDE_DIRS}) +target_include_directories(discord-rpc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/3rdparty/discord-rpc/src/backoff.h b/3rdparty/discord-rpc/discord_backoff.h similarity index 100% rename from 3rdparty/discord-rpc/src/backoff.h rename to 3rdparty/discord-rpc/discord_backoff.h diff --git a/3rdparty/discord-rpc/src/connection.h b/3rdparty/discord-rpc/discord_connection.h similarity index 94% rename from 3rdparty/discord-rpc/src/connection.h rename to 3rdparty/discord-rpc/discord_connection.h index 153a498bc..49548c7a8 100644 --- a/3rdparty/discord-rpc/src/connection.h +++ b/3rdparty/discord-rpc/discord_connection.h @@ -12,7 +12,7 @@ int GetProcessId(); struct BaseConnection { static BaseConnection *Create(); static void Destroy(BaseConnection *&); - bool isOpen { false }; + bool isOpen = false; bool Open(); bool Close(); bool Write(const void *data, size_t length); diff --git a/3rdparty/discord-rpc/src/connection_unix.cpp b/3rdparty/discord-rpc/discord_connection_unix.cpp similarity index 82% rename from 3rdparty/discord-rpc/src/connection_unix.cpp rename to 3rdparty/discord-rpc/discord_connection_unix.cpp index ff65cd632..fcfb742a2 100644 --- a/3rdparty/discord-rpc/src/connection_unix.cpp +++ b/3rdparty/discord-rpc/discord_connection_unix.cpp @@ -1,4 +1,4 @@ -#include "connection.h" +#include "discord_connection.h" #include #include @@ -28,28 +28,34 @@ static int MsgFlags = 0; #endif static const char *GetTempPath() { + const char *temp = getenv("XDG_RUNTIME_DIR"); temp = temp ? temp : getenv("TMPDIR"); temp = temp ? temp : getenv("TMP"); temp = temp ? temp : getenv("TEMP"); temp = temp ? temp : "/tmp"; + return temp; + } -/*static*/ BaseConnection *BaseConnection::Create() { +BaseConnection *BaseConnection::Create() { PipeAddr.sun_family = AF_UNIX; return &Connection; } -/*static*/ void BaseConnection::Destroy(BaseConnection *&c) { - auto self = reinterpret_cast(c); +void BaseConnection::Destroy(BaseConnection *&c) { + + auto self = reinterpret_cast(c); self->Close(); c = nullptr; + } bool BaseConnection::Open() { + const char *tempPath = GetTempPath(); - auto self = reinterpret_cast(this); + auto self = reinterpret_cast(this); self->sock = socket(AF_UNIX, SOCK_STREAM, 0); if (self->sock == -1) { return false; @@ -61,8 +67,7 @@ bool BaseConnection::Open() { #endif for (int pipeNum = 0; pipeNum < 10; ++pipeNum) { - snprintf( - PipeAddr.sun_path, sizeof(PipeAddr.sun_path), "%s/discord-ipc-%d", tempPath, pipeNum); + snprintf(PipeAddr.sun_path, sizeof(PipeAddr.sun_path), "%s/discord-ipc-%d", tempPath, pipeNum); int err = connect(self->sock, reinterpret_cast(&PipeAddr), sizeof(PipeAddr)); if (err == 0) { self->isOpen = true; @@ -70,10 +75,13 @@ bool BaseConnection::Open() { } } self->Close(); + return false; + } bool BaseConnection::Close() { + auto self = reinterpret_cast(this); if (self->sock == -1) { return false; @@ -81,11 +89,14 @@ bool BaseConnection::Close() { close(self->sock); self->sock = -1; self->isOpen = false; + return true; + } bool BaseConnection::Write(const void *data, size_t length) { - auto self = reinterpret_cast(this); + + auto self = reinterpret_cast(this); if (self->sock == -1) { return false; @@ -95,11 +106,14 @@ bool BaseConnection::Write(const void *data, size_t length) { if (sentBytes < 0) { Close(); } + return sentBytes == static_cast(length); + } bool BaseConnection::Read(void *data, size_t length) { - auto self = reinterpret_cast(this); + + auto self = reinterpret_cast(this); if (self->sock == -1) { return false; @@ -115,7 +129,9 @@ bool BaseConnection::Read(void *data, size_t length) { else if (res == 0) { Close(); } + return static_cast(res) == length; + } } // namespace discord_rpc diff --git a/3rdparty/discord-rpc/src/connection_win.cpp b/3rdparty/discord-rpc/discord_connection_win.cpp similarity index 89% rename from 3rdparty/discord-rpc/src/connection_win.cpp rename to 3rdparty/discord-rpc/discord_connection_win.cpp index 3d6e4f915..698e0d0e7 100644 --- a/3rdparty/discord-rpc/src/connection_win.cpp +++ b/3rdparty/discord-rpc/discord_connection_win.cpp @@ -1,9 +1,10 @@ -#include "connection.h" +#include "discord_connection.h" #define WIN32_LEAN_AND_MEAN #define NOMCX #define NOSERVICE #define NOIME + #include #include @@ -19,24 +20,26 @@ struct BaseConnectionWin : public BaseConnection { static BaseConnectionWin Connection; -/*static*/ BaseConnection *BaseConnection::Create() { +BaseConnection *BaseConnection::Create() { return &Connection; } -/*static*/ void BaseConnection::Destroy(BaseConnection *&c) { +void BaseConnection::Destroy(BaseConnection *&c) { + auto self = reinterpret_cast(c); self->Close(); c = nullptr; + } bool BaseConnection::Open() { + wchar_t pipeName[] { L"\\\\?\\pipe\\discord-ipc-0" }; const size_t pipeDigit = sizeof(pipeName) / sizeof(wchar_t) - 2; pipeName[pipeDigit] = L'0'; auto self = reinterpret_cast(this); for (;;) { - self->pipe = ::CreateFileW( - pipeName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); + self->pipe = ::CreateFileW(pipeName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (self->pipe != INVALID_HANDLE_VALUE) { self->isOpen = true; return true; @@ -57,17 +60,22 @@ bool BaseConnection::Open() { } return false; } + } bool BaseConnection::Close() { + auto self = reinterpret_cast(this); ::CloseHandle(self->pipe); self->pipe = INVALID_HANDLE_VALUE; self->isOpen = false; + return true; + } bool BaseConnection::Write(const void *data, size_t length) { + if (length == 0) { return true; } @@ -85,11 +93,13 @@ bool BaseConnection::Write(const void *data, size_t length) { } const DWORD bytesLength = static_cast(length); DWORD bytesWritten = 0; - return ::WriteFile(self->pipe, data, bytesLength, &bytesWritten, nullptr) == TRUE && - bytesWritten == bytesLength; + + return ::WriteFile(self->pipe, data, bytesLength, &bytesWritten, nullptr) == TRUE && bytesWritten == bytesLength; + } bool BaseConnection::Read(void *data, size_t length) { + assert(data); if (!data) { return false; @@ -119,7 +129,9 @@ bool BaseConnection::Read(void *data, size_t length) { else { Close(); } + return false; + } } // namespace discord_rpc diff --git a/3rdparty/discord-rpc/src/msg_queue.h b/3rdparty/discord-rpc/discord_msg_queue.h similarity index 100% rename from 3rdparty/discord-rpc/src/msg_queue.h rename to 3rdparty/discord-rpc/discord_msg_queue.h diff --git a/3rdparty/discord-rpc/include/discord_register.h b/3rdparty/discord-rpc/discord_register.h similarity index 65% rename from 3rdparty/discord-rpc/include/discord_register.h rename to 3rdparty/discord-rpc/discord_register.h index 0021b6bbe..c8e29755a 100644 --- a/3rdparty/discord-rpc/include/discord_register.h +++ b/3rdparty/discord-rpc/discord_register.h @@ -5,7 +5,6 @@ extern "C" { #endif void Discord_Register(const char *applicationId, const char *command); -void Discord_RegisterSteamGame(const char *applicationId, const char *steamId); #ifdef __cplusplus } diff --git a/3rdparty/discord-rpc/src/discord_register_linux.cpp b/3rdparty/discord-rpc/discord_register_linux.cpp similarity index 77% rename from 3rdparty/discord-rpc/src/discord_register_linux.cpp rename to 3rdparty/discord-rpc/discord_register_linux.cpp index ef77f943d..855e75be6 100644 --- a/3rdparty/discord-rpc/src/discord_register_linux.cpp +++ b/3rdparty/discord-rpc/discord_register_linux.cpp @@ -24,8 +24,9 @@ static bool Mkdir(const char *path) { } // namespace -// we want to register games so we can run them from Discord client as discord-:// +// We want to register games so we can run them from Discord client as discord-:// extern "C" void Discord_Register(const char *applicationId, const char *command) { + // Add a desktop file and update some mime handlers so that xdg-open does the right thing. const char *home = getenv("HOME"); @@ -33,9 +34,9 @@ extern "C" void Discord_Register(const char *applicationId, const char *command) return; } - char exePath[1024]; + char exePath[1024]{}; if (!command || !command[0]) { - ssize_t size = readlink("/proc/self/exe", exePath, sizeof(exePath)); + const ssize_t size = readlink("/proc/self/exe", exePath, sizeof(exePath)); if (size <= 0 || size >= static_cast(sizeof(exePath))) { return; } @@ -50,17 +51,16 @@ extern "C" void Discord_Register(const char *applicationId, const char *command) "NoDisplay=true\n" "Categories=Discord;Games;\n" "MimeType=x-scheme-handler/discord-%s;\n"; - char desktopFile[2048]; - int fileLen = snprintf( - desktopFile, sizeof(desktopFile), desktopFileFormat, applicationId, command, applicationId); + char desktopFile[2048]{}; + int fileLen = snprintf(desktopFile, sizeof(desktopFile), desktopFileFormat, applicationId, command, applicationId); if (fileLen <= 0) { return; } - char desktopFilename[256]; + char desktopFilename[256]{}; (void)snprintf(desktopFilename, sizeof(desktopFilename), "/discord-%s.desktop", applicationId); - char desktopFilePath[1024]; + char desktopFilePath[1024]{}; (void)snprintf(desktopFilePath, sizeof(desktopFilePath), "%s/.local", home); if (!Mkdir(desktopFilePath)) { return; @@ -84,7 +84,7 @@ extern "C" void Discord_Register(const char *applicationId, const char *command) return; } - char xdgMimeCommand[1024]; + char xdgMimeCommand[1024]{}; snprintf(xdgMimeCommand, sizeof(xdgMimeCommand), "xdg-mime default discord-%s.desktop x-scheme-handler/discord-%s", @@ -93,11 +93,5 @@ extern "C" void Discord_Register(const char *applicationId, const char *command) if (system(xdgMimeCommand) < 0) { fprintf(stderr, "Failed to register mime handler\n"); } -} -extern "C" void Discord_RegisterSteamGame(const char *applicationId, - const char *steamId) { - char command[256]; - sprintf(command, "xdg-open steam://rungameid/%s", steamId); - Discord_Register(applicationId, command); } diff --git a/3rdparty/discord-rpc/discord_register_osx.m b/3rdparty/discord-rpc/discord_register_osx.m new file mode 100644 index 000000000..663f73c15 --- /dev/null +++ b/3rdparty/discord-rpc/discord_register_osx.m @@ -0,0 +1,76 @@ +#include +#include + +#import + +#include "discord_register.h" + +static void RegisterCommand(const char *applicationId, const char *command) { + + // There does not appear to be a way to register arbitrary commands on OSX, so instead we'll save the command + // to a file in the Discord config path, and when it is needed, Discord can try to load the file there, open + // the command therein (will pass to js's window.open, so requires a url-like thing) + + // Note: will not work for sandboxed apps + NSString *home = NSHomeDirectory(); + if (!home) { + return; + } + + NSString *path = [[[[[[home stringByAppendingPathComponent:@"Library"] + stringByAppendingPathComponent:@"Application Support"] + stringByAppendingPathComponent:@"discord"] + stringByAppendingPathComponent:@"games"] + stringByAppendingPathComponent:[NSString stringWithUTF8String:applicationId]] + stringByAppendingPathExtension:@"json"]; + [[NSFileManager defaultManager] createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil]; + + NSString *jsonBuffer = [NSString stringWithFormat:@"{\"command\": \"%s\"}", command]; + [jsonBuffer writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:nil]; + +} + +static void RegisterURL(const char *applicationId) { + + char url[256]; + snprintf(url, sizeof(url), "discord-%s", applicationId); + CFStringRef cfURL = CFStringCreateWithCString(NULL, url, kCFStringEncodingUTF8); + + NSString* myBundleId = [[NSBundle mainBundle] bundleIdentifier]; + if (!myBundleId) { + fprintf(stderr, "No bundle id found\n"); + return; + } + + NSURL* myURL = [[NSBundle mainBundle] bundleURL]; + if (!myURL) { + fprintf(stderr, "No bundle url found\n"); + return; + } + + OSStatus status = LSSetDefaultHandlerForURLScheme(cfURL, (__bridge CFStringRef)myBundleId); + if (status != noErr) { + fprintf(stderr, "Error in LSSetDefaultHandlerForURLScheme: %d\n", (int)status); + return; + } + + status = LSRegisterURL((__bridge CFURLRef)myURL, true); + if (status != noErr) { + fprintf(stderr, "Error in LSRegisterURL: %d\n", (int)status); + } + +} + +void Discord_Register(const char *applicationId, const char *command) { + + if (command) { + RegisterCommand(applicationId, command); + } + else { + // raii lite + @autoreleasepool { + RegisterURL(applicationId); + } + } + +} diff --git a/3rdparty/discord-rpc/src/discord_register_win.cpp b/3rdparty/discord-rpc/discord_register_win.cpp similarity index 63% rename from 3rdparty/discord-rpc/src/discord_register_win.cpp rename to 3rdparty/discord-rpc/discord_register_win.cpp index dba4ec162..8c3ccf0e3 100644 --- a/3rdparty/discord-rpc/src/discord_register_win.cpp +++ b/3rdparty/discord-rpc/discord_register_win.cpp @@ -46,12 +46,8 @@ static HRESULT StringCbPrintfW(LPWSTR pszDest, size_t cbDest, LPCWSTR pszFormat, #endif #define RegSetKeyValueW regset -static LSTATUS regset(HKEY hkey, - LPCWSTR subkey, - LPCWSTR name, - DWORD type, - const void *data, - DWORD len) { +static LSTATUS regset(HKEY hkey, LPCWSTR subkey, LPCWSTR name, DWORD type, const void *data, DWORD len) { + HKEY htkey = hkey, hsubkey = nullptr; LSTATUS ret; if (subkey && subkey[0]) { @@ -64,16 +60,18 @@ static LSTATUS regset(HKEY hkey, if (hsubkey && hsubkey != hkey) RegCloseKey(hsubkey); return ret; + } static void Discord_RegisterW(const wchar_t *applicationId, const wchar_t *command) { + // https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx // we want to register games so we can run them as discord-:// // Update the HKEY_CURRENT_USER, because it doesn't seem to require special permissions. - wchar_t exeFilePath[MAX_PATH]; + wchar_t exeFilePath[MAX_PATH]{}; DWORD exeLen = GetModuleFileNameW(nullptr, exeFilePath, MAX_PATH); - wchar_t openCommand[1024]; + wchar_t openCommand[1024]{}; if (command && command[0]) { StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command); @@ -83,18 +81,16 @@ static void Discord_RegisterW(const wchar_t *applicationId, const wchar_t *comma StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", exeFilePath); } - wchar_t protocolName[64]; + wchar_t protocolName[64]{}; StringCbPrintfW(protocolName, sizeof(protocolName), L"discord-%s", applicationId); - wchar_t protocolDescription[128]; - StringCbPrintfW( - protocolDescription, sizeof(protocolDescription), L"URL:Run game %s protocol", applicationId); + wchar_t protocolDescription[128]{}; + StringCbPrintfW(protocolDescription, sizeof(protocolDescription), L"URL:Run game %s protocol", applicationId); wchar_t urlProtocol = 0; - wchar_t keyName[256]; + wchar_t keyName[256]{}; StringCbPrintfW(keyName, sizeof(keyName), L"Software\\Classes\\%s", protocolName); HKEY key; - auto status = - RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, nullptr, 0, KEY_WRITE, nullptr, &key, nullptr); + auto status = RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, nullptr, 0, KEY_WRITE, nullptr, &key, nullptr); if (status != ERROR_SUCCESS) { fprintf(stderr, "Error creating key\n"); return; @@ -102,8 +98,7 @@ static void Discord_RegisterW(const wchar_t *applicationId, const wchar_t *comma DWORD len; LSTATUS result; len = static_cast(lstrlenW(protocolDescription) + 1); - result = - RegSetKeyValueW(key, nullptr, nullptr, REG_SZ, protocolDescription, len * sizeof(wchar_t)); + result = RegSetKeyValueW(key, nullptr, nullptr, REG_SZ, protocolDescription, len * sizeof(wchar_t)); if (FAILED(result)) { fprintf(stderr, "Error writing description\n"); } @@ -114,26 +109,26 @@ static void Discord_RegisterW(const wchar_t *applicationId, const wchar_t *comma fprintf(stderr, "Error writing description\n"); } - result = RegSetKeyValueW( - key, L"DefaultIcon", nullptr, REG_SZ, exeFilePath, (exeLen + 1) * sizeof(wchar_t)); + result = RegSetKeyValueW(key, L"DefaultIcon", nullptr, REG_SZ, exeFilePath, (exeLen + 1) * sizeof(wchar_t)); if (FAILED(result)) { fprintf(stderr, "Error writing icon\n"); } len = static_cast(lstrlenW(openCommand) + 1); - result = RegSetKeyValueW( - key, L"shell\\open\\command", nullptr, REG_SZ, openCommand, len * sizeof(wchar_t)); + result = RegSetKeyValueW(key, L"shell\\open\\command", nullptr, REG_SZ, openCommand, len * sizeof(wchar_t)); if (FAILED(result)) { fprintf(stderr, "Error writing command\n"); } RegCloseKey(key); + } extern "C" void Discord_Register(const char *applicationId, const char *command) { - wchar_t appId[32]; + + wchar_t appId[32]{}; MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32); - wchar_t openCommand[1024]; + wchar_t openCommand[1024]{}; const wchar_t *wcommand = nullptr; if (command && command[0]) { const auto commandBufferLen = sizeof(openCommand) / sizeof(*openCommand); @@ -142,41 +137,6 @@ extern "C" void Discord_Register(const char *applicationId, const char *command) } Discord_RegisterW(appId, wcommand); + } -extern "C" void Discord_RegisterSteamGame(const char *applicationId, - const char *steamId) { - wchar_t appId[32]; - MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32); - - wchar_t wSteamId[32]; - MultiByteToWideChar(CP_UTF8, 0, steamId, -1, wSteamId, 32); - - HKEY key; - auto status = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, KEY_READ, &key); - if (status != ERROR_SUCCESS) { - fprintf(stderr, "Error opening Steam key\n"); - return; - } - - wchar_t steamPath[MAX_PATH]; - DWORD pathBytes = sizeof(steamPath); - status = RegQueryValueExW(key, L"SteamExe", nullptr, nullptr, (BYTE *)steamPath, &pathBytes); - RegCloseKey(key); - if (status != ERROR_SUCCESS || pathBytes < 1) { - fprintf(stderr, "Error reading SteamExe key\n"); - return; - } - - DWORD pathChars = pathBytes / sizeof(wchar_t); - for (DWORD i = 0; i < pathChars; ++i) { - if (steamPath[i] == L'/') { - steamPath[i] = L'\\'; - } - } - - wchar_t command[1024]; - StringCbPrintfW(command, sizeof(command), L"\"%s\" steam://rungameid/%s", steamPath, wSteamId); - - Discord_RegisterW(appId, command); -} diff --git a/3rdparty/discord-rpc/src/discord_rpc.cpp b/3rdparty/discord-rpc/discord_rpc.cpp similarity index 89% rename from 3rdparty/discord-rpc/src/discord_rpc.cpp rename to 3rdparty/discord-rpc/discord_rpc.cpp index e19ce4687..26e1da74a 100644 --- a/3rdparty/discord-rpc/src/discord_rpc.cpp +++ b/3rdparty/discord-rpc/discord_rpc.cpp @@ -1,18 +1,16 @@ -#include "discord_rpc.h" - -#include "backoff.h" -#include "discord_register.h" -#include "msg_queue.h" -#include "rpc_connection.h" -#include "serialization.h" - #include #include #include - #include #include +#include "discord_rpc.h" +#include "discord_backoff.h" +#include "discord_register.h" +#include "discord_msg_queue.h" +#include "discord_rpc_connection.h" +#include "discord_serialization.h" + namespace discord_rpc { constexpr size_t MaxMessageSize { 16 * 1024 }; @@ -67,8 +65,7 @@ static MsgQueue SendQueue; static MsgQueue JoinAskQueue; static User connectedUser; -// We want to auto connect, and retry on failure, but not as fast as possible. This does expoential -// backoff from 0.5 seconds to 1 minute +// We want to auto connect, and retry on failure, but not as fast as possible. This does expoential backoff from 0.5 seconds to 1 minute static Backoff ReconnectTimeMs(500, 60 * 1000); static auto NextConnect = std::chrono::system_clock::now(); static int Pid { 0 }; @@ -111,11 +108,13 @@ class IoThreadHolder { static IoThreadHolder *IoThread { nullptr }; static void UpdateReconnectTime() { - NextConnect = std::chrono::system_clock::now() + - std::chrono::duration { ReconnectTimeMs.nextDelay() }; + + NextConnect = std::chrono::system_clock::now() + std::chrono::duration { ReconnectTimeMs.nextDelay() }; + } -static void Discord_UpdateConnection(void) { +static void Discord_UpdateConnection() { + if (!Connection) { return; } @@ -217,54 +216,54 @@ static void Discord_UpdateConnection(void) { SendQueue.CommitSend(); } } + } static void SignalIOActivity() { + if (IoThread != nullptr) { IoThread->Notify(); } + } static bool RegisterForEvent(const char *evtName) { + auto qmessage = SendQueue.GetNextAddMessage(); if (qmessage) { - qmessage->length = - JsonWriteSubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName); + qmessage->length = JsonWriteSubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName); SendQueue.CommitAdd(); SignalIOActivity(); return true; } + return false; + } static bool DeregisterForEvent(const char *evtName) { + auto qmessage = SendQueue.GetNextAddMessage(); if (qmessage) { - qmessage->length = - JsonWriteUnsubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName); + qmessage->length = JsonWriteUnsubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName); SendQueue.CommitAdd(); SignalIOActivity(); return true; } + return false; + } -extern "C" void Discord_Initialize(const char *applicationId, - DiscordEventHandlers *handlers, - int autoRegister, - const char *optionalSteamId) { +extern "C" void Discord_Initialize(const char *applicationId, DiscordEventHandlers *handlers, const int autoRegister) { + IoThread = new (std::nothrow) IoThreadHolder(); if (IoThread == nullptr) { return; } if (autoRegister) { - if (optionalSteamId && optionalSteamId[0]) { - Discord_RegisterSteamGame(applicationId, optionalSteamId); - } - else { - Discord_Register(applicationId, nullptr); - } + Discord_Register(applicationId, nullptr); } Pid = GetProcessId(); @@ -323,9 +322,11 @@ extern "C" void Discord_Initialize(const char *applicationId, }; IoThread->Start(); + } extern "C" void Discord_Shutdown(void) { + if (!Connection) { return; } @@ -341,15 +342,19 @@ extern "C" void Discord_Shutdown(void) { } RpcConnection::Destroy(Connection); + } extern "C" void Discord_UpdatePresence(const DiscordRichPresence *presence) { + { std::lock_guard guard(PresenceMutex); QueuedPresence.length = JsonWriteRichPresenceObj(QueuedPresence.buffer, sizeof(QueuedPresence.buffer), Nonce++, Pid, presence); UpdatePresence.exchange(true); } + SignalIOActivity(); + } extern "C" void Discord_ClearPresence(void) { @@ -357,20 +362,22 @@ extern "C" void Discord_ClearPresence(void) { } extern "C" void Discord_Respond(const char *userId, /* DISCORD_REPLY_ */ int reply) { + // if we are not connected, let's not batch up stale messages for later if (!Connection || !Connection->IsOpen()) { return; } auto qmessage = SendQueue.GetNextAddMessage(); if (qmessage) { - qmessage->length = - JsonWriteJoinReply(qmessage->buffer, sizeof(qmessage->buffer), userId, reply, Nonce++); + qmessage->length = JsonWriteJoinReply(qmessage->buffer, sizeof(qmessage->buffer), userId, reply, Nonce++); SendQueue.CommitAdd(); SignalIOActivity(); } + } -extern "C" void Discord_RunCallbacks(void) { +extern "C" void Discord_RunCallbacks() { + // Note on some weirdness: internally we might connect, get other signals, disconnect any number // of times inbetween calls here. Externally, we want the sequence to seem sane, so any other // signals are book-ended by calls to ready and disconnect. @@ -379,8 +386,8 @@ extern "C" void Discord_RunCallbacks(void) { return; } - bool wasDisconnected = WasJustDisconnected.exchange(false); - bool isConnected = Connection->IsOpen(); + const bool wasDisconnected = WasJustDisconnected.exchange(false); + const bool isConnected = Connection->IsOpen(); if (isConnected) { // if we are connected, disconnect cb first @@ -393,10 +400,7 @@ extern "C" void Discord_RunCallbacks(void) { if (WasJustConnected.exchange(false)) { std::lock_guard guard(HandlerMutex); if (Handlers.ready) { - DiscordUser du { connectedUser.userId, - connectedUser.username, - connectedUser.discriminator, - connectedUser.avatar }; + DiscordUser du { connectedUser.userId, connectedUser.username, connectedUser.discriminator, connectedUser.avatar }; Handlers.ready(&du); } } @@ -428,7 +432,7 @@ extern "C" void Discord_RunCallbacks(void) { // maybe show them in one common dialog and/or start fetching the avatars in parallel, and if // not it should be trivial for the implementer to make a queue themselves. while (JoinAskQueue.HavePendingSends()) { - auto req = JoinAskQueue.GetNextSendMessage(); + const auto req = JoinAskQueue.GetNextSendMessage(); { std::lock_guard guard(HandlerMutex); if (Handlers.joinRequest) { @@ -446,9 +450,11 @@ extern "C" void Discord_RunCallbacks(void) { Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage); } } + } extern "C" void Discord_UpdateHandlers(DiscordEventHandlers *newHandlers) { + if (newHandlers) { #define HANDLE_EVENT_REGISTRATION(handler_name, event) \ if (!Handlers.handler_name && newHandlers->handler_name) { \ @@ -471,7 +477,7 @@ extern "C" void Discord_UpdateHandlers(DiscordEventHandlers *newHandlers) { std::lock_guard guard(HandlerMutex); Handlers = {}; } - return; + } } // namespace discord_rpc diff --git a/3rdparty/discord-rpc/include/discord_rpc.h b/3rdparty/discord-rpc/discord_rpc.h similarity index 86% rename from 3rdparty/discord-rpc/include/discord_rpc.h rename to 3rdparty/discord-rpc/discord_rpc.h index ce06704a0..285da7689 100644 --- a/3rdparty/discord-rpc/include/discord_rpc.h +++ b/3rdparty/discord-rpc/discord_rpc.h @@ -1,10 +1,6 @@ #pragma once #include -// clang-format off - -// clang-format on - namespace discord_rpc { #ifdef __cplusplus @@ -54,13 +50,10 @@ typedef struct DiscordEventHandlers { #define DISCORD_PARTY_PRIVATE 0 #define DISCORD_PARTY_PUBLIC 1 -void Discord_Initialize(const char *applicationId, - DiscordEventHandlers *handlers, - int autoRegister, - const char *optionalSteamId); +void Discord_Initialize(const char *applicationId, DiscordEventHandlers *handlers, const int autoRegister); void Discord_Shutdown(void); -/* checks for incoming messages, dispatches callbacks */ +// checks for incoming messages, dispatches callbacks void Discord_RunCallbacks(void); void Discord_UpdatePresence(const DiscordRichPresence *presence); diff --git a/3rdparty/discord-rpc/src/rpc_connection.cpp b/3rdparty/discord-rpc/discord_rpc_connection.cpp similarity index 93% rename from 3rdparty/discord-rpc/src/rpc_connection.cpp rename to 3rdparty/discord-rpc/discord_rpc_connection.cpp index 3789bf49f..7673acff3 100644 --- a/3rdparty/discord-rpc/src/rpc_connection.cpp +++ b/3rdparty/discord-rpc/discord_rpc_connection.cpp @@ -1,24 +1,29 @@ -#include "rpc_connection.h" -#include "serialization.h" +#include "discord_rpc_connection.h" +#include "discord_serialization.h" namespace discord_rpc { static const int RpcVersion = 1; static RpcConnection Instance; -/*static*/ RpcConnection *RpcConnection::Create(const char *applicationId) { +RpcConnection *RpcConnection::Create(const char *applicationId) { + Instance.connection = BaseConnection::Create(); StringCopy(Instance.appId, applicationId); return &Instance; + } -/*static*/ void RpcConnection::Destroy(RpcConnection *&c) { +void RpcConnection::Destroy(RpcConnection *&c) { + c->Close(); BaseConnection::Destroy(c->connection); c = nullptr; + } void RpcConnection::Open() { + if (state == State::Connected) { return; } @@ -51,17 +56,21 @@ void RpcConnection::Open() { Close(); } } + } void RpcConnection::Close() { + if (onDisconnect && (state == State::Connected || state == State::SentHandshake)) { onDisconnect(lastErrorCode, lastErrorMessage); } connection->Close(); state = State::Disconnected; + } bool RpcConnection::Write(const void *data, size_t length) { + sendFrame.opcode = Opcode::Frame; memcpy(sendFrame.message, data, length); sendFrame.length = static_cast(length); @@ -69,14 +78,17 @@ bool RpcConnection::Write(const void *data, size_t length) { Close(); return false; } + return true; + } bool RpcConnection::Read(JsonDocument &message) { + if (state != State::Connected && state != State::SentHandshake) { return false; } - MessageFrame readFrame; + MessageFrame readFrame{}; for (;;) { bool didRead = connection->Read(&readFrame, sizeof(MessageFrameHeader)); if (!didRead) { @@ -127,6 +139,7 @@ bool RpcConnection::Read(JsonDocument &message) { return false; } } + } } // namespace discord_rpc diff --git a/3rdparty/discord-rpc/src/rpc_connection.h b/3rdparty/discord-rpc/discord_rpc_connection.h similarity index 92% rename from 3rdparty/discord-rpc/src/rpc_connection.h rename to 3rdparty/discord-rpc/discord_rpc_connection.h index 4ca58049c..b2cca1c74 100644 --- a/3rdparty/discord-rpc/src/rpc_connection.h +++ b/3rdparty/discord-rpc/discord_rpc_connection.h @@ -1,12 +1,11 @@ #pragma once -#include "connection.h" -#include "serialization.h" +#include "discord_connection.h" +#include "discord_serialization.h" namespace discord_rpc { -// I took this from the buffer size libuv uses for named pipes; I suspect ours would usually be much -// smaller. +// I took this from the buffer size libuv uses for named pipes; I suspect ours would usually be much smaller. constexpr size_t MaxRpcFrameSize = 64 * 1024; struct RpcConnection { diff --git a/3rdparty/discord-rpc/src/serialization.cpp b/3rdparty/discord-rpc/discord_serialization.cpp similarity index 93% rename from 3rdparty/discord-rpc/src/serialization.cpp rename to 3rdparty/discord-rpc/discord_serialization.cpp index 49c06985e..18ab4b26b 100644 --- a/3rdparty/discord-rpc/src/serialization.cpp +++ b/3rdparty/discord-rpc/discord_serialization.cpp @@ -1,11 +1,12 @@ -#include "serialization.h" -#include "connection.h" +#include "discord_serialization.h" +#include "discord_connection.h" #include "discord_rpc.h" namespace discord_rpc { template void NumberToString(char *dest, T number) { + if (!number) { *dest++ = '0'; *dest++ = 0; @@ -26,6 +27,7 @@ void NumberToString(char *dest, T number) { *dest++ = temp[place]; } *dest = 0; + } // it's ever so slightly faster to not have to strlen the key @@ -62,24 +64,25 @@ struct WriteArray { template void WriteOptionalString(JsonWriter &w, T &k, const char *value) { + if (value && value[0]) { w.Key(k, sizeof(T) - 1); w.String(value); } + } -static void JsonWriteNonce(JsonWriter &writer, int nonce) { +static void JsonWriteNonce(JsonWriter &writer, const int nonce) { + WriteKey(writer, "nonce"); char nonceBuffer[32]; NumberToString(nonceBuffer, nonce); writer.String(nonceBuffer); + } -size_t JsonWriteRichPresenceObj(char *dest, - size_t maxLen, - int nonce, - int pid, - const DiscordRichPresence *presence) { +size_t JsonWriteRichPresenceObj(char *dest, const size_t maxLen, const int nonce, const int pid, const DiscordRichPresence *presence) { + JsonWriter writer(dest, maxLen); { @@ -168,6 +171,7 @@ size_t JsonWriteRichPresenceObj(char *dest, } size_t JsonWriteHandshakeObj(char *dest, size_t maxLen, int version, const char *applicationId) { + JsonWriter writer(dest, maxLen); { @@ -179,9 +183,11 @@ size_t JsonWriteHandshakeObj(char *dest, size_t maxLen, int version, const char } return writer.Size(); + } size_t JsonWriteSubscribeCommand(char *dest, size_t maxLen, int nonce, const char *evtName) { + JsonWriter writer(dest, maxLen); { @@ -197,9 +203,11 @@ size_t JsonWriteSubscribeCommand(char *dest, size_t maxLen, int nonce, const cha } return writer.Size(); + } size_t JsonWriteUnsubscribeCommand(char *dest, size_t maxLen, int nonce, const char *evtName) { + JsonWriter writer(dest, maxLen); { @@ -215,9 +223,11 @@ size_t JsonWriteUnsubscribeCommand(char *dest, size_t maxLen, int nonce, const c } return writer.Size(); + } -size_t JsonWriteJoinReply(char *dest, size_t maxLen, const char *userId, int reply, int nonce) { +size_t JsonWriteJoinReply(char *dest, size_t maxLen, const char *userId, const int reply, const int nonce) { + JsonWriter writer(dest, maxLen); { @@ -243,6 +253,7 @@ size_t JsonWriteJoinReply(char *dest, size_t maxLen, const char *userId, int rep } return writer.Size(); + } } // namespace discord_rpc diff --git a/3rdparty/discord-rpc/src/serialization.h b/3rdparty/discord-rpc/discord_serialization.h similarity index 92% rename from 3rdparty/discord-rpc/src/serialization.h rename to 3rdparty/discord-rpc/discord_serialization.h index b9c2dc682..be8838e32 100644 --- a/3rdparty/discord-rpc/src/serialization.h +++ b/3rdparty/discord-rpc/discord_serialization.h @@ -25,11 +25,7 @@ size_t JsonWriteHandshakeObj(char *dest, size_t maxLen, int version, const char // Commands struct DiscordRichPresence; -size_t JsonWriteRichPresenceObj(char *dest, - size_t maxLen, - int nonce, - int pid, - const DiscordRichPresence *presence); +size_t JsonWriteRichPresenceObj(char *dest, const size_t maxLen, const int nonce, const int pid, const DiscordRichPresence *presence); size_t JsonWriteSubscribeCommand(char *dest, size_t maxLen, int nonce, const char *evtName); size_t JsonWriteUnsubscribeCommand(char *dest, size_t maxLen, int nonce, const char *evtName); @@ -149,35 +145,42 @@ class JsonDocument : public JsonDocumentBase { using JsonValue = rapidjson::GenericValue; inline JsonValue *GetObjMember(JsonValue *obj, const char *name) { + if (obj) { auto member = obj->FindMember(name); if (member != obj->MemberEnd() && member->value.IsObject()) { return &member->value; } } + return nullptr; + } inline int GetIntMember(JsonValue *obj, const char *name, int notFoundDefault = 0) { + if (obj) { auto member = obj->FindMember(name); if (member != obj->MemberEnd() && member->value.IsInt()) { return member->value.GetInt(); } } + return notFoundDefault; + } -inline const char *GetStrMember(JsonValue *obj, - const char *name, - const char *notFoundDefault = nullptr) { +inline const char *GetStrMember(JsonValue *obj, const char *name, const char *notFoundDefault = nullptr) { + if (obj) { auto member = obj->FindMember(name); if (member != obj->MemberEnd() && member->value.IsString()) { return member->value.GetString(); } } + return notFoundDefault; + } } // namespace discord_rpc diff --git a/3rdparty/discord-rpc/src/CMakeLists.txt b/3rdparty/discord-rpc/src/CMakeLists.txt deleted file mode 100644 index b65b3b846..000000000 --- a/3rdparty/discord-rpc/src/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -set(DISCORD_RPC_SOURCES - ../include/discord_rpc.h - ../include/discord_register.h - discord_rpc.cpp - rpc_connection.h - rpc_connection.cpp - serialization.h - serialization.cpp - connection.h - backoff.h - msg_queue.h -) - -if(UNIX) - list(APPEND DISCORD_RPC_SOURCES connection_unix.cpp) - if(APPLE) - list(APPEND DISCORD_RPC_SOURCES discord_register_osx.m) - add_definitions(-DDISCORD_OSX) - else() - list(APPEND DISCORD_RPC_SOURCES discord_register_linux.cpp) - add_definitions(-DDISCORD_LINUX) - endif() -endif() - -if(WIN32) - list(APPEND DISCORD_RPC_SOURCES connection_win.cpp discord_register_win.cpp) - add_definitions(-DDISCORD_WINDOWS) -endif() - -add_library(discord-rpc STATIC ${DISCORD_RPC_SOURCES}) - -if(APPLE) - target_link_libraries(discord-rpc PRIVATE "-framework AppKit") -endif() - -if(WIN32) - target_link_libraries(discord-rpc PRIVATE psapi advapi32) -endif() - -target_include_directories(discord-rpc SYSTEM PRIVATE ${RapidJSON_INCLUDE_DIRS}) -target_include_directories(discord-rpc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include) diff --git a/3rdparty/discord-rpc/src/discord_register_osx.m b/3rdparty/discord-rpc/src/discord_register_osx.m deleted file mode 100644 index 4fbbbd179..000000000 --- a/3rdparty/discord-rpc/src/discord_register_osx.m +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include - -#import - -#include "discord_register.h" - -static void RegisterCommand(const char *applicationId, const char *command) -{ - // There does not appear to be a way to register arbitrary commands on OSX, so instead we'll save the command - // to a file in the Discord config path, and when it is needed, Discord can try to load the file there, open - // the command therein (will pass to js's window.open, so requires a url-like thing) - - // Note: will not work for sandboxed apps - NSString *home = NSHomeDirectory(); - if (!home) { - return; - } - - NSString *path = [[[[[[home stringByAppendingPathComponent:@"Library"] - stringByAppendingPathComponent:@"Application Support"] - stringByAppendingPathComponent:@"discord"] - stringByAppendingPathComponent:@"games"] - stringByAppendingPathComponent:[NSString stringWithUTF8String:applicationId]] - stringByAppendingPathExtension:@"json"]; - [[NSFileManager defaultManager] createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil]; - - NSString *jsonBuffer = [NSString stringWithFormat:@"{\"command\": \"%s\"}", command]; - [jsonBuffer writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:nil]; -} - -static void RegisterURL(const char *applicationId) -{ - char url[256]; - snprintf(url, sizeof(url), "discord-%s", applicationId); - CFStringRef cfURL = CFStringCreateWithCString(NULL, url, kCFStringEncodingUTF8); - - NSString* myBundleId = [[NSBundle mainBundle] bundleIdentifier]; - if (!myBundleId) { - fprintf(stderr, "No bundle id found\n"); - return; - } - - NSURL* myURL = [[NSBundle mainBundle] bundleURL]; - if (!myURL) { - fprintf(stderr, "No bundle url found\n"); - return; - } - - OSStatus status = LSSetDefaultHandlerForURLScheme(cfURL, (__bridge CFStringRef)myBundleId); - if (status != noErr) { - fprintf(stderr, "Error in LSSetDefaultHandlerForURLScheme: %d\n", (int)status); - return; - } - - status = LSRegisterURL((__bridge CFURLRef)myURL, true); - if (status != noErr) { - fprintf(stderr, "Error in LSRegisterURL: %d\n", (int)status); - } -} - -void Discord_Register(const char *applicationId, const char *command) -{ - if (command) { - RegisterCommand(applicationId, command); - } - else { - // raii lite - @autoreleasepool { - RegisterURL(applicationId); - } - } -} - -void Discord_RegisterSteamGame(const char *applicationId, const char *steamId) -{ - char command[256]; - snprintf(command, 256, "steam://rungameid/%s", steamId); - Discord_Register(applicationId, command); -} diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f9a08ed5..b9c044857 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1494,7 +1494,7 @@ endif() if(HAVE_DISCORD_RPC) add_subdirectory(3rdparty/discord-rpc) - target_include_directories(strawberry_lib PUBLIC 3rdparty/discord-rpc/include) + target_include_directories(strawberry_lib PUBLIC 3rdparty/discord-rpc) endif() if(HAVE_TRANSLATIONS) diff --git a/src/discord/richpresence.cpp b/src/discord/richpresence.cpp index 04737dbc4..1de03320f 100644 --- a/src/discord/richpresence.cpp +++ b/src/discord/richpresence.cpp @@ -52,7 +52,7 @@ RichPresence::RichPresence(const SharedPtr player, send_presence_timestamp_(0), enabled_(false) { - Discord_Initialize(kDiscordApplicationId, nullptr, 1, nullptr); + Discord_Initialize(kDiscordApplicationId, nullptr, 1); QObject::connect(&*player_->engine(), &EngineBase::StateChanged, this, &RichPresence::EngineStateChanged); QObject::connect(&*playlist_manager_, &PlaylistManager::CurrentSongChanged, this, &RichPresence::CurrentSongChanged);