Exemplo n.º 1
0
 def get_executable(self):
     if self.runner_config.get("lsi_steam") and system.find_executable("lsi-steam"):
         return system.find_executable("lsi-steam")
     runner_executable = self.runner_config.get("runner_executable")
     if runner_executable and os.path.isfile(runner_executable):
         return runner_executable
     return system.find_executable(self.runner_executable)
Exemplo n.º 2
0
def get_optirun_choices():
    """Return menu choices (label, value) for Optimus"""
    choices = [("Off", "off")]
    if system.find_executable("primusrun"):
        choices.append(("primusrun", "primusrun"))
    if system.find_executable("optirun"):
        choices.append(("optirun/virtualgl", "optirun"))
    if system.find_executable("pvkrun"):
        choices.append(("primus vk", "pvkrun"))
    return choices
Exemplo n.º 3
0
    def install(self):
        """Install runner using package management systems."""

        # Return false if runner has no package, must be then another method
        # and install method should be overridden by the specific runner
        if not hasattr(self, 'package'):
            return False
        if self.is_installable is False:
            ErrorDialog('This runner is not yet installable')
            return False

        package_installer_candidates = (
            'gpk-install-package-name'
            'software-center',
        )
        package_installer = None
        for candidate in package_installer_candidates:
            if find_executable(candidate):
                package_installer = candidate
                break

        if not package_installer:
            logger.error("The distribution you're running is not supported.")
            logger.error("Edit runners/runner.py to add support for it")
            return False

        if not self.package:
            ErrorDialog('This runner is not yet installable')
            logger.error("The requested runner %s can't be installed",
                         self.__class__.__name__)
            return False

        subprocess.Popen("%s %s" % (package_installer, self.package),
                         shell=True, stderr=subprocess.PIPE)
        return True
Exemplo n.º 4
0
 def is_installed(self):
     custom_path = self.runner_config.get('custom_wine_path', '')
     if self.wine_version == 'system':
         if system.find_executable('wine'):
             return True
         else:
             dialogs.ErrorDialog(
                 "Wine is not installed on your system.\n"
                 "Let's fall back on Wine " + DEFAULT_WINE +
                 " bundled with Lutris, alright?\n\n"
                 "(To get rid of this message, either install Wine \n"
                 "or change the Wine version in the game's configuration.)")
     elif self.wine_version == 'custom':
         if os.path.exists(custom_path):
             return True
         else:
             dialogs.ErrorDialog(
                 "Your custom Wine version can't be launched.\n"
                 "Let's fall back on Wine " + DEFAULT_WINE +
                 " bundled with Lutris, alright? \n\n"
                 "(To get rid of this message, fix your "
                 "Custom Wine path \n"
                 "or change the Wine version in the game's configuration.)")
     if os.path.exists(self.get_executable()):
         return True
     return False
Exemplo n.º 5
0
 def get_path_for_version(self, version):
     if version in WINE_PATHS.keys():
         return system.find_executable(WINE_PATHS[version])
     elif version == 'custom':
         return self.runner_config.get('custom_wine_path', '')
     else:
         return os.path.join(WINE_DIR, version, 'bin/wine')
Exemplo n.º 6
0
    def run(self):
        """Run the thread."""
        logger.debug("Command env: " + self.env_string)
        logger.debug("Running command: " + self.command_string)

        # Store provided environment variables so they can be used by future
        # processes.
        for key, value in self.env.items():
            logger.debug('Storing environment variable %s to %s', key, value)
            self.original_env[key] = os.environ.get(key)
            os.environ[key] = value

        # Reset library paths if they were not provided
        for key in ('LD_LIBRARY_PATH', 'LD_PRELOAD'):
            if key not in self.env and os.environ.get(key):
                del os.environ[key]

        # Copy the resulting environment to what will be passed to the process
        env = os.environ.copy()
        env.update(self.env)

        if self.terminal and system.find_executable(self.terminal):
            self.game_process = self.run_in_terminal()
        else:
            self.terminal = False
            self.game_process = self.execute_process(self.command, env)
        if not self.game_process:
            return
        if self.watch:
            GLib.timeout_add(HEARTBEAT_DELAY, self.watch_children)
            self.stdout_monitor = GLib.io_add_watch(self.game_process.stdout, GLib.IO_IN | GLib.IO_HUP, self.on_stdout_output)
        else:
            self.game_process.stdout.close()
Exemplo n.º 7
0
 def get_executable(self):
     scummvm_path = os.path.join(settings.DATA_DIR,
                                 'runners/scummvm/scummvm')
     if not os.path.exists(scummvm_path):
         return find_executable("scummvm")
     else:
         return scummvm_path
Exemplo n.º 8
0
 def get_command_args(app):
     """Return a tuple with absolute command path and an argument string"""
     command = shlex.split(app.get_commandline())
     # remove %U etc. and change %% to % in arguments
     args = list(map(lambda arg: re.sub("%[^%]", "", arg).replace("%%", "%"), command[1:]))
     exe = command[0]
     if not exe.startswith("/"):
         exe = system.find_executable(exe)
     return exe, subprocess.list2cmdline(args)
Exemplo n.º 9
0
def update_desktop_icons():
    """Update Icon for GTK+ desktop manager
    Other desktop manager icon cache commands must be added here if needed
    """
    gtk_update_icon_cache = system.find_executable("gtk-update-icon-cache")
    if gtk_update_icon_cache:
        os.system(
            "gtk-update-icon-cache -tf %s"
            % os.path.join(GLib.get_user_data_dir(), "icons", "hicolor")
        )
Exemplo n.º 10
0
def extract_7zip(path, dest, archive_type=None):
    _7zip_path = os.path.join(settings.RUNTIME_DIR, 'p7zip/7z')
    if not system.path_exists(_7zip_path):
        _7zip_path = system.find_executable('7z')
    if not system.path_exists(_7zip_path):
        raise OSError("7zip is not found in the lutris runtime or on the system")
    command = [_7zip_path, 'x', path, '-o{}'.format(dest), '-aoa']
    if archive_type:
        command.append('-t{}'.format(archive_type))
    subprocess.call(command)
