21 Commits
v1.1 ... v1.2

Author SHA1 Message Date
08feef776b Update version 2024-05-31 01:18:38 +03:00
8545467a85 Add .elf file to .gitignore 2024-05-30 05:33:28 +03:00
20728818d2 Update pipe creation parameters 2024-05-30 04:19:19 +03:00
751d9ab3b7 Add menu bar and tab navigation for GUI, --version command 2024-05-30 03:28:18 +03:00
b906c009f4 Update docs 2024-05-29 23:18:53 +03:00
38e620836e Update shebang to use bash instead of sh
"function" is a bash builtin
2024-05-29 23:11:14 +03:00
a640dd5af1 Remove unnecessary code for starting the bridge service 2024-05-29 23:06:16 +03:00
8fbe00b555 Display installation guide prompt for missing temp directory 2024-05-29 23:02:48 +03:00
bf98b8784c Fix formatting and remove unnecessary recursive rm's 2024-05-29 22:55:02 +03:00
3021f8e4ad Update installation instructions for MacOS 2024-05-29 21:01:05 +03:00
655caa2934 Update README.md 2024-05-29 20:35:52 +03:00
da2a006ffb Merge pull request #3 from OrigamingWasTaken/master
Add support for the windows service on MacOS
2024-05-29 20:32:54 +03:00
e61fbf556e Added github markdown link 2024-05-29 17:04:26 +02:00
2dce976038 Clearer instructions (pt1) 2024-05-29 17:02:13 +02:00
2fe1dd5ece Update README.md 2024-05-27 22:13:16 +02:00
c23af8300c Increase pipe buffer 2024-05-23 06:00:45 +03:00
14226489d7 Modified readme and script 2024-05-22 16:41:15 +02:00
66f3bc53f1 readme 2024-05-21 23:48:12 +02:00
dcd9cf520e Modified README and added warning in bridge.c 2024-05-21 23:46:19 +02:00
8dd5952a64 MacOS Fix 2024-05-21 23:39:51 +02:00
81aa5d8d18 Remove unused macro 2024-05-13 08:42:23 +03:00
11 changed files with 286 additions and 93 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.o
*.exe
*.res
*.elf

View File

