Remove 'game-stop' in favor of 'game-stopped' and the stop_game() method.

This means we have to filter the running-games list a bit more, since we don't guarantee a stopping game is removed from there before anything else.
This commit is contained in:
Daniel Johnson 2024-01-14 07:22:48 -05:00 committed by Mathieu Comandon
parent 19ff134fb3
commit 5081458f31
5 changed files with 33 additions and 32 deletions

View file

@ -30,14 +30,12 @@ def watch_game_errors(game_stop_result, game=None):
try: try:
result = function(*args, **kwargs) result = function(*args, **kwargs)
if game_stop_result is not None and result == game_stop_result and game.state != game.STATE_STOPPED: if game_stop_result is not None and result == game_stop_result and game.state != game.STATE_STOPPED:
game.state = game.STATE_STOPPED game.stop_game()
game.emit("game-stop")
return result return result
except Exception as ex: except Exception as ex:
logger.exception("%s has encountered an error: %s", game, ex, exc_info=ex) logger.exception("%s has encountered an error: %s", game, ex, exc_info=ex)
if game.state != game.STATE_STOPPED: if game.state != game.STATE_STOPPED:
game.state = game.STATE_STOPPED game.stop_game()
game.emit("game-stop")
game.signal_error(ex) game.signal_error(ex)
return game_stop_result return game_stop_result

View file

@ -11,7 +11,7 @@ import time
from gettext import gettext as _ from gettext import gettext as _
from typing import cast from typing import cast
from gi.repository import GLib, GObject, Gtk, Gio from gi.repository import Gio, GLib, GObject, Gtk
from lutris import settings from lutris import settings
from lutris.command import MonitoredCommand from lutris.command import MonitoredCommand
@ -59,7 +59,6 @@ class Game(GObject.Object):
"game-unhandled-error": (GObject.SIGNAL_RUN_FIRST, None, (object,)), "game-unhandled-error": (GObject.SIGNAL_RUN_FIRST, None, (object,)),
"game-start": (GObject.SIGNAL_RUN_FIRST, None, ()), "game-start": (GObject.SIGNAL_RUN_FIRST, None, ()),
"game-started": (GObject.SIGNAL_RUN_FIRST, None, ()), "game-started": (GObject.SIGNAL_RUN_FIRST, None, ()),
"game-stop": (GObject.SIGNAL_RUN_FIRST, None, ()),
"game-stopped": (GObject.SIGNAL_RUN_FIRST, None, ()), "game-stopped": (GObject.SIGNAL_RUN_FIRST, None, ()),
"game-updated": (GObject.SIGNAL_RUN_FIRST, None, ()), "game-updated": (GObject.SIGNAL_RUN_FIRST, None, ()),
"game-install": (GObject.SIGNAL_RUN_FIRST, None, ()), "game-install": (GObject.SIGNAL_RUN_FIRST, None, ()),
@ -673,10 +672,10 @@ class Game(GObject.Object):
@watch_game_errors(game_stop_result=False) @watch_game_errors(game_stop_result=False)
def launch(self, launch_ui_delegate=None): def launch(self, launch_ui_delegate=None):
"""Request launching a game. The game may not be installed yet."""
if not launch_ui_delegate: if not launch_ui_delegate:
launch_ui_delegate = Gio.Application.get_default().launch_ui_delegate launch_ui_delegate = Gio.Application.get_default().launch_ui_delegate
"""Request launching a game. The game may not be installed yet."""
if not self.check_launchable(): if not self.check_launchable():
logger.error("Game is not launchable") logger.error("Game is not launchable")
return False return False
@ -840,7 +839,7 @@ class Game(GObject.Object):
# Inspect why it could have crashed # Inspect why it could have crashed
self.state = self.STATE_STOPPED self.state = self.STATE_STOPPED
self.emit("game-stop") self.emit("game-stopped")
if os.path.exists(self.now_playing_path): if os.path.exists(self.now_playing_path):
os.unlink(self.now_playing_path) os.unlink(self.now_playing_path)
if not self.timer.finished: if not self.timer.finished:

View file

