diff --git a/Dotnet/AppApi/Common/AppApiCommon.cs b/Dotnet/AppApi/Common/AppApiCommon.cs index 305bb9e2..14d9c4ab 100644 --- a/Dotnet/AppApi/Common/AppApiCommon.cs +++ b/Dotnet/AppApi/Common/AppApiCommon.cs @@ -149,10 +149,11 @@ namespace VRCX return output; } - public void SetAppLauncherSettings(bool enabled, bool killOnExit) + public void SetAppLauncherSettings(bool enabled, bool killOnExit, bool runProcessOnce) { AutoAppLaunchManager.Instance.Enabled = enabled; AutoAppLaunchManager.Instance.KillChildrenOnExit = killOnExit; + AutoAppLaunchManager.Instance.RunProcessOnce = runProcessOnce; } public string GetFileBase64(string path) diff --git a/Dotnet/AutoAppLaunchManager.cs b/Dotnet/AutoAppLaunchManager.cs index 595c9354..cf5054e1 100644 --- a/Dotnet/AutoAppLaunchManager.cs +++ b/Dotnet/AutoAppLaunchManager.cs @@ -21,6 +21,7 @@ namespace VRCX public bool Enabled = false; /// Whether or not to kill child processes when VRChat closes. public bool KillChildrenOnExit = true; + public bool RunProcessOnce = true; public readonly string AppShortcutDirectory; private DateTime startTime = DateTime.Now; @@ -110,11 +111,15 @@ namespace VRCX UpdateChildProcesses(); var shortcutFiles = FindShortcutFiles(AppShortcutDirectory); - foreach (var file in shortcutFiles) { - if (!IsChildProcessRunning(file)) - StartChildProcess(file); + if (RunProcessOnce && IsProcessRunning(file)) + continue; + + if (IsChildProcessRunning(file)) + continue; + + StartChildProcess(file); } if (shortcutFiles.Length == 0) @@ -273,6 +278,20 @@ namespace VRCX { return startedProcesses.ContainsKey(path); } + + private bool IsProcessRunning(string filePath) + { + try + { + var processName = Path.GetFileNameWithoutExtension(filePath); + return Process.GetProcessesByName(processName).Length != 0; + } + catch (Exception ex) + { + logger.Error(ex, "Error checking if process is running: {0}", filePath); + return false; + } + } public void Init() { diff --git a/src/localization/en/en.json b/src/localization/en/en.json index 40613289..15d454a4 100644 --- a/src/localization/en/en.json +++ b/src/localization/en/en.json @@ -631,7 +631,8 @@ "folder": "Auto-Launch Folder", "folder_tooltip": "To auto-launch apps with VRChat, place shortcuts in this folder.", "enable": "Enable", - "auto_close": "Auto close apps" + "auto_close": "Auto close apps", + "run_process_once": "Open single instance of app" }, "cache_debug": { "header": "VRCX Instance Cache/Debug", diff --git a/src/stores/settings/advanced.js b/src/stores/settings/advanced.js index 1a9d4523..82b5a166 100644 --- a/src/stores/settings/advanced.js +++ b/src/stores/settings/advanced.js @@ -25,6 +25,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { avatarRemoteDatabase: true, enableAppLauncher: true, enableAppLauncherAutoClose: true, + enableAppLauncherRunProcessOnce: true, screenshotHelper: true, screenshotHelperModifyFilename: false, screenshotHelperCopyToClipboard: false, @@ -58,6 +59,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { avatarRemoteDatabase, enableAppLauncher, enableAppLauncherAutoClose, + enableAppLauncherRunProcessOnce, screenshotHelper, screenshotHelperModifyFilename, screenshotHelperCopyToClipboard, @@ -84,6 +86,10 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { configRepository.getBool('VRCX_avatarRemoteDatabase', true), configRepository.getBool('VRCX_enableAppLauncher', true), configRepository.getBool('VRCX_enableAppLauncherAutoClose', true), + configRepository.getBool( + 'VRCX_enableAppLauncherRunProcessOnce', + true + ), configRepository.getBool('VRCX_screenshotHelper', true), configRepository.getBool( 'VRCX_screenshotHelperModifyFilename', @@ -120,6 +126,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { state.avatarRemoteDatabase = avatarRemoteDatabase; state.enableAppLauncher = enableAppLauncher; state.enableAppLauncherAutoClose = enableAppLauncherAutoClose; + state.enableAppLauncherRunProcessOnce = enableAppLauncherRunProcessOnce; state.screenshotHelper = screenshotHelper; state.screenshotHelperModifyFilename = screenshotHelperModifyFilename; state.screenshotHelperCopyToClipboard = screenshotHelperCopyToClipboard; @@ -167,6 +174,9 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { const enableAppLauncherAutoClose = computed( () => state.enableAppLauncherAutoClose ); + const enableAppLauncherRunProcessOnce = computed( + () => state.enableAppLauncherRunProcessOnce + ); const screenshotHelper = computed(() => state.screenshotHelper); ``; const screenshotHelperModifyFilename = computed( @@ -280,6 +290,15 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { ); handleSetAppLauncherSettings(); } + async function setEnableAppLauncherRunProcessOnce() { + state.enableAppLauncherRunProcessOnce = + !state.enableAppLauncherRunProcessOnce; + await configRepository.setBool( + 'VRCX_enableAppLauncherRunProcessOnce', + state.enableAppLauncherRunProcessOnce + ); + handleSetAppLauncherSettings(); + } async function setScreenshotHelper() { state.screenshotHelper = !state.screenshotHelper; await configRepository.setBool( @@ -440,7 +459,8 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { function handleSetAppLauncherSettings() { AppApi.SetAppLauncherSettings( state.enableAppLauncher, - state.enableAppLauncherAutoClose + state.enableAppLauncherAutoClose, + state.enableAppLauncherRunProcessOnce ); } @@ -678,6 +698,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { avatarRemoteDatabase, enableAppLauncher, enableAppLauncherAutoClose, + enableAppLauncherRunProcessOnce, screenshotHelper, screenshotHelperModifyFilename, screenshotHelperCopyToClipboard, @@ -707,6 +728,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { setAvatarRemoteDatabase, setEnableAppLauncher, setEnableAppLauncherAutoClose, + setEnableAppLauncherRunProcessOnce, setScreenshotHelper, setScreenshotHelperModifyFilename, setScreenshotHelperCopyToClipboard, diff --git a/src/types/globals.d.ts b/src/types/globals.d.ts index 0b3245c7..f9ff0442 100644 --- a/src/types/globals.d.ts +++ b/src/types/globals.d.ts @@ -197,7 +197,8 @@ declare global { GetColourBulk(userIds: string[]): Promise>; SetAppLauncherSettings( enabled: boolean, - killOnExit: boolean + killOnExit: boolean, + runProcessOnce: boolean ): Promise; GetFileBase64(path: string): Promise; diff --git a/src/views/Settings/Settings.vue b/src/views/Settings/Settings.vue index 1b84af90..6436af51 100644 --- a/src/views/Settings/Settings.vue +++ b/src/views/Settings/Settings.vue @@ -1521,6 +1521,11 @@ :value="enableAppLauncherAutoClose" :long-label="true" @change="setEnableAppLauncherAutoClose" /> + @@ -2118,6 +2123,7 @@ avatarRemoteDatabase, enableAppLauncher, enableAppLauncherAutoClose, + enableAppLauncherRunProcessOnce, screenshotHelper, screenshotHelperModifyFilename, screenshotHelperCopyToClipboard, @@ -2143,6 +2149,7 @@ setAvatarRemoteDatabase, setEnableAppLauncher, setEnableAppLauncherAutoClose, + setEnableAppLauncherRunProcessOnce, setScreenshotHelper, setScreenshotHelperModifyFilename, setScreenshotHelperCopyToClipboard,