* Avoid exception if trying to delete missing "removed" key `.pop(key)` throws an exception if that `key` is missing. Using a default value (in this case None) means we don't throw any errors when deleting a missing key. Which is what we want here, for safety. We just want to delete the key. We don't care if it's existing or not. There are other places in the codebase that also use `.pop(key)`, but all of those first check the validity of the key before popping, so this was the only one that needed fixing. * Improve performance by removing keys() calls Every `.keys()` call create a new `[list...]` of all keys from the given dictionary. It's a total waste of performance, since we can already check if a key exists in a dictionary by just using `if "key" in the_dict`. * Use more pythonic "not in" syntax The syntax is supposed to be `if "thing" not in other_thing`. We already use this proper `not in` syntax everywhere else in the codebase. Just fixing this location.
180 lines
6.2 KiB
Python
180 lines
6.2 KiB
Python
# game.py
|
|
#
|
|
# Copyright 2022-2023 kramo
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import json
|
|
import os
|
|
import shlex # pylint: disable=unused-import
|
|
import subprocess
|
|
import sys
|
|
|
|
from gi.repository import GdkPixbuf, Gio, Gtk
|
|
|
|
from .save_game import save_game
|
|
|
|
|
|
@Gtk.Template(resource_path="/hu/kramo/Cartridges/gtk/game.ui")
|
|
class game(Gtk.Box): # pylint: disable=invalid-name
|
|
__gtype_name__ = "game"
|
|
|
|
overlay = Gtk.Template.Child()
|
|
title = Gtk.Template.Child()
|
|
button_play = Gtk.Template.Child()
|
|
cover = Gtk.Template.Child()
|
|
cover_button = Gtk.Template.Child()
|
|
menu_button = Gtk.Template.Child()
|
|
play_revealer = Gtk.Template.Child()
|
|
title_revealer = Gtk.Template.Child()
|
|
game_options = Gtk.Template.Child()
|
|
hidden_game_options = Gtk.Template.Child()
|
|
|
|
def __init__(self, parent_widget, data, **kwargs):
|
|
super().__init__(**kwargs)
|
|
|
|
self.parent_widget = parent_widget
|
|
self.added = data["added"]
|
|
self.executable = data["executable"]
|
|
self.game_id = data["game_id"]
|
|
self.hidden = data["hidden"]
|
|
self.last_played = data["last_played"]
|
|
self.name = data["name"]
|
|
self.developer = data["developer"] if "developer" in data else None
|
|
self.removed = "removed" in data
|
|
self.blacklisted = "blacklisted" in data
|
|
|
|
self.pixbuf = self.get_cover()
|
|
|
|
self.cover.set_pixbuf(self.pixbuf)
|
|
self.title.set_label(self.name)
|
|
|
|
self.event_contoller_motion = Gtk.EventControllerMotion.new()
|
|
self.add_controller(self.event_contoller_motion)
|
|
self.overlay.set_measure_overlay(self.play_revealer, True)
|
|
|
|
self.set_play_label()
|
|
|
|
self.cover_button.connect("clicked", self.cover_button_clicked)
|
|
self.button_play.connect("clicked", self.button_play_clicked)
|
|
|
|
self.event_contoller_motion.connect("enter", self.show_play)
|
|
self.event_contoller_motion.connect("leave", self.hide_play)
|
|
|
|
self.parent_widget.schema.connect("changed", self.schema_changed)
|
|
|
|
if self.hidden:
|
|
self.menu_button.set_menu_model(self.hidden_game_options)
|
|
else:
|
|
self.menu_button.set_menu_model(self.game_options)
|
|
self.menu_button.get_popover().connect("notify::visible", self.hide_play)
|
|
|
|
def launch(self):
|
|
# Generate launch arguments, either list (no shell) or a string (for shell).
|
|
args = (
|
|
["flatpak-spawn", "--host", *self.executable] # Flatpak
|
|
if os.getenv("FLATPAK_ID") == "hu.kramo.Cartridges"
|
|
else shlex.join(
|
|
self.executable
|
|
) # Windows (We need shell to support its "open" built-in).
|
|
if os.name == "nt"
|
|
else self.executable # Linux/Others
|
|
)
|
|
|
|
# The host environment vars are automatically passed through by Popen.
|
|
subprocess.Popen(
|
|
args,
|
|
shell=isinstance(args, str),
|
|
start_new_session=True,
|
|
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == "nt" else 0,
|
|
)
|
|
if Gio.Settings.new("hu.kramo.Cartridges").get_boolean("exit-after-launch"):
|
|
sys.exit()
|
|
|
|
def toggle_hidden(self):
|
|
games_dir = os.path.join(
|
|
os.getenv("XDG_DATA_HOME")
|
|
or os.path.expanduser(os.path.join("~", ".local", "share")),
|
|
"cartridges",
|
|
"games",
|
|
)
|
|
|
|
if not os.path.exists(games_dir):
|
|
return
|
|
|
|
with open(os.path.join(games_dir, f"{self.game_id}.json"), "r") as open_file:
|
|
data = json.loads(open_file.read())
|
|
|
|
data["hidden"] = not data["hidden"]
|
|
|
|
save_game(data)
|
|
|
|
def get_cover(self):
|
|
|
|
# If the cover is already in memory, return
|
|
if self.game_id in self.parent_widget.pixbufs:
|
|
return self.parent_widget.pixbufs[self.game_id]
|
|
|
|
# Create a new pixbuf
|
|
cover_path = os.path.join(
|
|
os.getenv("XDG_DATA_HOME")
|
|
or os.path.expanduser(os.path.join("~", ".local", "share")),
|
|
"cartridges",
|
|
"covers",
|
|
f"{self.game_id}.tiff",
|
|
)
|
|
|
|
if os.path.isfile(cover_path):
|
|
return GdkPixbuf.Pixbuf.new_from_file(cover_path)
|
|
|
|
# Return the placeholder pixbuf
|
|
return self.parent_widget.placeholder_pixbuf
|
|
|
|
def show_play(self, _widget, *_unused):
|
|
self.play_revealer.set_reveal_child(True)
|
|
self.title_revealer.set_reveal_child(False)
|
|
|
|
def hide_play(self, _widget, *_unused):
|
|
if not self.menu_button.get_active():
|
|
self.play_revealer.set_reveal_child(False)
|
|
self.title_revealer.set_reveal_child(True)
|
|
|
|
def launch_game(self, _widget, *_unused):
|
|
self.parent_widget.set_active_game(None, None, self.game_id)
|
|
self.parent_widget.get_application().on_launch_game_action(None)
|
|
|
|
def cover_button_clicked(self, _widget):
|
|
if self.parent_widget.schema.get_boolean("cover-launches-game"):
|
|
self.launch_game(None)
|
|
else:
|
|
self.parent_widget.show_overview(None, self.game_id)
|
|
|
|
def button_play_clicked(self, _widget):
|
|
if self.parent_widget.schema.get_boolean("cover-launches-game"):
|
|
self.parent_widget.show_overview(None, self.game_id)
|
|
else:
|
|
self.launch_game(None)
|
|
|
|
def set_play_label(self):
|
|
if self.parent_widget.schema.get_boolean("cover-launches-game"):
|
|
self.button_play.set_label(_("Details"))
|
|
else:
|
|
self.button_play.set_label(_("Play"))
|
|
|
|
def schema_changed(self, _settings, key):
|
|
if key == "cover-launches-game":
|
|
self.set_play_label()
|