@ -80,7 +80,7 @@ class Application(Gtk.Application):
init_exception_backstops() init_exception_backstops()
GObject.add_emission_hook(Game, "game-start", self.on_game_start) GObject.add_emission_hook(Game, "game-start", self.on_game_start)
GObject.add_emission_hook(Game, "game-stop", self.on_game_stop) GObject.add_emission_hook(Game, "game-stopped", self.on_game_stopped)
GObject.add_emission_hook(Game, "game-install", self.on_game_install) GObject.add_emission_hook(Game, "game-install", self.on_game_install)
GObject.add_emission_hook(Game, "game-install-update", self.on_game_install_update) GObject.add_emission_hook(Game, "game-install-update", self.on_game_install_update)
GObject.add_emission_hook(Game, "game-install-dlc", self.on_game_install_dlc) GObject.add_emission_hook(Game, "game-install-dlc", self.on_game_install_dlc)
@ -93,7 +93,7 @@ class Application(Gtk.Application):
self.launch_ui_delegate = LaunchUIDelegate() self.launch_ui_delegate = LaunchUIDelegate()
self.install_ui_delegate = InstallUIDelegate() self.install_ui_delegate = InstallUIDelegate()
self.running_games = [] self._running_games = []
self.app_windows = {} self.app_windows = {}
self.tray = None self.tray = None
@ -784,30 +784,29 @@ class Application(Gtk.Application):
return True return True
def on_game_start(self, game): def on_game_start(self, game):
self.running_games.append(game) self._running_games.append(game)
if settings.read_setting("hide_client_on_game_start") == "True": if settings.read_setting("hide_client_on_game_start") == "True":
self.window.hide() # Hide launcher window self.window.hide() # Hide launcher window
return True return True
def on_game_stop(self, game): def on_game_stopped(self, game):
"""Callback to remove the game from the running games""" """Callback to quit Lutris is last game stops while the window is hidden."""
ids = self.get_running_game_ids() running_game_ids = [g.id for g in self._running_games]
if game.id in ids: if game.id in running_game_ids:
logger.debug("Removing %s from running IDs", game.id) logger.debug("Removing %s from running IDs", game.id)
try: try:
del self.running_games[ids.index(game.id)] del self._running_games[running_game_ids.index(game.id)]
except ValueError: except ValueError:
pass pass
elif ids: elif running_game_ids:
logger.warning("%s not in %s", game.id, ids) logger.warning("%s not in %s", game.id, running_game_ids)
else: else:
logger.debug("Game has already been removed from running IDs?") logger.debug("Game has already been removed from running IDs?")
game.emit("game-stopped") if settings.read_bool_setting("hide_client_on_game_start") and not self.quit_on_game_exit:
if settings.read_setting("hide_client_on_game_start") == "True" and not self.quit_on_game_exit:
self.window.show() # Show launcher window self.window.show() # Show launcher window
elif not self.window.is_visible(): elif not self.window.is_visible():
if not self.running_games: if not self.has_running_games:
if self.quit_on_game_exit or not self.has_tray_icon(): if self.quit_on_game_exit or not self.has_tray_icon():
self.quit() self.quit()
return True return True
@ -880,22 +879,27 @@ class Application(Gtk.Application):
def get_launch_ui_delegate(self): def get_launch_ui_delegate(self):
return self.launch_ui_delegate return self.launch_ui_delegate
def get_running_games(self) -> List[str]:
# This method reflects games that have stopped even if the 'game-stopped' signal
# has not been handled yet; that handler will still clean up the list though.
return [g for g in self._running_games if g.state != g.STATE_STOPPED]
@property
def has_running_games(self):
return bool(self.get_running_games())
def get_running_game_ids(self) -> List[str]: def get_running_game_ids(self) -> List[str]:
"""Returns the ids of the games presently running.""" """Returns the ids of the games presently running."""
return [game.id for game in self.running_games] return [game.id for game in self.get_running_games()]
def is_game_running_by_id(self, game_id: str) -> bool: def is_game_running_by_id(self, game_id: str) -> bool:
"""True if the ID is the ID of a game that is running.""" """True if the ID is the ID of a game that is running."""
if game_id: return game_id and str(game_id) in self.get_running_game_ids()
for game in self.running_games:
if game.id == str(game_id):
return True
return False
def get_game_by_id(self, game_id: str) -> Game: def get_game_by_id(self, game_id: str) -> Game:
"""Returns the game with the ID given; if it's running this is the running """Returns the game with the ID given; if it's running this is the running
game instance, and if not it's a fresh copy off the database.""" game instance, and if not it's a fresh copy off the database."""
for game in self.running_games: for game in self.get_running_games():
if game.id == str(game_id): if game.id == str(game_id):
return game return game

View file

@ -871,7 +871,7 @@ class LutrisWindow(Gtk.ApplicationWindow,
def on_window_delete(self, *_args): def on_window_delete(self, *_args):
app = self.application app = self.application
if app.running_games: if app.has_running_games:
self.hide() self.hide()
return True return True
if app.has_tray_icon(): if app.has_tray_icon():

View file

@ -358,7 +358,7 @@ class LutrisSidebar(Gtk.ListBox):
GObject.add_emission_hook(ScriptInterpreter, "runners-installed", self.update_rows) GObject.add_emission_hook(ScriptInterpreter, "runners-installed", self.update_rows)
GObject.add_emission_hook(ServicesBox, "services-changed", self.update_rows) GObject.add_emission_hook(ServicesBox, "services-changed", self.update_rows)
GObject.add_emission_hook(Game, "game-start", self.on_game_start) GObject.add_emission_hook(Game, "game-start", self.on_game_start)
GObject.add_emission_hook(Game, "game-stop", self.on_game_stop) GObject.add_emission_hook(Game, "game-stopped", self.on_game_stopped)
GObject.add_emission_hook(Game, "game-updated", self.update_rows) GObject.add_emission_hook(Game, "game-updated", self.update_rows)
GObject.add_emission_hook(BaseService, "service-login", self.on_service_auth_changed) GObject.add_emission_hook(BaseService, "service-login", self.on_service_auth_changed)
GObject.add_emission_hook(BaseService, "service-logout", self.on_service_auth_changed) GObject.add_emission_hook(BaseService, "service-logout", self.on_service_auth_changed)
@ -593,9 +593,9 @@ class LutrisSidebar(Gtk.ListBox):
self.running_row.show() self.running_row.show()
return True return True
def on_game_stop(self, _game): def on_game_stopped(self, _game):
"""Hide the "running" section when no games are running""" """Hide the "running" section when no games are running"""
if not self.application.running_games: if not self.application.has_running_games:
self.running_row.hide() self.running_row.hide()
if self.get_selected_row() == self.running_row: if self.get_selected_row() == self.running_row: