Example #1
0
    def on_save(self, _button, callback=None):
        """Save game info and destroy widget. Return True if success."""
        if not self.is_valid():
            return False
        name = self.name_entry.get_text()

        # Do not modify slug
        if not self.slug:
            self.slug = slugify(name)

        if not self.game:
            self.game = Game()

        if self.lutris_config.game_config_id == TEMP_CONFIG:
            self.lutris_config.game_config_id = self.get_config_id()

        runner_class = runners.import_runner(self.runner_name)
        runner = runner_class(self.lutris_config)
        self.game.name = name
        self.game.slug = self.slug
        self.game.runner_name = self.runner_name
        self.game.config = self.lutris_config
        self.game.directory = runner.game_path
        self.game.is_installed = True
        if self.runner_name in ('steam', 'winesteam'):
            self.game.steamid = self.lutris_config.game_config['appid']
        self.game.save()
        self.destroy()
        logger.debug("Saved %s", name)
        self.saved = True
        if callback:
            callback()
Example #2
0
 def _monitor_task(self, thread):
     if not thread.is_running:
         logger.debug("Thread QUIT")
         self._iter_commands()
         self.heartbeat = None
         return False
     return True
Example #3
0
    def execute(self, data):
        """Run an executable file."""
        args = []
        if isinstance(data, dict):
            self._check_required_params("file", data, "execute")
            file_ref = data["file"]
            args_string = data.get("args", "")
            for arg in shlex.split(args_string):
                args.append(self._substitute(arg))
        else:
            file_ref = data
        # Determine whether 'file' value is a file id or a path
        exec_path = self._get_file(file_ref) or self._substitute(file_ref)
        if not exec_path:
            raise ScriptingError("Unable to find file %s" % file_ref, file_ref)
        if not os.path.exists(exec_path):
            raise ScriptingError("Unable to find required executable", exec_path)
        self.chmodx(exec_path)

        terminal = data.get("terminal")
        if terminal:
            terminal = system.get_default_terminal()

        command = [exec_path] + args
        logger.debug("Executing %s" % command)
        thread = LutrisThread(command, env=runtime.get_env(), term=terminal, cwd=self.target_path)
        self.abort_current_task = thread.killall
        thread.run()
        self.abort_current_task = None
Example #4
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()
Example #5
0
 def move(self, params):
     """Move a file or directory into a destination folder."""
     self._check_required_params(['src', 'dst'], params, 'move')
     src, dst = self._get_move_paths(params)
     logger.debug("Moving %s to %s" % (src, dst))
     if not os.path.exists(src):
         raise ScriptingError("I can't move %s, it does not exist" % src)
     if os.path.isfile(src):
         src_filename = os.path.basename(src)
         src_dir = os.path.dirname(src)
         dst_path = os.path.join(dst, src_filename)
         if src_dir == dst:
             logger.info("Source file is the same as destination, skipping")
         elif os.path.exists(dst_path):
             # May not be the best choice, but it's the safest.
             # Maybe should display confirmation dialog (Overwrite / Skip) ?
             logger.info("Destination file exists, skipping")
         else:
             self._killable_process(shutil.move, src, dst)
     else:
         try:
             self._killable_process(shutil.move, src, dst)
         except shutil.Error:
             raise ScriptingError("Can't move %s \nto destination %s"
                                  % (src, dst))
     if os.path.isfile(src) and params['src'] in self.game_files.keys():
         # Change game file reference so it can be used as executable
         self.game_files['src'] = src
Example #6
0
 def on_steam_game_installed(self, *args):
     logger.debug("Steam game installed")
     if self.steam_data['platform'] == 'windows':
         runner_class = winesteam.winesteam
     else:
         runner_class = steam.steam
     self._append_steam_data_to_files(runner_class)
Example #7
0
 def install(self, installer_path=None):
     logger.debug("Installing steam from %s", installer_path)
     if not self.is_wine_installed():
         super(winesteam, self).install()
     if not installer_path:
         installer_path = download_steam()
     self.msi_exec(installer_path, quiet=True)
Example #8
0
 def xboxdrv(self, config):
     command = ("pkexec xboxdrv --daemon --detach-kernel-driver "
                "--dbus session --silent %s"
                % config)
     logger.debug("xboxdrv command: %s", command)
     thread = LutrisThread(command)
     thread.start()
Example #9
0
    def play(self):
        """Runs the game"""
        rom = self.game_config.get('main_file') or ''
        machine = self.game_config.get('machine')

        if self.runner_config.get("fs"):
            fullscreen = "1"
        else:
            fullscreen = "0"
        resolution = get_current_resolution()
        (resolutionx, resolutiony) = resolution.split("x")
        xres = str(resolutionx)
        yres = str(resolutiony)
        options = ["-fs", fullscreen,
                   "-" + machine + ".xres", xres,
                   "-" + machine + ".yres", yres,
                   "-" + machine + ".stretch", "1",
                   "-" + machine + ".special", "hq4x",
                   "-" + machine + ".videoip", "1"]
        joy_ids = self.find_joysticks()
        if len(joy_ids) > 0:
            controls = self.set_joystick_controls(joy_ids)
            for control in controls:
                options.append(control)
        else:
            logger.debug("No Joystick found")

        if not os.path.exists(rom):
            return {'error': 'FILE_NOT_FOUND', 'file': rom}

        command = [self.get_executable()]
        for option in options:
            command.append(option)
        command.append(rom)
        return {'command': command}
Example #10
0
def check_config(force_wipe=False):
    """Check if initial configuration is correct."""
    directories = [CONFIG_DIR,
                   join(CONFIG_DIR, "runners"),
                   join(CONFIG_DIR, "games"),
                   DATA_DIR,
                   join(DATA_DIR, "covers"),
                   join(DATA_DIR, "icons"),
                   join(DATA_DIR, "banners"),
                   join(DATA_DIR, "runners"),
                   join(DATA_DIR, "lib"),
                   CACHE_DIR,
                   join(CACHE_DIR, "installer"),
                   join(CACHE_DIR, "tmp")]
    for directory in directories:
        if not os.path.exists(directory):
            logger.debug("creating directory %s" % directory)
            os.mkdir(directory)

    if force_wipe:
        os.remove(PGA_DB)

    if not os.path.isfile(PGA_DB) or force_wipe:
        logger.debug("creating PGA database in %s" % PGA_DB)
        pga.create()
