-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1ebd930
Showing
15 changed files
with
1,547 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/.cache | ||
/build | ||
/discord_game_sdk | ||
/compile_commands.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
cmake_minimum_required(VERSION 3.0.0) | ||
project(wine-rpc) | ||
|
||
add_subdirectory(src) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# wine-rpc | ||
wine-rpc is a Discord Rich Presence for any programs running under Wine. | ||
  | ||
## Why? | ||
Using the Discord app itself, you can already add anything running under Wine as your current activity. However, Discord is not so good at keeping track of Wine programs; after a while, the program won't get detected anymore and you would have to add the program again. | ||
|
||
Using a rich presence, we can literally put anything as your activity without relying on Discord to detect it. And as a bonus, supported apps could have their icon displayed on the rich presence. You could also have multiple presences for multiple apps at the same time. | ||
## Installing | ||
wine-rpc requires no installation. Simple download a prebuilt binary from the [Releases](https://github.com/LeadRDRK/wine-rpc/releases) page and extract it anywhere or build it yourself. | ||
|
||
Please put it somewhere with write access; wine-rpc keeps it simple by creating the config file in the same folder. | ||
|
||
You must also download the [app list](https://github.com/LeadRDRK/wine-rpc-app-list) yourself! It is not included in the binary packages or the source code. | ||
## Usage | ||
Using wine-rpc is fairly simple, simply open up a terminal and run the program. The program will now scan for any running Wine processes, check if it's in the app list, then show the rich presence. Any apps not in the app list will not be shown, but that behavior can be customized as shown in the Configuration section below. | ||
|
||
If you want it to run as a daemon, use `wine-rpc -d` to start it and `wine-rpc -k` to kill all running daemons. | ||
|
||
You should run the daemon at startup too. It's pretty lightweight and won't do much until a program of its interest starts, so having it in the background at all times is perfectly fine. | ||
## Configuration | ||
After running wine-rpc for the first time, a file named config.txt will be created. This file can be used to set a few options: | ||
- `show_unrecognized_apps` - By default, only apps that have an entry in the app list will be shown. Setting this option to 1 will show all running processes (including any background processes that you might have, except for Wine services) | ||
- `hide_exec_name` - If this is set to 1, when an app has an entry in the app list, the executable's name will be hidden and only the app's name will be shown. | ||
## App Support | ||
The names and icons of all applications are taken from the app list. If you don't see your favorite application in the app list, please create an issue in https://github.com/LeadRDRK/wine-rpc-app-list so I could add it for you. Every kind of application are supported, so please don't refrain yourself from requesting anything. | ||
|
||
You could also add it to the app list yourself, but please prefer the former so everyone could have a rich presence for the same app! | ||
## Compiling | ||
wine-rpc requires the Discord Game SDK which is not included in this repository. Download it from [here](https://discord.com/developers/docs/game-sdk/sdk-starter-guide) and extract it into `discord_game_sdk` in the project's root. | ||
|
||
CMake is used for generating the build files. Create a `build` folder in the project's root and run `cmake ..` to generate them, then run the build system. | ||
|
||
The full procedure should look something like this: | ||
```bash | ||
cd wine-rpc | ||
mkdir build | ||
cd build | ||
cmake .. -DCMAKE_BUILD_TYPE=Release | ||
make | ||
``` | ||
## License | ||
This project is licensed under the [GNU GPLv3](LICENSE) license. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# detect system processor for lib folder | ||
if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86_64|ia64") | ||
set(ARCH_DIR "x86_64") | ||
elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86|i686|i386") | ||
set(ARCH_DIR "x86") | ||
else() | ||
message(FATAL_ERROR "Processor architecture not supported by the Discord Game SDK") | ||
endif() | ||
|
||
# check if it exists | ||
set(DISCORD_LIB_PATH "${CMAKE_SOURCE_DIR}/discord_game_sdk/lib/${ARCH_DIR}/discord_game_sdk.so") | ||
if(NOT EXISTS "${DISCORD_LIB_PATH}") | ||
message(FATAL_ERROR "Discord Game SDK library not found!") | ||
endif() | ||
# copy the library to the build directory | ||
file(COPY ${DISCORD_LIB_PATH} DESTINATION ${CMAKE_BINARY_DIR}) | ||
add_library(discord_game_sdk SHARED IMPORTED) | ||
set_target_properties(discord_game_sdk PROPERTIES IMPORTED_LOCATION discord_game_sdk.so) | ||
|
||
set(WINE_RPC_SOURCES | ||
app_list.c | ||
config.c | ||
main.c | ||
procs.c | ||
) | ||
|
||
set(WINE_RPC_HEADERS | ||
app_list.h | ||
config.h | ||
procs.h | ||
) | ||
|
||
add_executable(wine-rpc ${WINE_RPC_SOURCES} ${WINE_RPC_HEADERS}) | ||
target_include_directories(wine-rpc PRIVATE "${CMAKE_SOURCE_DIR}/discord_game_sdk/c") | ||
target_link_libraries(wine-rpc discord_game_sdk) | ||
set_target_properties(wine-rpc PROPERTIES | ||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" | ||
INSTALL_RPATH $ORIGIN | ||
BUILD_WITH_INSTALL_RPATH TRUE | ||
) | ||
# copy the library to the binary's directory | ||
add_custom_command(TARGET wine-rpc POST_BUILD | ||
COMMAND ${CMAKE_COMMAND} -E copy | ||
${DISCORD_LIB_PATH} | ||
"${CMAKE_BINARY_DIR}/bin" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
#include "app_list.h" | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <stdbool.h> | ||
#include <search.h> | ||
#include <fcntl.h> | ||
#include "log.h" | ||
|
||
unsigned long djb2(const unsigned char *str) | ||
{ | ||
unsigned long hash = 5381; | ||
int c; | ||
|
||
while ((c = *str++)) | ||
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ | ||
|
||
return hash; | ||
} | ||
|
||
char get_escaped_char(char c) | ||
{ | ||
// subset of c escape sequences | ||
switch (c) | ||
{ | ||
case '"': return '"'; | ||
case 'n': return '\n'; | ||
case 'r': return '\r'; | ||
case 't': return '\t'; | ||
case 'v': return '\v'; | ||
case '\'': return '\''; | ||
case '\\': return '\\'; | ||
default: | ||
LOG_ERROR("App list parser: Unknown escape sequence: \\%c", c); | ||
return '\0'; | ||
} | ||
} | ||
|
||
#define BUFFER_SIZE 10 | ||
#define MAX_STR_LENGTH 128 | ||
size_t app_list_parse_file(const char* filename, AppEntry** entries) | ||
{ | ||
FILE* fp = fopen(filename, "r"); | ||
if (fp == NULL) return -1; | ||
|
||
// allocate a buffer of 10 entries | ||
int cur_size = BUFFER_SIZE; | ||
*entries = malloc(sizeof(AppEntry) * cur_size); | ||
|
||
int depth = 0; | ||
bool in_str = false; | ||
bool in_entry = false; | ||
int entry_index = 0; | ||
int value_index = 0; | ||
bool value_entered = false; | ||
char str[128]; | ||
int str_index = 0; | ||
int c; | ||
while ((c = fgetc(fp)) != EOF) | ||
{ | ||
switch (c) | ||
{ | ||
case '[': | ||
++depth; | ||
if (depth > 2) | ||
{ | ||
LOG_ERROR("App list parser: Array too deep (> 2)\n"); | ||
free(*entries); | ||
return -1; | ||
} | ||
if (depth == 2) in_entry = true; | ||
break; | ||
|
||
case ']': | ||
--depth; | ||
if (depth < 0) | ||
{ | ||
LOG_ERROR("App list parser: Syntax error / Trailing array closure\n"); | ||
free(*entries); | ||
return -1; | ||
} | ||
if (depth == 1) | ||
{ | ||
if (value_index < 2) | ||
{ | ||
LOG_ERROR("App list parser: Insufficient entry values (expected 3, got %d)\n", value_index + 1); | ||
free(*entries); | ||
return -1; | ||
} | ||
in_entry = false; | ||
} | ||
value_index = 0; | ||
value_entered = false; | ||
break; | ||
|
||
case '"': | ||
in_str = !in_str; | ||
if (value_entered && in_str) | ||
{ | ||
LOG_ERROR("App list parser: Syntax error / Unexpected string opening\n"); | ||
free(*entries); | ||
return -1; | ||
} | ||
if (!in_str) | ||
{ | ||
// write the null terminator | ||
str[str_index++] = '\0'; | ||
|
||
AppEntry* entry = &(*entries)[entry_index]; | ||
switch (value_index) | ||
{ | ||
case 0: | ||
entry->key = djb2((const unsigned char*)str); | ||
break; | ||
case 1: | ||
memcpy(entry->name, str, str_index); | ||
break; | ||
case 2: | ||
memcpy(entry->image, str, str_index); | ||
break; | ||
} | ||
str_index = 0; | ||
value_entered = true; | ||
} | ||
break; | ||
|
||
case ',': | ||
if (in_entry && !value_entered) | ||
{ | ||
LOG_ERROR("App list parser: Syntax error / Unexpected value separator (,)\n"); | ||
free(*entries); | ||
return -1; | ||
} | ||
if (depth == 1) | ||
{ | ||
++entry_index; | ||
if (cur_size <= entry_index) | ||
{ | ||
cur_size += BUFFER_SIZE; | ||
*entries = realloc(*entries, sizeof(AppEntry) * cur_size); | ||
} | ||
} | ||
else if (depth == 2) | ||
{ | ||
value_entered = false; | ||
++value_index; | ||
} | ||
break; | ||
|
||
// ignore whitespaces | ||
case ' ': | ||
case '\n': | ||
case '\r': | ||
case '\t': | ||
if (!in_str) break; | ||
// if in string then fallthrough... | ||
|
||
default: | ||
if (in_str) | ||
{ | ||
if (c == '\\') | ||
{ | ||
char escaped = get_escaped_char(fgetc(fp)); | ||
if (escaped == '\0') | ||
return -1; | ||
str[str_index++] = escaped; | ||
} | ||
else str[str_index++] = c; | ||
|
||
if (str_index > MAX_STR_LENGTH) | ||
{ | ||
LOG_ERROR("App list parser: String too long (> %d characters)\n", MAX_STR_LENGTH); | ||
free(*entries); | ||
return -1; | ||
} | ||
break; | ||
} | ||
|
||
LOG_ERROR("App list parser: Syntax error / Invalid character '%c'\n", c); | ||
free(*entries); | ||
return -1; | ||
} | ||
} | ||
|
||
return entry_index + 1; | ||
} | ||
|
||
int compare(const void *_l, const void *_r) | ||
{ | ||
const AppEntry* l = _l; | ||
const AppEntry* r = _r; | ||
|
||
if (l->key > r->key) return 1; | ||
else if (l->key < r->key) return -1; | ||
else return 0; | ||
} | ||
|
||
int app_list_init(AppList* list) | ||
{ | ||
list->root = NULL; | ||
size_t count = app_list_parse_file("app_list.txt", &list->entries); | ||
if (count == -1) return 0; | ||
LOG_INFO("App list: Loaded %lu entries\n", count); | ||
// insert into binary tree | ||
for (int i = 0; i < count; ++i) | ||
{ | ||
tsearch(&list->entries[i], &list->root, compare); | ||
} | ||
return 1; | ||
} | ||
|
||
const AppEntry* app_list_get_entry(AppList* list, const char* exec_name) | ||
{ | ||
AppEntry e; | ||
e.key = djb2((const unsigned char*)exec_name); | ||
|
||
void* r = tfind(&e, &list->root, compare); | ||
if (r == NULL) return NULL; | ||
return (*(AppEntry**)r); | ||
} | ||
|
||
void app_list_destroy(AppList* list) | ||
{ | ||
tdelete(list->root, &list->root, compare); | ||
free(list->entries); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#pragma once | ||
#include <stddef.h> | ||
|
||
typedef struct AppEntry | ||
{ | ||
unsigned long key; | ||
char name[128]; | ||
char image[128]; | ||
} AppEntry; | ||
|
||
typedef struct AppList | ||
{ | ||
void* root; | ||
AppEntry* entries; | ||
} AppList; | ||
|
||
|
||
size_t app_list_parse_file(const char* filename, AppEntry** app_list); | ||
int app_list_init(AppList* list); | ||
const AppEntry* app_list_get_entry(AppList* list, const char* exec_name); | ||
void app_list_destroy(AppList* list); |
Oops, something went wrong.