Exemplo n.º 11
0
 def get_executable(self):
     # Lutris provided emulator
     pcsxr_path = os.path.join(settings.RUNNER_DIR, 'pcsxr/bin/pcsxr')
     if os.path.exists(pcsxr_path):
         return pcsxr_path
     # System wide available emulator
     candidates = ('pcsx', 'pcsxr')
     for candidate in candidates:
         executable = system.find_executable(candidate)
         if executable:
             return executable
Exemplo n.º 12
0
    def is_installed(self):
        """Return  True if runner is installed else False."""
        # Check 'get_executable' first
        if hasattr(self, 'get_executable'):
            executable = self.get_executable()
            if executable and os.path.exists(executable):
                return True

        # Fallback to 'executable' attribute (ssytem-wide install)
        if not getattr(self, 'executable', None):
            return False
        return bool(system.find_executable(self.executable))
Exemplo n.º 13
0
def get_games():
    """Return the list of games stored in the XDG menu."""
    game_list = []

    apps = Gio.AppInfo.get_all()
    for app in apps:
        if app.get_nodisplay() or app.get_is_hidden():
            continue

        # Check app has an executable
        if not app.get_executable():
            continue

        appid = os.path.splitext(app.get_id())[0]
        exe = None
        args = []

        # must be in Game category
        categories = app.get_categories()
        if not categories:
            continue
        categories = list(filter(None, categories.lower().split(';')))
        if 'game' not in categories:
            continue

        # contains a blacklisted category
        ok = True
        for category in categories:
            if category in map(str.lower, IGNORED_CATEGORIES):
                ok = False
        if not ok:
            continue

        # game is blacklisted
        if appid.lower() in map(str.lower, IGNORED_GAMES):
            continue

        # executable is blacklisted
        if app.get_executable().lower() in IGNORED_EXECUTABLES:
            continue

        cli = shlex.split(app.get_commandline())
        exe = cli[0]
        args = cli[1:]
        # remove %U etc. and change %% to % in arguments
        args = list(map(lambda arg: re.sub('%[^%]', '', arg).replace('%%', '%'), args))

        args = subprocess.list2cmdline(args)

        if not exe.startswith('/'):
            exe = system.find_executable(exe)
        game_list.append((app.get_display_name(), appid, exe, args))
    return game_list
Exemplo n.º 14
0
def is_installed_systemwide():
    """Return whether Wine is installed outside of Lutris"""
    for build in WINE_PATHS.values():
        if system.find_executable(build):
            if (
                build == "wine"
                and system.path_exists("/usr/lib/wine/wine64")
                and not system.path_exists("/usr/lib/wine/wine")
            ):
                logger.warning("wine32 is missing from system")
                return False
            return True
    return False
Exemplo n.º 15
0
 def is_installed(self):
     if self.wine_version == 'system':
         if system.find_executable('wine'):
             return True
         else:
             return False
     elif self.wine_version == 'custom':
         custom_path = self.runner_config.get('custom_wine_path', '')
         if os.path.exists(custom_path):
             return True
         else:
             return False
     return os.path.exists(self.get_executable())
Exemplo n.º 16
0
def winetricks(app, prefix=None, winetricks_env=None, silent=True,
               blocking=False):
    """Execute winetricks."""
    path = (system.find_executable('winetricks')
            or os.path.join(datapath.get(), 'bin/winetricks'))
    arch = detect_prefix_arch(prefix) or 'win32'
    if not winetricks_env:
        winetricks_env = wine().get_executable()
    if str(silent).lower() in ('yes', 'on', 'true'):
        args = "-q " + app
    else:
        args = app
    wineexec(None, prefix=prefix, winetricks_env=winetricks_env,
             wine_path=path, arch=arch, args=args, blocking=blocking)
Exemplo n.º 17
0
    def _check_binary_dependencies(self):
        """Check if all required binaries are installed on the system.

        This reads a `require-binaries` entry in the script, parsed the same way as
        the `requires` entry.
        """
        binary_dependencies = unpack_dependencies(self.script.get("require-binaries"))
        for dependency in binary_dependencies:
            if isinstance(dependency, tuple):
                installed_binaries = {
                    dependency_option: bool(system.find_executable(dependency_option))
                    for dependency_option in dependency
                }
                if not any(installed_binaries.values()):
                    raise ScriptingError(
                        "This installer requires %s on your system"
                        % " or ".join(dependency)
                    )
            else:
                if not system.find_executable(dependency):
                    raise ScriptingError(
                        "This installer requires %s on your system" % dependency
                    )
Exemplo n.º 18
0
 def is_installed(self):
     """Return  True if runner is installed else False"""
     is_installed = False
     if not self.executable:
         return False
     if hasattr(self, 'get_executable'):
         if os.path.exists(self.get_executable()):
             return True
     result = find_executable(self.executable)
     if result == '':
         is_installed = False
     else:
         is_installed = True
     return is_installed
Exemplo n.º 19
0
Arquivo: wine.py Projeto: Freso/lutris
 def get_pids(self, wine_path=None):
     """Return a list of pids of processes using the current wine exe."""
     if wine_path:
         exe = wine_path
     else:
         exe = self.get_executable()
     if not exe.startswith('/'):
         exe = system.find_executable(exe)
     pids = system.get_pids_using_file(exe)
     if self.wine_arch == 'win64':
         wine64 = exe + '64'
         pids_64 = system.get_pids_using_file(wine64)
         pids = pids | pids_64
     return pids
Exemplo n.º 20
0
 def get_path_for_version(self, version):
     """Return the absolute path of a wine executable for a given version"""
     # logger.debug("Getting path for Wine %s", version)
     if version in WINE_PATHS.keys():
         return system.find_executable(WINE_PATHS[version])
     if "Proton" in version:
         for proton_path in get_proton_paths():
             if os.path.isfile(os.path.join(proton_path, version, "dist/bin/wine")):
                 return os.path.join(proton_path, version, "dist/bin/wine")
     if version.startswith("PlayOnLinux"):
         version, arch = version.split()[1].rsplit("-", 1)
         return os.path.join(POL_PATH, "wine", "linux-" + arch, version, "bin/wine")
     if version == "custom":
         return self.runner_config.get("custom_wine_path", "")
     return os.path.join(WINE_DIR, version, "bin/wine")