Example #11
0
def create_table(name, schema):
    fields = ", ".join([field_to_string(**f) for f in schema])
    fields = "(%s)" % fields
    query = "CREATE TABLE IF NOT EXISTS %s %s" % (name, fields)
    logger.debug("[PGAQuery] %s", query)
    with sql.db_cursor(PGA_DB) as cursor:
        cursor.execute(query)
Example #12
0
    def install(self, version=None, downloader=None, callback=None):
        """Install runner using package management systems."""
        logger.debug(
            "Installing %s (version=%s, downloader=%s, callback=%s)",
            self.name,
            version,
            downloader,
            callback,
        )
        runner = self.get_runner_version(version)
        if not runner:
            raise RunnerInstallationError(
                "{} is not available for the {} architecture".format(
                    self.name, self.arch
                )
            )
        if not downloader:
            raise RuntimeError("Missing mandatory downloader for runner %s" % self)
        opts = {
            "downloader": downloader,
            "callback": callback
        }
        if "wine" in self.name:
            opts["merge_single"] = True
            opts["dest"] = os.path.join(
                settings.RUNNER_DIR,
                self.name,
                "{}-{}".format(runner["version"], runner["architecture"])
            )

        if self.name == "libretro" and version:
            opts["merge_single"] = False
            opts["dest"] = os.path.join(settings.RUNNER_DIR, "retroarch/cores")
        self.download_and_extract(runner["url"], **opts)
Example #13
0
def get_path_from_config(config, appid):
    """ Given a steam config, return path for game 'appid' """
    if not config or 'apps' not in config:
        return False
    game_config = config["apps"].get(appid)
    if not game_config:
        return False
    if game_config.get('HasAllLocalContent'):
        installdir = game_config['installdir'].replace("\\\\", "/")
        if not installdir:
            return False
        if installdir.startswith('C'):
            installdir = os.path.join(os.path.expanduser('~'),
                                      '.wine/drive_c', installdir[3:])
        elif installdir[1] == ':':
            # Trim Windows path
            installdir = installdir[2:]
        logger.debug("Steam game found at %s" % installdir)
        if os.path.exists(installdir):
            return installdir
        elif os.path.exists(installdir.replace('steamapps', 'SteamApps')):
            return installdir.replace('steamapps', 'SteamApps')
        else:
            logger.debug("Path %s not found" % installdir)
    return False
Example #14
0
File: game.py Project: Freso/lutris
    def on_game_quit(self):
        """Restore some settings and cleanup after game quit."""
        self.heartbeat = None
        quit_time = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
        logger.debug("%s stopped at %s", self.name, quit_time)
        self.state = self.STATE_STOPPED

        os.chdir(os.path.expanduser('~'))

        if self.resolution_changed\
           or self.runner.system_config.get('reset_desktop'):
            display.change_resolution(self.original_outputs)

        if self.runner.system_config.get('use_us_layout'):
            subprocess.Popen(['setxkbmap'], env=os.environ).communicate()

        if self.runner.system_config.get('restore_gamma'):
            display.restore_gamma()

        if self.runner.system_config.get('xboxdrv') \
           and self.xboxdrv_thread.is_running:
            self.xboxdrv_thread.stop()

        if self.game_thread:
            self.game_thread.stop()
        self.process_return_codes()

        if self.exit_main_loop:
            exit()
Example #15
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)
Example #16
0
def execute(command, env=None, cwd=None, log_errors=False):
    """Execute a system command and return its results."""
    existing_env = os.environ.copy()
    if env:
        existing_env.update(env)
        logger.debug(' '.join('{}={}'.format(k, v) for k, v in env.iteritems()))
    logger.debug("Executing %s", ' '.join(command))

    # Piping stderr can cause slowness in the programs, use carefully
    # (especially when using regedit with wine)
    if log_errors:
        stderr_config = subprocess.PIPE
    else:
        stderr_config = None
    try:
        stdout, stderr = subprocess.Popen(command,
                                          shell=False,
                                          stdout=subprocess.PIPE,
                                          stderr=stderr_config,
                                          env=existing_env, cwd=cwd).communicate()
    except OSError as ex:
        logger.error('Could not run command %s: %s', command, ex)
        return
    if stderr and log_errors:
        logger.error(stderr)
    return stdout.strip()
Example #17
0
def extract_archive(path, to_directory='.', merge_single=True):
    path = os.path.abspath(path)
    logger.debug("Extracting %s to %s", path, to_directory)
    if path.endswith('.zip'):
        opener, mode = zipfile.ZipFile, 'r'
    elif path.endswith('.tar.gz') or path.endswith('.tgz'):
        opener, mode = tarfile.open, 'r:gz'
    elif path.endswith('.gz'):
        decompress_gz(path, to_directory)
        return

    elif path.endswith('.tar.bz2') or path.endswith('.tbz'):
        opener, mode = tarfile.open, 'r:bz2'
    else:
        raise RuntimeError(
            "Could not extract `%s` as no appropriate extractor is found"
            % path)
    destination = os.path.join(to_directory, ".lutris_extracted")
    handler = opener(path, mode)
    handler.extractall(destination)
    handler.close()
    if merge_single:
        extracted = os.listdir(destination)
        if len(extracted) == 1:
            destination = os.path.join(destination, extracted[0])
    for f in os.listdir(destination):
        source = os.path.join(destination, f)
        destination = os.path.join(to_directory, f)
        merge_folders(source, destination)
    logger.debug("Finished extracting %s", path)
