1
0
Fork 0
mirror of synced 2024-06-26 18:20:50 +12:00

Wine: Set environment through flatpak-spawn arguments

Partially fixes #305
This commit is contained in:
loathingKernel 2023-09-03 01:38:42 +03:00
parent 4350591411
commit 9a941e3001
No known key found for this signature in database
GPG key ID: CE0C72D0B53821FD
4 changed files with 74 additions and 49 deletions

View file

@ -68,7 +68,7 @@ class PreLaunchThread(QRunnable):
if launch_args.pre_launch_command: if launch_args.pre_launch_command:
proc = get_configured_process() proc = get_configured_process()
proc.setProcessEnvironment(launch_args.env) proc.setProcessEnvironment(launch_args.environment)
self.signals.started_pre_launch_command.emit() self.signals.started_pre_launch_command.emit()
proc.start(launch_args.pre_launch_command[0], launch_args.pre_launch_command[1:]) proc.start(launch_args.pre_launch_command[0], launch_args.pre_launch_command[1:])
if launch_args.pre_launch_wait: if launch_args.pre_launch_wait:
@ -119,6 +119,7 @@ class RareLauncher(RareApp):
def __init__(self, args: InitArgs): def __init__(self, args: InitArgs):
super(RareLauncher, self).__init__(args, f"{type(self).__name__}_{args.app_name}_{{0}}.log") super(RareLauncher, self).__init__(args, f"{type(self).__name__}_{args.app_name}_{{0}}.log")
self.socket: Optional[QLocalSocket] = None
self._hook.deleteLater() self._hook.deleteLater()
self._hook = RareLauncherException(self, args, self) self._hook = RareLauncherException(self, args, self)
@ -126,7 +127,11 @@ class RareLauncher(RareApp):
self.no_sync_on_exit = False self.no_sync_on_exit = False
self.args = args self.args = args
self.core = LegendaryCore() self.core = LegendaryCore()
self.rgame = RareGameSlim(self.core, self.core.get_game(args.app_name)) game = self.core.get_game(args.app_name)
if not game:
self.logger.error(f"Game {args.app_name} not found. Exiting")
QApplication.exit(1)
self.rgame = RareGameSlim(self.core, game)
lang = self.settings.value("language", self.core.language_code, type=str) lang = self.settings.value("language", self.core.language_code, type=str)
self.load_translator(lang) self.load_translator(lang)
@ -242,13 +247,14 @@ class RareLauncher(RareApp):
) )
self.stop() self.stop()
@pyqtSlot(object)
def launch_game(self, args: LaunchArgs): def launch_game(self, args: LaunchArgs):
# should never happen # should never happen
if not args: if not args:
self.stop() self.stop()
return return
if self.console: if self.console:
self.console.set_env(args.env) self.console.set_env(args.environment)
self.start_time = time.time() self.start_time = time.time()
if args.is_origin_game: if args.is_origin_game:
@ -256,9 +262,9 @@ class RareLauncher(RareApp):
self.stop() # stop because it is no subprocess self.stop() # stop because it is no subprocess
return return
if args.cwd: if args.working_directory:
self.game_process.setWorkingDirectory(args.cwd) self.game_process.setWorkingDirectory(args.working_directory)
self.game_process.setProcessEnvironment(args.env) self.game_process.setProcessEnvironment(args.environment)
# send start message after process started # send start message after process started
self.game_process.started.connect(lambda: self.send_message( self.game_process.started.connect(lambda: self.send_message(
StateChangedModel( StateChangedModel(
@ -268,8 +274,8 @@ class RareLauncher(RareApp):
)) ))
if self.rgame.app_name in DETACHED_APP_NAMES and platform.system() == "Windows": if self.rgame.app_name in DETACHED_APP_NAMES and platform.system() == "Windows":
self.game_process.deleteLater() self.game_process.deleteLater()
subprocess.Popen([args.executable] + args.args, cwd=args.cwd, subprocess.Popen([args.executable] + args.arguments, cwd=args.working_directory,
env={i: args.env.value(i) for i in args.env.keys()}) env={i: args.environment.value(i) for i in args.environment.keys()})
if self.console: if self.console:
self.console.log("Launching game detached") self.console.log("Launching game detached")
self.stop() self.stop()
@ -277,13 +283,13 @@ class RareLauncher(RareApp):
if self.args.dry_run: if self.args.dry_run:
self.logger.info("Dry run activated") self.logger.info("Dry run activated")
if self.console: if self.console:
self.console.log(f"{args.executable} {' '.join(args.args)}") self.console.log(f"{args.executable} {' '.join(args.arguments)}")
self.console.log(f"Do not start {self.rgame.app_name}") self.console.log(f"Do not start {self.rgame.app_name}")
self.console.accept_close = True self.console.accept_close = True
print(args.executable, " ".join(args.args)) print(args.executable, " ".join(args.arguments))
self.stop() self.stop()
return return
self.game_process.start(args.executable, args.args) self.game_process.start(args.executable, args.arguments)
def error_occurred(self, error_str: str): def error_occurred(self, error_str: str):
self.logger.warning(error_str) self.logger.warning(error_str)

View file

@ -44,9 +44,9 @@ class InitArgs(Namespace):
@dataclass @dataclass
class LaunchArgs: class LaunchArgs:
executable: str = "" executable: str = ""
args: List[str] = None arguments: List[str] = None
cwd: str = None working_directory: str = None
env: QProcessEnvironment = None environment: QProcessEnvironment = None
pre_launch_command: str = "" pre_launch_command: str = ""
pre_launch_wait: bool = False pre_launch_wait: bool = False
is_origin_game: bool = False # only for windows to launch as url is_origin_game: bool = False # only for windows to launch as url
@ -60,7 +60,7 @@ def get_origin_params(core: LegendaryCore, app_name, offline: bool,
origin_uri = core.get_origin_uri(app_name, offline) origin_uri = core.get_origin_uri(app_name, offline)
if platform.system() == "Windows": if platform.system() == "Windows":
launch_args.executable = origin_uri launch_args.executable = origin_uri
launch_args.args = [] launch_args.arguments = []
# only set it here true, because on linux it is a launch command like every other game # only set it here true, because on linux it is a launch command like every other game
launch_args.is_origin_game = True launch_args.is_origin_game = True
return launch_args return launch_args
@ -71,12 +71,19 @@ def get_origin_params(core: LegendaryCore, app_name, offline: bool,
command.append(origin_uri) command.append(origin_uri)
env = core.get_app_environment(app_name) env = core.get_app_environment(app_name)
launch_args.env = QProcessEnvironment.systemEnvironment()
if os.environ.get("container") == "flatpak":
flatpak_command = ["flatpak-spawn", "--host"]
for name, value in env.items():
flatpak_command.append(f"--env={name}={value}")
command = flatpak_command.extend(command)
launch_args.environment = QProcessEnvironment.systemEnvironment()
for name, value in env.items(): for name, value in env.items():
launch_args.env.insert(name, value) launch_args.environment.insert(name, value)
launch_args.executable = command[0] launch_args.executable = command[0]
launch_args.args = command[1:] launch_args.arguments = command[1:]
return launch_args return launch_args
@ -100,10 +107,12 @@ def get_game_params(core: LegendaryCore, igame: InstalledGame, args: InitArgs,
app_name=igame.app_name, offline=args.offline app_name=igame.app_name, offline=args.offline
) )
full_params = list() full_params = []
if os.environ.get("container") == "flatpak": if os.environ.get("container") == "flatpak":
full_params.extend(["flatpak-spawn", "--host"]) full_params.extend(["flatpak-spawn", "--host"])
for name, value in params.environment.items():
full_params.append(f"--env={name}={value}")
full_params.extend(params.launch_command) full_params.extend(params.launch_command)
full_params.append( full_params.append(
@ -114,12 +123,12 @@ def get_game_params(core: LegendaryCore, igame: InstalledGame, args: InitArgs,
full_params.extend(params.user_parameters) full_params.extend(params.user_parameters)
launch_args.executable = full_params[0] launch_args.executable = full_params[0]
launch_args.args = full_params[1:] launch_args.arguments = full_params[1:]
launch_args.env = QProcessEnvironment.systemEnvironment() launch_args.environment = QProcessEnvironment.systemEnvironment()
for name, value in params.environment.items(): for name, value in params.environment.items():
launch_args.env.insert(name, value) launch_args.environment.insert(name, value)
launch_args.cwd = params.working_directory launch_args.working_directory = params.working_directory
return launch_args return launch_args

View file

@ -38,7 +38,7 @@ class WineResolver(Worker):
# pylint: disable=E1136 # pylint: disable=E1136
self.signals.result_ready[str].emit("") self.signals.result_ready[str].emit("")
return return
if not os.path.exists(self.wine_exec) or not os.path.exists(wine.winepath(self.wine_exec)): if not os.path.exists(self.wine_exec):
# pylint: disable=E1136 # pylint: disable=E1136
self.signals.result_ready[str].emit("") self.signals.result_ready[str].emit("")
return return
@ -82,28 +82,33 @@ class OriginWineWorker(QRunnable):
wine_env = wine.environ(self.core, rgame.app_name) wine_env = wine.environ(self.core, rgame.app_name)
wine_exec = wine.wine(self.core, rgame.app_name) wine_exec = wine.wine(self.core, rgame.app_name)
# lk: this is the original way of gettijng the path by parsing "system.reg" use_wine = False
wine_prefix = wine.prefix(self.core, rgame.app_name) if not use_wine:
reg = self.__cache.get(wine_prefix, None) or wine.read_registry("system.reg", wine_prefix) # lk: this is the original way of gettijng the path by parsing "system.reg"
self.__cache[wine_prefix] = reg wine_prefix = wine.prefix(self.core, rgame.app_name)
reg = self.__cache.get(wine_prefix, None) or wine.read_registry("system.reg", wine_prefix)
self.__cache[wine_prefix] = reg
reg_path = reg_path.replace("SOFTWARE", "Software").replace("WOW6432Node", "Wow6432Node") reg_path = reg_path.replace("SOFTWARE", "Software").replace("WOW6432Node", "Wow6432Node")
# lk: split and rejoin the registry path to avoid slash expansion # lk: split and rejoin the registry path to avoid slash expansion
reg_path = "\\\\".join([x for x in reg_path.split("\\") if bool(x)]) reg_path = "\\\\".join([x for x in reg_path.split("\\") if bool(x)])
install_dir = reg.get(reg_path, f'"{reg_key}"', fallback=None) install_dir = reg.get(reg_path, f'"{reg_key}"', fallback=None)
else:
# lk: this is the alternative way of getting the path by using wine itself
install_dir = wine.query_reg_key(wine_exec, wine_env, f"HKLM\\{reg_path}", reg_key)
# lk: this is the alternative way of getting the path by using wine itself logger.debug("Found Wine install directory %s", install_dir)
# install_dir = wine.query_reg_key(wine_exec, wine_env, f"HKLM\\{reg_path}", reg_key)
if install_dir: if install_dir:
install_dir = wine.convert_to_unix_path(wine_exec, wine_env, install_dir) install_dir = wine.convert_to_unix_path(wine_exec, wine_env, install_dir)
logger.debug("Found Unix install directory %s", install_dir)
if install_dir: if install_dir:
if os.path.isdir(install_dir): if os.path.isdir(install_dir):
install_size = path_size(install_dir) install_size = path_size(install_dir)
rgame.set_origin_attributes(install_dir, install_size) rgame.set_origin_attributes(install_dir, install_size)
logger.debug(f"Found Origin game {rgame.title} ({install_dir}, {format_size(install_size)})") logger.debug(f"Origin game {rgame.title} ({install_dir}, {format_size(install_size)})")
else: else:
logger.warning(f"Found Origin game {rgame.title} ({install_dir} does not exist)") logger.warning(f"Origin game {rgame.title} ({install_dir} does not exist)")
logger.info(f"Origin registry worker finished in {time.time() - t}s") logger.info(f"Origin worker finished in {time.time() - t}s")

View file

@ -2,10 +2,13 @@ import os
import shutil import shutil
import subprocess import subprocess
from configparser import ConfigParser from configparser import ConfigParser
from logging import getLogger
from typing import Mapping, Dict, List, Tuple from typing import Mapping, Dict, List, Tuple
from rare.lgndr.core import LegendaryCore from rare.lgndr.core import LegendaryCore
logger = getLogger("Wine")
# this is a copied function from legendary.utils.wine_helpers, but registry file can be specified # this is a copied function from legendary.utils.wine_helpers, but registry file can be specified
def read_registry(registry: str, wine_pfx: str) -> ConfigParser: def read_registry(registry: str, wine_pfx: str) -> ConfigParser:
@ -21,16 +24,22 @@ def read_registry(registry: str, wine_pfx: str) -> ConfigParser:
def execute(cmd: List, wine_env: Mapping) -> Tuple[str, str]: def execute(cmd: List, wine_env: Mapping) -> Tuple[str, str]:
if os.environ.get("container") == "flatpak": if os.environ.get("container") == "flatpak":
cmd = ["flatpak-spawn", "--host"] + cmd flatpak_cmd = ["flatpak-spawn", "--host"]
for name, value in wine_env.items():
flatpak_cmd.append(f"--env={name}={value}")
cmd = flatpak_cmd + cmd
proc = subprocess.Popen( proc = subprocess.Popen(
cmd, cmd,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
env=wine_env, # Use the current environment if we are in flatpak or our own if we are on host
# In flatpak our environment is passed through `flatpak-spawn` arguments
env=os.environ.copy() if os.environ.get("container") == "flatpak" else wine_env,
shell=False, shell=False,
text=True, text=True,
) )
return proc.communicate() res = proc.communicate()
return res
def resolve_path(wine_exec: str, wine_env: Mapping, path: str) -> str: def resolve_path(wine_exec: str, wine_env: Mapping, path: str) -> str:
@ -65,18 +74,11 @@ def convert_to_windows_path(wine_exec: str, wine_env: Mapping, path: str) -> str
def convert_to_unix_path(wine_exec: str, wine_env: Mapping, path: str) -> str: def convert_to_unix_path(wine_exec: str, wine_env: Mapping, path: str) -> str:
path = path.strip().strip('"') path = path.strip().strip('"')
cmd = [winepath(wine_exec), "-u", path] cmd = [wine_exec, "winepath.exe", "-u", path]
out, err = execute(cmd, wine_env) out, err = execute(cmd, wine_env)
return os.path.realpath(out.strip()) return os.path.realpath(out.strip())
def winepath(wine_exec: str) -> str:
_winepath = os.path.join(os.path.dirname(wine_exec), "winepath")
if not os.path.isfile(_winepath):
return ""
return _winepath
def wine(core: LegendaryCore, app_name: str = "default") -> str: def wine(core: LegendaryCore, app_name: str = "default") -> str:
_wine = core.lgd.config.get( _wine = core.lgd.config.get(
app_name, "wine_executable", fallback=core.lgd.config.get( app_name, "wine_executable", fallback=core.lgd.config.get(
@ -87,8 +89,11 @@ def wine(core: LegendaryCore, app_name: str = "default") -> str:
def environ(core: LegendaryCore, app_name: str = "default") -> Dict: def environ(core: LegendaryCore, app_name: str = "default") -> Dict:
_environ = os.environ.copy() # Get a clean environment if we are in flatpak, this environment will be pass
# to `flatpak-spawn`, otherwise use the system's.
_environ = {} if os.environ.get("container") == "flatpak" else os.environ.copy()
_environ.update(core.get_app_environment(app_name)) _environ.update(core.get_app_environment(app_name))
_environ["WINEDEBUG"] = "-all"
_environ["WINEDLLOVERRIDES"] = "winemenubuilder=d;mscoree=d;mshtml=d;" _environ["WINEDLLOVERRIDES"] = "winemenubuilder=d;mscoree=d;mshtml=d;"
_environ["DISPLAY"] = "" _environ["DISPLAY"] = ""
return _environ return _environ