mirror of
https://github.com/libretro/RetroArch.git
synced 2025-02-26 12:48:27 +00:00
(Discord) Convert some files to C
This commit is contained in:
parent
12698f561f
commit
a363af7796
98
deps/discord-rpc/src/discord_register_linux.c
vendored
Normal file
98
deps/discord-rpc/src/discord_register_linux.c
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
#include "discord_rpc.h"
|
||||
#include "discord_register.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static bool Mkdir(const char* path)
|
||||
{
|
||||
int result = mkdir(path, 0755);
|
||||
if (result == 0)
|
||||
return true;
|
||||
if (errno == EEXIST)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we want to register games so we can run them from
|
||||
* Discord client as discord-<appid>:// */
|
||||
void Discord_Register(const char* applicationId, const char* command)
|
||||
{
|
||||
FILE* fp;
|
||||
int fileLen;
|
||||
char xdgMimeCommand[1024];
|
||||
char desktopFilename[256];
|
||||
char desktopFilePath[1024];
|
||||
char desktopFile[2048];
|
||||
/* Add a desktop file and update some MIME handlers
|
||||
* so that xdg-open does the right thing. */
|
||||
char exePath[1024];
|
||||
const char* home = getenv("HOME");
|
||||
if (!home)
|
||||
return;
|
||||
|
||||
if (!command || !command[0])
|
||||
{
|
||||
ssize_t size = readlink("/proc/self/exe", exePath, sizeof(exePath));
|
||||
if (size <= 0 || size >= (ssize_t)sizeof(exePath))
|
||||
return;
|
||||
exePath[size] = '\0';
|
||||
command = exePath;
|
||||
}
|
||||
|
||||
{
|
||||
const char* desktopFileFormat = "[Desktop Entry]\n"
|
||||
"Name=Game %s\n"
|
||||
"Exec=%s\n" /* note: it really wants that %u in there */
|
||||
"Type=Application\n"
|
||||
"NoDisplay=true\n"
|
||||
"Categories=Discord;Games;\n"
|
||||
"MimeType=x-scheme-handler/discord-%s;\n";
|
||||
fileLen = snprintf(
|
||||
desktopFile, sizeof(desktopFile), desktopFileFormat, applicationId, command, applicationId);
|
||||
if (fileLen <= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(desktopFilename, sizeof(desktopFilename), "/discord-%s.desktop", applicationId);
|
||||
|
||||
snprintf(desktopFilePath, sizeof(desktopFilePath), "%s/.local", home);
|
||||
if (!Mkdir(desktopFilePath))
|
||||
return;
|
||||
strcat(desktopFilePath, "/share");
|
||||
if (!Mkdir(desktopFilePath))
|
||||
return;
|
||||
strcat(desktopFilePath, "/applications");
|
||||
if (!Mkdir(desktopFilePath))
|
||||
return;
|
||||
strcat(desktopFilePath, desktopFilename);
|
||||
|
||||
fp = fopen(desktopFilePath, "w");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
fwrite(desktopFile, 1, fileLen, fp);
|
||||
fclose(fp);
|
||||
|
||||
snprintf(xdgMimeCommand,
|
||||
sizeof(xdgMimeCommand),
|
||||
"xdg-mime default discord-%s.desktop x-scheme-handler/discord-%s",
|
||||
applicationId,
|
||||
applicationId);
|
||||
if (system(xdgMimeCommand) < 0)
|
||||
fprintf(stderr, "Failed to register mime handler\n");
|
||||
}
|
||||
|
||||
void Discord_RegisterSteamGame(
|
||||
const char* applicationId,
|
||||
const char* steamId)
|
||||
{
|
||||
char command[256];
|
||||
sprintf(command, "xdg-open steam://rungameid/%s", steamId);
|
||||
Discord_Register(applicationId, command);
|
||||
}
|
102
deps/discord-rpc/src/discord_register_linux.cpp
vendored
102
deps/discord-rpc/src/discord_register_linux.cpp
vendored
@ -1,102 +0,0 @@
|
||||
#include "discord_rpc.h"
|
||||
#include "discord_register.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static bool Mkdir(const char* path)
|
||||
{
|
||||
int result = mkdir(path, 0755);
|
||||
if (result == 0) {
|
||||
return true;
|
||||
}
|
||||
if (errno == EEXIST) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// we want to register games so we can run them from Discord client as discord-<appid>://
|
||||
extern "C" DISCORD_EXPORT 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");
|
||||
if (!home) {
|
||||
return;
|
||||
}
|
||||
|
||||
char exePath[1024];
|
||||
if (!command || !command[0]) {
|
||||
ssize_t size = readlink("/proc/self/exe", exePath, sizeof(exePath));
|
||||
if (size <= 0 || size >= (ssize_t)sizeof(exePath)) {
|
||||
return;
|
||||
}
|
||||
exePath[size] = '\0';
|
||||
command = exePath;
|
||||
}
|
||||
|
||||
const char* destopFileFormat = "[Desktop Entry]\n"
|
||||
"Name=Game %s\n"
|
||||
"Exec=%s\n" // note: it really wants that %u in there
|
||||
"Type=Application\n"
|
||||
"NoDisplay=true\n"
|
||||
"Categories=Discord;Games;\n"
|
||||
"MimeType=x-scheme-handler/discord-%s;\n";
|
||||
char desktopFile[2048];
|
||||
int fileLen = snprintf(
|
||||
desktopFile, sizeof(desktopFile), destopFileFormat, applicationId, command, applicationId);
|
||||
if (fileLen <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char desktopFilename[256];
|
||||
snprintf(desktopFilename, sizeof(desktopFilename), "/discord-%s.desktop", applicationId);
|
||||
|
||||
char desktopFilePath[1024];
|
||||
snprintf(desktopFilePath, sizeof(desktopFilePath), "%s/.local", home);
|
||||
if (!Mkdir(desktopFilePath)) {
|
||||
return;
|
||||
}
|
||||
strcat(desktopFilePath, "/share");
|
||||
if (!Mkdir(desktopFilePath)) {
|
||||
return;
|
||||
}
|
||||
strcat(desktopFilePath, "/applications");
|
||||
if (!Mkdir(desktopFilePath)) {
|
||||
return;
|
||||
}
|
||||
strcat(desktopFilePath, desktopFilename);
|
||||
|
||||
FILE* fp = fopen(desktopFilePath, "w");
|
||||
if (fp) {
|
||||
fwrite(desktopFile, 1, fileLen, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
char xdgMimeCommand[1024];
|
||||
snprintf(xdgMimeCommand,
|
||||
sizeof(xdgMimeCommand),
|
||||
"xdg-mime default discord-%s.desktop x-scheme-handler/discord-%s",
|
||||
applicationId,
|
||||
applicationId);
|
||||
if (system(xdgMimeCommand) < 0) {
|
||||
fprintf(stderr, "Failed to register mime handler\n");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId,
|
||||
const char* steamId)
|
||||
{
|
||||
char command[256];
|
||||
sprintf(command, "xdg-open steam://rungameid/%s", steamId);
|
||||
Discord_Register(applicationId, command);
|
||||
}
|
191
deps/discord-rpc/src/discord_register_win.c
vendored
Normal file
191
deps/discord-rpc/src/discord_register_win.c
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
#include "discord_rpc.h"
|
||||
#include "discord_register.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMCX
|
||||
#define NOSERVICE
|
||||
#define NOIME
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <wchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* Updated fixes for MinGW and WinXP
|
||||
* This block is written the way it does not involve changing the rest of the code
|
||||
* Checked to be compiling
|
||||
* 1) strsafe.h belongs to Windows SDK and cannot be added to MinGW
|
||||
* #include guarded, functions redirected to <string.h> substitutes
|
||||
* 2) RegSetKeyValueW and LSTATUS are not declared in <winreg.h>
|
||||
* The entire function is rewritten
|
||||
*/
|
||||
#ifdef __MINGW32__
|
||||
/* strsafe.h fixes */
|
||||
static HRESULT StringCbPrintfW(LPWSTR pszDest,
|
||||
size_t cbDest, LPCWSTR pszFormat, ...)
|
||||
{
|
||||
HRESULT ret;
|
||||
va_list va;
|
||||
va_start(va, pszFormat);
|
||||
cbDest /= 2; /* Size is divided by 2 to convert from bytes to wide characters - causes segfault */
|
||||
/* othervise */
|
||||
ret = vsnwprintf(pszDest, cbDest, pszFormat, va);
|
||||
pszDest[cbDest - 1] = 0; /* Terminate the string in case a buffer overflow; -1 will be returned */
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#include <strsafe.h>
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
/* winreg.h fixes */
|
||||
#ifndef LSTATUS
|
||||
#define LSTATUS LONG
|
||||
#endif
|
||||
#ifdef RegSetKeyValueW
|
||||
#undefine RegSetKeyValueW
|
||||
#endif
|
||||
#define RegSetKeyValueW regset
|
||||
static LSTATUS regset(HKEY hkey,
|
||||
LPCWSTR subkey,
|
||||
LPCWSTR name,
|
||||
DWORD type,
|
||||
const void* data,
|
||||
DWORD len)
|
||||
{
|
||||
HKEY htkey = hkey, hsubkey = NULL;
|
||||
LSTATUS ret;
|
||||
if (subkey && subkey[0])
|
||||
{
|
||||
if ((ret = RegCreateKeyExW(hkey, subkey, 0, 0, 0, KEY_ALL_ACCESS, 0, &hsubkey, 0)) !=
|
||||
ERROR_SUCCESS)
|
||||
return ret;
|
||||
htkey = hsubkey;
|
||||
}
|
||||
ret = RegSetValueExW(htkey, name, 0, type, (const BYTE*)data, len);
|
||||
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-<appid>://
|
||||
* Update the HKEY_CURRENT_USER, because it doesn't seem to require special permissions. */
|
||||
|
||||
DWORD len;
|
||||
LSTATUS result;
|
||||
wchar_t urlProtocol = 0;
|
||||
wchar_t keyName[256];
|
||||
wchar_t protocolName[64];
|
||||
wchar_t protocolDescription[128];
|
||||
wchar_t exeFilePath[MAX_PATH];
|
||||
DWORD exeLen = GetModuleFileNameW(NULL, exeFilePath, MAX_PATH);
|
||||
wchar_t openCommand[1024];
|
||||
|
||||
if (command && command[0])
|
||||
StringCbPrintfW(openCommand, sizeof(openCommand), L"%S", command);
|
||||
else
|
||||
StringCbPrintfW(openCommand, sizeof(openCommand), L"%S", exeFilePath);
|
||||
|
||||
StringCbPrintfW(protocolName, sizeof(protocolName),
|
||||
L"discord-%S", applicationId);
|
||||
StringCbPrintfW(
|
||||
protocolDescription, sizeof(protocolDescription),
|
||||
L"URL:Run game %S protocol", applicationId);
|
||||
StringCbPrintfW(keyName, sizeof(keyName), L"Software\\Classes\\%S", protocolName);
|
||||
HKEY key;
|
||||
LSTATUS status =
|
||||
RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, NULL, 0, KEY_WRITE, NULL, &key, NULL);
|
||||
if (status != ERROR_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "Error creating key\n");
|
||||
return;
|
||||
}
|
||||
len = (DWORD)lstrlenW(protocolDescription) + 1;
|
||||
result =
|
||||
RegSetKeyValueW(key, NULL, NULL, REG_SZ, protocolDescription, len * sizeof(wchar_t));
|
||||
if (FAILED(result)) {
|
||||
fprintf(stderr, "Error writing description\n");
|
||||
}
|
||||
|
||||
len = (DWORD)lstrlenW(protocolDescription) + 1;
|
||||
result = RegSetKeyValueW(key, NULL, L"URL Protocol", REG_SZ, &urlProtocol, sizeof(wchar_t));
|
||||
if (FAILED(result))
|
||||
fprintf(stderr, "Error writing description\n");
|
||||
|
||||
result = RegSetKeyValueW(
|
||||
key, L"DefaultIcon", NULL, REG_SZ, exeFilePath, (exeLen + 1) * sizeof(wchar_t));
|
||||
if (FAILED(result))
|
||||
fprintf(stderr, "Error writing icon\n");
|
||||
|
||||
len = (DWORD)lstrlenW(openCommand) + 1;
|
||||
result = RegSetKeyValueW(
|
||||
key, L"shell\\open\\command", NULL, REG_SZ, openCommand, len * sizeof(wchar_t));
|
||||
if (FAILED(result))
|
||||
fprintf(stderr, "Error writing command\n");
|
||||
RegCloseKey(key);
|
||||
}
|
||||
|
||||
void Discord_Register(const char* applicationId, const char* command)
|
||||
{
|
||||
wchar_t openCommand[1024];
|
||||
const wchar_t* wcommand = NULL;
|
||||
wchar_t appId[32];
|
||||
|
||||
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
|
||||
if (command && command[0])
|
||||
{
|
||||
const int commandBufferLen =
|
||||
sizeof(openCommand) / sizeof(*openCommand);
|
||||
MultiByteToWideChar(CP_UTF8, 0, command, -1,
|
||||
openCommand, commandBufferLen);
|
||||
wcommand = openCommand;
|
||||
}
|
||||
|
||||
Discord_RegisterW(appId, wcommand);
|
||||
}
|
||||
|
||||
void Discord_RegisterSteamGame(
|
||||
const char* applicationId,
|
||||
const char* steamId)
|
||||
{
|
||||
DWORD pathChars, pathBytes, i;
|
||||
HKEY key;
|
||||
wchar_t steamPath[MAX_PATH];
|
||||
wchar_t command[1024];
|
||||
wchar_t appId[32];
|
||||
wchar_t wSteamId[32];
|
||||
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
|
||||
MultiByteToWideChar(CP_UTF8, 0, steamId, -1, wSteamId, 32);
|
||||
LSTATUS 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;
|
||||
}
|
||||
|
||||
pathBytes = sizeof(steamPath);
|
||||
status = RegQueryValueExW(key,
|
||||
L"SteamExe", NULL, NULL, (BYTE*)steamPath, &pathBytes);
|
||||
RegCloseKey(key);
|
||||
if (status != ERROR_SUCCESS || pathBytes < 1) {
|
||||
fprintf(stderr, "Error reading SteamExe key\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pathChars = pathBytes / sizeof(wchar_t);
|
||||
for (i = 0; i < pathChars; ++i)
|
||||
{
|
||||
if (steamPath[i] == L'/')
|
||||
steamPath[i] = L'\\';
|
||||
}
|
||||
|
||||
StringCbPrintfW(command, sizeof(command),
|
||||
L"\"%s\" steam://rungameid/%s", steamPath, wSteamId);
|
||||
|
||||
Discord_RegisterW(appId, command);
|
||||
}
|
185
deps/discord-rpc/src/discord_register_win.cpp
vendored
185
deps/discord-rpc/src/discord_register_win.cpp
vendored
@ -1,185 +0,0 @@
|
||||
#include "discord_rpc.h"
|
||||
#include "discord_register.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMCX
|
||||
#define NOSERVICE
|
||||
#define NOIME
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <cwchar>
|
||||
#include <cstdio>
|
||||
|
||||
/**
|
||||
* Updated fixes for MinGW and WinXP
|
||||
* This block is written the way it does not involve changing the rest of the code
|
||||
* Checked to be compiling
|
||||
* 1) strsafe.h belongs to Windows SDK and cannot be added to MinGW
|
||||
* #include guarded, functions redirected to <string.h> substitutes
|
||||
* 2) RegSetKeyValueW and LSTATUS are not declared in <winreg.h>
|
||||
* The entire function is rewritten
|
||||
*/
|
||||
#ifdef __MINGW32__
|
||||
/// strsafe.h fixes
|
||||
static HRESULT StringCbPrintfW(LPWSTR pszDest, size_t cbDest, LPCWSTR pszFormat, ...)
|
||||
{
|
||||
HRESULT ret;
|
||||
va_list va;
|
||||
va_start(va, pszFormat);
|
||||
cbDest /= 2; // Size is divided by 2 to convert from bytes to wide characters - causes segfault
|
||||
// othervise
|
||||
ret = vsnwprintf(pszDest, cbDest, pszFormat, va);
|
||||
pszDest[cbDest - 1] = 0; // Terminate the string in case a buffer overflow; -1 will be returned
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#include <strsafe.h>
|
||||
#endif // __MINGW32__
|
||||
|
||||
/// winreg.h fixes
|
||||
#ifndef LSTATUS
|
||||
#define LSTATUS LONG
|
||||
#endif
|
||||
#ifdef RegSetKeyValueW
|
||||
#undefine RegSetKeyValueW
|
||||
#endif
|
||||
#define RegSetKeyValueW regset
|
||||
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]) {
|
||||
if ((ret = RegCreateKeyExW(hkey, subkey, 0, 0, 0, KEY_ALL_ACCESS, 0, &hsubkey, 0)) !=
|
||||
ERROR_SUCCESS)
|
||||
return ret;
|
||||
htkey = hsubkey;
|
||||
}
|
||||
ret = RegSetValueExW(htkey, name, 0, type, (const BYTE*)data, len);
|
||||
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-<appid>://
|
||||
// Update the HKEY_CURRENT_USER, because it doesn't seem to require special permissions.
|
||||
|
||||
wchar_t exeFilePath[MAX_PATH];
|
||||
DWORD exeLen = GetModuleFileNameW(nullptr, exeFilePath, MAX_PATH);
|
||||
wchar_t openCommand[1024];
|
||||
|
||||
if (command && command[0]) {
|
||||
StringCbPrintfW(openCommand, sizeof(openCommand), L"%S", command);
|
||||
}
|
||||
else {
|
||||
// StringCbCopyW(openCommand, sizeof(openCommand), exeFilePath);
|
||||
StringCbPrintfW(openCommand, sizeof(openCommand), L"%S", exeFilePath);
|
||||
}
|
||||
|
||||
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 urlProtocol = 0;
|
||||
|
||||
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);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
fprintf(stderr, "Error creating key\n");
|
||||
return;
|
||||
}
|
||||
DWORD len;
|
||||
LSTATUS result;
|
||||
len = (DWORD)lstrlenW(protocolDescription) + 1;
|
||||
result =
|
||||
RegSetKeyValueW(key, nullptr, nullptr, REG_SZ, protocolDescription, len * sizeof(wchar_t));
|
||||
if (FAILED(result)) {
|
||||
fprintf(stderr, "Error writing description\n");
|
||||
}
|
||||
|
||||
len = (DWORD)lstrlenW(protocolDescription) + 1;
|
||||
result = RegSetKeyValueW(key, nullptr, L"URL Protocol", REG_SZ, &urlProtocol, sizeof(wchar_t));
|
||||
if (FAILED(result)) {
|
||||
fprintf(stderr, "Error writing description\n");
|
||||
}
|
||||
|
||||
result = RegSetKeyValueW(
|
||||
key, L"DefaultIcon", nullptr, REG_SZ, exeFilePath, (exeLen + 1) * sizeof(wchar_t));
|
||||
if (FAILED(result)) {
|
||||
fprintf(stderr, "Error writing icon\n");
|
||||
}
|
||||
|
||||
len = (DWORD)lstrlenW(openCommand) + 1;
|
||||
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" DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command)
|
||||
{
|
||||
wchar_t appId[32];
|
||||
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
|
||||
|
||||
wchar_t openCommand[1024];
|
||||
const wchar_t* wcommand = nullptr;
|
||||
if (command && command[0]) {
|
||||
const auto commandBufferLen = sizeof(openCommand) / sizeof(*openCommand);
|
||||
MultiByteToWideChar(CP_UTF8, 0, command, -1, openCommand, commandBufferLen);
|
||||
wcommand = openCommand;
|
||||
}
|
||||
|
||||
Discord_RegisterW(appId, wcommand);
|
||||
}
|
||||
|
||||
extern "C" DISCORD_EXPORT 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);
|
||||
}
|
288
deps/discord-rpc/src/discord_rpc.cpp
vendored
288
deps/discord-rpc/src/discord_rpc.cpp
vendored
@ -19,20 +19,21 @@ constexpr size_t MaxMessageSize{16 * 1024};
|
||||
constexpr size_t MessageQueueSize{8};
|
||||
constexpr size_t JoinQueueSize{8};
|
||||
|
||||
struct QueuedMessage {
|
||||
size_t length;
|
||||
char buffer[MaxMessageSize];
|
||||
struct QueuedMessage
|
||||
{
|
||||
size_t length;
|
||||
char buffer[MaxMessageSize];
|
||||
|
||||
void Copy(const QueuedMessage& other)
|
||||
{
|
||||
length = other.length;
|
||||
if (length) {
|
||||
memcpy(buffer, other.buffer, length);
|
||||
}
|
||||
}
|
||||
void Copy(const QueuedMessage& other)
|
||||
{
|
||||
length = other.length;
|
||||
if (length)
|
||||
memcpy(buffer, other.buffer, length);
|
||||
}
|
||||
};
|
||||
|
||||
struct User {
|
||||
struct User
|
||||
{
|
||||
// snowflake (64bit int), turned into a ascii decimal string, at most 20 chars +1 null
|
||||
// terminator = 21
|
||||
char userId[32];
|
||||
@ -104,9 +105,8 @@ public:
|
||||
{
|
||||
keepRunning.exchange(false);
|
||||
Notify();
|
||||
if (ioThread.joinable()) {
|
||||
if (ioThread.joinable())
|
||||
ioThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
~IoThreadHolder() { Stop(); }
|
||||
@ -123,7 +123,7 @@ static IoThreadHolder* IoThread{nullptr};
|
||||
|
||||
static void UpdateReconnectTime()
|
||||
{
|
||||
NextConnect = std::chrono::system_clock::now() +
|
||||
NextConnect = std::chrono::system_clock::now() +
|
||||
std::chrono::duration<int64_t, std::milli>{ReconnectTimeMs.nextDelay()};
|
||||
}
|
||||
|
||||
@ -133,102 +133,111 @@ extern "C" DISCORD_EXPORT void Discord_UpdateConnection(void)
|
||||
static void Discord_UpdateConnection(void)
|
||||
#endif
|
||||
{
|
||||
if (!Connection) {
|
||||
if (!Connection)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Connection->IsOpen()) {
|
||||
if (std::chrono::system_clock::now() >= NextConnect) {
|
||||
if (!Connection->IsOpen())
|
||||
{
|
||||
if (std::chrono::system_clock::now() >= NextConnect)
|
||||
{
|
||||
UpdateReconnectTime();
|
||||
Connection->Open();
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// reads
|
||||
|
||||
for (;;) {
|
||||
for (;;)
|
||||
{
|
||||
JsonDocument message;
|
||||
|
||||
if (!Connection->Read(message)) {
|
||||
if (!Connection->Read(message))
|
||||
break;
|
||||
}
|
||||
|
||||
const char* evtName = GetStrMember(&message, "evt");
|
||||
const char* nonce = GetStrMember(&message, "nonce");
|
||||
|
||||
if (nonce) {
|
||||
if (nonce)
|
||||
{
|
||||
// in responses only -- should use to match up response when needed.
|
||||
|
||||
if (evtName && strcmp(evtName, "ERROR") == 0) {
|
||||
if (evtName && strcmp(evtName, "ERROR") == 0)
|
||||
{
|
||||
auto data = GetObjMember(&message, "data");
|
||||
LastErrorCode = GetIntMember(data, "code");
|
||||
StringCopy(LastErrorMessage, GetStrMember(data, "message", ""));
|
||||
GotErrorMessage.store(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// should have evt == name of event, optional data
|
||||
if (evtName == nullptr) {
|
||||
if (evtName == nullptr)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto data = GetObjMember(&message, "data");
|
||||
|
||||
if (strcmp(evtName, "ACTIVITY_JOIN") == 0) {
|
||||
if (strcmp(evtName, "ACTIVITY_JOIN") == 0)
|
||||
{
|
||||
auto secret = GetStrMember(data, "secret");
|
||||
if (secret) {
|
||||
if (secret)
|
||||
{
|
||||
StringCopy(JoinGameSecret, secret);
|
||||
WasJoinGame.store(true);
|
||||
}
|
||||
}
|
||||
else if (strcmp(evtName, "ACTIVITY_SPECTATE") == 0) {
|
||||
auto secret = GetStrMember(data, "secret");
|
||||
if (secret) {
|
||||
StringCopy(SpectateGameSecret, secret);
|
||||
WasSpectateGame.store(true);
|
||||
}
|
||||
else if (strcmp(evtName, "ACTIVITY_SPECTATE") == 0)
|
||||
{
|
||||
auto secret = GetStrMember(data, "secret");
|
||||
if (secret)
|
||||
{
|
||||
StringCopy(SpectateGameSecret, secret);
|
||||
WasSpectateGame.store(true);
|
||||
}
|
||||
}
|
||||
else if (strcmp(evtName, "ACTIVITY_JOIN_REQUEST") == 0) {
|
||||
auto user = GetObjMember(data, "user");
|
||||
auto userId = GetStrMember(user, "id");
|
||||
auto username = GetStrMember(user, "username");
|
||||
auto avatar = GetStrMember(user, "avatar");
|
||||
auto joinReq = JoinAskQueue.GetNextAddMessage();
|
||||
if (userId && username && joinReq) {
|
||||
StringCopy(joinReq->userId, userId);
|
||||
StringCopy(joinReq->username, username);
|
||||
auto discriminator = GetStrMember(user, "discriminator");
|
||||
if (discriminator) {
|
||||
StringCopy(joinReq->discriminator, discriminator);
|
||||
}
|
||||
if (avatar) {
|
||||
StringCopy(joinReq->avatar, avatar);
|
||||
}
|
||||
else {
|
||||
joinReq->avatar[0] = 0;
|
||||
}
|
||||
JoinAskQueue.CommitAdd();
|
||||
}
|
||||
else if (strcmp(evtName, "ACTIVITY_JOIN_REQUEST") == 0)
|
||||
{
|
||||
auto user = GetObjMember(data, "user");
|
||||
auto userId = GetStrMember(user, "id");
|
||||
auto username = GetStrMember(user, "username");
|
||||
auto avatar = GetStrMember(user, "avatar");
|
||||
auto joinReq = JoinAskQueue.GetNextAddMessage();
|
||||
if (userId && username && joinReq)
|
||||
{
|
||||
StringCopy(joinReq->userId, userId);
|
||||
StringCopy(joinReq->username, username);
|
||||
auto discriminator = GetStrMember(user, "discriminator");
|
||||
if (discriminator)
|
||||
StringCopy(joinReq->discriminator, discriminator);
|
||||
if (avatar)
|
||||
StringCopy(joinReq->avatar, avatar);
|
||||
else
|
||||
joinReq->avatar[0] = 0;
|
||||
JoinAskQueue.CommitAdd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// writes
|
||||
if (QueuedPresence.length) {
|
||||
QueuedMessage local;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||
local.Copy(QueuedPresence);
|
||||
QueuedPresence.length = 0;
|
||||
}
|
||||
if (!Connection->Write(local.buffer, local.length)) {
|
||||
// if we fail to send, requeue
|
||||
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||
QueuedPresence.Copy(local);
|
||||
}
|
||||
if (QueuedPresence.length)
|
||||
{
|
||||
QueuedMessage local;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||
local.Copy(QueuedPresence);
|
||||
QueuedPresence.length = 0;
|
||||
}
|
||||
if (!Connection->Write(local.buffer, local.length)) {
|
||||
// if we fail to send, requeue
|
||||
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||
QueuedPresence.Copy(local);
|
||||
}
|
||||
}
|
||||
|
||||
while (SendQueue.HavePendingSends()) {
|
||||
while (SendQueue.HavePendingSends())
|
||||
{
|
||||
auto qmessage = SendQueue.GetNextSendMessage();
|
||||
Connection->Write(qmessage->buffer, qmessage->length);
|
||||
SendQueue.CommitSend();
|
||||
@ -236,13 +245,6 @@ static void Discord_UpdateConnection(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void SignalIOActivity()
|
||||
{
|
||||
if (IoThread != nullptr) {
|
||||
IoThread->Notify();
|
||||
}
|
||||
}
|
||||
|
||||
static bool RegisterForEvent(const char* evtName)
|
||||
{
|
||||
auto qmessage = SendQueue.GetNextAddMessage();
|
||||
@ -250,7 +252,8 @@ static bool RegisterForEvent(const char* evtName)
|
||||
qmessage->length =
|
||||
JsonWriteSubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
|
||||
SendQueue.CommitAdd();
|
||||
SignalIOActivity();
|
||||
if (IoThread)
|
||||
IoThread->Notify();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -263,7 +266,8 @@ static bool DeregisterForEvent(const char* evtName)
|
||||
qmessage->length =
|
||||
JsonWriteUnsubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
|
||||
SendQueue.CommitAdd();
|
||||
SignalIOActivity();
|
||||
if (IoThread)
|
||||
IoThread->Notify();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -308,31 +312,31 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
||||
}
|
||||
|
||||
Connection = RpcConnection::Create(applicationId);
|
||||
Connection->onConnect = [](JsonDocument& readyMessage) {
|
||||
Connection->onConnect = [](JsonDocument& readyMessage)
|
||||
{
|
||||
Discord_UpdateHandlers(&QueuedHandlers);
|
||||
auto data = GetObjMember(&readyMessage, "data");
|
||||
auto user = GetObjMember(data, "user");
|
||||
auto userId = GetStrMember(user, "id");
|
||||
auto username = GetStrMember(user, "username");
|
||||
auto avatar = GetStrMember(user, "avatar");
|
||||
if (userId && username) {
|
||||
if (userId && username)
|
||||
{
|
||||
StringCopy(connectedUser.userId, userId);
|
||||
StringCopy(connectedUser.username, username);
|
||||
auto discriminator = GetStrMember(user, "discriminator");
|
||||
if (discriminator) {
|
||||
if (discriminator)
|
||||
StringCopy(connectedUser.discriminator, discriminator);
|
||||
}
|
||||
if (avatar) {
|
||||
if (avatar)
|
||||
StringCopy(connectedUser.avatar, avatar);
|
||||
}
|
||||
else {
|
||||
else
|
||||
connectedUser.avatar[0] = 0;
|
||||
}
|
||||
}
|
||||
WasJustConnected.exchange(true);
|
||||
ReconnectTimeMs.reset();
|
||||
};
|
||||
Connection->onDisconnect = [](int err, const char* message) {
|
||||
Connection->onDisconnect = [](int err, const char* message)
|
||||
{
|
||||
LastDisconnectErrorCode = err;
|
||||
StringCopy(LastDisconnectErrorMessage, message);
|
||||
{
|
||||
@ -348,13 +352,13 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
||||
|
||||
extern "C" DISCORD_EXPORT void Discord_Shutdown(void)
|
||||
{
|
||||
if (!Connection) {
|
||||
if (!Connection)
|
||||
return;
|
||||
}
|
||||
Connection->onConnect = nullptr;
|
||||
Connection->onConnect = nullptr;
|
||||
Connection->onDisconnect = nullptr;
|
||||
Handlers = {};
|
||||
if (IoThread != nullptr) {
|
||||
if (IoThread != nullptr)
|
||||
{
|
||||
IoThread->Stop();
|
||||
delete IoThread;
|
||||
IoThread = nullptr;
|
||||
@ -370,7 +374,8 @@ extern "C" DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence*
|
||||
QueuedPresence.length = JsonWriteRichPresenceObj(
|
||||
QueuedPresence.buffer, sizeof(QueuedPresence.buffer), Nonce++, Pid, presence);
|
||||
}
|
||||
SignalIOActivity();
|
||||
if (IoThread)
|
||||
IoThread->Notify();
|
||||
}
|
||||
|
||||
extern "C" DISCORD_EXPORT void Discord_ClearPresence(void)
|
||||
@ -381,15 +386,16 @@ extern "C" DISCORD_EXPORT void Discord_ClearPresence(void)
|
||||
extern "C" DISCORD_EXPORT 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()) {
|
||||
if (!Connection || !Connection->IsOpen())
|
||||
return;
|
||||
}
|
||||
auto qmessage = SendQueue.GetNextAddMessage();
|
||||
if (qmessage) {
|
||||
if (qmessage)
|
||||
{
|
||||
qmessage->length =
|
||||
JsonWriteJoinReply(qmessage->buffer, sizeof(qmessage->buffer), userId, reply, Nonce++);
|
||||
SendQueue.CommitAdd();
|
||||
SignalIOActivity();
|
||||
if (IoThread)
|
||||
IoThread->Notify();
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,24 +405,25 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
|
||||
// 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.
|
||||
|
||||
if (!Connection) {
|
||||
if (!Connection)
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasDisconnected = WasJustDisconnected.exchange(false);
|
||||
bool isConnected = Connection->IsOpen();
|
||||
bool isConnected = Connection->IsOpen();
|
||||
|
||||
if (isConnected) {
|
||||
if (isConnected)
|
||||
{
|
||||
// if we are connected, disconnect cb first
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
if (wasDisconnected && Handlers.disconnected) {
|
||||
if (wasDisconnected && Handlers.disconnected)
|
||||
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (WasJustConnected.exchange(false)) {
|
||||
if (WasJustConnected.exchange(false))
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
if (Handlers.ready) {
|
||||
if (Handlers.ready)
|
||||
{
|
||||
DiscordUser du{connectedUser.userId,
|
||||
connectedUser.username,
|
||||
connectedUser.discriminator,
|
||||
@ -425,25 +432,25 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (GotErrorMessage.exchange(false)) {
|
||||
if (GotErrorMessage.exchange(false))
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
if (Handlers.errored) {
|
||||
if (Handlers.errored)
|
||||
Handlers.errored(LastErrorCode, LastErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (WasJoinGame.exchange(false)) {
|
||||
if (WasJoinGame.exchange(false))
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
if (Handlers.joinGame) {
|
||||
if (Handlers.joinGame)
|
||||
Handlers.joinGame(JoinGameSecret);
|
||||
}
|
||||
}
|
||||
|
||||
if (WasSpectateGame.exchange(false)) {
|
||||
if (WasSpectateGame.exchange(false))
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
if (Handlers.spectateGame) {
|
||||
if (Handlers.spectateGame)
|
||||
Handlers.spectateGame(SpectateGameSecret);
|
||||
}
|
||||
}
|
||||
|
||||
// Right now this batches up any requests and sends them all in a burst; I could imagine a world
|
||||
@ -451,11 +458,13 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
|
||||
// is sent. I left it this way because I could also imagine wanting to process these all and
|
||||
// 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()) {
|
||||
while (JoinAskQueue.HavePendingSends())
|
||||
{
|
||||
auto req = JoinAskQueue.GetNextSendMessage();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
if (Handlers.joinRequest) {
|
||||
if (Handlers.joinRequest)
|
||||
{
|
||||
DiscordUser du{req->userId, req->username, req->discriminator, req->avatar};
|
||||
Handlers.joinRequest(&du);
|
||||
}
|
||||
@ -463,38 +472,37 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
|
||||
JoinAskQueue.CommitSend();
|
||||
}
|
||||
|
||||
if (!isConnected) {
|
||||
if (!isConnected)
|
||||
{
|
||||
// if we are not connected, disconnect message last
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
if (wasDisconnected && Handlers.disconnected) {
|
||||
if (wasDisconnected && Handlers.disconnected)
|
||||
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HANDLE_EVENT_REGISTRATION
|
||||
#define HANDLE_EVENT_REGISTRATION(handler_name, event) \
|
||||
if (!Handlers.handler_name && newHandlers->handler_name) \
|
||||
RegisterForEvent(event); \
|
||||
else if (Handlers.handler_name && !newHandlers->handler_name) \
|
||||
DeregisterForEvent(event)
|
||||
#endif
|
||||
|
||||
extern "C" DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* newHandlers)
|
||||
{
|
||||
if (newHandlers) {
|
||||
#define HANDLE_EVENT_REGISTRATION(handler_name, event) \
|
||||
if (!Handlers.handler_name && newHandlers->handler_name) { \
|
||||
RegisterForEvent(event); \
|
||||
} \
|
||||
else if (Handlers.handler_name && !newHandlers->handler_name) { \
|
||||
DeregisterForEvent(event); \
|
||||
}
|
||||
if (newHandlers)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
HANDLE_EVENT_REGISTRATION(joinGame, "ACTIVITY_JOIN");
|
||||
HANDLE_EVENT_REGISTRATION(spectateGame, "ACTIVITY_SPECTATE");
|
||||
HANDLE_EVENT_REGISTRATION(joinRequest, "ACTIVITY_JOIN_REQUEST");
|
||||
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
HANDLE_EVENT_REGISTRATION(joinGame, "ACTIVITY_JOIN")
|
||||
HANDLE_EVENT_REGISTRATION(spectateGame, "ACTIVITY_SPECTATE")
|
||||
HANDLE_EVENT_REGISTRATION(joinRequest, "ACTIVITY_JOIN_REQUEST")
|
||||
|
||||
#undef HANDLE_EVENT_REGISTRATION
|
||||
|
||||
Handlers = *newHandlers;
|
||||
}
|
||||
else {
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
Handlers = {};
|
||||
}
|
||||
return;
|
||||
Handlers = *newHandlers;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||
Handlers = {};
|
||||
}
|
||||
}
|
||||
|
8
deps/discord-rpc/src/dllmain.cpp
vendored
8
deps/discord-rpc/src/dllmain.cpp
vendored
@ -1,8 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
// outsmart GCC's missing-declarations warning
|
||||
BOOL WINAPI DllMain(HMODULE, DWORD, LPVOID);
|
||||
BOOL WINAPI DllMain(HMODULE, DWORD, LPVOID)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
9
deps/discord-rpc/src/msg_queue.h
vendored
9
deps/discord-rpc/src/msg_queue.h
vendored
@ -2,8 +2,8 @@
|
||||
|
||||
#include <atomic>
|
||||
|
||||
// A simple queue. No locks, but only works with a single thread as producer and a single thread as
|
||||
// a consumer. Mutex up as needed.
|
||||
/* A simple queue. No locks, but only works with a single thread as producer and a single thread as
|
||||
* a consumer. Mutex up as needed. */
|
||||
|
||||
template <typename ElementType, size_t QueueSize>
|
||||
class MsgQueue {
|
||||
@ -17,10 +17,9 @@ public:
|
||||
|
||||
ElementType* GetNextAddMessage()
|
||||
{
|
||||
// if we are falling behind, bail
|
||||
if (pendingSends_.load() >= QueueSize) {
|
||||
/* if we are falling behind, bail */
|
||||
if (pendingSends_.load() >= QueueSize)
|
||||
return nullptr;
|
||||
}
|
||||
auto index = (nextAdd_++) % QueueSize;
|
||||
return &queue_[index];
|
||||
}
|
||||
|
129
deps/discord-rpc/src/rpc_connection.cpp
vendored
129
deps/discord-rpc/src/rpc_connection.cpp
vendored
@ -22,50 +22,49 @@ static RpcConnection Instance;
|
||||
|
||||
void RpcConnection::Open()
|
||||
{
|
||||
if (state == State::Connected) {
|
||||
if (state == State::Connected)
|
||||
return;
|
||||
|
||||
if (state == State::Disconnected)
|
||||
{
|
||||
if (!connection->Open())
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == State::Disconnected) {
|
||||
if (connection->Open()) {
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
if (state == State::SentHandshake)
|
||||
{
|
||||
JsonDocument message;
|
||||
if (Read(message))
|
||||
{
|
||||
auto cmd = GetStrMember(&message, "cmd");
|
||||
auto evt = GetStrMember(&message, "evt");
|
||||
if (cmd && evt
|
||||
&& !strcmp(cmd, "DISPATCH")
|
||||
&& !strcmp(evt, "READY"))
|
||||
{
|
||||
state = State::Connected;
|
||||
if (onConnect)
|
||||
onConnect(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == State::SentHandshake) {
|
||||
JsonDocument message;
|
||||
if (Read(message)) {
|
||||
auto cmd = GetStrMember(&message, "cmd");
|
||||
auto evt = GetStrMember(&message, "evt");
|
||||
if (cmd && evt && !strcmp(cmd, "DISPATCH") && !strcmp(evt, "READY")) {
|
||||
state = State::Connected;
|
||||
if (onConnect) {
|
||||
onConnect(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
sendFrame.opcode = Opcode::Handshake;
|
||||
sendFrame.length = (uint32_t)JsonWriteHandshakeObj(
|
||||
sendFrame.message, sizeof(sendFrame.message), RpcVersion, appId);
|
||||
|
||||
if (connection->Write(&sendFrame, sizeof(MessageFrameHeader) + sendFrame.length)) {
|
||||
if (connection->Write(&sendFrame, sizeof(MessageFrameHeader) + sendFrame.length))
|
||||
state = State::SentHandshake;
|
||||
}
|
||||
else {
|
||||
else
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RpcConnection::Close()
|
||||
{
|
||||
if (onDisconnect && (state == State::Connected || state == State::SentHandshake)) {
|
||||
if (onDisconnect && (state == State::Connected || state == State::SentHandshake))
|
||||
onDisconnect(lastErrorCode, lastErrorMessage);
|
||||
}
|
||||
connection->Close();
|
||||
state = State::Disconnected;
|
||||
}
|
||||
@ -75,7 +74,8 @@ bool RpcConnection::Write(const void* data, size_t length)
|
||||
sendFrame.opcode = Opcode::Frame;
|
||||
memcpy(sendFrame.message, data, length);
|
||||
sendFrame.length = (uint32_t)length;
|
||||
if (!connection->Write(&sendFrame, sizeof(MessageFrameHeader) + length)) {
|
||||
if (!connection->Write(&sendFrame, sizeof(MessageFrameHeader) + length))
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
@ -84,14 +84,16 @@ bool RpcConnection::Write(const void* data, size_t length)
|
||||
|
||||
bool RpcConnection::Read(JsonDocument& message)
|
||||
{
|
||||
if (state != State::Connected && state != State::SentHandshake) {
|
||||
if (state != State::Connected && state != State::SentHandshake)
|
||||
return false;
|
||||
}
|
||||
MessageFrame readFrame;
|
||||
for (;;) {
|
||||
for (;;)
|
||||
{
|
||||
bool didRead = connection->Read(&readFrame, sizeof(MessageFrameHeader));
|
||||
if (!didRead) {
|
||||
if (!connection->isOpen) {
|
||||
if (!didRead)
|
||||
{
|
||||
if (!connection->isOpen)
|
||||
{
|
||||
lastErrorCode = (int)ErrorCode::PipeClosed;
|
||||
StringCopy(lastErrorMessage, "Pipe closed");
|
||||
Close();
|
||||
@ -99,9 +101,11 @@ bool RpcConnection::Read(JsonDocument& message)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (readFrame.length > 0) {
|
||||
if (readFrame.length > 0)
|
||||
{
|
||||
didRead = connection->Read(readFrame.message, readFrame.length);
|
||||
if (!didRead) {
|
||||
if (!didRead)
|
||||
{
|
||||
lastErrorCode = (int)ErrorCode::ReadCorrupt;
|
||||
StringCopy(lastErrorMessage, "Partial data in frame");
|
||||
Close();
|
||||
@ -110,32 +114,33 @@ bool RpcConnection::Read(JsonDocument& message)
|
||||
readFrame.message[readFrame.length] = 0;
|
||||
}
|
||||
|
||||
switch (readFrame.opcode) {
|
||||
case Opcode::Close: {
|
||||
message.ParseInsitu(readFrame.message);
|
||||
lastErrorCode = GetIntMember(&message, "code");
|
||||
StringCopy(lastErrorMessage, GetStrMember(&message, "message", ""));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
case Opcode::Frame:
|
||||
message.ParseInsitu(readFrame.message);
|
||||
return true;
|
||||
case Opcode::Ping:
|
||||
readFrame.opcode = Opcode::Pong;
|
||||
if (!connection->Write(&readFrame, sizeof(MessageFrameHeader) + readFrame.length)) {
|
||||
Close();
|
||||
}
|
||||
break;
|
||||
case Opcode::Pong:
|
||||
break;
|
||||
case Opcode::Handshake:
|
||||
default:
|
||||
// something bad happened
|
||||
lastErrorCode = (int)ErrorCode::ReadCorrupt;
|
||||
StringCopy(lastErrorMessage, "Bad ipc frame");
|
||||
Close();
|
||||
return false;
|
||||
switch (readFrame.opcode)
|
||||
{
|
||||
case Opcode::Close:
|
||||
{
|
||||
message.ParseInsitu(readFrame.message);
|
||||
lastErrorCode = GetIntMember(&message, "code");
|
||||
StringCopy(lastErrorMessage, GetStrMember(&message, "message", ""));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
case Opcode::Frame:
|
||||
message.ParseInsitu(readFrame.message);
|
||||
return true;
|
||||
case Opcode::Ping:
|
||||
readFrame.opcode = Opcode::Pong;
|
||||
if (!connection->Write(&readFrame, sizeof(MessageFrameHeader) + readFrame.length))
|
||||
Close();
|
||||
break;
|
||||
case Opcode::Pong:
|
||||
break;
|
||||
case Opcode::Handshake:
|
||||
default:
|
||||
// something bad happened
|
||||
lastErrorCode = (int)ErrorCode::ReadCorrupt;
|
||||
StringCopy(lastErrorMessage, "Bad ipc frame");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1526,6 +1526,15 @@ HTTP SERVER
|
||||
============================================================ */
|
||||
#if defined(HAVE_DISCORD)
|
||||
#include "../discord/discord.c"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "../deps/discord-rpc/src/discord_register_win.c"
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#include "../deps/discord-rpc/src/discord_register_linux.c"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
|
@ -144,12 +144,8 @@ FONTS
|
||||
#include "../deps/discord-rpc/src/serialization.cpp"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "../deps/discord-rpc/src/discord_register_win.cpp"
|
||||
#include "../deps/discord-rpc/src/connection_win.cpp"
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
#include "../deps/discord-rpc/src/discord_register_linux.cpp"
|
||||
#endif
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
#include "../deps/discord-rpc/src/connection_unix.cpp"
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user