Example #18
0
 def on_game_stop(self):
     """TODO: Call this once it is possible to monitor Steam games"""
     if bool(self.runner_config.get("quit_steam_on_exit")):
         logger.debug("Game configured to stop Steam on exit")
         self.shutdown()
         return True
     return False
Example #19
0
 def stop(self):
     """The kill command runs wineserver -k"""
     wine_path = self.get_wine_path()
     wine_root = os.path.dirname(wine_path)
     wineserver = os.path.join(wine_root, wine_root)
     logger.debug("Killing wineserver")
     os.popen(wineserver + " -k")
Example #20
0
 def cancel(self):
     """Request download stop and remove destination file."""
     logger.debug("Download cancelled")
     self.thread.stop_request.set()
     self.cancelled = True
     if os.path.isfile(self.dest):
         os.remove(self.dest)
Example #21
0
def download_icon(game, icon_type, overwrite=False, callback=None):
    icon_url = get_icon_url(game, icon_type)
    icon_path = get_icon_path(game, icon_type)
    icon_downloaded = http.download_asset(icon_url, icon_path, overwrite)
    if icon_downloaded and callback:
        logger.debug("Downloaded %s for %s" % (icon_type, game))
        callback(game)
Example #22
0
    def move(self, params):
        """Move a file or directory into a destination folder."""
        self._check_required_params(["src", "dst"], params, "move")
        src, dst = self._get_move_paths(params)
        logger.debug("Moving %s to %s", src, dst)
        if not os.path.exists(src):
            raise ScriptingError("I can't move %s, it does not exist" % src)

        if os.path.isfile(src):
            if os.path.dirname(src) == dst:
                logger.info("Source file is the same as destination, skipping")
                return

            if os.path.exists(os.path.join(dst, os.path.basename(src))):
                # May not be the best choice, but it's the safest.
                # Maybe should display confirmation dialog (Overwrite / Skip) ?
                logger.info("Destination file exists, skipping")
                return
        try:
            if self._is_cached_file(src):
                action = shutil.copy
            else:
                action = shutil.move
            self._killable_process(action, src, dst)
        except shutil.Error:
            raise ScriptingError("Can't move %s \nto destination %s" % (src, dst))
Example #23
0
    def on_save(self, _button):
        """Save game info and destroy widget. Return True if success."""
        if not self.is_valid():
            return False
        name = self.name_entry.get_text()

        # Do not modify slug
        if not self.slug:
            self.slug = slugify(name)

        if not self.game:
            self.game = Game(self.slug)
            self.game.config = self.lutris_config

        if not self.lutris_config.game_slug:
            self.lutris_config.game_slug = self.slug
        self.lutris_config.save()

        runner_class = runners.import_runner(self.runner_name)
        runner = runner_class(self.lutris_config)
        self.game.name = name
        self.game.slug = self.slug
        self.game.runner_name = self.runner_name
        self.game.directory = runner.game_path
        self.game.is_installed = True
        self.game.save()
        self.destroy()
        logger.debug("Saved %s", name)
        self.saved = True
Example #24
0
File: wine.py Project: Freso/lutris
def create_prefix(prefix, wine_path=None, arch='win32'):
    """Create a new Wine prefix."""
    logger.debug("Creating a %s prefix in %s", arch, 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_path = wine().get_executable()

    wineboot_path = os.path.join(os.path.dirname(wine_path), 'wineboot')

    env = {
        'WINEARCH': arch,
        'WINEPREFIX': prefix
    }
    system.execute([wineboot_path], env=env)
    for i in range(20):
        time.sleep(.25)
        if os.path.exists(os.path.join(prefix, 'user.reg')):
            break
    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')
    logger.info('%s Prefix created in %s', arch, prefix)
    prefix_manager = WinePrefixManager(prefix)
    prefix_manager.setup_defaults()
Example #25
0
File: wine.py Project: Freso/lutris
    def play(self):
        game_exe = self.game_exe
        arguments = self.game_config.get('args') or ''

        if not os.path.exists(game_exe):
            return {'error': 'FILE_NOT_FOUND', 'file': game_exe}

        launch_info = {}
        launch_info['env'] = self.get_env(full=False)

        if self.runner_config.get('xinput'):
            xinput_path = self.get_xinput_path()
            if xinput_path:
                logger.debug('Preloading %s', xinput_path)
                launch_info['ld_preload'] = self.get_xinput_path()
            else:
                logger.error('Missing koku-xinput-wine.so, Xinput won\'t be enabled')

        command = [self.get_executable()]
        if game_exe.endswith(".msi"):
            command.append('msiexec')
            command.append('/i')
        if game_exe.endswith('.lnk'):
            command.append('start')
            command.append('/unix')
        command.append(game_exe)

        if arguments:
            for arg in shlex.split(arguments):
                command.append(arg)
        launch_info['command'] = command
        return launch_info
Example #26
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
Example #27
0
File: wine.py Project: Freso/lutris
def set_regedit(path, key, value='', type='REG_SZ', wine_path=None,
                prefix=None, arch='win32'):
    """Add keys to the windows registry.

    Path is something like HKEY_CURRENT_USER\Software\Wine\Direct3D
    """
    logger.debug("Setting wine registry key : %s\\%s to %s",
                 path, key, value)
    reg_path = os.path.join(settings.CACHE_DIR, 'winekeys.reg')
    formatted_value = {
        'REG_SZ': '"%s"' % value,
        'REG_DWORD': 'dword:' + value,
        'REG_BINARY': 'hex:' + value.replace(' ', ','),
        'REG_MULTI_SZ': 'hex(2):' + value,
        'REG_EXPAND_SZ': 'hex(7):' + value,
    }
    # Make temporary reg file
    reg_file = open(reg_path, "w")
    reg_file.write(dedent(
        """
        REGEDIT4
        [%s]
        "%s"=%s
        """ % (path, key, formatted_value[type])))
    reg_file.close()
    set_regedit_file(reg_path, wine_path=wine_path, prefix=prefix, arch=arch)
    os.remove(reg_path)
Example #28
0
    def find_joysticks(self):
        """ Detect connected joysticks and return their ids """
        joy_ids = []
        if not self.is_installed:
            return []
        output = subprocess.Popen(
            [self.get_executable(), "dummy"],
            stdout=subprocess.PIPE,
            universal_newlines=True,
        ).communicate()[0]
        ouput = output.split("\n")
        found = False
        joy_list = []
        for line in ouput:
            if found and "Joystick" in line:
                joy_list.append(line)
            else:
                found = False
            if "Initializing joysticks" in line:
                found = True

        for joy in joy_list:
            index = joy.find("Unique ID:")
            joy_id = joy[index + 11:]
            logger.debug("Joystick found id %s ", joy_id)
            joy_ids.append(joy_id)
        return joy_ids
Example #29
0
def download_banner(game, overwrite=False, callback=None):
    banner_url = settings.INSTALLER_URL + '%s.jpg' % game
    banner_path = get_banner_path(game)
    cover_downloaded = http.download_asset(banner_url, banner_path, overwrite)
    if cover_downloaded and callback:
        logger.debug("Icon downloaded for %s", game)
        callback(game)
Example #30
0
 def install(self, version=None, downloader=None, callback=None):
     """Install runner using package management systems."""
     logger.debug("Installing %s (version=%s, downloader=%s, callback=%s)",
                  self.name, version, downloader, callback)
     runner_info = self.get_runner_info(version)
     if not runner_info:
         raise RunnerInstallationError(
             '{} is not available for the {} architecture'.format(
                 self.name, self.arch
             )
         )
         dialogs.ErrorDialog(
         )
         return False
     opts = {}
     if downloader:
         opts['downloader'] = downloader
     if callback:
         opts['callback'] = callback
     if 'wine' in self.name:
         version = runner_info['version']
         opts['merge_single'] = True
         dirname = '{}-{}'.format(version, runner_info['architecture'])
         opts['dest'] = os.path.join(settings.RUNNER_DIR,
                                     self.name, dirname)
     if self.name == 'libretro' and version:
         opts['merge_single'] = False
         opts['dest'] = os.path.join(settings.RUNNER_DIR, 'retroarch/cores')
     url = runner_info['url']
     self.download_and_extract(url, **opts)
Example #31
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"

        fps_limit = system_config.get("fps_limit") or ""
        if fps_limit:
            strangle_cmd = system.find_executable("strangle")
            launch_arguments = [strangle_cmd, fps_limit] + launch_arguments

        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)

        # 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"),
                        "/usr/$LIB/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)],
                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()
Example #32
0
File: base.py Project: bsdf/lutris
 def wipe_game_cache(self):
     logger.debug("Deleting games from service-games for %s", self.id)
     sql.db_delete(PGA_DB, "service_games", "service", self.id)
Example #33
0
File: base.py Project: bsdf/lutris
 def login(self, parent=None):
     logger.debug("Connecting to %s", self.name)
     dialog = WebConnectDialog(self, parent)
     dialog.set_modal(True)
     dialog.show()
Example #34
0
 def on_confirm(self, button):  # pylint: disable=unused-argument
     logger.debug("Action %s confirmed", self.action)
     self.action_confirmed = True
     self.destroy()
Example #35
0
 def on_button_toggled(self, button, action):  # pylint: disable=unused-argument
     logger.debug("Action set to %s", action)
     self.action = action
Example #36
0
File: game.py Project: Lucki/lutris
 def notify_steam_game_changed(self, appmanifest):
     logger.debug(appmanifest)
     logger.debug(appmanifest.states)
Example #37
0
File: game.py Project: Lucki/lutris
    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 = sorted(display.get_outputs(), key=lambda e: e[0] == system_config.get('display'))
        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_gamecontrollerconfig = system_config.get('sdl_gamecontrollerconfig')
        if sdl_gamecontrollerconfig:
            path = os.path.expanduser(sdl_gamecontrollerconfig)
            if os.path.exists(path):
                with open(path, "r") as f:
                    sdl_gamecontrollerconfig = f.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':
            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:
            launch_arguments = shlex.split(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
                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

        dri_prime = system_config.get('dri_prime')
        if dri_prime:
            env["DRI_PRIME"] = "1"
        else:
            env["DRI_PRIME"] = "0"

        # 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

        include_processes = shlex.split(system_config.get('include_processes', ''))
        exclude_processes = shlex.split(system_config.get('exclude_processes', ''))

        monitoring_disabled = system_config.get('disable_monitoring')
        process_watch = not monitoring_disabled

        self.game_thread = LutrisThread(launch_arguments,
                                        runner=self.runner,
                                        env=env,
                                        rootpid=gameplay_info.get('rootpid'),
                                        watch=process_watch,
                                        term=terminal,
                                        log_buffer=self.log_buffer,
                                        include_processes=include_processes,
                                        exclude_processes=exclude_processes)
        if hasattr(self.runner, 'stop'):
            self.game_thread.set_stop_command(self.runner.stop)
        self.game_thread.start()
        self.state = self.STATE_RUNNING

        # xboxdrv setup
        xboxdrv_config = system_config.get('xboxdrv')
        if xboxdrv_config:
            self.xboxdrv_start(xboxdrv_config)

        if monitoring_disabled:
            logger.info("Process monitoring disabled")
        else:
            self.heartbeat = GLib.timeout_add(HEARTBEAT_DELAY, self.beat)
Example #38
0
def remove_folder(path):
    if os.path.exists(path):
        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)
Example #39
0
    def watch_children(self):
        """Poke at the running process(es).

        Return:
            bool: True to keep monitoring, False to stop (Used by GLib.timeout_add)
        """
        if not self.game_process:
            logger.error('No game process available')
            return False
        if not self.is_running:
            logger.error('Game is not running')
            return False

        if not self.ready_state:
            # Don't monitor processes until the thread is in a ready state
            self.cycles_without_children = 0
            return True

        processes, num_children, num_watched_children, terminated_children = self.get_processes(
        )
        if num_watched_children > 0 and not self.monitoring_started:
            logger.debug("Start process monitoring")
            self.monitoring_started = True

        for key in processes:
            if processes[key] != self.monitored_processes[key]:
                self.monitored_processes[key] = processes[key]
                logger.debug("Processes %s: %s", key, ', '.join(processes[key])
                             or 'none')

        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
                cycles_left = MAX_CYCLES_WITHOUT_CHILDREN - self.cycles_without_children
                if cycles_left:
                    if cycles_left < 4:
                        logger.debug("Thread aborting in %d cycle",
                                     cycles_left)
                else:
                    logger.warning("Thread aborting now")
        else:
            self.cycles_without_children = 0

        if self.cycles_without_children >= MAX_CYCLES_WITHOUT_CHILDREN:
            self.is_running = False

            resume_stop = self.stop()
            if not resume_stop:
                logger.info("Full shutdown prevented")
                return False

            if num_children == 0:
                logger.debug("No children left in thread")
                self.game_process.communicate()
            else:
                logger.debug('%d processes are still active', num_children)
                if self.is_zombie():
                    logger.warning(
                        'Zombie process detected, killing game process')
                    self.game_process.kill()
            self.return_code = self.game_process.returncode
            return False

        if terminated_children and terminated_children == num_watched_children:
            try:
                # Really not sure if that's necessary...
                self.game_process.wait(2)
            except subprocess.TimeoutExpired:
                logger.warning("Processes are still running")
        return True
Example #40
0
def get_default_terminal():
    terms = get_terminal_apps()
    if terms:
        return terms[0]
    else:
        logger.debug("Couldn't find a terminal emulator.")
Example #41
0
    def check_runner_install(self):
        """Check if the runner is installed before starting the installation
        Install the required runner(s) if necessary. This should handle runner
        dependencies (wine for winesteam) or runners used for installer tasks.
        """
        required_runners = []
        runner = self.get_runner_class(self.runner)
        if runner.depends_on is not None:
            required_runners.append(runner.depends_on())
        required_runners.append(runner())

        for command in self.script.get("installer", []):
            command_name, command_params = self._get_command_name_and_params(
                command)
            if command_name == "task":
                runner_name, _task_name = self._get_task_runner_and_name(
                    command_params["name"])
                runner_names = [r.name for r in required_runners]
                if runner_name not in runner_names:
                    required_runners.append(
                        self.get_runner_class(runner_name)())

        logger.debug("Required runners: %s", required_runners)
        for runner in required_runners:
            params = {}
            if self.runner == "libretro":
                params["core"] = self.script["game"]["core"]
            if self.runner.startswith("wine"):
                # Force the wine version to be installed
                params["fallback"] = False
                params["min_version"] = wine.MIN_SAFE_VERSION
                version = self._get_runner_version()
                if version:
                    params["version"] = version
                else:
                    # Looking up default wine version
                    default_wine = runner.get_runner_version() or {}
                    if "version" in default_wine:
                        logger.debug("Default wine version is %s",
                                     default_wine["version"])
                        # Set the version to both the is_installed params and
                        # the script itself so the version gets saved at the
                        # end of the install.
                        if self.runner not in self.script:
                            self.script[self.runner] = {}
                        params["version"] = self.script[
                            self.runner]["version"] = "{}-{}".format(
                                default_wine["version"],
                                default_wine["architecture"])
                    else:
                        logger.error(
                            "Failed to get default wine version (got %s)",
                            default_wine)

            if not runner.is_installed(**params):
                logger.info("Runner %s needs to be installed")
                self.runners_to_install.append(runner)

        if self.runner.startswith("wine") and not get_system_wine_version():
            WineNotInstalledWarning(parent=self.parent)
        self.install_runners()
Example #42
0
def shutdown():
    """Cleanly quit Steam."""
    logger.debug("Shutting down Steam")
    if is_running():
        subprocess.call(["steam", "-shutdown"])
Example #43
0
 def on_game_collection_changed(self, _sender):
     """Simple method used to refresh the view"""
     logger.debug("Game collection changed")
     self.emit("view-updated")
     return True
Example #44
0
    def _save_game(self):
        """Write the game configuration in the DB and config file.

        This needs to be unfucked
        """
        if self.extends:
            logger.info(
                "This is an extension to %s, not creating a new game entry",
                self.extends,
            )
            return
        configpath = make_game_config_id(self.slug)
        config_filename = os.path.join(settings.CONFIG_DIR,
                                       "games/%s.yml" % configpath)

        if self.requires:
            # Load the base game config
            required_game = pga.get_game_by_field(self.requires,
                                                  field="installer_slug")
            base_config = LutrisConfig(
                runner_slug=self.runner,
                game_config_id=required_game["configpath"])
            config = base_config.game_level
        else:
            config = {"game": {}}

        self.game_id = pga.add_or_update(
            name=self.game_name,
            runner=self.runner,
            slug=self.game_slug,
            directory=self.target_path,
            installed=1,
            installer_slug=self.slug,
            parent_slug=self.requires,
            year=self.year,
            steamid=self.steamid,
            configpath=configpath,
            id=self.game_id,
        )

        game = Game(self.game_id)
        game.save()

        logger.debug("Saved game entry %s (%d)", self.game_slug, self.game_id)

        # Config update
        if "system" in self.script:
            config["system"] = self._substitute_config(self.script["system"])
        if self.runner in self.script and self.script[self.runner]:
            config[self.runner] = self._substitute_config(
                self.script[self.runner])

        # Game options such as exe or main_file can be added at the root of the
        # script as a shortcut, this integrates them into the game config
        # properly
        launcher, launcher_value = _get_game_launcher(self.script)
        if isinstance(launcher_value, list):
            game_files = []
            for game_file in launcher_value:
                if game_file in self.game_files:
                    game_files.append(self.game_files[game_file])
                else:
                    game_files.append(game_file)
            config["game"][launcher] = game_files
        elif launcher_value:
            if launcher_value in self.game_files:
                launcher_value = self.game_files[launcher_value]
            elif self.target_path and os.path.exists(
                    os.path.join(self.target_path, launcher_value)):
                launcher_value = os.path.join(self.target_path, launcher_value)
            config["game"][launcher] = launcher_value

        if "game" in self.script:
            try:
                config["game"].update(self.script["game"])
            except ValueError:
                raise ScriptingError("Invalid 'game' section",
                                     self.script["game"])
            config["game"] = self._substitute_config(config["game"])

        yaml_config = yaml.safe_dump(config, default_flow_style=False)
        with open(config_filename, "w") as config_file:
            config_file.write(yaml_config)
        game.emit("game-installed")
Example #45
0
    def watch_children(self):
        """Poke at the running process(es)."""
        if not self.game_process:
            logger.error('No game process available')
            return False
        if not self.is_running:
            logger.error('Game is not running')
            return False

        if not self.ready_state:
            # Don't monitor processes until the thread is in a ready state
            self.cycles_without_children = 0
            return True

        processes, num_children, num_watched_children, terminated_children = self.get_processes(
        )
        if num_watched_children > 0 and not self.monitoring_started:
            logger.debug("Start process monitoring")
            self.monitoring_started = True

        for key in processes:
            if processes[key] != self.monitored_processes[key]:
                self.monitored_processes[key] = processes[key]
                logger.debug("Processes %s: %s", key, ', '.join(processes[key])
                             or 'none')

        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
                cycles_left = MAX_CYCLES_WITHOUT_CHILDREN - self.cycles_without_children
                if cycles_left:
                    if cycles_left < 4:
                        logger.debug("Thread aborting in %d cycle",
                                     cycles_left)
                else:
                    logger.warning("Thread aborting now")
        else:
            self.cycles_without_children = 0

        if self.cycles_without_children >= MAX_CYCLES_WITHOUT_CHILDREN:
            self.is_running = False

            # Remove logger early to avoid issues with zombie processes
            # (unconfirmed)
            if self.stdout_monitor:
                logger.debug("Detaching logger")
                GLib.source_remove(self.stdout_monitor)

            resume_stop = self.stop()
            if not resume_stop:
                logger.info("Full shutdown prevented")
                return False

            if num_children == 0:
                logger.debug("No children left in thread")
                self.game_process.communicate()
            else:
                logger.debug('%d processes are still active', num_children)
                if self.is_zombie():
                    logger.warning(
                        'Zombie process detected, killing game process')
                    self.game_process.kill()
            self.return_code = self.game_process.returncode
            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
Example #46
0
 def show_update_runtime_dialog():
     if os.environ.get("LUTRIS_SKIP_INIT"):
         logger.debug("Skipping initialization")
     else:
         init_dialog = LutrisInitDialog(update_runtime)
         init_dialog.run()
Example #47
0
 def login(self, parent=None):
     """Connect to GOG"""
     logger.debug("Connecting to GOG")
     dialog = WebConnectDialog(self, parent)
     dialog.set_modal(True)
     dialog.show()
Example #48
0
 def load(self):
     user_id, _persona_id, _user_name = self.get_identity()
     games = self.get_library(user_id)
     logger.debug(games)
Example #49
0
def winekill(prefix, arch=WINE_DEFAULT_ARCH, wine_path=None, env=None, initial_pids=None):
    """Kill processes in Wine prefix."""

    initial_pids = initial_pids or []

    if not wine_path:
        wine = import_runner("wine")
        wine_path = wine().get_executable()
    wine_root = os.path.dirname(wine_path)
    if not env:
        env = {"WINEARCH": arch, "WINEPREFIX": prefix}
    command = [os.path.join(wine_root, "wineserver"), "-k"]

    logger.debug("Killing all wine processes: %s", command)
    logger.debug("\tWine prefix: %s", prefix)
    logger.debug("\tWine arch: %s", arch)
    if initial_pids:
        logger.debug("\tInitial pids: %s", initial_pids)

    system.execute(command, env=env, quiet=True)

    logger.debug("Waiting for wine processes to terminate")
    # Wineserver needs time to terminate processes
    num_cycles = 0
    while True:
        num_cycles += 1
        running_processes = [
            pid for pid in initial_pids if system.path_exists("/proc/%s" % pid)
        ]

        if not running_processes:
            break
        if num_cycles > 20:
            logger.warning(
                "Some wine processes are still running: %s",
                ", ".join(running_processes),
            )
            break
        time.sleep(0.1)
    logger.debug("Done waiting.")
Example #50
0
    def configure_game(self, prelaunched, error=None):  # noqa: C901
        """Get the game ready to start, applying all the options
        This methods sets the game_runtime_config attribute.
        """
        # pylint: disable=too-many-locals,too-many-branches,too-many-statements
        # TODO: split into multiple methods to reduce complexity (42)
        if error:
            logger.error(error)
            dialogs.ErrorDialog(str(error))
        if not prelaunched:
            logger.error("Game prelaunch unsuccessful")
            dialogs.ErrorDialog(_("An error prevented the game from running"))
            self.state = self.STATE_STOPPED
            self.emit("game-stop")
            return
        system_config = self.runner.system_config
        self.original_outputs = DISPLAY_MANAGER.get_config()

        gameplay_info = self.runner.play()
        if "error" in gameplay_info:
            self.show_error_message(gameplay_info)
            self.state = self.STATE_STOPPED
            self.emit("game-stop")
            return
        logger.debug("Launching %s", self.name)
        logger.debug(json.dumps(gameplay_info, indent=2))

        env = {}
        sdl_gamecontrollerconfig = system_config.get(
            "sdl_gamecontrollerconfig")
        if sdl_gamecontrollerconfig:
            path = os.path.expanduser(sdl_gamecontrollerconfig)
            if system.path_exists(path):
                with open(path, "r") as controllerdb_file:
                    sdl_gamecontrollerconfig = controllerdb_file.read()
            env["SDL_GAMECONTROLLERCONFIG"] = sdl_gamecontrollerconfig

        sdl_video_fullscreen = system_config.get("sdl_video_fullscreen") or ""
        env["SDL_VIDEO_FULLSCREEN_DISPLAY"] = sdl_video_fullscreen

        restrict_to_display = system_config.get("display")
        if restrict_to_display != "off":
            if restrict_to_display == "primary":
                restrict_to_display = None
                for output in self.original_outputs:
                    if output.primary:
                        restrict_to_display = output.name
                        break
                if not restrict_to_display:
                    logger.warning("No primary display set")
            else:
                found = False
                for output in self.original_outputs:
                    if output.name == restrict_to_display:
                        found = True
                        break
                if not found:
                    logger.warning("Selected display %s not found",
                                   restrict_to_display)
                    restrict_to_display = None
            if restrict_to_display:
                turn_off_except(restrict_to_display)
                time.sleep(3)
                self.resolution_changed = True

        resolution = system_config.get("resolution")
        if resolution != "off":
            DISPLAY_MANAGER.set_resolution(resolution)
            time.sleep(3)
            self.resolution_changed = True

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

        self.killswitch = system_config.get("killswitch")
        if self.killswitch and not system.path_exists(self.killswitch):
            # Prevent setting a killswitch to a file that doesn't exists
            self.killswitch = None

        # Command
        launch_arguments = gameplay_info["command"]

        optimus = system_config.get("optimus")
        if optimus == "primusrun" and system.find_executable("primusrun"):
            launch_arguments.insert(0, "primusrun")
        elif optimus == "optirun" and system.find_executable("optirun"):
            launch_arguments.insert(0, "virtualgl")
            launch_arguments.insert(0, "-b")
            launch_arguments.insert(0, "optirun")
        elif optimus == "pvkrun" and system.find_executable("pvkrun"):
            launch_arguments.insert(0, "pvkrun")

        xephyr = system_config.get("xephyr") or "off"
        if xephyr != "off":
            if not system.find_executable("Xephyr"):
                raise GameConfigError(
                    "Unable to find Xephyr, install it or disable the Xephyr option"
                )

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

            xephyr_thread = MonitoredCommand(xephyr_command)
            xephyr_thread.start()
            time.sleep(3)
            env["DISPLAY"] = ":2"

        if system_config.get("use_us_layout"):
            setxkbmap_command = [
                "setxkbmap", "-model", "pc101", "us", "-print"
            ]
            xkbcomp_command = ["xkbcomp", "-", os.environ.get("DISPLAY", ":0")]
            xkbcomp = subprocess.Popen(xkbcomp_command, stdin=subprocess.PIPE)
            subprocess.Popen(setxkbmap_command,
                             env=os.environ,
                             stdout=xkbcomp.stdin).communicate()
            xkbcomp.communicate()

        if system_config.get("aco"):
            env["RADV_PERFTEST"] = "aco"

        pulse_latency = system_config.get("pulse_latency")
        if pulse_latency:
            env["PULSE_LATENCY_MSEC"] = "60"

        vk_icd = system_config.get("vk_icd")
        if vk_icd and vk_icd != "off" and system.path_exists(vk_icd):
            env["VK_ICD_FILENAMES"] = vk_icd

        fps_limit = system_config.get("fps_limit") or ""
        if fps_limit:
            strangle_cmd = system.find_executable("strangle")
            if strangle_cmd:
                launch_arguments = [strangle_cmd, fps_limit] + launch_arguments
            else:
                logger.warning(
                    "libstrangle is not available on this system, FPS limiter disabled"
                )

        prefix_command = system_config.get("prefix_command") or ""
        if prefix_command:
            launch_arguments = (
                shlex.split(os.path.expandvars(prefix_command)) +
                launch_arguments)

        single_cpu = system_config.get("single_cpu") or False
        if single_cpu:
            logger.info("The game will run on a single CPU core")
            launch_arguments.insert(0, "0")
            launch_arguments.insert(0, "-c")
            launch_arguments.insert(0, "taskset")

        terminal = system_config.get("terminal")
        if terminal:
            terminal = system_config.get("terminal_app",
                                         system.get_default_terminal())
            if terminal and not system.find_executable(terminal):
                dialogs.ErrorDialog(
                    _("The selected terminal application could not be launched:\n%s"
                      ) % terminal)
                self.state = self.STATE_STOPPED
                self.emit("game-stop")
                return

        # Env vars
        game_env = gameplay_info.get("env") or self.runner.get_env()
        env.update(game_env)
        env["game_name"] = self.name

        # Prime vars
        prime = system_config.get("prime")
        if prime:
            env["__NV_PRIME_RENDER_OFFLOAD"] = "1"
            env["__GLX_VENDOR_LIBRARY_NAME"] = "nvidia"
            env["__VK_LAYER_NV_optimus"] = "NVIDIA_only"

        # LD_PRELOAD
        ld_preload = gameplay_info.get("ld_preload")
        if ld_preload:
            env["LD_PRELOAD"] = ld_preload

        # Feral gamemode
        gamemode = system_config.get(
            "gamemode") 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
                ])

        # LD_LIBRARY_PATH
        game_ld_libary_path = gameplay_info.get("ld_library_path")
        if game_ld_libary_path:
            ld_library_path = env.get("LD_LIBRARY_PATH")
            if not ld_library_path:
                ld_library_path = "$LD_LIBRARY_PATH"
            env["LD_LIBRARY_PATH"] = ":".join(
                [game_ld_libary_path, ld_library_path])

        include_processes = shlex.split(
            system_config.get("include_processes", ""))
        exclude_processes = shlex.split(
            system_config.get("exclude_processes", ""))

        self.game_runtime_config = {
            "args": launch_arguments,
            "env": env,
            "terminal": terminal,
            "include_processes": include_processes,
            "exclude_processes": exclude_processes,
        }

        if system_config.get("disable_compositor"):
            self.set_desktop_compositing(False)

        prelaunch_command = system_config.get("prelaunch_command")
        if prelaunch_command:
            command_array = shlex.split(prelaunch_command)
            if system.path_exists(command_array[0]):
                self.prelaunch_executor = MonitoredCommand(
                    command_array,
                    include_processes=[os.path.basename(command_array[0])],
                    env=self.game_runtime_config["env"],
                    cwd=self.directory,
                )
                self.prelaunch_executor.start()
                logger.info("Running %s in the background", prelaunch_command)
        if system_config.get("prelaunch_wait"):
            self.heartbeat = GLib.timeout_add(HEARTBEAT_DELAY,
                                              self.prelaunch_beat)
        else:
            self.start_game()
