From 21dcc51e83bb7289ab7dddd30f05dddb06b0bcdb Mon Sep 17 00:00:00 2001 From: kubectl Date: Wed, 27 Aug 2025 02:52:17 +0200 Subject: [PATCH] Fix launchcommands on Linux (#1352) * feat: expose IPC listener to electron world * feat: add listener + rouing to old function + remove old functions * feat: register vrcx prefix + route launch arguments to electron --- src-electron/main.js | 34 +++++++++++++++++++++++++++++++++- src-electron/preload.js | 17 +++++++++++++++-- src/stores/vrcx.js | 21 +++++---------------- src/types/globals.d.ts | 3 +++ 4 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src-electron/main.js b/src-electron/main.js index f5ca9e1b..421f1c25 100644 --- a/src-electron/main.js +++ b/src-electron/main.js @@ -14,8 +14,19 @@ const { spawn, spawnSync } = require('child_process'); const fs = require('fs'); const https = require('https'); +const VRCX_URI_PREFIX = "vrcx" + //app.disableHardwareAcceleration(); +if (process.defaultApp) { + if (process.argv.length >= 2) { + app.setAsDefaultProtocolClient(VRCX_URI_PREFIX, process.execPath, [path.resolve(process.argv[1])]) + } else { + app.setAsDefaultProtocolClient(VRCX_URI_PREFIX) + } +} + + if (process.platform === 'linux') { // Include bundled .NET runtime const bundledDotNetPath = path.join( @@ -105,6 +116,7 @@ ipcMain.handle('callDotNetMethod', (event, className, methodName, args) => { return interopApi.callMethod(className, methodName, args); }); +/** @type {BrowserWindow} */ let mainWindow = undefined; const VRCXStorage = interopApi.getDotNetObject('VRCXStorage'); @@ -112,6 +124,26 @@ const hasAskedToMoveAppImage = VRCXStorage.Get('VRCX_HasAskedToMoveAppImage') === 'true'; let isCloseToTray = VRCXStorage.Get('VRCX_CloseToTray') === 'true'; +const gotTheLock = app.requestSingleInstanceLock() +const strip_vrcx_prefix_regex = new RegExp("^" + VRCX_URI_PREFIX + ":\/\/") + +if (!gotTheLock) { + app.quit() +} else { + app.on('second-instance', (event, commandLine, workingDirectory) => { + if (mainWindow && commandLine.length >= 2) { + mainWindow.webContents.send('launch-command', commandLine.pop().trim().replace(strip_vrcx_prefix_regex, "")) + } + }) + + app.on('open-url', (event, url) => { + if (mainWindow && url) { + mainWindow.webContents.send('launch-command', url.replace(strip_vrcx_prefix_regex, "")) + } + }) +} + + ipcMain.handle('applyWindowSettings', (event, position, size, state) => { if (position) { mainWindow.setPosition(parseInt(position.x), parseInt(position.y)); @@ -270,7 +302,7 @@ function createWindow() { icon: path.join(rootDir, 'VRCX.png'), autoHideMenuBar: true, webPreferences: { - preload: path.join(__dirname, 'preload.js') + preload: path.join(__dirname, 'preload.js'), } }); applyWindowState(); diff --git a/src-electron/preload.js b/src-electron/preload.js index e4c58656..6207fc68 100644 --- a/src-electron/preload.js +++ b/src-electron/preload.js @@ -19,6 +19,10 @@ contextBridge.exposeInMainWorld('interopApi', { } }); + +const validChannels = ['launch-command'] + + contextBridge.exposeInMainWorld('electron', { openFileDialog: () => ipcRenderer.invoke('dialog:openFile'), openDirectoryDialog: () => ipcRenderer.invoke('dialog:openDirectory'), @@ -33,6 +37,15 @@ contextBridge.exposeInMainWorld('electron', { restartApp: () => ipcRenderer.invoke('app:restart'), getWristOverlayWindow: () => ipcRenderer.invoke('app:getWristOverlayWindow'), getHmdOverlayWindow: () => ipcRenderer.invoke('app:getHmdOverlayWindow'), - updateVr: (active, hmdOverlay, wristOverlay, menuButton, overlayHand) => - ipcRenderer.invoke('app:updateVr', active, hmdOverlay, wristOverlay, menuButton, overlayHand) + updateVr: (active, hmdOverlay, wristOverlay, menuButton, overlayHand) => + ipcRenderer.invoke('app:updateVr', active, hmdOverlay, wristOverlay, menuButton, overlayHand), + + ipcRenderer: { + on(channel, func) { + if (validChannels.includes(channel)) { + console.log("contextBridge", channel, func) + ipcRenderer.on(channel, (event, ...args) => func(...args)); + } + }, + } }); \ No newline at end of file diff --git a/src/stores/vrcx.js b/src/stores/vrcx.js index 2ffed405..573680f9 100644 --- a/src/stores/vrcx.js +++ b/src/stores/vrcx.js @@ -516,12 +516,6 @@ export const useVrcxStore = defineStore('Vrcx', () => { case 'MsgPing': state.externalNotifierVersion = data.version; break; - case 'LaunchCommand': - eventLaunchCommand(data.command); - break; - case 'VRCXLaunch': - console.log('VRCXLaunch:', data); - break; default: console.log('IPC:', data); } @@ -538,22 +532,17 @@ export const useVrcxStore = defineStore('Vrcx', () => { watch( () => watchState.isLoggedIn, - (isLoggedIn) => { + (_isLoggedIn) => { state.isRegistryBackupDialogVisible = false; - if (isLoggedIn) { - startupLaunchCommand(); - } }, { flush: 'sync' } ); - async function startupLaunchCommand() { - const command = await AppApi.GetLaunchCommand(); + window.electron.ipcRenderer.on('launch-command', (command) => { if (command) { - eventLaunchCommand(command); + eventLaunchCommand(command) } - } - + }) function eventLaunchCommand(input) { if (!watchState.isLoggedIn) { return; @@ -766,8 +755,8 @@ export const useVrcxStore = defineStore('Vrcx', () => { maxTableSize, showConsole, clearVRCXCache, - startupLaunchCommand, eventVrcxMessage, + eventLaunchCommand, showRegistryBackupDialog, checkAutoBackupRestoreVrcRegistry, tryAutoBackupVrcRegistry, diff --git a/src/types/globals.d.ts b/src/types/globals.d.ts index b4d352de..6c4d94dd 100644 --- a/src/types/globals.d.ts +++ b/src/types/globals.d.ts @@ -62,6 +62,9 @@ declare global { menuButton: bool, overlayHand: int ) => Promise; + ipcRenderer: { + on(channel: String, func: (...args: unknown[]) => void) + }; }; __APP_GLOBALS__: AppGlobals; }