@ -25,6 +25,8 @@ Logs are stored in `C:\windows\logs\bridge.log`.
- ![gui](docs/assets/gui.png)
- To remove, the same process can be followed, but click `Remove` instead.
*Note, an [extra step](https://github.com/EnderIce2/rpc-bridge?tab=readme-ov-file#macos) is needed on MacOS*
##### Lutris
- Click on a game and select `Run EXE inside Wine prefix`.
@ -50,9 +52,17 @@ Logs are stored in `C:\windows\logs\bridge.log`.
- Globally
- `flatpak override --user --filesystem=xdg-run/discord-ipc-0`
## macOS
##### MacOS
On macOS, follow [these instructions](https://enderice2.github.io/rpc-bridge/installation.html#macos).
The steps for MacOS are almost the same, but due to the way `$TMPDIR` works, you will have to install a **LaunchAgent**.
- Download the latest build from the [releases](https://github.com/EnderIce2/rpc-bridge/releases)
- Open the archive and make the `launchd.sh` script executable by doing: `chmod +x launchd.sh`
- To **install** the LaunchAgent, run `./launchd install` and to **remove** it simply run `./launchd remove`.
The script will add a LaunchAgent to your user, that will symlink the `$TMPDIR` directory to `/tmp/rpc-bridge/tmpdir`.
*Note: You will need to launch the `bridge.exe` file manually in Wine at least once for it to register and launch automatically the next time.*
## Compiling from source

View File

@ -43,6 +43,8 @@
__attribute__((__always_inline__, __gnu_inline__))
#define naked __attribute__((naked))
#define BUFFER_LENGTH 2048
typedef unsigned short sa_family_t;
typedef char *caddr_t;
typedef unsigned socklen_t;
@ -252,15 +254,29 @@ void ConnectToSocket(int fd)
if (IsLinux)
runtime = native_getenv("XDG_RUNTIME_DIR");
else
runtime = native_getenv("TMPDIR");
if (runtime == NULL)
{
print("IPC directory not set\n");
if (!RunningAsService)
MessageBox(NULL, "IPC directory not set",
"Environment variable not set",
MB_OK | MB_ICONSTOP);
ExitProcess(1);
runtime = native_getenv("TMPDIR");
if (runtime == NULL)
{
runtime = "/tmp/rpc-bridge/tmpdir";
print("IPC directory not set, fallback to /tmp/rpc-bridge/tmpdir\n");
// Check if the directory exists
DWORD dwAttrib = GetFileAttributes(runtime);
if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
{
print("IPC directory does not exist: %s. If you're on MacOS, see the github guide on how to install the launchd service.\n", runtime);
// Handle the case where the directory doesn't exist
// For example, create the directory
int result = MessageBox(NULL, "IPC directory does not exist\nDo you want to open the installation guide?",
"Directory not found",
MB_YESNO | MB_ICONSTOP);
if (result == IDYES)
ShellExecute(NULL, "open", "https://enderice2.github.io/rpc-bridge/installation.html#macos", NULL, NULL, SW_SHOWNORMAL);
ExitProcess(1);
}
}
}
print("IPC directory: %s\n", runtime);
@ -322,7 +338,7 @@ void PipeBufferInThread(LPVOID lpParam)
int EOFCount = 0;
while (TRUE)
{
char buffer[1024];
char buffer[BUFFER_LENGTH];
int read = sys_read(bt->fd, buffer, sizeof(buffer));
if (unlikely(read < 0))
@ -417,7 +433,7 @@ void PipeBufferOutThread(LPVOID lpParam)
bt->fd, bt->hPipe);
while (TRUE)
{
char buffer[1024];
char buffer[BUFFER_LENGTH];
DWORD dwRead;
if (unlikely(!ReadFile(bt->hPipe, buffer, sizeof(buffer),
@ -488,7 +504,7 @@ NewConnection:
CreateNamedPipe("\\\\.\\pipe\\discord-ipc-0",
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1, 1024, 1024, 0, NULL);
PIPE_UNLIMITED_INSTANCES, BUFFER_LENGTH, BUFFER_LENGTH, 0, NULL);
if (hPipe == INVALID_HANDLE_VALUE)
{

View File

@ -2,14 +2,7 @@
#include <winuser.h>
#include <winresrc.h>
#define VER_VERSION 1,1,0,0
#define VER_VERSION_STR "1.1\0"
#ifndef DEBUG
#define VER_DEBUG 0
#else
#define VER_DEBUG VS_FF_DEBUG
#endif
#include "resource.h"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_VERSION
@ -39,6 +32,22 @@ BEGIN
END
END
IDR_MAINMENU MENU
BEGIN
POPUP "&View"
BEGIN
MENUITEM "&Log", IDM_VIEW_LOG
END
POPUP "&Help"
BEGIN
MENUITEM "&Documentation", IDM_HELP_DOCUMENTATION
MENUITEM "&License", IDM_HELP_LICENSE
MENUITEM "&About", IDM_HELP_ABOUT
END
END
IDR_LICENSE_TXT RCDATA "LICENSE"
IDI_ICON_128 ICON "bridge.ico"
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST bridge.manifest

83
build/launchd.sh Executable file
View File

@ -0,0 +1,83 @@
#!/bin/bash
# This script is used to create a LaunchAgent on MacOS, to support the service functionality.
# Usage: ./launchd.sh (install|remove)
SYMLINK=/tmp/rpc-bridge/tmpdir
LOCATION=~/Library/Application\ Support/rpc-bridge
SCRIPT=$LOCATION/rpc-bridge
AGENT=~/Library/LaunchAgents/com.enderice2.rpc-bridge.plist
function install() {
# Directories
if [ ! -d "$SYMLINK" ]; then
mkdir -p "$SYMLINK"
fi
if [ ! -d "$LOCATION" ]; then
mkdir -p "$LOCATION"
fi
# Link script
if [ -f "$SCRIPT" ]; then
rm -f "$SCRIPT"
fi
echo "#!/bin/bash
TARGET_DIR=/tmp/rpc-bridge/tmpdir
if [ ! -d "\$TARGET_DIR" ]; then
mkdir -p "\$TARGET_DIR"
fi
rm -rf "\$TARGET_DIR"
ln -s "\$TMPDIR" "\$TARGET_DIR"" > "$SCRIPT"
chmod +x "$SCRIPT"
# LaunchAgent
if [ -f "$AGENT" ]; then
rm -f "$AGENT"
fi
echo "<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST File Format//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.enderice2.rpc-bridge</string>
<key>ProgramArguments</key>
<array>
<string>$SCRIPT</string>
</array>
<key>RunAtLoad</key>
<true />
</dict>
</plist>" > "$AGENT"
launchctl load "$AGENT"
echo "LaunchAgent has been installed."
}
function remove() {
rm -f "$SYMLINK"
rm -f "$SCRIPT"
rmdir "$LOCATION"
if [ -f "$AGENT" ]; then
launchctl unload "$AGENT"
fi
rm -f "$AGENT"
echo "LaunchAgent has been removed."
}
# CLI
if [ $# -eq 0 ]; then
echo "Usage: $0 (install|remove)"
exit 1
fi
case $1 in
install)
install
;;
remove)
remove
;;
*)
echo "Invalid argument. Please use 'install' or 'remove'."
exit 1
;;
esac

View File

@ -64,14 +64,16 @@ This method is recommended because it's easier to manage.
## macOS
If using the default Wine prefix (`~/.wine`), you can follow the same steps as in Wine.
The steps for MacOS are almost the same, but due to the way `$TMPDIR` works, you will have to install a **LaunchAgent**.
Registering as a service [is not supported](https://github.com/EnderIce2/rpc-bridge/issues/1#issuecomment-2103423242 "Bridge can't get $TMPDIR unless is set with --rpc. See note below").
- Download the latest build from the [releases](https://github.com/EnderIce2/rpc-bridge/releases)
- Open the archive and make the `launchd.sh` script executable by doing: `chmod +x launchd.sh`
- To **install** the LaunchAgent, run `./launchd install` and to **remove** it simply run `./launchd remove`.
The script will add a LaunchAgent to your user, that will symlink the `$TMPDIR` directory to `/tmp/rpc-bridge/tmpdir`.
!!! info "Note"
Since macOS doesn't have `/proc/self/environ` and `$TMPDIR` is neither always set nor the same after reboots, so you will need to [add `--rpc "$TMPDIR"` to the `bridge.exe` arguments](https://github.com/EnderIce2/rpc-bridge/issues/1#issuecomment-2104797235).
If you don't encounter any issues, you can ignore this step.
You will need to launch the `bridge.exe` file manually in Wine at least once for it to register and launch automatically the next time.
## Run without installing the service

View File

@ -11,7 +11,10 @@
## Commands
- `--help` Show help message
- This will show the help message
- This will show the help message
- `--version` Show version
- This will show the version of the program
- `--install` Install the service
- This will copy the binary to `C:\windows\bridge.exe` and register it as a service

115
gui.c
View File

@ -4,6 +4,8 @@
#include <assert.h>
#include <stdio.h>
#include "resource.h"
/**
* The entire code could be better written, but at least it works.
*
@ -31,7 +33,7 @@ VOID HandleStartButton(BOOL Silent)
SetWindowText(item, "Do you want to start, install or remove the bridge?");
RedrawWindow(item, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
item = GetDlgItem(hwnd, /* Start Button */ 1);
Button_SetText(item, "Start");
Button_SetText(item, "&Start");
EnableWindow(item, FALSE);
RedrawWindow(item, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
@ -47,20 +49,6 @@ VOID HandleStartButton(BOOL Silent)
return;
}
if (!IsLinux)
{
hBridge = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateBridge,
NULL, 0, NULL);
HWND item = GetDlgItem(hwnd, /* Start Button */ 1);
Button_SetText(item, "Stop");
item = GetDlgItem(hwnd, 4);
SetWindowText(item, "Bridge is running...");
IsAlreadyRunning = TRUE;
ShowWindow(hwnd, SW_MINIMIZE);
return;
}
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCManager == NULL)
{
@ -72,7 +60,7 @@ VOID HandleStartButton(BOOL Silent)
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START);
if (schService == NULL)
{
// print("OpenService failed: %s\n", GetErrorMessage());
print("Service doesn't exist: %s\n", GetErrorMessage());
/* Service doesn't exist; running without any service */
@ -80,7 +68,7 @@ VOID HandleStartButton(BOOL Silent)
NULL, 0, NULL);
HWND item = GetDlgItem(hwnd, /* Start Button */ 1);
Button_SetText(item, "Stop");
Button_SetText(item, "&Stop");
item = GetDlgItem(hwnd, 4);
SetWindowText(item, "Bridge is running...");
IsAlreadyRunning = TRUE;
@ -115,6 +103,7 @@ VOID HandleStartButton(BOOL Silent)
CloseServiceHandle(hSCManager);
if (Silent == FALSE)
MessageBox(NULL, "Bridge service started successfully", "Info", MB_OK);
print("Bridge service started successfully\n");
}
VOID HandleInstallButton()
@ -135,6 +124,44 @@ VOID HandleRemoveButton()
ExitProcess(0);
}
void ShowLicenseDialog()
{
HMODULE hModule = GetModuleHandle(NULL);
HRSRC hRes = FindResource(hModule, MAKEINTRESOURCE(IDR_LICENSE_TXT), RT_RCDATA);
if (!hRes)
{
MessageBox(NULL, "Resource not found", "Error", MB_OK | MB_ICONERROR);
return;
}
HGLOBAL hResData = LoadResource(NULL, hRes);
if (!hResData)
{
MessageBox(NULL, "Resource failed to load", "Error", MB_OK | MB_ICONERROR);
return;
}
DWORD resSize = SizeofResource(NULL, hRes);
void *pRes = LockResource(hResData);
if (!pRes)
{
MessageBox(NULL, "Resource failed to lock", "Error", MB_OK | MB_ICONERROR);
return;
}
char *licenseText = (char *)malloc(resSize + 1);
if (!licenseText)
{
MessageBox(NULL, "Memory allocation failed", "Error", MB_OK | MB_ICONERROR);
return;
}
memcpy(licenseText, pRes, resSize);
licenseText[resSize] = '\0';
MessageBoxA(hwnd, licenseText, "About", MB_OK);
free(licenseText);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
@ -152,6 +179,26 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
case 3:
HandleRemoveButton();
break;
case IDM_VIEW_LOG:
ShellExecute(NULL, "open", "C:\\windows\\notepad.exe", "C:\\windows\\logs\\bridge.log", NULL, SW_SHOW);
break;
case IDM_HELP_DOCUMENTATION:
ShellExecute(NULL, "open", "https://enderice2.github.io/rpc-bridge/index.html", NULL, NULL, SW_SHOWNORMAL);
break;
case IDM_HELP_LICENSE:
ShowLicenseDialog();
break;
case IDM_HELP_ABOUT:
{
char msg[256];
sprintf(msg, "rpc-bridge v%s\n\n"
"Simple bridge that allows you to use Discord Rich Presence with Wine games/software.\n\n"
"Created by EnderIce2\n\n"
"Licensed under the MIT License",
VER_VERSION_STR);
MessageBox(NULL, msg, "About", MB_OK);
break;
}
default:
break;
}
@ -178,16 +225,16 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
VOID SetButtonStyles(INT *btnStartStyle, INT *btnRemoveStyle, INT *btnInstallStyle)
{
*btnStartStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
*btnRemoveStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
*btnInstallStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
*btnStartStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP;
*btnRemoveStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP;
*btnInstallStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP;
if (!IsLinux)
{
*btnInstallStyle |= WS_DISABLED;
*btnRemoveStyle |= WS_DISABLED;
return;
}
// if (!IsLinux)
// {
// *btnInstallStyle |= WS_DISABLED;
// *btnRemoveStyle |= WS_DISABLED;
// return;
// }
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
SC_HANDLE schService = OpenService(hSCManager, "rpc-bridge", SERVICE_START | SERVICE_QUERY_STATUS);
@ -254,21 +301,24 @@ int WINAPI __WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
0, 15, 400, 25,
hwnd, (HMENU)4, hInstance, NULL);
HWND hbtn1 = CreateWindow("BUTTON", "Start",
HWND hbtn1 = CreateWindow("BUTTON", "&Start",
btnStartStyle,
40, 60, 100, 30,
hwnd, (HMENU)1, hInstance, NULL);
HWND hbtn2 = CreateWindow("BUTTON", "Install",
HWND hbtn2 = CreateWindow("BUTTON", "&Install",
btnInstallStyle,
150, 60, 100, 30,
hwnd, (HMENU)2, hInstance, NULL);
HWND hbtn3 = CreateWindow("BUTTON", "Remove",
HWND hbtn3 = CreateWindow("BUTTON", "&Remove",
btnRemoveStyle,
260, 60, 100, 30,
hwnd, (HMENU)3, hInstance, NULL);
HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINMENU));
SetMenu(hwnd, hMenu);
HDC hDC = GetDC(hwnd);
int nHeight = -MulDiv(11, GetDeviceCaps(hDC, LOGPIXELSY), 72);
@ -289,8 +339,11 @@ int WINAPI __WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (!IsDialogMessage(hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}

63
main.c
View File

@ -3,6 +3,8 @@
#include <assert.h>
#include <stdio.h>
#include "resource.h"
FILE *g_logFile = NULL;
BOOL RunningAsService = FALSE;
@ -72,14 +74,11 @@ void DetectWine()
{
int result = MessageBox(NULL, "This program is designed for Linux and macOS only!\nDo you want to proceed?",
NULL, MB_YESNO | MB_ICONQUESTION);
if (result == IDYES)
return;
else if (result == IDNO)
if (result == IDNO)
ExitProcess(1);
}
IsLinux = strcmp(__sysname, "Linux") == 0;
printf("Running on %s\n", __sysname);
}
void print(char const *fmt, ...)
@ -272,32 +271,40 @@ void HandleArguments(int argc, char *argv[])
CreateBridge();
ExitProcess(0);
}
else if (strcmp(argv[1], "--version") == 0)
{
printf("%s\n", VER_VERSION_STR);
ExitProcess(0);
}
else if (strcmp(argv[1], "--help") == 0)
{
printf("Usage:\n");
printf(" %s [args]\n\n", argv[0]);
printf("Arguments:\n");
printf(" --help Show this help\n\n");
printf(" --install Install service\n");
printf(" This will copy the binary to C:\\windows\\bridge.exe and register it as a service\n\n");
printf(" --uninstall Uninstall service\n");
printf(" This will remove the service and delete C:\\windows\\bridge.exe\n\n");
printf(" --steam Reserved for Steam\n");
printf(" This will start the service and exit (used with bridge.sh)\n\n");
printf(" --no-service Do not run as service\n");
printf(" (only for --steam)\n\n");
printf(" --service Reserved for service\n\n");
printf(" --rpc <dir> Set RPC_PATH environment variable\n");
printf(" This is used to specify the directory where 'discord-ipc-0' is located\n\n");
printf("Note: If no arguments are provided, the GUI will be shown instead\n");
printf("Usage:\n"
" %s [args]\n"
"\n"
"Arguments:\n"
" --help Show this help\n"
"\n"
" --version Show version\n"
"\n"
" --install Install service\n"
" This will copy the binary to C:\\windows\\bridge.exe and register it as a service\n"
"\n"
" --uninstall Uninstall service\n"
" This will remove the service and delete C:\\windows\\bridge.exe\n"
"\n"
" --steam Reserved for Steam\n"
" This will start the service and exit (used with bridge.sh)\n"
"\n"
" --no-service Do not run as service\n"
" (only for --steam)\n"
"\n"
" --service Reserved for service\n"
"\n"
" --rpc <dir> Set RPC_PATH environment variable\n"
" This is used to specify the directory where 'discord-ipc-0' is located\n"
"\n"
"Note: If no arguments are provided, the GUI will be shown instead\n",
argv[0]);
ExitProcess(0);
}
}

9
resource.h Normal file
View File

@ -0,0 +1,9 @@
#define IDR_MAINMENU 101
#define IDR_LICENSE_TXT 102
#define IDM_HELP_DOCUMENTATION 40001
#define IDM_HELP_LICENSE 40002
#define IDM_HELP_ABOUT 40003
#define IDM_VIEW_LOG 40004
#define VER_VERSION 1, 2, 0, 0
#define VER_VERSION_STR "1.2\0"

View File

@ -88,13 +88,13 @@ void InstallService(int ServiceStartType, LPCSTR Path)
{
print("Registering service\n");
if (IsLinux == FALSE)
{
/* FIXME: I don't know how to get the TMPDIR without getenv */
MessageBox(NULL, "Registering as a service is not supported on macOS at the moment.",
"Unsupported", MB_OK | MB_ICONINFORMATION);
ExitProcess(1);
}
// if (IsLinux == FALSE)
// {
// /* FIXME: I don't know how to get the TMPDIR without getenv */
// MessageBox(NULL, "Registering as a service is not supported on macOS at the moment.",
// "Unsupported", MB_OK | MB_ICONINFORMATION);
// ExitProcess(1);
// }
SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (schSCManager == NULL)