Exemplo n.º 21
0
    def get_executable(self):
        """Return the path to the Wine executable."""
        path = WINE_DIR
        custom_path = self.runner_config.get('custom_wine_path', '')
        version = self.wine_version

        if version == 'system':
            if system.find_executable('wine'):
                return 'wine'
            # Fall back on bundled Wine
            version = DEFAULT_WINE
        elif version == 'custom':
            if os.path.exists(custom_path):
                return custom_path
            version = DEFAULT_WINE

        return os.path.join(path, version, 'bin/wine')
Exemplo n.º 22
0
 def cancel_installation(self, widget=None):
     """Ask a confirmation before cancelling the install"""
     confirm_cancel_dialog = QuestionDialog(
         {
             "question": "Are you sure you want to cancel the installation?",
             "title": "Cancel installation?",
         }
     )
     if confirm_cancel_dialog.result != Gtk.ResponseType.YES:
         logger.warning("Attempting to terminate with the system wineserver. "
                        "This is most likely to fail or to have no effect.")
         system.execute([system.find_executable("wineserver"), "-k9"])
         return True
     if self.interpreter:
         self.interpreter.revert()
         self.interpreter.cleanup()
     self.destroy()
Exemplo n.º 23
0
 def get_path_for_version(self, version):
     """Return the absolute path of a wine executable for a given version"""
     # logger.debug("Getting path for Wine %s", version)
     if version in WINE_PATHS.keys():
         return system.find_executable(WINE_PATHS[version])
     if "Proton" in version:
         for proton_path in get_proton_paths():
             if os.path.isfile(
                     os.path.join(proton_path, version, "dist/bin/wine")):
                 return os.path.join(proton_path, version, "dist/bin/wine")
     if version.startswith("PlayOnLinux"):
         version, arch = version.split()[1].rsplit("-", 1)
         return os.path.join(POL_PATH, "wine", "linux-" + arch, version,
                             "bin/wine")
     if version == "custom":
         return self.runner_config.get("custom_wine_path", "")
     return os.path.join(WINE_DIR, version, "bin/wine")
Exemplo n.º 24
0
    def run(self):
        """Run the thread"""
        logger.debug("Thread running: %s", self.command)
        GLib.timeout_add(HEARTBEAT_DELAY, self.watch_children)

        if self.terminal and find_executable(self.terminal):
            self.run_in_terminal()
        else:
            self.game_process = subprocess.Popen(self.command, bufsize=1,
                                                 stdout=subprocess.PIPE,
                                                 stderr=subprocess.STDOUT,
                                                 cwd=self.path, env=self.env)

        for line in iter(self.game_process.stdout.readline, ''):
            self.stdout += line
            if self.debug_output:
                sys.stdout.write(line)
Exemplo n.º 25
0
Arquivo: wine.py Projeto: xnick/lutris
    def get_pids(self, wine_path=None):
        """Return a list of pids of processes using the current wine exe."""
        if wine_path:
            exe = wine_path
        else:
            exe = self.get_executable()
        if not exe.startswith("/"):
            exe = system.find_executable(exe)
        pids = system.get_pids_using_file(exe)
        if self.wine_arch == "win64" and os.path.basename(exe) == "wine":
            pids = pids | system.get_pids_using_file(exe + "64")

        # Add wineserver PIDs to the mix (at least one occurence of fuser not
        # picking the games's PID from wine/wine64 but from wineserver for some
        # unknown reason.
        pids = pids | system.get_pids_using_file(
            os.path.join(os.path.dirname(exe), "wineserver"))
        return pids
Exemplo n.º 26
0
    def is_installed(self):
        """Return  True if runner is installed else False."""
        is_installed = False
        # Check 'get_executable' first
        if hasattr(self, 'get_executable'):
            executable = self.get_executable()
            if executable and os.path.exists(executable):
                return True

        # Fallback to 'executable' attribute (ssytem-wide install)
        if not getattr(self, 'executable', None):
            return False
        result = find_executable(self.executable)
        if result == '':
            is_installed = False
        else:
            is_installed = True
        return is_installed
Exemplo n.º 27
0
def extract_exe(path, dest):
    if check_inno_exe(path):
        decompress_gog(path, dest)
    else:
        # use 7za to check if exe is an archive
        _7zip_path = os.path.join(settings.RUNTIME_DIR, "p7zip/7za")
        if not system.path_exists(_7zip_path):
            _7zip_path = system.find_executable("7za")
        if not system.path_exists(_7zip_path):
            raise OSError(
                "7zip is not found in the lutris runtime or on the system")
        command = [_7zip_path, "t", path]
        return_code = subprocess.call(command)
        if return_code == 0:
            extract_7zip(path, dest)
        else:
            raise RuntimeError(
                "specified exe is not an archive or GOG setup file")
Exemplo n.º 28
0
    def run(self):
        """Run the thread."""
        logger.debug("Command env: " + self.env_string)
        logger.debug("Running command: " + self.command_string)
        if self.watch:
            GLib.timeout_add(HEARTBEAT_DELAY, self.watch_children)

        if self.terminal and find_executable(self.terminal):
            self.run_in_terminal()
        else:
            self.terminal = False
            env = os.environ.copy()
            env.update(self.env)
            self.game_process = self.execute_process(self.command, env)
        for line in iter(self.game_process.stdout.readline, ''):
            self.stdout += line
            if self.debug_output:
                sys.stdout.write(line)
Exemplo n.º 29
0
def get_wine_version(wine_path="wine"):
    """Return the version of Wine installed on the system."""
    if wine_path != "wine" and not system.path_exists(wine_path):
        return
    if wine_path == "wine" and not system.find_executable("wine"):
        return
    if os.path.isabs(wine_path):
        wine_stats = os.stat(wine_path)
        if wine_stats.st_size < 2000:
            # This version is a script, ignore it
            return
    version = system.read_process_output([wine_path, "--version"])
    if not version:
        logger.error("Error reading wine version for %s", wine_path)
        return
    if version.startswith("wine-"):
        version = version[5:]
    return version
Exemplo n.º 30
0
    def get_executable(self):
        """Return the path to the Wine executable."""
        path = WINE_DIR
        custom_path = self.runner_config.get('custom_wine_path', '')
        version = self.wine_version

        if version == 'system':
            if find_executable('wine'):
                return 'wine'
            # Fall back on bundled Wine
            version = DEFAULT_WINE
        elif version == 'custom':
            if os.path.exists(custom_path):
                return custom_path
            version = DEFAULT_WINE

        version += '-i386'
        return os.path.join(path, version, 'bin/wine')
Exemplo n.º 31
0
def _get_graphics_adapters():
    """Return the list of graphics cards available on a system

    Returns:
        list: list of tuples containing PCI ID and description of the VGA adapter
    """
    lspci_path = system.find_executable("lspci")
    if not lspci_path:
        logger.warning("lspci is not available. List of graphics cards not available")
        return []
    return [
        (pci_id, vga_desc.split(": ")[1])
        for pci_id, vga_desc in [
            line.split(maxsplit=1)
            for line in system.execute(lspci_path).split("\n")
            if "VGA" in line
        ]
    ]
Exemplo n.º 32
0
    def __init__(
        self,
        command,
        runner=None,
        env=None,
        term=None,
        cwd=None,
        include_processes=None,
        exclude_processes=None,
        log_buffer=None,
    ):
        self.ready_state = True
        if env is None:
            self.env = {}
        else:
            self.env = env

        # not clear why this needs to be added, the path is already added in
        # the wrappper script.
        self.env['PYTHONPATH'] = ':'.join(sys.path)

        self.original_env = {}
        self.command = command
        self.runner = runner
        self.stop_func = lambda: True
        self.game_process = None
        self.return_code = None
        self.terminal = system.find_executable(term)
        self.is_running = True
        self.stdout = ""
        self.daemon = True
        self.error = None
        self.log_handlers = [
            self.log_handler_stdout,
            self.log_handler_console_output,
        ]
        self.set_log_buffer(log_buffer)
        self.stdout_monitor = None
        self.watch_children_running = False
        self.include_processes = include_processes or []
        self.exclude_processes = exclude_processes or []

        # Keep a copy of previously running processes
        self.cwd = self.get_cwd(cwd)
Exemplo n.º 33
0
    def run(self):
        """Run the thread."""
        logger.debug("Command env: " + self.env_string)
        logger.debug("Running command: " + self.command_string)
        if self.watch:
            GLib.timeout_add(HEARTBEAT_DELAY, self.watch_children)

        # Store provided environment variables so they can be used by future
        # processes.
        for key, value in self.env.items():
            logger.debug('Storing environment variable %s to %s', key, value)
            self.original_env[key] = os.environ.get(key)
            os.environ[key] = value

        # Reset library paths if they were not provided
        for key in ('LD_LIBRARY_PATH', 'LD_PRELOAD'):
            if key not in self.env and os.environ.get(key):
                del os.environ[key]

        # Copy the resulting environment to what will be passed to the process
        env = os.environ.copy()
        env.update(self.env)

        if self.terminal and system.find_executable(self.terminal):
            self.run_in_terminal()
        else:
            self.terminal = False
            self.game_process = self.execute_process(self.command, env)
        if not self.game_process:
            return
        for line in iter(self.game_process.stdout.readline, b''):
            if not self.is_running:
                break
            try:
                line = line.decode()
            except UnicodeDecodeError:
                line = ''
            if not line:
                continue
            self.stdout += line
            if self.debug_output:
                with contextlib.suppress(BlockingIOError):
                    sys.stdout.write(line)
                    sys.stdout.flush()
Exemplo n.º 34
0
    def run(self):
        """Run the thread."""
        logger.debug("Command env: " + self.env_string)
        logger.debug("Running command: " + self.command_string)
        if self.watch:
            GLib.timeout_add(HEARTBEAT_DELAY, self.watch_children)

        # Store provided environment variables so they can be used by future
        # processes.
        for key, value in self.env.items():
            logger.debug('Storing environment variable %s to %s', key, value)
            self.original_env[key] = os.environ.get(key)
            os.environ[key] = value

        # Reset library paths if they were not provided
        for key in ('LD_LIBRARY_PATH', 'LD_PRELOAD'):
            if key not in self.env and os.environ.get(key):
                del os.environ[key]

        # Copy the resulting environment to what will be passed to the process
        env = os.environ.copy()
        env.update(self.env)

        if self.terminal and system.find_executable(self.terminal):
            self.run_in_terminal()
        else:
            self.terminal = False
            self.game_process = self.execute_process(self.command, env)
        if not self.game_process:
            return
        for line in iter(self.game_process.stdout.readline, b''):
            if not self.is_running:
                break
            try:
                line = line.decode()
            except UnicodeDecodeError:
                line = ''
            if not line:
                continue
            self.stdout += line
            if self.debug_output:
                with contextlib.suppress(BlockingIOError):
                    sys.stdout.write(line)
                    sys.stdout.flush()
Exemplo n.º 35
0
 def joy2key(self, config):
     """Run a joy2key thread."""
     if not system.find_executable('joy2key'):
         logger.error("joy2key is not installed")
         return
     win = "grep %s" % config['window']
     if 'notwindow' in config:
         win += ' | grep -v %s' % config['notwindow']
     wid = "xwininfo -root -tree | %s | awk '{print $1}'" % win
     buttons = config['buttons']
     axis = "Left Right Up Down"
     rcfile = os.path.expanduser("~/.joy2keyrc")
     rc_option = '-rcfile %s' % rcfile if os.path.exists(rcfile) else ''
     command = "sleep 5 "
     command += "&& joy2key $(%s) -X %s -buttons %s -axis %s" % (
         wid, rc_option, buttons, axis)
     joy2key_thread = LutrisThread(command)
     self.game_thread.attach_thread(joy2key_thread)
     joy2key_thread.start()
Exemplo n.º 36
0
 def prelaunch(self):
     def check_shutdown(is_running, times=10):
         for i in range(1, times):
             time.sleep(1)
             if not is_running():
                 return True
     # If using primusrun, shutdown existing Steam first
     primusrun = self.system_config.get('primusrun')
     if primusrun and system.find_executable('primusrun'):
         if is_running():
             logger.info("Waiting for Steam shutdown...")
             shutdown()
             if not check_shutdown(is_running):
                 logger.info("Steam does not shut down, killing it...")
                 kill()
                 if not check_shutdown(is_running, 5):
                     logger.error("Failed to shut down Steam :(")
                     return False
     return True
Exemplo n.º 37
0
    def get_pids(self, wine_path=None):
        """Return a list of pids of processes using the current wine exe."""
        if wine_path:
            exe = wine_path
        else:
            exe = self.get_executable()
        if not exe.startswith("/"):
            exe = system.find_executable(exe)
        pids = system.get_pids_using_file(exe)
        if self.wine_arch == "win64" and os.path.basename(exe) == "wine":
            pids = pids | system.get_pids_using_file(exe + "64")

        # Add wineserver PIDs to the mix (at least one occurence of fuser not
        # picking the games's PID from wine/wine64 but from wineserver for some
        # unknown reason.
        pids = pids | system.get_pids_using_file(
            os.path.join(os.path.dirname(exe), "wineserver")
        )
        return pids
Exemplo n.º 38
0
 def prelaunch(self):
     def check_shutdown(is_running, times=10):
         for i in range(1, times):
             time.sleep(1)
             if not is_running():
                 return True
     # If using primusrun, shutdown existing Steam first
     primusrun = self.system_config.get('primusrun')
     if primusrun and system.find_executable('primusrun'):
         if is_running():
             logger.info("Waiting for Steam shutdown...")
             shutdown()
             if not check_shutdown(is_running):
                 logger.info("Steam does not shut down, killing it...")
                 kill()
                 if not check_shutdown(is_running, 5):
                     logger.error("Failed to shut down Steam :(")
                     return False
     return True
Exemplo n.º 39
0
def _get_graphics_adapters():
    """Return the list of graphics cards available on a system

    Returns:
        list: list of tuples containing PCI ID and description of the display controller
    """
    lspci_path = system.find_executable("lspci")
    dev_subclasses = ["VGA", "XGA", "3D controller", "Display controller"]
    if not lspci_path:
        logger.warning("lspci is not available. List of graphics cards not available")
        return []
    return [
        (pci_id, device_desc.split(": ")[1])
        for pci_id, device_desc in [
            line.split(maxsplit=1)
            for line in system.execute(lspci_path).split("\n")
            if any(subclass in line for subclass in dev_subclasses)
        ]
    ]
Exemplo n.º 40
0
    def __init__(
        self,
        command,
        runner=None,
        env=None,
        term=None,
        watch=True,
        cwd=None,
        include_processes=None,
        exclude_processes=None,
        log_buffer=None,
    ):
        self.ready_state = True
        if env is None:
            self.env = {}
        else:
            self.env = env
        self.original_env = {}
        self.command = command
        self.runner = runner
        self.stop_func = lambda: True
        self.game_process = None
        self.return_code = None
        self.terminal = system.find_executable(term)
        self.watch = watch
        self.is_running = True
        self.stdout = ""
        self.daemon = True
        self.error = None
        self.log_handlers = [
            self.log_handler_stdout,
            self.log_handler_console_output,
        ]
        self.set_log_buffer(log_buffer)
        self.stdout_monitor = None
        self.watch_children_running = False

        # Keep a copy of previously running processes
        self.cwd = self.get_cwd(cwd)
        self.process_monitor = ProcessMonitor(
            include_processes, exclude_processes,
            "run_in_term.sh" if self.terminal else None)
Exemplo n.º 41
0
Arquivo: wine.py Projeto: TRPB/lutris
def get_system_wine_version(wine_path="wine"):
    """Return the version of Wine installed on the system."""
    if wine_path != "wine" and not system.path_exists(wine_path):
        return
    if wine_path == "wine" and not system.find_executable("wine"):
        return
    if os.path.isabs(wine_path):
        wine_stats = os.stat(wine_path)
        if wine_stats.st_size < 2000:
            # This version is a script, ignore it
            return
    try:
        version = subprocess.check_output([wine_path, "--version"]).decode().strip()
    except (OSError, subprocess.CalledProcessError) as ex:
        logger.exception("Error reading wine version for %s: %s", wine_path, ex)
        return
    else:
        if version.startswith("wine-"):
            version = version[5:]
        return version
Exemplo n.º 42
0
 def joy2key(self, config):
     """Run a joy2key thread."""
     if not system.find_executable('joy2key'):
         logger.error("joy2key is not installed")
         return
     win = "grep %s" % config['window']
     if 'notwindow' in config:
         win += ' | grep -v %s' % config['notwindow']
     wid = "xwininfo -root -tree | %s | awk '{print $1}'" % win
     buttons = config['buttons']
     axis = "Left Right Up Down"
     rcfile = os.path.expanduser("~/.joy2keyrc")
     rc_option = '-rcfile %s' % rcfile if os.path.exists(rcfile) else ''
     command = "sleep 5 "
     command += "&& joy2key $(%s) -X %s -buttons %s -axis %s" % (
         wid, rc_option, buttons, axis
     )
     joy2key_thread = LutrisThread(command)
     self.game_thread.attach_thread(joy2key_thread)
     joy2key_thread.start()
Exemplo n.º 43
0
def get_system_wine_version(wine_path="wine"):
    """Return the version of Wine installed on the system."""
    if wine_path != "wine" and not system.path_exists(wine_path):
        return
    if wine_path == "wine" and not system.find_executable("wine"):
        return
    if os.path.isabs(wine_path):
        wine_stats = os.stat(wine_path)
        if wine_stats.st_size < 2000:
            # This version is a script, ignore it
            return
    try:
        version = subprocess.check_output([wine_path, "--version"]).decode().strip()
    except (OSError, subprocess.CalledProcessError) as ex:
        logger.exception("Error reading wine version for %s: %s", wine_path, ex)
        return
    else:
        if version.startswith("wine-"):
            version = version[5:]
        return version
Exemplo n.º 44
0
    def run(self):
        """Run the thread"""
        logger.debug("Command env: " + self.env_string)
        logger.debug("Running command: " + self.command_string)
        GLib.timeout_add(HEARTBEAT_DELAY, self.watch_children)

        if self.terminal and find_executable(self.terminal):
            self.run_in_terminal()
        else:
            env = os.environ.copy()
            env.update(self.env)
            self.game_process = subprocess.Popen(self.command, bufsize=1,
                                                 stdout=subprocess.PIPE,
                                                 stderr=subprocess.STDOUT,
                                                 cwd=self.path, env=env)
        os.chdir(os.path.expanduser('~'))

        for line in iter(self.game_process.stdout.readline, ''):
            self.stdout += line
            if self.debug_output:
                sys.stdout.write(line)
Exemplo n.º 45
0
def decompress_gog(file_path, destination_path):
    _innoextract_path = os.path.join(settings.RUNTIME_DIR,
                                     "innoextract/innoextract")
    if not system.path_exists(_innoextract_path):
        _innoextract_path = system.find_executable("innoextract")
    if not system.path_exists(_innoextract_path):
        raise OSError(
            "innoextract is not found in the lutris runtime or on the system")
    # innoextract cannot do mkdir -p, so we have to do it here:
    try:
        os.makedirs(destination_path)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise OSError(
                "cannot make output directory for extracting setup file")
    command = [
        _innoextract_path, "-g", "-d", destination_path, "-e", file_path
    ]
    return_code = subprocess.call(command)
    if return_code != 0:
        raise RuntimeError("innoextract failed to extract GOG setup file")
Exemplo n.º 46
0
    def run(self):
        """Run the thread."""
        logger.debug("Command env: " + self.env_string)
        logger.debug("Running command: " + self.command_string)

        if self.terminal and system.find_executable(self.terminal):
            self.game_process = self.run_in_terminal()
        else:
            self.terminal = False
            env = self.apply_environment()
            self.game_process = self.execute_process(self.command, env)

        if not self.game_process:
            logger.warning("No game process available")
            return

        if self.watch:
            GLib.timeout_add(HEARTBEAT_DELAY, self.watch_children)
            self.stdout_monitor = GLib.io_add_watch(self.game_process.stdout,
                                                    GLib.IO_IN | GLib.IO_HUP,
                                                    self.on_stdout_output)
Exemplo n.º 47
0
def winetricks(
    app,
    prefix=None,
    arch=None,
    silent=True,
    wine_path=None,
    config=None,
    env=None,
    disable_runtime=False,
):
    """Execute winetricks."""
    wine_config = config or LutrisConfig(runner_slug="wine")
    winetricks_path = os.path.join(settings.RUNTIME_DIR,
                                   "winetricks/winetricks")
    if (wine_config.runner_config.get("system_winetricks")
            or not system.path_exists(winetricks_path)):
        winetricks_path = system.find_executable("winetricks")
        if not winetricks_path:
            raise RuntimeError("No installation of winetricks found")
    if wine_path:
        winetricks_wine = wine_path
    else:
        wine = import_runner("wine")
        winetricks_wine = wine().get_executable()
    if arch not in ("win32", "win64"):
        arch = detect_arch(prefix, winetricks_wine)
    args = app
    if str(silent).lower() in ("yes", "on", "true"):
        args = "--unattended " + args
    return wineexec(
        None,
        prefix=prefix,
        winetricks_wine=winetricks_wine,
        wine_path=winetricks_path,
        arch=arch,
        args=args,
        config=config,
        env=env,
        disable_runtime=disable_runtime,
    )
Exemplo n.º 48
0
    def __init__(
        self,
        command,
        runner=None,
        env=None,
        term=None,
        cwd=None,
        include_processes=None,
        exclude_processes=None,
        log_buffer=None,
        title=None,
    ):  # pylint: disable=too-many-arguments
        self.ready_state = True
        self.env = self.get_environment(env)

        self.command = command
        self.runner = runner
        self.stop_func = lambda: True
        self.game_process = None
        self.prevent_on_stop = False
        self.return_code = None
        self.terminal = system.find_executable(term)
        self.is_running = True
        self.error = None
        self.log_handlers = [
            self.log_handler_stdout,
            self.log_handler_console_output,
        ]
        self.set_log_buffer(log_buffer)
        self.stdout_monitor = None
        self.include_processes = include_processes or []
        self.exclude_processes = exclude_processes or []

        self.cwd = self.get_cwd(cwd)

        self._stdout = io.StringIO()

        self._title = title if title else command[0]
Exemplo n.º 49
0
    def install(self):
        """Install runner using package management systems."""
        # Prioritize provided tarballs.
        tarball = self.tarballs.get(self.arch)
        if tarball:
            self.download_and_extract(tarball)
            return True

        # Return false if runner has no package, must be then another method
        # and install method should be overridden by the specific runner
        if not hasattr(self, 'package'):
            return False

        package_installer_candidates = (
            'gpk-install-package-name',
            'software-center',
        )
        package_installer = None
        for candidate in package_installer_candidates:
            if find_executable(candidate):
                package_installer = candidate
                break

        if not package_installer:
            logger.error("The distribution you're running is not supported.")
            logger.error("Edit runners/runner.py to add support for it")
            return False

        if not self.package:
            dialogs.ErrorDialog('This runner is not yet installable')
            logger.error("The requested runner %s can't be installed",
                         self.__class__.__name__)
            return False

        subprocess.Popen("%s %s" % (package_installer, self.package),
                         shell=True,
                         stderr=subprocess.PIPE)
        return True
Exemplo n.º 50
0
 def cancel_installation(self, widget=None):
     """Ask a confirmation before cancelling the install"""
     remove_checkbox = Gtk.CheckButton.new_with_label("Remove game files")
     if self.interpreter:
         remove_checkbox.set_active(self.interpreter.game_dir_created)
         remove_checkbox.show()
     confirm_cancel_dialog = QuestionDialog(
         {
             "question": "Are you sure you want to cancel the installation?",
             "title": "Cancel installation?",
             "widgets": [remove_checkbox]
         }
     )
     if confirm_cancel_dialog.result != Gtk.ResponseType.YES:
         logger.warning("Attempting to terminate with the system wineserver. "
                        "This is most likely to fail or to have no effect.")
         system.execute([system.find_executable("wineserver"), "-k9"])
         return True
     if self.interpreter:
         self.interpreter.game_dir_created = remove_checkbox.get_active()
         self.interpreter.revert()
         self.interpreter.cleanup()
     self.destroy()
Exemplo n.º 51
0
def get_launcher_path(game_slug, game_id):
    """Return the path of a XDG game launcher.
    When legacy is set, it will return the old path with only the slug,
    otherwise it will return the path with slug + id
    """
    xdg_executable = 'xdg-user-dir'
    if not system.find_executable(xdg_executable):
        logger.error("%s not found", xdg_executable)
        return
    desktop_dir = subprocess.Popen([xdg_executable, 'DESKTOP'],
                                   stdout=subprocess.PIPE).communicate()[0]
    desktop_dir = str(desktop_dir).strip()

    legacy_launcher_path = os.path.join(
        desktop_dir, get_xdg_basename(game_slug, game_id, legacy=True)
    )
    # First check if legacy path exists, for backward compatibility
    if system.path_exists(legacy_launcher_path):
        return legacy_launcher_path
    # Otherwise return new path, whether it exists or not
    return os.path.join(
        desktop_dir, get_xdg_basename(game_slug, game_id, legacy=False)
    )
Exemplo n.º 52
0
    def get_executable(self, version=None):
        """Return the path to the Wine executable.
        A specific version can be specified if needed.
        """
        path = WINE_DIR
        custom_path = self.runner_config.get('custom_wine_path', '')
        if version is None:
            version = self.get_version()
        if not version:
            return

        if version in WINE_PATHS.keys():
            abs_path = system.find_executable(WINE_PATHS[version])
            if abs_path:
                return abs_path
            # Fall back on bundled Wine
            version = get_default_version()
        elif version == 'custom':
            if os.path.exists(custom_path):
                return custom_path
            version = get_default_version()

        return os.path.join(path, version, 'bin/wine')
Exemplo n.º 53
0
    def start_xephyr(self, display=":2"):
        """Start a monitored Xephyr instance"""
        if not system.find_executable("Xephyr"):
            raise GameConfigError("Unable to find Xephyr, install it or disable the Xephyr option")

        xephyr_depth = "8" if self.runner.system_config.get("xephyr") == "8bpp" else "16"
        xephyr_resolution = self.runner.system_config.get("xephyr_resolution") or "640x480"
        xephyr_command = [
            "Xephyr",
            display,
            "-ac",
            "-screen",
            xephyr_resolution + "x" + xephyr_depth,
            "-glamor",
            "-reset",
            "-terminate",
        ]
        if self.runner.system_config.get("xephyr_fullscreen"):
            xephyr_command.append("-fullscreen")

        xephyr_thread = MonitoredCommand(xephyr_command)
        xephyr_thread.start()
        time.sleep(3)
        return display
Exemplo n.º 54
0
    def run(self):
        """Run the thread."""
        logger.debug("Command env: %s", self.env_string)
        logger.debug("Running command: %s", self.command_string)
        result = ctypes.CDLL(find_library('c')).prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0, 0)
        if result == -1:
            logger.warning("PR_SET_CHILD_SUBREAPER failed, process watching may fail")

        if self.terminal and system.find_executable(self.terminal):
            self.game_process = self.run_in_terminal()
        else:
            self.terminal = False
            env = self.apply_environment()
            self.game_process = self.execute_process(self.command, env)

        if not self.game_process:
            logger.warning("No game process available")
            return

        if self.watch:
            GLib.timeout_add(HEARTBEAT_DELAY, self.watch_children)
            self.stdout_monitor = GLib.io_add_watch(self.game_process.stdout,
                                                    GLib.IO_IN | GLib.IO_HUP,
                                                    self.on_stdout_output)