Example #51
0
 def launch(self, game):
     """Launch a Lutris game"""
     logger.debug("Adding game %s (%s) to running games", game, id(game))
     self.running_games.append(game)
     game.connect("game-stop", self.on_game_stop)
     game.play()
Example #52
0
 def notify_finish(self, runtime):
     """A runtime has finished downloading"""
     logger.debug("Runtime %s is now updated and available", runtime.name)
     self.current_updates -= 1
     if self.current_updates == 0:
         logger.info("Runtime updated")
Example #53
0
 def refresh_icon(self, game_slug):
     logger.debug("Getting icon for %s", game_slug)
     AsyncCall(self.fetch_icon, None, game_slug)
Example #54
0
 def launch(self, game):
     """Launch a Lutris game"""
     logger.debug("Adding game %s (%s) to running games", game, id(game))
     self.running_games.append(game)
     game.play()
Example #55
0
 def shutdown(self):
     logger.debug("Stopping all winesteam processes")
     super(winesteam, self).stop()
Example #56
0
 def on_media_loaded(self, response):
     for slug in self.games_to_refresh:
         logger.debug("Refreshing %s", slug)
         self.refresh_icon(slug)
Example #57
0
 def on_files_ready(self, _widget, files_ready):
     """Toggle state of continue button based on ready state"""
     logger.debug("Files are ready: %s", files_ready)
     self.continue_button.set_sensitive(files_ready)
Example #58
0
 def stop(self):
     if self.killall_on_exit():
         logger.debug("Game configured to stop Steam on exit")
         self.shutdown()
         return True
     return False
Example #59
0
def execute(command,
            env=None,
            cwd=None,
            log_errors=False,
            quiet=False,
            shell=False):
    """
        Execute a system command and return its results.

        Params:
            command (list): A list containing an executable and its parameters
            env (dict): Dict of values to add to the current environment
            cwd (str): Working directory
            log_errors (bool): Pipe stderr to stdout (might cause slowdowns)
            quiet (bool): Do not display log messages

        Returns:
            str: stdout output
    """

    # Check if the executable exists
    if not command:
        logger.error("No executable provided!")
        return
    if os.path.isabs(command[0]) and not path_exists(command[0]):
        logger.error("No executable found in %s", command)
        return

    if not quiet:
        logger.debug("Executing %s", " ".join(command))

    # Set up environment
    existing_env = os.environ.copy()
    if env:
        if not quiet:
            logger.debug(" ".join("{}={}".format(k, v)
                                  for k, v in env.items()))
        env = {k: v for k, v in env.items() if v is not None}
        existing_env.update(env)

    # Piping stderr can cause slowness in the programs, use carefully
    # (especially when using regedit with wine)
    if log_errors:
        stderr_handler = subprocess.PIPE
        stderr_needs_closing = False
    else:
        stderr_handler = open(os.devnull, "w")
        stderr_needs_closing = True
    try:
        stdout, stderr = subprocess.Popen(
            command,
            shell=shell,
            stdout=subprocess.PIPE,
            stderr=stderr_handler,
            env=existing_env,
            cwd=cwd,
        ).communicate()
    except (OSError, TypeError) as ex:
        logger.error("Could not run command %s (env: %s): %s", command, env,
                     ex)
        return
    finally:
        if stderr_needs_closing:
            stderr_handler.close()
    if stderr and log_errors:
        logger.error(stderr)
    return stdout.decode(errors="replace").strip()
Example #60
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 extractor is None:
        if path.endswith(".tar.gz") or path.endswith(".tgz"):
            extractor = "tgz"
        elif path.endswith(".tar.xz") or path.endswith(".txz"):
            extractor = "txz"
        elif path.endswith(".tar"):
            extractor = "tar"
        elif path.endswith(".tar.bz2") or path.endswith(".tbz"):
            extractor = "bz2"
        elif path.endswith(".gz"):
            extractor = "gzip"
        elif is_7zip_supported(path, None):
            extractor = None
        else:
            raise RuntimeError(
                "Could not extract `%s` - no appropriate extractor found" %
                path)

    if extractor == "tgz":
        opener, mode = tarfile.open, "r:gz"
    elif extractor == "txz":
        opener, mode = tarfile.open, "r:xz"
    elif extractor == "tar":
        opener, mode = tarfile.open, "r:"
    elif extractor == "bz2":
        opener, mode = tarfile.open, "r:bz2"
    elif extractor == "gzip":
        decompress_gz(path, to_directory)
        return
    elif extractor is None or is_7zip_supported(path, extractor):
        opener = "7zip"
    else:
        raise RuntimeError(
            "Could not extract `%s` - unknown format specified" % 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, zlib.error, tarfile.ReadError) 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):
                    try:
                        system.merge_folders(source_path, destination_path)
                    except OSError as ex:
                        logger.error("Failed to merge to destination %s: %s",
                                     destination_path, ex)
                        raise ExtractFailure(str(ex))
            else:
                shutil.move(source_path, destination_path)
        system.remove_folder(temp_dir)
    logger.debug("Finished extracting %s to %s", path, to_directory)
    return path, to_directory