Also do registering on OSX and Linux.

This commit is contained in:
Chris Marsh 2017-09-14 08:59:32 -07:00
parent 11e74bca5e
commit a5a56bcf68
9 changed files with 253 additions and 27 deletions

11
build.py Normal file → Executable file
View file

@ -1,3 +1,5 @@
#!/usr/bin/env python
import click
import os
import subprocess
@ -45,7 +47,7 @@ def build_lib(build_name, generator, options):
def create_archive():
archive_file_path = os.path.join(SCRIPT_PATH, 'builds', 'discord-rpc.zip')
archive_file_path = os.path.join(SCRIPT_PATH, 'builds', 'discord-rpc-%s.zip' % sys.platform)
archive_file = zipfile.ZipFile(archive_file_path, 'w', zipfile.ZIP_DEFLATED)
archive_src_base_path = os.path.join(SCRIPT_PATH, 'builds', 'install')
archive_dst_base_path = 'discord-rpc'
@ -64,6 +66,8 @@ def main(clean):
if clean:
shutil.rmtree('builds', ignore_errors=True)
mkdir_p('builds')
if sys.platform.startswith('win'):
generator32 = 'Visual Studio 14 2015'
generator64 = 'Visual Studio 14 2015 Win64'
@ -79,9 +83,12 @@ def main(clean):
shutil.copy(src_dll, dst_dll)
dst_dll = os.path.join(SCRIPT_PATH, 'examples', 'unrealstatus', 'Plugins', 'discordrpc', 'Binaries', 'ThirdParty', 'discordrpcLibrary', 'Win64', 'discord-rpc.dll')
shutil.copy(src_dll, dst_dll)
elif sys.platform == 'darwin':
build_lib('osx-static', None, {})
build_lib('osx-dynamic', None, {'BUILD_DYNAMIC_LIB': True})
create_archive()
if __name__ == '__main__':
sys.exit(main())
sys.exit(main())

View file

@ -10,7 +10,7 @@ By committing to an RPC-only integration, you decide to forego the work our libr
## Application Protocol Registration
One thing that cannot be explicitly done over RPC is registering an application protocol for your game. If you choose to do an RPC-only implementation, you will have to register your application protocol yourself in the format of `discord-[your_app_id]://`. You can use `Discord_Register()` from `src/discord-register.cpp` as a good example of how to properly register an application protocol for use with Discord.
One thing that cannot be explicitly done over RPC is registering an application protocol for your game. If you choose to do an RPC-only implementation, you will have to register your application protocol yourself in the format of `discord-[your_app_id]://`. You can use `Discord_Register()` as a good(?) example of how to properly register an application protocol for use with Discord. For OSX and Linux it is probably simpler to handle the protocol registration as part of your install/packaging.
## New RPC Command

View file