Exemplo n.º 55
0
     "type":
     "bool",
     "default":
     False,
     "advanced":
     True,
     "help": ("Disable desktop effects while game is running, "
              "reducing stuttering and increasing performance"),
 },
 {
     "option": "reset_pulse",
     "type": "bool",
     "label": "Reset PulseAudio",
     "default": False,
     "advanced": True,
     "condition": system.find_executable("pulseaudio"),
     "help": "Restart PulseAudio before launching the game.",
 },
 {
     "option":
     "pulse_latency",
     "type":
     "bool",
     "label":
     "Reduce PulseAudio latency",
     "default":
     False,
     "advanced":
     True,
     "condition":
     system.find_executable("pulseaudio"),
Exemplo n.º 56
0
    def play(self):
        """Launch the game."""
        if not self.runner:
            dialogs.ErrorDialog("Invalid game configuration: Missing runner")
            return False
        if not self.prelaunch():
            return False

        system_config = self.runner.system_config
        self.original_outputs = display.get_outputs()
        gameplay_info = self.runner.play()
        logger.debug("Launching %s: %s" % (self.name, gameplay_info))
        if 'error' in gameplay_info:
            show_error_message(gameplay_info)
            return False

        launch_arguments = gameplay_info['command']

        restrict_to_display = system_config.get('display')
        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:
            display.change_resolution(resolution)
            time.sleep(3)
            self.resolution_changed = True

        if system_config.get('reset_pulse'):
            audio.reset_pulse()

        primusrun = system_config.get('primusrun')
        if primusrun and system.find_executable('primusrun'):
            launch_arguments.insert(0, 'primusrun')

        prefix_command = system_config.get("prefix_command", '').strip()
        if prefix_command:
            launch_arguments.insert(0, prefix_command)

        ld_preload = gameplay_info.get('ld_preload')
        if ld_preload:
            launch_arguments.insert(0, 'LD_PRELOAD="{}"'.format(ld_preload))

        ld_library_path = []
        if self.use_runtime(system_config):
            runtime64_path = os.path.join(settings.RUNTIME_DIR, "lib64")
            if os.path.exists(runtime64_path):
                ld_library_path.append(runtime64_path)
            runtime32_path = os.path.join(settings.RUNTIME_DIR, "lib32")
            if os.path.exists(runtime32_path):
                ld_library_path.append(runtime32_path)

        game_ld_libary_path = gameplay_info.get('ld_library_path')
        if game_ld_libary_path:
            ld_library_path.append(game_ld_libary_path)

        if ld_library_path:
            ld_full = ':'.join(ld_library_path)
            ld_arg = 'LD_LIBRARY_PATH="{}:$LD_LIBRARY_PATH"'.format(ld_full)
            launch_arguments.insert(0, ld_arg)

        env = gameplay_info.get('env') or []
        for var in env:
            launch_arguments.insert(0, var)

        killswitch = system_config.get('killswitch')
        self.game_thread = LutrisThread(" ".join(launch_arguments),
                                        path=self.runner.working_dir,
                                        killswitch=killswitch)
        if hasattr(self.runner, 'stop'):
            self.game_thread.set_stop_command(self.runner.stop)
        self.game_thread.start()
        if 'joy2key' in gameplay_info:
            self.joy2key(gameplay_info['joy2key'])
        xboxdrv_config = system_config.get('xboxdrv')
        if xboxdrv_config:
            self.xboxdrv_start(xboxdrv_config)
        if self.runner.is_watchable:
            # Create heartbeat every
            self.heartbeat = GLib.timeout_add(5000, self.poke_process)
Exemplo n.º 57
0
    def do_play(self, prelaunched, _error=None):
        if not prelaunched:
            self.state = self.STATE_STOPPED
            return
        system_config = self.runner.system_config
        self.original_outputs = display.get_outputs()
        gameplay_info = self.runner.play()

        env = {}

        logger.debug("Launching %s: %s" % (self.name, gameplay_info))
        if 'error' in gameplay_info:
            self.show_error_message(gameplay_info)
            self.state = self.STATE_STOPPED
            return

        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':
            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 os.path.exists(self.killswitch):
            # Prevent setting a killswitch to a file that doesn't exists
            self.killswitch = None

        # Command
        launch_arguments = gameplay_info['command']

        primusrun = system_config.get('primusrun')
        if primusrun and system.find_executable('primusrun'):
            launch_arguments.insert(0, 'primusrun')

        xephyr = system_config.get('xephyr') or 'off'
        if xephyr != 'off':
            if xephyr == '8bpp':
                xephyr_depth = '8'
            else:
                xephyr_depth = '16'
            xephyr_resolution = system_config.get('xephyr_resolution') or '640x480'
            xephyr_command = ['Xephyr', ':2', '-ac', '-screen',
                              xephyr_resolution + 'x' + xephyr_depth, '-glamor',
                              '-reset', '-terminate', '-fullscreen']
            xephyr_thread = LutrisThread(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'

        prefix_command = system_config.get("prefix_command") or ''
        if prefix_command.strip():
            launch_arguments.insert(0, prefix_command)

        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
                return
        # Env vars
        game_env = gameplay_info.get('env') or {}
        env.update(game_env)
        system_env = system_config.get('env') or {}
        env.update(system_env)

        ld_preload = gameplay_info.get('ld_preload') or ''
        env["LD_PRELOAD"] = ld_preload

        # Runtime management
        ld_library_path = ""
        if self.runner.use_runtime():
            runtime_env = runtime.get_env()
            if 'STEAM_RUNTIME' in runtime_env and 'STEAM_RUNTIME' not in env:
                env['STEAM_RUNTIME'] = runtime_env['STEAM_RUNTIME']
            if 'LD_LIBRARY_PATH' in runtime_env:
                ld_library_path = runtime_env['LD_LIBRARY_PATH']
        game_ld_libary_path = gameplay_info.get('ld_library_path')
        if game_ld_libary_path:
            if not ld_library_path:
                ld_library_path = '$LD_LIBRARY_PATH'
            ld_library_path = ":".join([game_ld_libary_path, ld_library_path])
        env["LD_LIBRARY_PATH"] = ld_library_path

        # /Env vars

        self.game_thread = LutrisThread(launch_arguments,
                                        runner=self.runner, env=env,
                                        rootpid=gameplay_info.get('rootpid'),
                                        term=terminal)
        if hasattr(self.runner, 'stop'):
            self.game_thread.set_stop_command(self.runner.stop)
        self.game_thread.start()
        self.state = self.STATE_RUNNING
        if 'joy2key' in gameplay_info:
            self.joy2key(gameplay_info['joy2key'])
        xboxdrv_config = system_config.get('xboxdrv')
        if xboxdrv_config:
            self.xboxdrv_start(xboxdrv_config)
        self.heartbeat = GLib.timeout_add(HEARTBEAT_DELAY, self.beat)
Exemplo n.º 58
0
       "closed or when they crash. This is when this option comes \n"
       "into play to save your bacon."),
 },
 {
     "option":
     "gamescope",
     "type":
     "bool",
     "label":
     _("Enable gamescope"),
     "default":
     False,
     "advanced":
     True,
     "condition":
     bool(system.find_executable("gamescope")),
     "help":
     _("Use gamescope to draw the game window isolated from your desktop.\n"
       "Use Ctrl+Super+F to toggle fullscreen"),
 },
 {
     "option": "gamescope_output_res",
     "type": "string",
     "label": _("Gamescope output resolution"),
     "default": False,
     "advanced": True,
     "condition": bool(system.find_executable("gamescope")),
     "help": _("Resolution of the window on your desktop"),
 },
 {
     "option": "gamescope_game_res",
Exemplo n.º 59
0
 def has_steam(self):
     """Return whether Steam is installed locally"""
     return bool(system.find_executable("steam"))
Exemplo n.º 60
0
def get_launch_parameters(runner, gameplay_info):
    system_config = runner.system_config
    launch_arguments = gameplay_info["command"]
    env = {}

    # Optimus
    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")

    mango_args, mango_env = get_mangohud_conf(system_config)
    if mango_args:
        launch_arguments = mango_args + launch_arguments
        env.update(mango_env)

    # Libstrangle
    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.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 and system.find_executable("gamemoderun"):
        launch_arguments.insert(0, "gamemoderun")

    return launch_arguments, env