def shutdown(self): """Orders Steam to shutdown""" logger.info("Shutting down Steam") shutdown_command = MonitoredCommand((self.launch_args + ["-shutdown"]), runner=self, env=self.get_env(os_env=False)) shutdown_command.start()
def start_xephyr(self, display=":2"): """Start a monitored Xephyr instance""" if not system.find_executable("Xephyr"): raise GameConfigError( "Unable to find Xephyr, install it or disable the Xephyr option" ) xephyr_depth = "8" if self.runner.system_config.get( "xephyr") == "8bpp" else "16" xephyr_resolution = self.runner.system_config.get( "xephyr_resolution") or "640x480" xephyr_command = [ "Xephyr", display, "-ac", "-screen", xephyr_resolution + "x" + xephyr_depth, "-glamor", "-reset", "-terminate", ] if self.runner.system_config.get("xephyr_fullscreen"): xephyr_command.append("-fullscreen") xephyr_thread = MonitoredCommand(xephyr_command) xephyr_thread.start() time.sleep(3) return display
def start_xephyr(self, display=":2"): """Start a monitored Xephyr instance""" if not system.find_executable("Xephyr"): raise GameConfigError("Unable to find Xephyr, install it or disable the Xephyr option") xephyr_command = get_xephyr_command(display, self.runner.system_config) xephyr_thread = MonitoredCommand(xephyr_command) xephyr_thread.start() time.sleep(3) return display
def remove_game_data(self, appid=None, **kwargs): if not self.is_installed(): return False command = MonitoredCommand( [self.get_executable(), "steam://uninstall/%s" % (appid or self.appid)], runner=self, env=self.get_env(), ) command.start()
def on_game_quit(self): """Restore some settings and cleanup after game quit.""" if self.prelaunch_executor and self.prelaunch_executor.is_running: logger.info("Stopping prelaunch script") self.prelaunch_executor.stop() self.heartbeat = None if self.state != self.STATE_STOPPED: logger.warning("Game still running (state: %s)", self.state) self.stop() # Check for post game script postexit_command = self.runner.system_config.get("postexit_command") if postexit_command: command_array = shlex.split(postexit_command) if system.path_exists(command_array[0]): logger.info("Running post-exit command: %s", postexit_command) postexit_thread = MonitoredCommand( command_array, include_processes=[os.path.basename(postexit_command)], env=self.game_runtime_config["env"], cwd=self.directory, ) postexit_thread.start() quit_time = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime()) logger.debug("%s stopped at %s", self.name, quit_time) self.lastplayed = int(time.time()) self.save(save_config=False) os.chdir(os.path.expanduser("~")) if self.antimicro_thread: self.antimicro_thread.stop() if self.resolution_changed or self.runner.system_config.get( "reset_desktop"): DISPLAY_MANAGER.set_resolution(self.original_outputs) if self.compositor_disabled: self.set_desktop_compositing(True) if self.screen_saver_inhibitor_cookie is not None: SCREEN_SAVER_INHIBITOR.uninhibit( self.screen_saver_inhibitor_cookie) self.screen_saver_inhibitor_cookie = None if self.runner.system_config.get("use_us_layout"): with subprocess.Popen(["setxkbmap"], env=os.environ) as setxkbmap: setxkbmap.communicate() if self.runner.system_config.get("restore_gamma"): restore_gamma() self.process_return_codes()
def start_antimicrox(self, antimicro_config): """Start Antimicrox with a given config path""" antimicro_path = system.find_executable("antimicrox") if not antimicro_path: logger.warning("Antimicrox is not installed.") return logger.info("Starting Antic") antimicro_command = [antimicro_path, "--hidden", "--tray", "--profile", antimicro_config] self.antimicro_thread = MonitoredCommand(antimicro_command) self.antimicro_thread.start()
def on_execute_script_clicked(self, _widget): """Execute the game's associated script""" manual_command = self.game.runner.system_config.get("manual_command") if path_exists(manual_command): MonitoredCommand( [manual_command], include_processes=[os.path.basename(manual_command)], cwd=self.game.directory, ).start() logger.info("Running %s in the background", manual_command)
def remove_game_data(self, appid=None, **kwargs): if not self.is_installed(): logger.warning("Trying to remove a winesteam game but it's not installed.") return False self.force_shutdown() uninstall_command = MonitoredCommand( (self.launch_args + ["steam://uninstall/%s" % (appid or self.appid)]), runner=self, env=self.get_env(os_env=False), ) uninstall_command.start()
def on_game_quit(self): """Restore some settings and cleanup after game quit.""" if self.prelaunch_executor and self.prelaunch_executor.is_running: logger.info("Stopping prelaunch script") self.prelaunch_executor.stop() self.heartbeat = None if self.state != self.STATE_STOPPED: logger.warning("Game still running (state: %s)", self.state) self.stop() # Check for post game script postexit_command = self.runner.system_config.get("postexit_command") if system.path_exists(postexit_command): logger.info("Running post-exit command: %s", postexit_command) postexit_thread = MonitoredCommand( [postexit_command], include_processes=[os.path.basename(postexit_command)], env=self.game_runtime_config["env"], cwd=self.directory, ) postexit_thread.start() if self.discord_presence.available: self.discord_presence.clear_discord_rich_presence() quit_time = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime()) logger.debug("%s stopped at %s", self.name, quit_time) self.lastplayed = int(time.time()) self.save(metadata_only=True) os.chdir(os.path.expanduser("~")) if self.resolution_changed or self.runner.system_config.get( "reset_desktop"): DISPLAY_MANAGER.set_resolution(self.original_outputs) if self.compositor_disabled: self.set_desktop_compositing(True) if self.runner.system_config.get("use_us_layout"): subprocess.Popen(["setxkbmap"], env=os.environ).communicate() if self.runner.system_config.get("restore_gamma"): restore_gamma() self.process_return_codes() if self.exit_main_loop: exit()
def xboxdrv_start(self, config): command = [ "pkexec", "xboxdrv", "--daemon", "--detach-kernel-driver", "--dbus", "session", "--silent", ] + shlex.split(config) logger.debug("[xboxdrv] %s", " ".join(command)) self.xboxdrv_thread = MonitoredCommand(command, include_processes=["xboxdrv"]) self.xboxdrv_thread.stop_func = self.xboxdrv_stop self.xboxdrv_thread.start()
def start_prelaunch_command(self): """Start the prelaunch command specified in the system options""" prelaunch_command = self.runner.system_config.get("prelaunch_command") command_array = shlex.split(prelaunch_command) if not system.path_exists(command_array[0]): logger.warning("Command %s not found", command_array[0]) return self.prelaunch_executor = MonitoredCommand( command_array, include_processes=[os.path.basename(command_array[0])], env=self.game_runtime_config["env"], cwd=self.directory, ) self.prelaunch_executor.start() logger.info("Running %s in the background", prelaunch_command)
def start_game(self): self.game_thread = MonitoredCommand( self.game_runtime_config["args"], runner=self.runner, env=self.game_runtime_config["env"], term=self.game_runtime_config["terminal"], log_buffer=self.log_buffer, include_processes=self.game_runtime_config["include_processes"], exclude_processes=self.game_runtime_config["exclude_processes"], ) if hasattr(self.runner, "stop"): self.game_thread.stop_func = self.runner.stop self.game_thread.start() self.emit("game-started") self.state = self.STATE_RUNNING self.heartbeat = GLib.timeout_add(HEARTBEAT_DELAY, self.beat)
def remove_game_data(self, appid=None, **kwargs): if not self.is_installed(): installed = self.install_dialog() if not installed: return False appid = appid if appid else self.appid if appid is None: raise RuntimeError("No appid given for uninstallation " "(game config=%s)" % self.game_config) logger.debug("Launching Steam uninstall of game %s", appid) command = [self.get_executable(), "steam://uninstall/%s" % appid] thread = MonitoredCommand(command, runner=self, env=self.get_env(), watch=False) thread.start()
def run(self, *args): """Run the runner alone.""" if not self.runnable_alone: return if not self.is_installed() and not self.install_dialog(): logger.info("Runner install cancelled") return command_data = self.get_run_data() command = command_data.get("command") env = (command_data.get("env") or {}).copy() if hasattr(self, "prelaunch"): self.prelaunch() command_runner = MonitoredCommand(command, runner=self, env=env) command_runner.start()
def start_game(self): """Run a background command to lauch the game""" self.game_thread = MonitoredCommand( self.game_runtime_config["args"], title=self.name, runner=self.runner, env=self.game_runtime_config["env"], term=self.game_runtime_config["terminal"], log_buffer=self.log_buffer, include_processes=self.game_runtime_config["include_processes"], exclude_processes=self.game_runtime_config["exclude_processes"], ) if hasattr(self.runner, "stop"): self.game_thread.stop_func = self.runner.stop self.game_uuid = self.game_thread.env["LUTRIS_GAME_UUID"] self.game_thread.start() self.timer.start() self.state = self.STATE_RUNNING self.emit("game-started") self.heartbeat = GLib.timeout_add(HEARTBEAT_DELAY, self.beat)
def execute(self, data): """Run an executable file.""" args = [] terminal = None working_dir = None env = {} if isinstance(data, dict): self._check_required_params([("file", "command")], data, "execute") if "command" in data and "file" in data: raise ScriptingError( "Parameters file and command can't be used " "at the same time for the execute command", data, ) file_ref = data.get("file", "") command = data.get("command", "") args_string = data.get("args", "") for arg in shlex.split(args_string): args.append(self._substitute(arg)) terminal = data.get("terminal") working_dir = data.get("working_dir") if not data.get("disable_runtime"): # Possibly need to handle prefer_system_libs here env.update(runtime.get_env()) # Loading environment variables set in the script env.update(self.script_env) # Environment variables can also be passed to the execute command local_env = data.get("env") or {} env.update({key: self._substitute(value) for key, value in local_env.items()}) include_processes = shlex.split(data.get("include_processes", "")) exclude_processes = shlex.split(data.get("exclude_processes", "")) elif isinstance(data, str): command = data include_processes = [] exclude_processes = [] else: raise ScriptingError("No parameters supplied to execute command.", data) if command: file_ref = "bash" args = ["-c", self._get_file(command.strip())] include_processes.append("bash") else: # Determine whether 'file' value is a file id or a path file_ref = self._get_file(file_ref) if system.path_exists(file_ref) and not system.is_executable(file_ref): logger.warning("Making %s executable", file_ref) system.make_executable(file_ref) exec_path = system.find_executable(file_ref) if not exec_path: raise ScriptingError("Unable to find executable %s" % file_ref) if terminal: terminal = system.get_default_terminal() if not working_dir or not os.path.exists(working_dir): working_dir = self.target_path command = MonitoredCommand( [exec_path] + args, env=env, term=terminal, cwd=working_dir, include_processes=include_processes, exclude_processes=exclude_processes, ) command.start() GLib.idle_add(self.parent.attach_logger, command) self.heartbeat = GLib.timeout_add(1000, self._monitor_task, command) return "STOP"
def configure_game(self, prelaunched, error=None): """Get the game ready to start, applying all the options This methods sets the game_runtime_config attribute. """ if error: logger.error(error) dialogs.ErrorDialog(str(error)) if not prelaunched: logger.error("Game prelaunch unsuccessful") dialogs.ErrorDialog("An error prevented the game from running") self.state = self.STATE_STOPPED self.emit("game-stop") return system_config = self.runner.system_config self.original_outputs = DISPLAY_MANAGER.get_config() gameplay_info = self.runner.play() if "error" in gameplay_info: self.show_error_message(gameplay_info) self.state = self.STATE_STOPPED self.emit("game-stop") return logger.debug("Launching %s: %s", self.name, gameplay_info) logger.debug("Game info: %s", json.dumps(gameplay_info, indent=2)) env = {} sdl_gamecontrollerconfig = system_config.get( "sdl_gamecontrollerconfig") if sdl_gamecontrollerconfig: path = os.path.expanduser(sdl_gamecontrollerconfig) if system.path_exists(path): with open(path, "r") as controllerdb_file: sdl_gamecontrollerconfig = controllerdb_file.read() env["SDL_GAMECONTROLLERCONFIG"] = sdl_gamecontrollerconfig sdl_video_fullscreen = system_config.get("sdl_video_fullscreen") or "" env["SDL_VIDEO_FULLSCREEN_DISPLAY"] = sdl_video_fullscreen restrict_to_display = system_config.get("display") if restrict_to_display != "off": if restrict_to_display == "primary": restrict_to_display = None for output in self.original_outputs: if output.primary: restrict_to_display = output.name break if not restrict_to_display: logger.warning("No primary display set") else: found = False for output in self.original_outputs: if output.name == restrict_to_display: found = True break if not found: logger.warning("Selected display %s not found", restrict_to_display) restrict_to_display = None if restrict_to_display: turn_off_except(restrict_to_display) time.sleep(3) self.resolution_changed = True resolution = system_config.get("resolution") if resolution != "off": DISPLAY_MANAGER.set_resolution(resolution) time.sleep(3) self.resolution_changed = True if system_config.get("reset_pulse"): audio.reset_pulse() self.killswitch = system_config.get("killswitch") if self.killswitch and not system.path_exists(self.killswitch): # Prevent setting a killswitch to a file that doesn't exists self.killswitch = None # Command launch_arguments = gameplay_info["command"] optimus = system_config.get("optimus") if optimus == "primusrun" and system.find_executable("primusrun"): launch_arguments.insert(0, "primusrun") elif optimus == "optirun" and system.find_executable("optirun"): launch_arguments.insert(0, "virtualgl") launch_arguments.insert(0, "-b") launch_arguments.insert(0, "optirun") elif optimus == "pvkrun" and system.find_executable("pvkrun"): launch_arguments.insert(0, "pvkrun") xephyr = system_config.get("xephyr") or "off" if xephyr != "off": if not system.find_executable("Xephyr"): raise GameConfigError( "Unable to find Xephyr, install it or disable the Xephyr option" ) xephyr_depth = "8" if xephyr == "8bpp" else "16" xephyr_resolution = system_config.get( "xephyr_resolution") or "640x480" xephyr_command = [ "Xephyr", ":2", "-ac", "-screen", xephyr_resolution + "x" + xephyr_depth, "-glamor", "-reset", "-terminate", ] if system_config.get("xephyr_fullscreen"): xephyr_command.append("-fullscreen") xephyr_thread = MonitoredCommand(xephyr_command) xephyr_thread.start() time.sleep(3) env["DISPLAY"] = ":2" if system_config.get("use_us_layout"): setxkbmap_command = [ "setxkbmap", "-model", "pc101", "us", "-print" ] xkbcomp_command = ["xkbcomp", "-", os.environ.get("DISPLAY", ":0")] xkbcomp = subprocess.Popen(xkbcomp_command, stdin=subprocess.PIPE) subprocess.Popen(setxkbmap_command, env=os.environ, stdout=xkbcomp.stdin).communicate() xkbcomp.communicate() if system_config.get("aco"): env["RADV_PERFTEST"] = "aco" pulse_latency = system_config.get("pulse_latency") if pulse_latency: env["PULSE_LATENCY_MSEC"] = "60" vk_icd = system_config.get("vk_icd") if vk_icd and vk_icd != "off" and system.path_exists(vk_icd): env["VK_ICD_FILENAMES"] = vk_icd fps_limit = system_config.get("fps_limit") or "" if fps_limit: strangle_cmd = system.find_executable("strangle") if strangle_cmd: launch_arguments = [strangle_cmd, fps_limit] + launch_arguments else: logger.warning( "libstrangle is not available on this system, FPS limiter disabled" ) prefix_command = system_config.get("prefix_command") or "" if prefix_command: launch_arguments = ( shlex.split(os.path.expandvars(prefix_command)) + launch_arguments) single_cpu = system_config.get("single_cpu") or False if single_cpu: logger.info("The game will run on a single CPU core") launch_arguments.insert(0, "0") launch_arguments.insert(0, "-c") launch_arguments.insert(0, "taskset") terminal = system_config.get("terminal") if terminal: terminal = system_config.get("terminal_app", system.get_default_terminal()) if terminal and not system.find_executable(terminal): dialogs.ErrorDialog("The selected terminal application " "could not be launched:\n" "%s" % terminal) self.state = self.STATE_STOPPED self.emit("game-stop") return # Env vars game_env = gameplay_info.get("env") or self.runner.get_env() env.update(game_env) env["game_name"] = self.name # Prime vars prime = system_config.get("prime") if prime: env["__NV_PRIME_RENDER_OFFLOAD"] = "1" env["__GLX_VENDOR_LIBRARY_NAME"] = "nvidia" env["__VK_LAYER_NV_optimus"] = "NVIDIA_only" # LD_PRELOAD ld_preload = gameplay_info.get("ld_preload") if ld_preload: env["LD_PRELOAD"] = ld_preload # Feral gamemode gamemode = system_config.get("gamemode") if gamemode: env["LD_PRELOAD"] = ":".join([ path for path in [ env.get("LD_PRELOAD"), "libgamemodeauto.so", ] if path ]) # LD_LIBRARY_PATH game_ld_libary_path = gameplay_info.get("ld_library_path") if game_ld_libary_path: ld_library_path = env.get("LD_LIBRARY_PATH") if not ld_library_path: ld_library_path = "$LD_LIBRARY_PATH" env["LD_LIBRARY_PATH"] = ":".join( [game_ld_libary_path, ld_library_path]) include_processes = shlex.split( system_config.get("include_processes", "")) exclude_processes = shlex.split( system_config.get("exclude_processes", "")) self.game_runtime_config = { "args": launch_arguments, "env": env, "terminal": terminal, "include_processes": include_processes, "exclude_processes": exclude_processes, } if system_config.get("disable_compositor"): self.set_desktop_compositing(False) prelaunch_command = system_config.get("prelaunch_command") if system.path_exists(prelaunch_command): self.prelaunch_executor = MonitoredCommand( [prelaunch_command], include_processes=[os.path.basename(prelaunch_command)], env=self.game_runtime_config["env"], cwd=self.directory, ) self.prelaunch_executor.start() logger.info("Running %s in the background", prelaunch_command) if system_config.get("prelaunch_wait"): self.heartbeat = GLib.timeout_add(HEARTBEAT_DELAY, self.prelaunch_beat) else: self.start_game()
def wineexec( executable, args="", wine_path=None, prefix=None, arch=None, # pylint: disable=too-many-locals working_dir=None, winetricks_wine="", blocking=False, config=None, include_processes=[], exclude_processes=[], disable_runtime=False, env={}, overrides=None, ): """ Execute a Wine command. Args: executable (str): wine program to run, pass None to run wine itself args (str): program arguments wine_path (str): path to the wine version to use prefix (str): path to the wine prefix to use arch (str): wine architecture of the prefix working_dir (str): path to the working dir for the process winetricks_wine (str): path to the wine version used by winetricks blocking (bool): if true, do not run the process in a thread config (LutrisConfig): LutrisConfig object for the process context watch (list): list of process names to monitor (even when in a ignore list) Returns: Process results if the process is running in blocking mode or MonitoredCommand instance otherwise. """ executable = str(executable) if executable else "" if isinstance(include_processes, str): include_processes = shlex.split(include_processes) if isinstance(exclude_processes, str): exclude_processes = shlex.split(exclude_processes) if not wine_path: wine = import_runner("wine") wine_path = wine().get_executable() if not wine_path: raise RuntimeError("Wine is not installed") if not working_dir: if os.path.isfile(executable): working_dir = os.path.dirname(executable) executable, _args, working_dir = get_real_executable( executable, working_dir) if _args: args = '{} "{}"'.format(_args[0], _args[1]) # Create prefix if necessary if arch not in ("win32", "win64"): arch = detect_arch(prefix, wine_path) if not detect_prefix_arch(prefix): wine_bin = winetricks_wine if winetricks_wine else wine_path create_prefix(prefix, wine_path=wine_bin, arch=arch) wineenv = {"WINEARCH": arch} if winetricks_wine: wineenv["WINE"] = winetricks_wine else: wineenv["WINE"] = wine_path if prefix: wineenv["WINEPREFIX"] = prefix wine_config = config or LutrisConfig(runner_slug="wine") disable_runtime = disable_runtime or wine_config.system_config[ "disable_runtime"] if use_lutris_runtime(wine_path=wineenv["WINE"], force_disable=disable_runtime): if WINE_DIR in wine_path: wine_root_path = os.path.dirname(os.path.dirname(wine_path)) elif WINE_DIR in winetricks_wine: wine_root_path = os.path.dirname(os.path.dirname(winetricks_wine)) else: wine_root_path = None wineenv["LD_LIBRARY_PATH"] = ":".join( runtime.get_paths( prefer_system_libs=wine_config. system_config["prefer_system_libs"], wine_path=wine_root_path, )) if overrides: wineenv["WINEDLLOVERRIDES"] = get_overrides_env(overrides) wineenv.update(env) command_parameters = [wine_path] if executable: command_parameters.append(executable) command_parameters += shlex.split(args) if blocking: return system.execute(command_parameters, env=wineenv, cwd=working_dir) wine = import_runner("wine") command = MonitoredCommand( command_parameters, runner=wine(), env=wineenv, cwd=working_dir, include_processes=include_processes, exclude_processes=exclude_processes, ) command.start() return command