@ -1,5 +1,13 @@
include_directories(${PROJECT_SOURCE_DIR}/include)
add_executable(send-presence send-presence.c)
add_executable(
send-presence
MACOSX_BUNDLE
send-presence.c
)
set_target_properties(send-presence PROPERTIES
MACOSX_BUNDLE_BUNDLE_NAME "Send Presence"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.discordapp.examples.send-presence"
)
target_link_libraries(send-presence discord-rpc)
install(
@ -7,4 +15,7 @@ install(
RUNTIME
DESTINATION "bin"
CONFIGURATIONS Release
BUNDLE
DESTINATION "bin"
CONFIGURATIONS Release
)

View file

@ -6,8 +6,7 @@ option(BUILD_DYNAMIC_LIB "Build library as a DLL" OFF)
set(BASE_RPC_SRC
${PROJECT_SOURCE_DIR}/include/discord-rpc.h
discord-rpc.cpp
discord-register.h
discord-register.cpp
discord_register.h
rpc_connection.h
rpc_connection.cpp
serialization.h
@ -18,20 +17,39 @@ set(BASE_RPC_SRC
if (${BUILD_DYNAMIC_LIB})
set(RPC_LIBRARY_TYPE SHARED)
set(BASE_RPC_SRC ${BASE_RPC_SRC} dllmain.cpp)
if(WIN32)
set(BASE_RPC_SRC ${BASE_RPC_SRC} dllmain.cpp)
endif(WIN32)
else(${BUILD_DYNAMIC_LIB})
set(RPC_LIBRARY_TYPE STATIC)
endif(${BUILD_DYNAMIC_LIB})
if(WIN32)
add_library(discord-rpc ${RPC_LIBRARY_TYPE} ${BASE_RPC_SRC} connection_win.cpp)
add_definitions(-DDISCORD_WINDOWS)
set(BASE_RPC_SRC ${BASE_RPC_SRC} connection_win.cpp discord_register_win.cpp)
add_library(discord-rpc ${RPC_LIBRARY_TYPE} ${BASE_RPC_SRC})
target_compile_options(discord-rpc PRIVATE /W4)
endif(WIN32)
if(UNIX)
add_library(discord-rpc ${RPC_LIBRARY_TYPE} ${BASE_RPC_SRC} connection_unix.cpp)
set(BASE_RPC_SRC ${BASE_RPC_SRC} connection_unix.cpp)
if (APPLE)
add_definitions(-DDISCORD_OSX)
set(BASE_RPC_SRC ${BASE_RPC_SRC} discord_register_osx.m)
else (APPLE)
add_definitions(-DDISCORD_LINUX)
set(BASE_RPC_SRC ${BASE_RPC_SRC} discord_register_linux.cpp)
endif(APPLE)
add_library(discord-rpc ${RPC_LIBRARY_TYPE} ${BASE_RPC_SRC})
target_link_libraries(discord-rpc PUBLIC pthread)
target_compile_options(discord-rpc PRIVATE -g -Wall -std=c++14)
target_compile_options(discord-rpc PRIVATE -g -Wall)
target_compile_options(discord-rpc PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-std=c++14>)
if (APPLE)
target_link_libraries(discord-rpc PRIVATE "-framework AppKit")
endif (APPLE)
endif(UNIX)
target_include_directories(discord-rpc PRIVATE ${RAPIDJSON}/include)

View file

@ -1,7 +1,7 @@
#include "discord-rpc.h"
#include "backoff.h"
#include "discord-register.h"
#include "discord_register.h"
#include "rpc_connection.h"
#include "serialization.h"
@ -93,7 +93,7 @@ static void SendQueueCommitMessage()
SendQueuePendingSends++;
}
extern "C" void Discord_UpdateConnection()
DISCORD_EXPORT void Discord_UpdateConnection()
{
if (!Connection) {
return;
@ -210,7 +210,7 @@ bool RegisterForEvent(const char* evtName)
return false;
}
extern "C" void Discord_Initialize(const char* applicationId,
DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers,
int autoRegister,
const char* optionalSteamId)
@ -263,7 +263,7 @@ extern "C" void Discord_Initialize(const char* applicationId,
#endif
}
extern "C" void Discord_Shutdown()
DISCORD_EXPORT void Discord_Shutdown()
{
if (!Connection) {
return;
@ -281,7 +281,7 @@ extern "C" void Discord_Shutdown()
RpcConnection::Destroy(Connection);
}
extern "C" void Discord_UpdatePresence(const DiscordRichPresence* presence)
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence)
{
PresenceMutex.lock();
QueuedPresence.length = JsonWriteRichPresenceObj(
@ -290,7 +290,7 @@ extern "C" void Discord_UpdatePresence(const DiscordRichPresence* presence)
SignalIOActivity();
}
extern "C" void Discord_RunCallbacks()
DISCORD_EXPORT 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

View file

@ -1,4 +1,12 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void Discord_Register(const char* applicationId, const char* command);
void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,96 @@
#include "discord-rpc.h"
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
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" 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]) {
if (readlink("/proc/self/exe", exePath, sizeof(exePath)) <= 0) {
return;
}
command = exePath;
}
const char* destopFileFormat = "[Desktop Entry]\n"
"Name=Game %s\n"
"Exec=%s %%u\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);
system(xdgMimeCommand);
}
extern "C" void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
{
char command[256];
sprintf(command, "xdg-open steam://run/%s", steamId);
Discord_Register(applicationId, command);
}

View file

@ -0,0 +1,95 @@
#include <stdio.h>
#include <sys/stat.h>
#import <AppKit/AppKit.h>
static bool Mkdir(const char* path)
{
int result = mkdir(path, 0755);
if (result == 0) {
return true;
}
if (errno == EEXIST) {
return true;
}
return false;
}
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)
const char* home = getenv("HOME");
if (!home) {
return;
}
char path[2048];
sprintf(path, "%s/Library/Application Support/discord", home);
Mkdir(path);
strcat(path, "/games");
Mkdir(path);
strcat(path, "/");
strcat(path, applicationId);
strcat(path, ".json");
FILE* f = fopen(path, "w");
if (f) {
char jsonBuffer[2048];
int len = snprintf(jsonBuffer, sizeof(jsonBuffer), "{\"command\": \"%s\"}", command);
fwrite(jsonBuffer, len, 1, f);
fclose(f);
}
}
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
void* pool = [[NSAutoreleasePool alloc] init];
RegisterURL(applicationId);
[(id)pool drain];
}
}
void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
{
char command[256];
sprintf(command, "steam://run/%s", steamId);
Discord_Register(applicationId, command);
}

View file

@ -1,7 +1,6 @@
#include "discord-rpc.h"
#include <stdio.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMCX
#define NOSERVICE
@ -10,9 +9,7 @@
#include <Psapi.h>
#include <Strsafe.h>
#pragma comment(lib, "Psapi.lib")
#endif
#ifdef _WIN32
void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
{
// https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
@ -76,12 +73,9 @@ void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
}
RegCloseKey(key);
}
#endif
void Discord_Register(const char* applicationId, const char* command)
extern "C" void Discord_Register(const char* applicationId, const char* command)
{
#ifdef _WIN32
wchar_t appId[32];
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
@ -94,12 +88,10 @@ void Discord_Register(const char* applicationId, const char* command)
}
Discord_RegisterW(appId, wcommand);
#endif
}
void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
extern "C" void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
{
#ifdef _WIN32
wchar_t appId[32];
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
@ -133,5 +125,4 @@ void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
StringCbPrintfW(command, sizeof(command), L"\"%s\" steam://run/%s", steamPath, wSteamId);
Discord_RegisterW(appId, command);
#endif
}