def change_resolution(resolution): """change desktop resolution""" logger.debug("Switching resolution to %s", resolution) if resolution not in get_resolutions(): logger.warning("Resolution %s doesn't exist.") else: subprocess.Popen("xrandr -s %s" % resolution, shell=True)
def get_game_path_from_appid(self, appid): """Return the game directory.""" for apps_path in self.get_steamapps_dirs(): game_path = get_path_from_appmanifest(apps_path, appid) if game_path: return game_path logger.warning("Data path for SteamApp %s not found.", appid)
def change_resolution(resolution): """ Change display resolution. Takes a string for single monitors or a list of displays as returned by get_outputs() """ if isinstance(resolution, basestring): logger.debug("Switching resolution to %s", resolution) if resolution not in get_resolutions(): logger.warning("Resolution %s doesn't exist." % resolution) else: subprocess.Popen("xrandr -s %s" % resolution, shell=True) else: print resolution for display in resolution: display_name = display[0] display_geom = display[1] logger.debug("Switching to %s on %s", display[1], display[0]) display_resolution = display_geom.split('+')[0] cmd = "xrandr --output %s --mode %s" % (display_name, display_resolution) subprocess.Popen(cmd, shell=True).communicate() cmd = "xrandr --output %s --panning %s" % (display_name, display_geom) logger.debug(cmd) subprocess.Popen(cmd, shell=True)
def game_path(self): appid = self.game_config.get('appid') for apps_path in self.get_steamapps_dirs(): game_path = get_path_from_appmanifest(apps_path, appid) if game_path: return game_path logger.warning("Data path for SteamApp %s not found.", appid)
def get_gog_installers(self, gog_service): """Return available installers for a GOG game""" self.gog_data = gog_service.get_game_details(self.gogid) if not self.gog_data: logger.warning("Unable to get GOG data for game %s", self.gogid) return [] # Filter out Mac installers gog_installers = [ installer for installer in self.gog_data["downloads"]["installers"] if installer["os"] != "mac" ] available_platforms = set([installer["os"] for installer in gog_installers]) # If it's a Linux game, also filter out Windows games if "linux" in available_platforms: gog_installers = [ installer for installer in gog_installers if installer["os"] != "windows" ] # Keep only the english installer until we have locale detection # and / or language selection implemented gog_installers = [ installer for installer in gog_installers if installer["language"] == "en" ] return gog_installers
def play(self): rompath = self.runner_config.get("rompath") or "" if not system.path_exists(rompath): logger.warning("BIOS path provided in %s doesn't exist", rompath) rompath = os.path.join(settings.RUNNER_DIR, "mess/bios") if not system.path_exists(rompath): logger.error("Couldn't find %s", rompath) return {"error": "NO_BIOS"} machine = self.game_config.get("machine") if not machine: return {"error": "INCOMPLETE_CONFIG"} rom = self.game_config.get("main_file") or "" if rom and not system.path_exists(rom): return {"error": "FILE_NOT_FOUND", "file": rom} device = self.game_config.get("device") command = [self.get_executable()] if self.runner_config.get("uimodekey"): command += ["-uimodekey", self.runner["uimodekey"]] command += ["-rompath", rompath, machine] if device: command.append("-" + device) if rom: command.append(rom) return {"command": command}
def add_to_last(self, line): try: last_subkey = next(reversed(self.subkeys)) except StopIteration: logger.warning("Should this be happening?") return self.subkeys[last_subkey] += "\n{}".format(line)
def get_wine_versions(): """Return the list of Wine versions installed""" versions = [] for build in sorted(WINE_PATHS.keys()): version = get_system_wine_version(WINE_PATHS[build]) if version: versions.append(build) if system.path_exists(WINE_DIR): dirs = version_sort(os.listdir(WINE_DIR), reverse=True) for dirname in dirs: if is_version_installed(dirname): versions.append(dirname) for proton_path in get_proton_paths(): proton_versions = [p for p in os.listdir(proton_path) if "Proton" in p] for version in proton_versions: path = os.path.join(proton_path, version, "dist/bin/wine") if os.path.isfile(path): versions.append(version) if POL_PATH: for arch in ['x86', 'amd64']: builds_path = os.path.join(POL_PATH, "wine/linux-%s" % arch) if not system.path_exists(builds_path): continue for version in os.listdir(builds_path): if system.path_exists(os.path.join(builds_path, version, "bin/wine")): logger.debug("Adding PoL version %s", version) versions.append("PlayOnLinux %s-%s" % (version, arch)) else: logger.warning(os.path.join(builds_path, "bin/wine")) return versions
def get_api_games(game_slugs=None, page="1", query_type="games", inject_aliases=False): """Return all games from the Lutris API matching the given game slugs""" response_data = get_game_api_page(game_slugs, page=page, query_type=query_type) if not response_data: return [] results = response_data.get("results", []) while response_data.get("next"): page_match = re.search(r"page=(\d+)", response_data["next"]) if page_match: next_page = page_match.group(1) else: logger.error("No page found in %s", response_data["next"]) break response_data = get_game_api_page(game_slugs, page=next_page, query_type=query_type) if not response_data.get("results"): logger.warning("Unable to get response for page %s", next_page) break else: results += response_data.get("results") if game_slugs and inject_aliases: matched_games = [] for game in results: for alias_slug in [alias["slug"] for alias in game.get("aliases", [])]: if alias_slug in game_slugs: matched_games.append((alias_slug, game)) for alias_slug, game in matched_games: game["slug"] = alias_slug results.append(game) return results
def parse_installer_url(url): """ Parses `lutris:` urls, extracting any info necessary to install or run a game. """ action = None try: parsed_url = urllib.parse.urlparse(url, scheme="lutris") except Exception: # pylint: disable=broad-except logger.warning("Unable to parse url %s", url) return False if parsed_url.scheme != "lutris": return False url_path = parsed_url.path if not url_path: return False # urlparse can't parse if the path only contain numbers # workaround to remove the scheme manually: if url_path.startswith("lutris:"): url_path = url_path[7:] url_parts = url_path.split("/") if len(url_parts) == 2: action = url_parts[0] game_slug = url_parts[1] elif len(url_parts) == 1: game_slug = url_parts[0] else: raise ValueError("Invalid lutris url %s" % url) revision = None if parsed_url.query: query = dict(urllib.parse.parse_qsl(parsed_url.query)) revision = query.get("revision") return {"game_slug": game_slug, "revision": revision, "action": action}
def on_remove_clicked(self, widget, runner, runner_label): if not runner.is_installed(): logger.warning("Runner %s is not installed", runner) return if runner.multiple_versions: logger.info("Removing multiple versions") builder = get_builder_from_file('runner-remove-all-versions-dialog.ui') builder.connect_signals(self) remove_confirm_button = builder.get_object('remove_confirm_button') remove_confirm_button.connect( "clicked", self.on_remove_all_clicked, runner, runner_label ) all_versions_label = builder.get_object('runner_all_versions_label') all_versions_label.set_markup(all_versions_label.get_label() % runner.human_name) self.all_versions_dialog = builder.get_object('runner_remove_all_versions_dialog') self.all_versions_dialog.set_parent(self.dialog) self.all_versions_dialog.show() else: builder = get_builder_from_file('runner-remove-confirm-dialog.ui') builder.connect_signals(self) remove_confirm_button = builder.get_object('remove_confirm_button') remove_confirm_button.connect( "clicked", self.on_remove_confirm_clicked, runner, runner_label ) runner_remove_label = builder.get_object('runner_remove_label') runner_remove_label.set_markup(runner_remove_label.get_label() % runner.human_name) self.remove_confirm_dialog = builder.get_object('runner_remove_confirm_dialog') self.remove_confirm_dialog.show()
def get_game_api_page(game_ids, page="1", query_type="games"): """Read a single page of games from the API and return the response Args: game_ids (list): list of game IDs, the ID type is determined by `query_type` page (str): Page of results to get query_type (str): Type of the IDs in game_ids, by default 'games' queries games by their Lutris slug. 'gogid' can also be used. """ url = settings.SITE_URL + "/api/games" if int(page) > 1: url += "?page={}".format(page) response = http.Request(url, headers={"Content-Type": "application/json"}) if game_ids: payload = json.dumps({query_type: game_ids, "page": page}).encode("utf-8") else: raise ValueError("No game id provided will fetch all games from the API") try: response.get(data=payload) except http.HTTPError as ex: logger.error("Unable to get games from API: %s", ex) return None response_data = response.json num_games = len(response_data.get("results")) if num_games: logger.debug("Loaded %s games from page %s", num_games, page) else: logger.debug("No game found for %s", ', '.join(game_ids)) if not response_data: logger.warning("Unable to get games from API, status code: %s", response.status_code) return None return response_data
def generate_widgets(self): """ Parses the config dict and generates widget accordingly.""" # Select what data to load based on caller. if self.caller == "system": self.real_config = self.lutris_config.system_config elif self.caller == "runner": self.real_config = self.lutris_config.runner_config elif self.caller == "game": self.real_config = self.lutris_config.game_config # Select part of config to load or create it. if self.save_in_key in self.real_config: config = self.real_config[self.save_in_key] else: config = self.real_config[self.save_in_key] = {} #Go thru all options. for option in self.options: option_key = option["option"] #Load value if there is one. if option_key in config: value = config[option_key] else: value = None #Different types of widgets. if option["type"] == "one_choice": self.generate_combobox(option_key, option["choices"], option["label"], value) elif option["type"] == "bool": if value is None and 'default' in option: value = option['default'] self.generate_checkbox(option, value) elif option["type"] == "range": self.generate_range(option_key, option["min"], option["max"], option["label"], value) elif option["type"] == "string": if not 'label' in option: raise ValueError("Option %s has no label" % option) self.generate_entry(option_key, option["label"], value) elif option["type"] == "directory_chooser": self.generate_directory_chooser(option_key, option["label"], value) elif option["type"] in ("file_chooser", "file"): if option["type"] == "file_chooser": logger.warning("'file_chooser' option deprecated") self.generate_file_chooser(option, value) elif option["type"] == "multiple": self.generate_multiple_file_chooser(option_key, option["label"], value) elif option["type"] == "label": self.generate_label(option["label"]) else: raise ValueError("Unknown widget type %s" % option["type"])
def disconnect(self): """Disconnect from GOG by removing all credentials""" for auth_file in self.credential_files + [self.cache_path]: try: os.remove(auth_file) except OSError: logger.warning("Unable to remove %s", auth_file)
def on_done(self, _result, error): if error: logger.error("Download failed: %s", error) self.state = self.ERROR self.error = error if self.file_pointer: self.file_pointer.close() self.file_pointer = None return if self.state == self.CANCELLED: return logger.debug("Finished downloading %s", self.url) if not self.downloaded_size: logger.warning("Downloaded file is empty") if not self.full_size: self.progress_fraction = 1.0 self.progress_percentage = 100 self.state = self.COMPLETED self.file_pointer.close() self.file_pointer = None if self.callback: self.callback()
def get_games(game_slugs=None, page=1): url = settings.SITE_URL + "/api/games" if int(page) > 1: url += "?page={}".format(page) response = http.Request(url, headers={'Content-Type': 'application/json'}) if game_slugs: payload = json.dumps({'games': game_slugs, 'page': page}).encode('utf-8') else: payload = None response.get(data=payload) response_data = response.json if not response_data: logger.warning('Unable to get games from API') return None results = response_data.get('results', []) while response_data.get('next'): page_match = re.search(r'page=(\d+)', response_data['next']) if page_match: page = page_match.group(1) else: logger.error("No page found in %s", response_data['next']) break page_result = get_games(game_slugs=game_slugs, page=page) if not page_result: logger.warning("Unable to get response for page %s", page) break else: results += page_result return results
def change_resolution(resolution): """Change display resolution. Takes a string for single monitors or a list of displays as returned by get_outputs(). """ if not resolution: logger.warning("No resolution provided") return if isinstance(resolution, str): logger.debug("Switching resolution to %s", resolution) if resolution not in get_resolutions(): logger.warning("Resolution %s doesn't exist." % resolution) else: subprocess.Popen(["xrandr", "-s", resolution]) else: for display in resolution: display_name = display[0] logger.debug("Switching to %s on %s", display[1], display[0]) display_geom = display[1].split('+') display_resolution = display_geom[0] position = (display_geom[1], display_geom[2]) subprocess.Popen([ "xrandr", "--output", display_name, "--mode", display_resolution, "--pos", "{}x{}".format(position[0], position[1]) ]).communicate()
def load_mappings(self, device, mappings, index=1): product_guid = "{:04x}{:04x}-0000-0000-0000-504944564944".format( device.info.product, device.info.vendor ) instance_guid = "9e573eda-7734-11d{}-8d4a-23903fb6bdf7".format(index + 1) section_name = "IG_{}".format(instance_guid.replace("-", "")) self.config["Mappings"]["PAD{}".format(index)] = section_name self.config[section_name] = {} self.config[section_name]["ProductName"] = "{} (event)".format(device.name) self.config[section_name]["ProductGuid"] = product_guid self.config[section_name]["InstanceGuid"] = instance_guid for option, value in self.default_controller.items(): self.config[section_name][option] = str(value) for xinput_key, sdl_key in mappings.keys.items(): if xinput_key == "platform": continue xinput_name = self.gamecontroller_map.get(xinput_key) if not xinput_name: logger.warning("No mapping for %s", xinput_key) button_name = self.convert_sdl_key(sdl_key) if button_name.startswith("x"): if xinput_name.endswith("Y"): button_name = button_name.replace("x", "x-") if xinput_name.endswith("Trigger"): button_name = button_name.replace("x", "a") self.config[section_name][xinput_name] = button_name
def playtime_text(self): """Playtime duration in hours (textual representation)""" try: playtime_text = get_formatted_playtime(self._pga_data["playtime"]) except ValueError: logger.warning("Invalid playtime value %s for %s", self.playtime, self) playtime_text = "" # Do not show erroneous values return playtime_text
def on_file_selected(self, widget): file_path = os.path.expanduser(self.location_entry.get_text()) if os.path.isfile(file_path): self.selected_directory = os.path.dirname(file_path) else: logger.warning('%s is not a file', file_path) return self.interpreter.file_selected(file_path)
def game_path(self): if not self.appid: return for apps_path in self.get_steamapps_dirs(): game_path = get_path_from_appmanifest(apps_path, self.appid) if game_path: return game_path logger.warning("Data path for SteamApp %s not found.", self.appid)
def prefix_path(self): """Return the absolute path of the Wine prefix""" _prefix_path = self.game_config.get("prefix") if not _prefix_path: logger.warning("Wine prefix not provided, defaulting to $WINEPREFIX then ~/.wine." " This is probably not the intended behavior.") _prefix_path = os.environ.get("WINEPREFIX") or "~/.wine" return os.path.expanduser(_prefix_path)
def get_game_data_path(self, appid): data_path = get_path_from_appmanifest(self.steam_data_dir, appid) if not data_path: steam_config = self.get_steam_config() data_path = get_path_from_config(steam_config, appid) if not data_path: logger.warning("Data path for SteamApp %s not found.", appid) return data_path
def set_platform_from_runner(self): """Set the game's platform from the runner""" if not self.runner: logger.warning("Game has no runner, can't set platform") return self.platform = self.runner.get_platform() if not self.platform: logger.warning("Can't get platform for runner %s", self.runner.human_name)
def remove_folder(path): """Delete a folder specified by path""" if not os.path.exists(path): logger.warning("Non existent path: %s", path) return logger.debug("Removing folder %s", path) if os.path.samefile(os.path.expanduser("~"), path): raise RuntimeError("Lutris tried to erase home directory!") shutil.rmtree(path)
def get_glxinfo(self): """Return a GlxInfo instance if the gfxinfo tool is available""" if not self.get("glxinfo"): return _glxinfo = glxinfo.GlxInfo() if not hasattr(_glxinfo, "display"): logger.warning("Invalid glxinfo received") return return _glxinfo
def prelaunch(self): config_file = self.get_config_file() if not os.path.exists(config_file): logger.warning("Unable to find retroarch config. Except erratic behavior") return True retro_config = RetroConfig(config_file) retro_config['libretro_directory'] = get_default_cores_directory() retro_config['libretro_info_path'] = get_default_info_directory() # Change assets path to the Lutris provided one if necessary assets_directory = os.path.expanduser(retro_config['assets_directory']) if system.path_is_empty(assets_directory): retro_config['assets_directory'] = get_default_assets_directory() retro_config.save() core = self.game_config.get('core') info_file = os.path.join(get_default_info_directory(), '{}_libretro.info'.format(core)) if os.path.exists(info_file): core_config = RetroConfig(info_file) try: firmware_count = int(core_config['firmware_count']) except (ValueError, TypeError): firmware_count = 0 system_path = self.get_system_directory(retro_config) notes = core_config['notes'] or '' checksums = {} if notes.startswith('Suggested md5sums:'): parts = notes.split('|') for part in parts[1:]: checksum, filename = part.split(' = ') checksums[filename] = checksum for index in range(firmware_count): firmware_filename = core_config['firmware%d_path' % index] firmware_path = os.path.join(system_path, firmware_filename) if os.path.exists(firmware_path): if firmware_filename in checksums: checksum = system.get_md5_hash(firmware_path) if checksum == checksums[firmware_filename]: checksum_status = 'Checksum good' else: checksum_status = 'Checksum failed' else: checksum_status = 'No checksum info' logger.info("Firmware '{}' found ({})".format(firmware_filename, checksum_status)) else: logger.warning("Firmware '{}' not found!".format(firmware_filename)) # Before closing issue #431 # TODO check for firmware*_opt and display an error message if # firmware is missing # TODO Add dialog for copying the firmware in the correct # location return True
def get_game_path_from_appid(self, appid): """Return the game directory""" for apps_path in self.get_steamapps_dirs(): logger.debug("Checking for game %s in %s", appid, apps_path) game_path = get_path_from_appmanifest(apps_path, appid) if game_path: logger.debug("Game found in %s", game_path) return game_path logger.warning("Data path for SteamApp %s not found.", appid)
def md5sum(self, filename): """checks the md5sum of a file, does not belong here""" logger.warning("please remove md5sum from Runner") md5check = hashlib.md5() file_ = open(filename, "rb") content = file_.readlines() file_.close() for line in content: md5check.update(line) return md5check.hexdigest()
def parse(self): key_maps = self.mapping.split(',') for key_map in key_maps: if not key_map: continue xinput_key, sdl_key = key_map.split(':') if xinput_key not in self.valid_keys: logger.warning('Unrecognized key %s', xinput_key) continue self.keys[xinput_key] = sdl_key
def do_command_line(self, command_line): options = command_line.get_options_dict() # Use stdout to output logs, only if no command line argument is # provided argc = len(sys.argv) - 1 if "-d" in sys.argv or "--debug" in sys.argv: argc -= 1 if not argc: # Switch back the log output to stderr (the default in Python) # to avoid messing with any output from command line options. # Use when targetting Python 3.7 minimum # console_handler.setStream(sys.stderr) # Until then... logger.removeHandler(log.console_handler) log.console_handler = logging.StreamHandler(stream=sys.stdout) log.console_handler.setFormatter(log.SIMPLE_FORMATTER) logger.addHandler(log.console_handler) # Set up logger if options.contains("debug"): log.console_handler.setFormatter(log.DEBUG_FORMATTER) logger.setLevel(logging.DEBUG) # Text only commands # Print Lutris version and exit if options.contains("version"): executable_name = os.path.basename(sys.argv[0]) print(executable_name + "-" + settings.VERSION) logger.setLevel(logging.NOTSET) return 0 logger.info("Running Lutris %s", settings.VERSION) run_all_checks() migrate() AsyncCall(init_dxvk_versions) # List game if options.contains("list-games"): game_list = pga.get_games() if options.contains("installed"): game_list = [game for game in game_list if game["installed"]] if options.contains("json"): self.print_game_json(command_line, game_list) else: self.print_game_list(command_line, game_list) return 0 # List Steam games elif options.contains("list-steam-games"): self.print_steam_list(command_line) return 0 # List Steam folders elif options.contains("list-steam-folders"): self.print_steam_folders(command_line) return 0 # Execute command in Lutris context elif options.contains("exec"): command = options.lookup_value("exec").get_string() self.execute_command(command) return 0 try: url = options.lookup_value(GLib.OPTION_REMAINING) installer_info = self.get_lutris_action(url) except ValueError: self._print(command_line, "%s is not a valid URI" % url.get_strv()) return 1 game_slug = installer_info["game_slug"] action = installer_info["action"] revision = installer_info["revision"] installer_file = None if options.contains("install"): installer_file = options.lookup_value("install").get_string() installer_file = os.path.abspath(installer_file) action = "install" if not os.path.isfile(installer_file): self._print(command_line, "No such file: %s" % installer_file) return 1 # Graphical commands self.activate() self.set_tray_icon() db_game = None if game_slug: if action == "rungameid": # Force db_game to use game id db_game = pga.get_game_by_field(game_slug, "id") elif action == "rungame": # Force db_game to use game slug db_game = pga.get_game_by_field(game_slug, "slug") elif action == "install": # Installers can use game or installer slugs db_game = pga.get_game_by_field( game_slug, "slug" ) or pga.get_game_by_field(game_slug, "installer_slug") else: # Dazed and confused, try anything that might works db_game = ( pga.get_game_by_field(game_slug, "id") or pga.get_game_by_field(game_slug, "slug") or pga.get_game_by_field(game_slug, "installer_slug") ) if not action: if db_game and db_game["installed"]: # Game found but no action provided, ask what to do dlg = InstallOrPlayDialog(db_game["name"]) if not dlg.action_confirmed: action = None elif dlg.action == "play": action = "rungame" elif dlg.action == "install": action = "install" elif game_slug or installer_file: # No game found, default to install if a game_slug or # installer_file is provided action = "install" if action == "install": InstallerWindow( game_slug=game_slug, installer_file=installer_file, revision=revision, parent=self.window, application=self, ) elif action in ("rungame", "rungameid"): if not db_game or not db_game["id"]: logger.warning("No game found in library") return 0 logger.info("Launching %s", db_game["name"]) # If game is not installed, show the GUI before running. Otherwise leave the GUI closed. if not db_game["installed"]: self.window.present() self.launch(Game(db_game["id"])) else: self.window.present() return 0
def get_downloads(self, gogid): gog_data = self.get_game_details(gogid) if not gog_data: logger.warning("Unable to get GOG data for game %s", gogid) return [] return gog_data["downloads"]
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', False): env.update(runtime.get_env()) userenv = data.get('env') or {} for key in userenv: env_value = userenv[key] userenv[key] = self._get_file(env_value) env.update(userenv) 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) exec_path = system.find_executable(file_ref) if not exec_path: raise ScriptingError("Unable to find executable %s" % file_ref) if not os.access(exec_path, os.X_OK): logger.warning("Making %s executable", exec_path) self.chmodx(exec_path) if terminal: terminal = system.get_default_terminal() if not working_dir or not os.path.exists(working_dir): working_dir = self.target_path command = [exec_path] + args logger.debug("Executing %s", command) thread = LutrisThread(command, env=env, term=terminal, cwd=working_dir, include_processes=include_processes, exclude_processes=exclude_processes) thread.start() GLib.idle_add(self.parent.attach_logger, thread) self.heartbeat = GLib.timeout_add(1000, self._monitor_task, thread) return 'STOP'
def watch_children(self): """Poke at the running process(es).""" if not self.game_process or not self.is_running: logger.error('No game process available') return False if not self.ready_state: self.cycles_without_children = 0 return True processes, num_children, num_watched_children, terminated_children = self.get_processes( ) if processes != self.monitored_processes: self.monitored_processes = processes logger.debug("Processes: " + " | ".join([ "{}: {}".format(key, ', '.join(processes[key])) for key in processes if processes[key] ])) if num_watched_children > 0 and not self.monitoring_started: logger.debug("Start process monitoring") self.monitoring_started = True if self.runner and hasattr(self.runner, 'watch_game_process'): if not self.runner.watch_game_process(): self.is_running = False return False if num_watched_children == 0: time_since_start = time.time() - self.startup_time if self.monitoring_started or time_since_start > WARMUP_TIME: self.cycles_without_children += 1 logger.debug("Cycles without children: %s", self.cycles_without_children) else: self.cycles_without_children = 0 max_cycles_reached = (self.cycles_without_children >= MAX_CYCLES_WITHOUT_CHILDREN) if num_children == 0 or max_cycles_reached: if max_cycles_reached: logger.debug( 'Maximum number of cycles without children reached') self.is_running = False self.stop() if num_children == 0: logger.debug("No children left in thread") self.game_process.communicate() else: logger.debug('Some processes are still active (%d)', num_children) if self.is_zombie(): logger.debug('Killing game process') self.game_process.kill() self.return_code = self.game_process.returncode if self.stdout_monitor: GLib.source_remove(self.stdout_monitor) return False if terminated_children and terminated_children == num_watched_children: logger.debug("Waiting for processes to exit") try: self.game_process.wait(2) except subprocess.TimeoutExpired: logger.warning("Processes are still running") return True if self.stdout_monitor: logger.debug("Removing stdout monitor") GLib.source_remove(self.stdout_monitor) logger.debug("Thread is no longer running") self.is_running = False self.restore_environment() return False return True
def appid(self): logger.warning("Do not access appid from interpreter") return self.installer.service_appid
def get_filename(self): """Deprecated""" logger.warning("Just use get_text") return self.get_text()
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", False): # Possibly need to handle prefer_system_libs here env.update(runtime.get_env()) userenv = data.get("env") or {} for key in userenv: env_value = userenv[key] userenv[key] = self._get_file(env_value) env.update(userenv) 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) exec_path = system.find_executable(file_ref) if not exec_path: raise ScriptingError("Unable to find executable %s" % file_ref) if not os.access(exec_path, os.X_OK): logger.warning("Making %s executable", exec_path) self.chmodx(exec_path) 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 prelaunch(self): # pylint: disable=too-many-locals,too-many-branches,too-many-statements config_file = self.get_config_file() # TODO: review later # Create retroarch.cfg if it doesn't exist. if not system.path_exists(config_file): f = open(config_file, "w") f.write("# Lutris RetroArch Configuration") f.close() # Build the default config settings. retro_config = RetroConfig(config_file) retro_config["libretro_directory"] = get_default_config_path( "cores") retro_config["libretro_info_path"] = get_default_config_path( "info") retro_config["content_database_path"] = get_default_config_path( "database/rdb") retro_config["cheat_database_path"] = get_default_config_path( "database/cht") retro_config["cursor_directory"] = get_default_config_path( "database/cursors") retro_config["screenshot_directory"] = get_default_config_path( "screenshots") retro_config[ "input_remapping_directory"] = get_default_config_path( "remaps") retro_config["video_shader_dir"] = get_default_config_path( "shaders") retro_config["core_assets_directory"] = get_default_config_path( "downloads") retro_config["thumbnails_directory"] = get_default_config_path( "thumbnails") retro_config["playlist_directory"] = get_default_config_path( "playlists") retro_config["joypad_autoconfig_dir"] = get_default_config_path( "autoconfig") retro_config["rgui_config_directory"] = get_default_config_path( "config") retro_config["overlay_directory"] = get_default_config_path( "overlay") retro_config["assets_directory"] = get_default_config_path( "assets") retro_config.save() else: retro_config = RetroConfig(config_file) core = self.game_config.get("core") info_file = os.path.join(get_default_config_path("info"), "{}_libretro.info".format(core)) if system.path_exists(info_file): retro_config = RetroConfig(info_file) try: firmware_count = int(retro_config["firmware_count"]) except (ValueError, TypeError): firmware_count = 0 system_path = self.get_system_directory(retro_config) notes = str(retro_config["notes"] or "") checksums = {} if notes.startswith("Suggested md5sums:"): parts = notes.split("|") for part in parts[1:]: checksum, filename = part.split(" = ") checksums[filename] = checksum for index in range(firmware_count): firmware_filename = retro_config["firmware%d_path" % index] firmware_path = os.path.join(system_path, firmware_filename) if system.path_exists(firmware_path): if firmware_filename in checksums: checksum = system.get_md5_hash(firmware_path) if checksum == checksums[firmware_filename]: checksum_status = "Checksum good" else: checksum_status = "Checksum failed" else: checksum_status = "No checksum info" logger.info("Firmware '%s' found (%s)", firmware_filename, checksum_status) else: logger.warning("Firmware '%s' not found!", firmware_filename) # Before closing issue #431 # TODO check for firmware*_opt and display an error message if # firmware is missing # TODO Add dialog for copying the firmware in the correct # location return True
def load(self): logger.warning("Load method not implemented")
def add_arguments(self): if hasattr(self, "set_option_context_summary"): self.set_option_context_summary( "Run a game directly by adding the parameter lutris:rungame/game-identifier.\n" "If several games share the same identifier you can use the numerical ID " "(displayed when running lutris --list-games) and add " "lutris:rungameid/numerical-id.\n" "To install a game, add lutris:install/game-identifier.") else: logger.warning("GLib.set_option_context_summary missing, " "was added in GLib 2.56 (Released 2018-03-12)") self.add_main_option( "version", ord("v"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _("Print the version of Lutris and exit"), None, ) self.add_main_option( "debug", ord("d"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _("Show debug messages"), None, ) self.add_main_option( "install", ord("i"), GLib.OptionFlags.NONE, GLib.OptionArg.STRING, _("Install a game from a yml file"), None, ) self.add_main_option( "exec", ord("e"), GLib.OptionFlags.NONE, GLib.OptionArg.STRING, _("Execute a program with the lutris runtime"), None, ) self.add_main_option( "list-games", ord("l"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _("List all games in database"), None, ) self.add_main_option( "installed", ord("o"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _("Only list installed games"), None, ) self.add_main_option( "list-steam-games", ord("s"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _("List available Steam games"), None, ) self.add_main_option( "list-steam-folders", 0, GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _("List all known Steam library folders"), None, ) self.add_main_option( "json", ord("j"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _("Display the list of games in JSON format"), None, ) self.add_main_option( "reinstall", 0, GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _("Reinstall game"), None, ) self.add_main_option("submit-issue", 0, GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _("Submit an issue"), None) self.add_main_option( GLib.OPTION_REMAINING, 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING_ARRAY, _("uri to open"), "URI", )
def do_command_line(self, command_line): options = command_line.get_options_dict() # Use stdout to output logs, only if no command line argument is # provided argc = len(sys.argv) - 1 if "-d" in sys.argv or "--debug" in sys.argv: argc -= 1 if not argc: # Switch back the log output to stderr (the default in Python) # to avoid messing with any output from command line options. # Use when targetting Python 3.7 minimum # console_handler.setStream(sys.stderr) # Until then... logger.removeHandler(log.console_handler) log.console_handler = logging.StreamHandler(stream=sys.stdout) log.console_handler.setFormatter(log.SIMPLE_FORMATTER) logger.addHandler(log.console_handler) # Set up logger if options.contains("debug"): log.console_handler.setFormatter(log.DEBUG_FORMATTER) logger.setLevel(logging.DEBUG) # Text only commands # Print Lutris version and exit if options.contains("version"): executable_name = os.path.basename(sys.argv[0]) print(executable_name + "-" + settings.VERSION) logger.setLevel(logging.NOTSET) return 0 logger.info("Running Lutris %s", settings.VERSION) migrate() run_all_checks() AsyncCall(init_dxvk_versions) # List game if options.contains("list-games"): game_list = pga.get_games() if options.contains("installed"): game_list = [game for game in game_list if game["installed"]] if options.contains("json"): self.print_game_json(command_line, game_list) else: self.print_game_list(command_line, game_list) return 0 # List Steam games elif options.contains("list-steam-games"): self.print_steam_list(command_line) return 0 # List Steam folders elif options.contains("list-steam-folders"): self.print_steam_folders(command_line) return 0 # Execute command in Lutris context elif options.contains("exec"): command = options.lookup_value("exec").get_string() self.execute_command(command) return 0 elif options.contains("submit-issue"): IssueReportWindow(application=self) return 0 try: url = options.lookup_value(GLib.OPTION_REMAINING) installer_info = self.get_lutris_action(url) except ValueError: self._print(command_line, "%s is not a valid URI" % url.get_strv()) return 1 game_slug = installer_info["game_slug"] action = installer_info["action"] revision = installer_info["revision"] installer_file = None if options.contains("install"): installer_file = options.lookup_value("install").get_string() if installer_file.startswith(("http:", "https:")): try: request = Request(installer_file).get() except HTTPError: self._print(command_line, "Failed to download %s" % installer_file) return 1 try: headers = dict(request.response_headers) file_name = headers["Content-Disposition"].split("=", 1)[-1] except (KeyError, IndexError): file_name = os.path.basename(installer_file) file_path = os.path.join(tempfile.gettempdir(), file_name) self._print( command_line, "download %s to %s started" % (installer_file, file_path)) with open(file_path, 'wb') as dest_file: dest_file.write(request.content) installer_file = file_path action = "install" else: installer_file = os.path.abspath(installer_file) action = "install" if not os.path.isfile(installer_file): self._print(command_line, "No such file: %s" % installer_file) return 1 db_game = None if game_slug: if action == "rungameid": # Force db_game to use game id self.run_in_background = True db_game = pga.get_game_by_field(game_slug, "id") elif action == "rungame": # Force db_game to use game slug self.run_in_background = True db_game = pga.get_game_by_field(game_slug, "slug") elif action == "install": # Installers can use game or installer slugs self.run_in_background = True db_game = pga.get_game_by_field( game_slug, "slug") or pga.get_game_by_field( game_slug, "installer_slug") else: # Dazed and confused, try anything that might works db_game = (pga.get_game_by_field(game_slug, "id") or pga.get_game_by_field(game_slug, "slug") or pga.get_game_by_field(game_slug, "installer_slug")) # Graphical commands self.activate() self.set_tray_icon() if not action: if db_game and db_game["installed"]: # Game found but no action provided, ask what to do dlg = InstallOrPlayDialog(db_game["name"]) if not dlg.action_confirmed: action = None elif dlg.action == "play": action = "rungame" elif dlg.action == "install": action = "install" elif game_slug or installer_file: # No game found, default to install if a game_slug or # installer_file is provided action = "install" if action == "install": self.show_window( InstallerWindow, parent=self.window, game_slug=game_slug, installer_file=installer_file, revision=revision, ) elif action in ("rungame", "rungameid"): if not db_game or not db_game["id"]: logger.warning("No game found in library") if not self.window.is_visible(): self.do_shutdown() return 0 self.launch(Game(db_game["id"])) return 0
def get_launch_parameters(runner, gameplay_info): system_config = runner.system_config 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") # Mangohud activation mangohud = system_config.get("mangohud") or "" if mangohud and system.find_executable("mangohud"): launch_arguments = ["mangohud"] + launch_arguments 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") env = {} env.update(runner.get_env()) env.update(gameplay_info.get("env") or {}) # Set environment variables dependent on gameplay info # LD_PRELOAD ld_preload = gameplay_info.get("ld_preload") if ld_preload: env["LD_PRELOAD"] = ld_preload # 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]) # Feral gamemode gamemode = system_config.get( "gamemode") and LINUX_SYSTEM.gamemode_available() if gamemode: if system.find_executable("gamemoderun"): launch_arguments.insert(0, "gamemoderun") else: env["LD_PRELOAD"] = ":".join([ path for path in [ env.get("LD_PRELOAD"), "libgamemodeauto.so", ] if path ]) return launch_arguments, env
def check_vulkan(): """Reports if Vulkan is enabled on the system""" if not vkquery.is_vulkan_supported(): logger.warning( "Vulkan is not available or your system isn't Vulkan capable")
def __init__(self, path): if not path: logger.warning("No path specified for Wine prefix") self.path = path
def set_joystick_controls(joy_ids, machine): """ Setup joystick mappings per machine """ # Get the controller mappings controller_mappings = get_controller_mappings() if not controller_mappings: logger.warning("No controller detected for joysticks %s.", joy_ids) return [] # TODO currently only supports the first controller. Add support for other controllers. mapping = controller_mappings[0][1] # Construct a dictionnary of button codes to parse to mendafen map_code = { "a": "", "b": "", "c": "", "x": "", "y": "", "z": "", "back": "", "start": "", "leftshoulder": "", "rightshoulder": "", "lefttrigger": "", "righttrigger": "", "leftstick": "", "rightstick": "", "select": "", "shoulder_l": "", "shoulder_r": "", "i": "", "ii": "", "iii": "", "iv": "", "v": "", "vi": "", "run": "", "ls": "", "rs": "", "fire1": "", "fire2": "", "option_1": "", "option_2": "", "cross": "", "circle": "", "square": "", "triangle": "", "r1": "", "r2": "", "l1": "", "l2": "", "option": "", "l": "", "r": "", "right-x": "", "right-y": "", "left-x": "", "left-y": "", "up-x": "", "up-y": "", "down-x": "", "down-y": "", "up-l": "", "up-r": "", "down-l": "", "down-r": "", "left-l": "", "left-r": "", "right-l": "", "right-r": "", "lstick_up": "0000c001", "lstick_down": "00008001", "lstick_right": "00008000", "lstick_left": "0000c000", "rstick_up": "0000c003", "rstick_down": "00008003", "rstick_left": "0000c002", "rstick_right": "00008002", "dpup": "0000c005", "dpdown": "00008005", "dpleft": "0000c004", "dpright": "00008004", } # Insert the button mapping number into the map_codes for button in mapping.keys: bttn_id = mapping.keys[button] if bttn_id[0] == "b": # it's a button map_code[button] = "000000" + bttn_id[1:].zfill(2) # Duplicate button names that are emulated in mednanfen map_code["up"] = map_code["dpup"] # map_code["down"] = map_code["dpdown"] # map_code["left"] = map_code["dpleft"] # Multiple systems map_code["right"] = map_code["dpright"] map_code["select"] = map_code["back"] # map_code["shoulder_r"] = map_code["rightshoulder"] # GBA map_code["shoulder_l"] = map_code["leftshoulder"] # map_code["i"] = map_code["b"] # map_code["ii"] = map_code["a"] # map_code["iii"] = map_code["leftshoulder"] map_code["iv"] = map_code["y"] # PCEngine and PCFX map_code["v"] = map_code["x"] # map_code["vi"] = map_code["rightshoulder"] map_code["run"] = map_code["start"] # map_code["ls"] = map_code["leftshoulder"] # map_code["rs"] = map_code["rightshoulder"] # Saturn map_code["c"] = map_code["righttrigger"] # map_code["z"] = map_code["lefttrigger"] # map_code["fire1"] = map_code["a"] # Master System map_code["fire2"] = map_code["b"] # map_code["option_1"] = map_code["x"] # Lynx map_code["option_2"] = map_code["y"] # map_code["r1"] = map_code["rightshoulder"] # map_code["r2"] = map_code["righttrigger"] # map_code["l1"] = map_code["leftshoulder"] # map_code["l2"] = map_code["lefttrigger"] # PlayStation map_code["cross"] = map_code["a"] # map_code["circle"] = map_code["b"] # map_code["square"] = map_code["x"] # map_code["triangle"] = map_code["y"] # map_code["option"] = map_code["select"] # NeoGeo pocket map_code["l"] = map_code["leftshoulder"] # SNES map_code["r"] = map_code["rightshoulder"] # map_code["right-x"] = map_code["dpright"] # map_code["left-x"] = map_code["dpleft"] # map_code["up-x"] = map_code["dpup"] # map_code["down-x"] = map_code["dpdown"] # Wonder Swan map_code["right-y"] = map_code["lstick_right"] map_code["left-y"] = map_code["lstick_left"] # map_code["up-y"] = map_code["lstick_up"] # map_code["down-y"] = map_code["lstick_down"] # map_code["up-l"] = map_code["dpup"] # map_code["down-l"] = map_code["dpdown"] # map_code["left-l"] = map_code["dpleft"] # map_code["right-l"] = map_code["dpright"] # map_code["up-r"] = map_code["rstick_up"] # map_code["down-r"] = map_code["rstick_down"] # Virtual boy map_code["left-r"] = map_code["rstick_left"] # map_code["right-r"] = map_code["rstick_right"] # map_code["lt"] = map_code["leftshoulder"] # map_code["rt"] = map_code["rightshoulder"] # # Define which buttons to use for each machine layout = { "nes": ["a", "b", "start", "select", "up", "down", "left", "right"], "gb": ["a", "b", "start", "select", "up", "down", "left", "right"], "gba": [ "a", "b", "shoulder_r", "shoulder_l", "start", "select", "up", "down", "left", "right", ], "pce": [ "i", "ii", "iii", "iv", "v", "vi", "run", "select", "up", "down", "left", "right", ], "ss": [ "a", "b", "c", "x", "y", "z", "ls", "rs", "start", "up", "down", "left", "right", ], "gg": ["button1", "button2", "start", "up", "down", "left", "right"], "md": [ "a", "b", "c", "x", "y", "z", "start", "up", "down", "left", "right", ], "sms": ["fire1", "fire2", "up", "down", "left", "right"], "lynx": ["a", "b", "option_1", "option_2", "up", "down", "left", "right"], "psx": [ "cross", "circle", "square", "triangle", "l1", "l2", "r1", "r2", "start", "select", "lstick_up", "lstick_down", "lstick_right", "lstick_left", "rstick_up", "rstick_down", "rstick_left", "rstick_right", "up", "down", "left", "right", ], "pcfx": [ "i", "ii", "iii", "iv", "v", "vi", "run", "select", "up", "down", "left", "right", ], "ngp": ["a", "b", "option", "up", "down", "left", "right"], "snes": [ "a", "b", "x", "y", "l", "r", "start", "select", "up", "down", "left", "right", ], "wswan": [ "a", "b", "right-x", "right-y", "left-x", "left-y", "up-x", "up-y", "down-x", "down-y", "start", ], "vb": [ "up-l", "down-l", "left-l", "right-l", "up-r", "down-r", "left-r", "right-r", "a", "b", "lt", "rt", ], } # Select a the gamepad type controls = [] if machine in ["gg", "lynx", "wswan", "gb", "gba", "vb"]: gamepad = "builtin.gamepad" elif machine in ["md"]: gamepad = "port1.gamepad6" controls.append("-md.input.port1") controls.append("gamepad6") elif machine in ["psx"]: gamepad = "port1.dualshock" controls.append("-psx.input.port1") controls.append("dualshock") else: gamepad = "port1.gamepad" # Construct the controlls options for button in layout[machine]: controls.append("-{}.input.{}.{}".format(machine, gamepad, button)) controls.append("joystick {} {}".format(joy_ids[0], map_code[button])) return controls
def create_prefix( # noqa: C901 prefix, wine_path=None, arch=WINE_DEFAULT_ARCH, overrides=None, install_gecko=None, install_mono=None, ): """Create a new Wine prefix.""" # pylint: disable=too-many-locals if overrides is None: overrides = {} if not prefix: raise ValueError("No Wine prefix path given") logger.info("Creating a %s prefix in %s", arch, prefix) # Follow symlinks, don't delete existing ones as it would break some setups if os.path.islink(prefix): prefix = os.readlink(prefix) # Avoid issue of 64bit Wine refusing to create win32 prefix # over an existing empty folder. if os.path.isdir(prefix) and not os.listdir(prefix): os.rmdir(prefix) if not wine_path: wine = import_runner("wine") wine_path = wine().get_executable() if not wine_path: logger.error("Wine not found, can't create prefix") return wineboot_path = os.path.join(os.path.dirname(wine_path), "wineboot") if not system.path_exists(wineboot_path): logger.error( "No wineboot executable found in %s, " "your wine installation is most likely broken", wine_path, ) return if install_gecko == "False": overrides["mshtml"] = "disabled" if install_mono == "False": overrides["mscoree"] = "disabled" wineenv = { "WINEARCH": arch, "WINEPREFIX": prefix, "WINEDLLOVERRIDES": get_overrides_env(overrides), } system.execute([wineboot_path], env=wineenv) for loop_index in range(50): time.sleep(0.25) if system.path_exists(os.path.join(prefix, "user.reg")): break if loop_index == 20: logger.warning( "Wine prefix creation is taking longer than expected...") if not os.path.exists(os.path.join(prefix, "user.reg")): logger.error("No user.reg found after prefix creation. " "Prefix might not be valid") return logger.info("%s Prefix created in %s", arch, prefix) prefix_manager = WinePrefixManager(prefix) prefix_manager.setup_defaults() if 'steamapps/common' in prefix.lower(): from lutris.runners.winesteam import winesteam runner = winesteam() logger.info( "Transfering Steam information from default prefix to new prefix") dest_path = '/tmp/steam.reg' default_prefix = runner.get_default_prefix(runner.default_arch) wineexec("regedit", args=r"/E '%s' 'HKEY_CURRENT_USER\Software\Valve\Steam'" % dest_path, prefix=default_prefix) set_regedit_file(dest_path, wine_path=wine_path, prefix=prefix, arch=arch) try: os.remove(dest_path) except FileNotFoundError: logger.error("File %s was already removed", dest_path) steam_drive_path = os.path.join(prefix, 'dosdevices', 's:') if not system.path_exists(steam_drive_path): logger.info("Linking Steam default prefix to drive S:") os.symlink(os.path.join(default_prefix, 'drive_c'), steam_drive_path)
def shutdown(self): logger.warning("Steam shutdown has not been implemented " "(well it was but then we removed it and now we need it back)")
def generate_installer(self, db_game): details = json.loads(db_game["details"]) platforms = [download["platform"] for download in details["downloads"]] logger.debug(details) system_config = {} if "linux" in platforms and self.platform_has_downloads( details["downloads"], "linux"): runner = "linux" game_config = {"exe": AUTO_ELF_EXE} filename = self.get_filename_for_platform(details["downloads"], "linux") if filename.lower().endswith(".sh"): script = [ { "extract": { "file": "humblegame", "format": "zip", "dst": "$CACHE" } }, { "merge": { "src": "$CACHE/data/noarch", "dst": "$GAMEDIR", "optional": True } }, { "move": { "src": "$CACHE/data/noarch", "dst": "$CACHE/noarch", "optional": True } }, { "merge": { "src": "$CACHE/data/x86_64", "dst": "$GAMEDIR", "optional": True } }, { "move": { "src": "$CACHE/data/x86_64", "dst": "$CACHE/x86_64", "optional": True } }, { "merge": { "src": "$CACHE/data/x86", "dst": "$GAMEDIR", "optional": True } }, { "move": { "src": "$CACHE/data/x86", "dst": "$CACHE/x86", "optional": True } }, { "merge": { "src": "$CACHE/data/", "dst": "$GAMEDIR", "optional": True } }, ] elif filename.endswith("-bin"): script = [ { "extract": { "file": "humblegame", "format": "zip", "dst": "$CACHE" } }, { "merge": { "src": "$CACHE/data/", "dst": "$GAMEDIR" } }, ] else: script = [{"extract": {"file": "humblegame"}}] system_config = { "gamemode": 'false' } # Unity games crash with gamemode elif "windows" in platforms: runner = "wine" game_config = {"exe": AUTO_WIN32_EXE, "prefix": "$GAMEDIR"} filename = self.get_filename_for_platform(details["downloads"], "windows") if filename.lower().endswith(".zip"): script = [{ "task": { "name": "create_prefix", "prefix": "$GAMEDIR" } }, { "extract": { "file": "humblegame", "dst": "$GAMEDIR/drive_c/%s" % db_game["slug"] } }] else: script = [{ "task": { "name": "wineexec", "executable": "humblegame" } }] else: logger.warning("Unsupported platforms: %s", platforms) return {} return { "name": db_game["name"], "version": "Humble Bundle", "slug": details["machine_name"], "game_slug": slugify(db_game["name"]), "runner": runner, "humbleid": db_game["appid"], "script": { "game": game_config, "system": system_config, "files": [{ "humblegame": "N/A:Select the installer from Humble Bundle" }], "installer": script } }
def get_launch_parameters(self, gameplay_info): system_config = self.runner.system_config 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") # Mangohud activation mangohud = system_config.get("mangohud") or "" if mangohud and system.find_executable("mangohud"): # This is probably not the way to go. This only work with a few # Wine games. It will probably crash it, or do nothing at all. # I have never got mangohud to work on anything other than a Wine # game. dialogs.NoticeDialog( "MangoHud support is experimental. Expect the " "game to crash or the framerate counter not to " "appear at all.") launch_arguments = ["mangohud"] + launch_arguments 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") env = {} env.update(self.runner.get_env()) env.update(gameplay_info.get("env") or {}) env["game_name"] = self.name # Set environment variables dependent on gameplay info # LD_PRELOAD ld_preload = gameplay_info.get("ld_preload") if ld_preload: env["LD_PRELOAD"] = ld_preload # 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]) # Feral gamemode gamemode = system_config.get( "gamemode") and LINUX_SYSTEM.gamemode_available() if gamemode: if system.find_executable("gamemoderun"): launch_arguments.insert(0, "gamemoderun") else: env["LD_PRELOAD"] = ":".join([ path for path in [ env.get("LD_PRELOAD"), "libgamemodeauto.so", ] if path ]) return launch_arguments, env
def get_running_game(self): ids = self.application.get_running_game_ids() for game_id in ids: if game_id == self.game.id: return self.game logger.warning("Game %s not in %s", self.game_id, ids)
def do_command_line(self, command_line): options = command_line.get_options_dict() # Set up logger if options.contains("debug"): console_handler.setFormatter(DEBUG_FORMATTER) logger.setLevel(logging.DEBUG) # Text only commands # Print Lutris version and exit if options.contains("version"): executable_name = os.path.basename(sys.argv[0]) print(executable_name + "-" + VERSION) logger.setLevel(logging.NOTSET) return 0 # List game if options.contains("list-games"): game_list = pga.get_games() if options.contains("installed"): game_list = [game for game in game_list if game["installed"]] if options.contains("json"): self.print_game_json(command_line, game_list) else: self.print_game_list(command_line, game_list) return 0 # List Steam games elif options.contains("list-steam-games"): self.print_steam_list(command_line) return 0 # List Steam folders elif options.contains("list-steam-folders"): self.print_steam_folders(command_line) return 0 # Execute command in Lutris context elif options.contains("exec"): command = options.lookup_value("exec").get_string() self.execute_command(command) return 0 try: url = options.lookup_value(GLib.OPTION_REMAINING) installer_info = self.get_lutris_action(url) except ValueError: self._print(command_line, "%s is not a valid URI" % url.get_strv()) return 1 game_slug = installer_info["game_slug"] action = installer_info["action"] revision = installer_info["revision"] installer_file = None if options.contains("install"): installer_file = options.lookup_value("install").get_string() installer_file = os.path.abspath(installer_file) action = "install" if not os.path.isfile(installer_file): self._print(command_line, "No such file: %s" % installer_file) return 1 # Graphical commands self.activate() db_game = None if game_slug: if action == "rungameid": # Force db_game to use game id db_game = pga.get_game_by_field(game_slug, "id") elif action == "rungame": # Force db_game to use game slug db_game = pga.get_game_by_field(game_slug, "slug") elif action == "install": # Installers can use game or installer slugs db_game = pga.get_game_by_field( game_slug, "slug") or pga.get_game_by_field( game_slug, "installer_slug") else: # Dazed and confused, try anything that might works db_game = (pga.get_game_by_field(game_slug, "id") or pga.get_game_by_field(game_slug, "slug") or pga.get_game_by_field(game_slug, "installer_slug")) if not action: if db_game and db_game["installed"]: # Game found but no action provided, ask what to do dlg = InstallOrPlayDialog(db_game["name"]) if not dlg.action_confirmed: action = None elif dlg.action == "play": action = "rungame" elif dlg.action == "install": action = "install" elif game_slug or installer_file: # No game found, default to install if a game_slug or # installer_file is provided action = "install" if action == "install": InstallerWindow( game_slug=game_slug, installer_file=installer_file, revision=revision, parent=self, application=self.application, ) elif action in ("rungame", "rungameid"): if not db_game or not db_game["id"]: logger.warning("No game found in library") return 0 logger.info("Launching %s", db_game["name"]) # If game is not installed, show the GUI before running. Otherwise leave the GUI closed. if not db_game["installed"]: self.window.present() self.launch(db_game["id"]) else: self.window.present() return 0
import gi from lutris.util.log import logger NOTIFY_SUPPORT = True try: gi.require_version('Notify', '0.7') from gi.repository import Notify except ImportError: NOTIFY_SUPPORT = False if NOTIFY_SUPPORT: Notify.init("lutris") else: logger.warning( "Notifications are disabled, please install GObject bindings for 'Notify' to enable them." ) def send_notification(title, text, file_path_to_icon="lutris"): if NOTIFY_SUPPORT: notification = Notify.Notification.new(title, text, file_path_to_icon) notification.show() else: logger.info(title) logger.info(text)
def extract_archive(path, to_directory=".", merge_single=True, extractor=None): path = os.path.abspath(path) mode = None logger.debug("Extracting %s to %s", path, to_directory) if path.endswith(".tar.gz") or path.endswith(".tgz") or extractor == "tgz": opener, mode = tarfile.open, "r:gz" elif path.endswith(".tar.xz") or path.endswith( ".txz") or extractor == "txz": opener, mode = tarfile.open, "r:xz" elif path.endswith(".tar") or extractor == "tar": opener, mode = tarfile.open, "r:" elif path.endswith(".gz") or extractor == "gzip": decompress_gz(path, to_directory) return elif path.endswith(".tar.bz2") or path.endswith( ".tbz") or extractor == "bz2": opener, mode = tarfile.open, "r:bz2" elif is_7zip_supported(path, extractor): opener = "7zip" else: raise RuntimeError( "Could not extract `%s` as no appropriate extractor is found" % path) temp_name = ".extract-" + str(uuid.uuid4())[:8] temp_path = temp_dir = os.path.join(to_directory, temp_name) try: _do_extract(path, temp_path, opener, mode, extractor) except OSError as ex: logger.exception("Extraction failed: %s", ex) raise ExtractFailure(str(ex)) if merge_single: extracted = os.listdir(temp_path) if len(extracted) == 1: temp_path = os.path.join(temp_path, extracted[0]) if os.path.isfile(temp_path): destination_path = os.path.join(to_directory, extracted[0]) if os.path.isfile(destination_path): logger.warning("Overwrite existing file %s", destination_path) os.remove(destination_path) shutil.move(temp_path, to_directory) os.removedirs(temp_dir) else: for archive_file in os.listdir(temp_path): source_path = os.path.join(temp_path, archive_file) destination_path = os.path.join(to_directory, archive_file) # logger.debug("Moving extracted files from %s to %s", source_path, destination_path) if system.path_exists(destination_path): logger.warning("Overwrite existing path %s", destination_path) if os.path.isfile(destination_path): os.remove(destination_path) shutil.move(source_path, destination_path) elif os.path.isdir(destination_path): system.merge_folders(source_path, destination_path) else: shutil.move(source_path, destination_path) system.remove_folder(temp_dir) logger.debug("Finished extracting %s", path) return path, to_directory
def do_command_line(self, command_line): # noqa: C901 # pylint: disable=arguments-differ # pylint: disable=too-many-locals,too-many-return-statements,too-many-branches # pylint: disable=too-many-statements # TODO: split into multiple methods to reduce complexity (35) options = command_line.get_options_dict() # Use stdout to output logs, only if no command line argument is # provided argc = len(sys.argv) - 1 if "-d" in sys.argv or "--debug" in sys.argv: argc -= 1 if not argc: # Switch back the log output to stderr (the default in Python) # to avoid messing with any output from command line options. # Use when targetting Python 3.7 minimum # console_handler.setStream(sys.stderr) # Until then... logger.removeHandler(log.console_handler) log.console_handler = logging.StreamHandler(stream=sys.stdout) log.console_handler.setFormatter(log.SIMPLE_FORMATTER) logger.addHandler(log.console_handler) # Set up logger if options.contains("debug"): log.console_handler.setFormatter(log.DEBUG_FORMATTER) logger.setLevel(logging.DEBUG) # Text only commands # Print Lutris version and exit if options.contains("version"): executable_name = os.path.basename(sys.argv[0]) print(executable_name + "-" + settings.VERSION) logger.setLevel(logging.NOTSET) return 0 init_lutris() migrate() run_all_checks() if options.contains("dest"): dest_dir = options.lookup_value("dest").get_string() else: dest_dir = None # List game if options.contains("list-games"): game_list = games_db.get_games() if options.contains("installed"): game_list = [game for game in game_list if game["installed"]] if options.contains("json"): self.print_game_json(command_line, game_list) else: self.print_game_list(command_line, game_list) return 0 # List Steam games if options.contains("list-steam-games"): self.print_steam_list(command_line) return 0 # List Steam folders if options.contains("list-steam-folders"): self.print_steam_folders(command_line) return 0 # List Runners if options.contains("list-runners"): self.print_runners() return 0 # List Wine Runners if options.contains("list-wine-runners"): self.print_wine_runners() return 0 # install Runner if options.contains("install-runner"): runner = options.lookup_value("install-runner").get_string() self.install_runner(runner) return 0 # Uninstall Runner if options.contains("uninstall-runner"): runner = options.lookup_value("uninstall-runner").get_string() self.uninstall_runner(runner) return 0 if options.contains("export"): slug = options.lookup_value("export").get_string() if not dest_dir: print("No destination dir given") else: export_game(slug, dest_dir) return 0 if options.contains("import"): filepath = options.lookup_value("import").get_string() if not dest_dir: print("No destination dir given") else: import_game(filepath, dest_dir) return 0 # Execute command in Lutris context if options.contains("exec"): command = options.lookup_value("exec").get_string() self.execute_command(command) return 0 if options.contains("submit-issue"): IssueReportWindow(application=self) return 0 try: url = options.lookup_value(GLib.OPTION_REMAINING) installer_info = self.get_lutris_action(url) except ValueError: self._print(command_line, _("%s is not a valid URI") % url.get_strv()) return 1 game_slug = installer_info["game_slug"] action = installer_info["action"] service = installer_info["service"] appid = installer_info["appid"] if options.contains("output-script"): action = "write-script" revision = installer_info["revision"] installer_file = None if options.contains("install"): installer_file = options.lookup_value("install").get_string() if installer_file.startswith(("http:", "https:")): try: request = Request(installer_file).get() except HTTPError: self._print(command_line, _("Failed to download %s") % installer_file) return 1 try: headers = dict(request.response_headers) file_name = headers["Content-Disposition"].split("=", 1)[-1] except (KeyError, IndexError): file_name = os.path.basename(installer_file) file_path = os.path.join(tempfile.gettempdir(), file_name) self._print( command_line, _("download {url} to {file} started").format( url=installer_file, file=file_path)) with open(file_path, 'wb') as dest_file: dest_file.write(request.content) installer_file = file_path action = "install" else: installer_file = os.path.abspath(installer_file) action = "install" if not os.path.isfile(installer_file): self._print(command_line, _("No such file: %s") % installer_file) return 1 db_game = None if game_slug and not service: if action == "rungameid": # Force db_game to use game id self.run_in_background = True db_game = games_db.get_game_by_field(game_slug, "id") elif action == "rungame": # Force db_game to use game slug self.run_in_background = True db_game = games_db.get_game_by_field(game_slug, "slug") elif action == "install": # Installers can use game or installer slugs self.run_in_background = True db_game = games_db.get_game_by_field(game_slug, "slug") \ or games_db.get_game_by_field(game_slug, "installer_slug") else: # Dazed and confused, try anything that might works db_game = (games_db.get_game_by_field(game_slug, "id") or games_db.get_game_by_field(game_slug, "slug") or games_db.get_game_by_field( game_slug, "installer_slug")) # If reinstall flag is passed, force the action to install if options.contains("reinstall"): action = "install" if action == "write-script": if not db_game or not db_game["id"]: logger.warning("No game provided to generate the script") return 1 self.generate_script( db_game, options.lookup_value("output-script").get_string()) return 0 # Graphical commands self.activate() self.set_tray_icon() if not action: if db_game and db_game["installed"]: # Game found but no action provided, ask what to do dlg = InstallOrPlayDialog(db_game["name"]) if not dlg.action_confirmed: action = None elif dlg.action == "play": action = "rungame" elif dlg.action == "install": action = "install" elif game_slug or installer_file or service: # No game found, default to install if a game_slug or # installer_file is provided action = "install" if service: service_game = ServiceGameCollection.get_game(service, appid) if service_game: service = get_enabled_services()[service]() service.install(service_game) return 0 if action == "install": installers = get_installers( game_slug=game_slug, installer_file=installer_file, revision=revision, ) if installers: self.show_installer_window(installers) elif action in ("rungame", "rungameid"): if not db_game or not db_game["id"]: logger.warning("No game found in library") if not self.window.is_visible(): self.do_shutdown() return 0 game = Game(db_game["id"]) self.on_game_launch(game) return 0
def prelaunch(self): if not LINUX_SYSTEM.get_soundfonts(): logger.warning( "FluidSynth is not installed, you might not have any music") return True
def prelaunch(self): config_file = self.get_config_file() # Create retroarch.cfg if it doesn't exist. if not os.path.exists(config_file): f = open(config_file, 'w') f.write('# Lutris RetroArch Configuration') f.close() # Build the default config settings. retro_config = RetroConfig(config_file) retro_config['libretro_directory'] = get_default_config_path('cores') retro_config['libretro_info_path'] = get_default_config_path('info') retro_config['content_database_path'] = get_default_config_path('database/rdb') retro_config['cheat_database_path'] = get_default_config_path('database/cht') retro_config['cursor_directory'] = get_default_config_path('database/cursors') retro_config['screenshot_directory'] = get_default_config_path('screenshots') retro_config['input_remapping_directory'] = get_default_config_path('remaps') retro_config['video_shader_dir'] = get_default_config_path('shaders') retro_config['core_assets_directory'] = get_default_config_path('downloads') retro_config['thumbnails_directory'] = get_default_config_path('thumbnails') retro_config['playlist_directory'] = get_default_config_path('playlists') retro_config['joypad_autoconfig_dir'] = get_default_config_path('autoconfig') retro_config['rgui_config_directory'] = get_default_config_path('config') retro_config['overlay_directory'] = get_default_config_path('overlay') retro_config['assets_directory'] = get_default_config_path('assets') retro_config.save() else: retro_config = RetroConfig(config_file) core = self.game_config.get('core') info_file = os.path.join(get_default_config_path('info'), '{}_libretro.info'.format(core)) if os.path.exists(info_file): core_config = RetroConfig(info_file) try: firmware_count = int(core_config['firmware_count']) except (ValueError, TypeError): firmware_count = 0 system_path = self.get_system_directory(retro_config) notes = core_config['notes'] or '' checksums = {} if notes.startswith('Suggested md5sums:'): parts = notes.split('|') for part in parts[1:]: checksum, filename = part.split(' = ') checksums[filename] = checksum for index in range(firmware_count): firmware_filename = core_config['firmware%d_path' % index] firmware_path = os.path.join(system_path, firmware_filename) if os.path.exists(firmware_path): if firmware_filename in checksums: checksum = system.get_md5_hash(firmware_path) if checksum == checksums[firmware_filename]: checksum_status = 'Checksum good' else: checksum_status = 'Checksum failed' else: checksum_status = 'No checksum info' logger.info("Firmware '{}' found ({})".format(firmware_filename, checksum_status)) else: logger.warning("Firmware '{}' not found!".format(firmware_filename)) # Before closing issue #431 # TODO check for firmware*_opt and display an error message if # firmware is missing # TODO Add dialog for copying the firmware in the correct # location return True
def create_prefix( # noqa: C901 prefix, wine_path=None, arch=WINE_DEFAULT_ARCH, overrides=None, install_gecko=None, install_mono=None, ): """Create a new Wine prefix.""" # pylint: disable=too-many-locals if overrides is None: overrides = {} if not prefix: raise ValueError("No Wine prefix path given") logger.info("Creating a %s prefix in %s", arch, prefix) # Follow symlinks, don't delete existing ones as it would break some setups if os.path.islink(prefix): prefix = os.readlink(prefix) # Avoid issue of 64bit Wine refusing to create win32 prefix # over an existing empty folder. if os.path.isdir(prefix) and not os.listdir(prefix): os.rmdir(prefix) if not wine_path: wine = import_runner("wine") wine_path = wine().get_executable() if not wine_path: logger.error("Wine not found, can't create prefix") return wineboot_path = os.path.join(os.path.dirname(wine_path), "wineboot") if not system.path_exists(wineboot_path): logger.error( "No wineboot executable found in %s, " "your wine installation is most likely broken", wine_path, ) return wineenv = { "WINEARCH": arch, "WINEPREFIX": prefix, "WINEDLLOVERRIDES": get_overrides_env(overrides), "WINE_MONO_CACHE_DIR": os.path.join(os.path.dirname(os.path.dirname(wine_path)), "mono"), "WINE_GECKO_CACHE_DIR": os.path.join(os.path.dirname(os.path.dirname(wine_path)), "gecko"), } if install_gecko == "False": wineenv["WINE_SKIP_GECKO_INSTALLATION"] = "1" overrides["mshtml"] = "disabled" if install_mono == "False": wineenv["WINE_SKIP_MONO_INSTALLATION"] = "1" overrides["mscoree"] = "disabled" system.execute([wineboot_path], env=wineenv) for loop_index in range(60): time.sleep(0.5) if system.path_exists(os.path.join(prefix, "user.reg")): break if loop_index == 30: logger.warning( "Wine prefix creation is taking longer than expected...") if not os.path.exists(os.path.join(prefix, "user.reg")): logger.error("No user.reg found after prefix creation. " "Prefix might not be valid") return logger.info("%s Prefix created in %s", arch, prefix) prefix_manager = WinePrefixManager(prefix) prefix_manager.setup_defaults()
import resource import shutil import subprocess import sys from collections import Counter, defaultdict # Lutris Modules from lutris.util import system from lutris.util.disks import get_drive_for_path from lutris.util.graphics import drivers, glxinfo, vkquery from lutris.util.log import logger try: from distro import linux_distribution except ImportError: logger.warning( "Package 'distro' unavailable. Unable to read Linux distribution") linux_distribution = None # Linux components used by lutris SYSTEM_COMPONENTS = { "COMMANDS": [ "xrandr", "fuser", "glxinfo", "vulkaninfo", "optirun", "primusrun", "pvkrun", "pulseaudio", "lsi-steam", "fuser",
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 = sorted( display.get_outputs(), key=lambda e: e.name == system_config.get("display")) 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: display.turn_off_except(restrict_to_display) time.sleep(3) self.resolution_changed = True resolution = system_config.get("resolution") if resolution != "off": display.change_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() 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" # 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) # xboxdrv setup xboxdrv_config = system_config.get("xboxdrv") if xboxdrv_config: self.xboxdrv_start(xboxdrv_config) 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 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, ) # Accept return codes other than 0 if "return_code" in data: return_code = data.pop("return_code") else: return_code = "0" exec_path = 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: exec_path = "bash" args = ["-c", self._get_file_path(command.strip())] include_processes.append("bash") else: # Determine whether 'file' value is a file id or a path exec_path = self._get_file_path(exec_path) if system.path_exists( exec_path) and not system.is_executable(exec_path): logger.warning("Making %s executable", exec_path) system.make_executable(exec_path) exec_abs_path = system.find_executable(exec_path) if not exec_abs_path: raise ScriptingError(_("Unable to find executable %s") % exec_path) if terminal: terminal = linux.get_default_terminal() if not working_dir or not os.path.exists(working_dir): working_dir = self.target_path command = MonitoredCommand( [exec_abs_path] + args, env=env, term=terminal, cwd=working_dir, include_processes=include_processes, exclude_processes=exclude_processes, ) command.accepted_return_code = return_code command.start() GLib.idle_add(self.parent.attach_logger, command) self.heartbeat = GLib.timeout_add(1000, self._monitor_task, command) return "STOP"