From 55ea7552e0af65adbaa1299f57035f151d0643ff Mon Sep 17 00:00:00 2001 From: Ross Brunton Date: Wed, 2 Oct 2024 23:58:46 +0100 Subject: [PATCH] Option to use Flatpaks under Linux This adds a configuration setting "linuxUseFlatpak" which controls whether Steam should be launched as a Flatpak or not. By default this is detected automatically based on the path, but an option in the settings can override the behaviour. If this option is enabled, we first check if we are able to read from our config directory. If not, we display an error and ask the user to give us permission. If we do, then we launch Steam through flatpak rather than the binary directly. --- .../settings-components/SettingsView.vue | 17 ++++++++++ src/pages/Manager.vue | 7 ++++ .../runners/linux/SteamGameRunner_Linux.ts | 33 ++++++++++++++++--- src/r2mm/manager/ManagerSettings.ts | 5 +++ src/r2mm/manager/SettingsDexieStore.ts | 2 ++ 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/components/settings-components/SettingsView.vue b/src/components/settings-components/SettingsView.vue index 0b5b3551e..1cb286b9e 100644 --- a/src/components/settings-components/SettingsView.vue +++ b/src/components/settings-components/SettingsView.vue @@ -299,6 +299,23 @@ let settingsList = [ 'fa-file-alt', () => emitInvoke('ShowDependencyStrings') ), + new SettingsRow( + 'Other', + 'Launch as Flatpak (Linux)', + 'Launch Steam via flatpak', + async () => { + switch (settings.value.getContext().global.linuxUseFlatpak) { + case null: + return 'Automatic'; + case true: + return 'Flatpak'; + case false: + return 'Native'; + } + }, + 'fa-exchange-alt', + () => emitInvoke('ToggleLinuxUseFlatpak') + ), ]; watch(search, () => { diff --git a/src/pages/Manager.vue b/src/pages/Manager.vue index 1d6ad6028..a96a84a66 100644 --- a/src/pages/Manager.vue +++ b/src/pages/Manager.vue @@ -338,6 +338,10 @@ function setFunkyMode(value: boolean) { settings.value.setFunkyMode(value); } +function setLinuxUseFlatpak(value: boolean) { + settings.value.setLinuxUseFlatpak(value); +} + function browseDataFolder() { LinkProvider.instance.openLink('file://' + PathResolver.ROOT); } @@ -511,6 +515,9 @@ async function handleSettingsCallbacks(invokedSetting: any) { case "ToggleFunkyMode": setFunkyMode(!settings.value.getContext().global.funkyModeEnabled); break; + case "ToggleLinuxUseFlatpak": + setLinuxUseFlatpak(!settings.value.getContext().global.linuxUseFlatpak); + break; case "SwitchTheme": toggleDarkTheme(); document.documentElement.classList.toggle('html--dark', settings.value.getContext().global.darkTheme); diff --git a/src/r2mm/launching/runners/linux/SteamGameRunner_Linux.ts b/src/r2mm/launching/runners/linux/SteamGameRunner_Linux.ts index c4f61684c..0a9d5580e 100644 --- a/src/r2mm/launching/runners/linux/SteamGameRunner_Linux.ts +++ b/src/r2mm/launching/runners/linux/SteamGameRunner_Linux.ts @@ -55,26 +55,51 @@ export default class SteamGameRunner_Linux extends GameRunnerProvider { if (args instanceof R2Error) { return args } - return this.start(game, args); + return this.start(game, args, profile.getProfilePath()); } public async startVanilla(game: Game, profile: Profile): Promise { const instructions = await GameInstructions.getInstructionsForGame(game, profile); - return this.start(game, instructions.vanillaParameters); + return this.start(game, instructions.vanillaParameters, null); } - async start(game: Game, args: string): Promise { + async start(game: Game, args: string, profiledir: string | null): Promise { const settings = await ManagerSettings.getSingleton(game); const steamDir = await GameDirectoryResolverProvider.instance.getSteamDirectory(); if(steamDir instanceof R2Error) { return steamDir; } + let as_flatpak = settings.getContext().global.linuxUseFlatpak; + if (as_flatpak === null) { + // Assume that if the path contains the flatpak directory we must want to use flatpak + as_flatpak = steamDir.includes("/com.valvesoftware.Steam/"); + } + + if (as_flatpak && profiledir) { + // Ensure we have permission to read our config dir + let visible = true; + const check_cmd = `flatpak run --command='sh' com.valvesoftware.Steam -c 'ls "${profiledir}"'`; + await ChildProcess.exec(check_cmd, undefined, (err: Error) => { + if (err) { + visible = false; + } + }); + if (!visible) { + throw new R2Error('Flatpak Permissions Error', + `The directory "${profiledir}" is not visible from within the Flatpak container`, + '`flatpak override com.valvesoftware.Steam --user --filesystem="${XDG_CONFIG_HOME:-$HOME/.config}/r2modmanPlus-local"` to grant access, then restart Steam.'); + } + } LoggerProvider.instance.Log(LogSeverity.INFO, `Steam folder is: ${steamDir}`); try { - const cmd = `"${steamDir}/steam.sh" -applaunch ${game.activePlatform.storeIdentifier} ${args} ${settings.getContext().gameSpecific.launchParameters}`; + let cmd = `"${steamDir}/steam.sh" -applaunch ${game.activePlatform.storeIdentifier} ${args} ${settings.getContext().gameSpecific.launchParameters}`; + if (as_flatpak) { + cmd = `flatpak run com.valvesoftware.Steam ${cmd}`; + } + LoggerProvider.instance.Log(LogSeverity.INFO, `Running command: ${cmd}`); await ChildProcess.exec(cmd); } catch(err) { diff --git a/src/r2mm/manager/ManagerSettings.ts b/src/r2mm/manager/ManagerSettings.ts index 978760a00..45f724c49 100644 --- a/src/r2mm/manager/ManagerSettings.ts +++ b/src/r2mm/manager/ManagerSettings.ts @@ -98,6 +98,11 @@ export default class ManagerSettings { await this.save(); } + public async setLinuxUseFlatpak(enabled: boolean) { + ManagerSettings.CONTEXT.global.linuxUseFlatpak = enabled; + await this.save(); + } + public async expandCards() { ManagerSettings.CONTEXT.global.expandedCards = true; await this.save(); diff --git a/src/r2mm/manager/SettingsDexieStore.ts b/src/r2mm/manager/SettingsDexieStore.ts index 27c603393..1045c4987 100644 --- a/src/r2mm/manager/SettingsDexieStore.ts +++ b/src/r2mm/manager/SettingsDexieStore.ts @@ -123,6 +123,7 @@ export default class SettingsDexieStore extends Dexie { defaultStore: undefined, gameSelectionViewMode: GameSelectionViewMode.CARD, previewPanelWidth: 500, + linuxUseFlatpak: null, }, gameSpecific: { version: 2, @@ -212,6 +213,7 @@ export interface ManagerSettingsInterfaceGlobal_V2 { defaultStore: Platform | undefined; gameSelectionViewMode: GameSelectionViewMode; previewPanelWidth: number; + linuxUseFlatpak: boolean | null; } /**