Beispiel #1
0
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)
Beispiel #2
0
 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)
Beispiel #3
0
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)
Beispiel #4
0
 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)
Beispiel #5
0
    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
Beispiel #6
0
    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}
Beispiel #7
0
 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)
Beispiel #8
0
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
Beispiel #9
0
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
Beispiel #10
0
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}
Beispiel #11
0
    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()
Beispiel #12
0
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
Beispiel #13
0
    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"])
Beispiel #14
0
 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)
Beispiel #15
0
    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()
Beispiel #16
0
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
Beispiel #17
0
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()
Beispiel #18
0
    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
Beispiel #19
0
 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
Beispiel #20
0
 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)
Beispiel #21
0
 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)
Beispiel #22
0
 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)
Beispiel #23
0
 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
Beispiel #24
0
 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)
Beispiel #25
0
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)
Beispiel #26
0
 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
Beispiel #27
0
    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
Beispiel #28
0
 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)
Beispiel #29
0
 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()
Beispiel #30
0
 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
Beispiel #31
0
    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
Beispiel #32
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"]
Beispiel #33
0
    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'
Beispiel #34
0
    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
Beispiel #35
0
 def appid(self):
     logger.warning("Do not access appid from interpreter")
     return self.installer.service_appid
Beispiel #36
0
 def get_filename(self):
     """Deprecated"""
     logger.warning("Just use get_text")
     return self.get_text()
Beispiel #37
0
    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"
Beispiel #38
0
    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
Beispiel #39
0
 def load(self):
     logger.warning("Load method not implemented")
Beispiel #40
0
 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",
     )
Beispiel #41
0
    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
Beispiel #42
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
Beispiel #43
0
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")
Beispiel #44
0
 def __init__(self, path):
     if not path:
         logger.warning("No path specified for Wine prefix")
     self.path = path
Beispiel #45
0
    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
Beispiel #46
0
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)
Beispiel #47
0
 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)")
Beispiel #48
0
 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
         }
     }
Beispiel #49
0
    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
Beispiel #50
0
 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)
Beispiel #51
0
    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
Beispiel #52
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)
Beispiel #53
0
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
Beispiel #54
0
    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
Beispiel #55
0
 def prelaunch(self):
     if not LINUX_SYSTEM.get_soundfonts():
         logger.warning(
             "FluidSynth is not installed, you might not have any music")
     return True
Beispiel #56
0
    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
Beispiel #57
0
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()
Beispiel #58
0
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",
Beispiel #59
0
    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()
Beispiel #60
0
    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"