diff --git a/dzgui/app_init.py b/dzgui/app_init.py index 45df2a7..1a942ab 100644 --- a/dzgui/app_init.py +++ b/dzgui/app_init.py @@ -121,7 +121,7 @@ def load_steam_icon() -> None: logger.critical(e) -def load_gui(version: str, is_debug: bool) -> None: +def build_gui(version: str, is_debug: bool) -> App | None: lock = lock_acquire() # noqa set_locale() @@ -151,7 +151,7 @@ def load_gui(version: str, is_debug: bool) -> None: wizard = SetupWizard(_is_steam_deck, XDG, xdg_paths) if not wizard.is_setup_complete(): - return + return None setup_logger(XDG.debug) with open(XDG.debug, "w") as f: @@ -186,4 +186,4 @@ def load_gui(version: str, is_debug: bool) -> None: print(boot.all_ok) load_steam_icon() app = App(prefs) - app.run(None) + return app diff --git a/dzgui/main.py b/dzgui/main.py index 68ed0b2..5e660cf 100644 --- a/dzgui/main.py +++ b/dzgui/main.py @@ -42,6 +42,9 @@ def main() -> None: test_libgi_missing() # NOTE: only import ligbirepository modules after the above check - from dzgui.app_init import load_gui + from dzgui.app_init import build_gui - load_gui(version, args.debug) + app = build_gui(version, args.debug) + if app is None: + sys.exit(0) + app.run(None) diff --git a/dzgui/util/save_screenshot.py b/dzgui/util/save_screenshot.py new file mode 100644 index 0000000..8c106bc --- /dev/null +++ b/dzgui/util/save_screenshot.py @@ -0,0 +1,80 @@ +from pathlib import Path +from typing import cast, TYPE_CHECKING + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, GLib # noqa E402 + +if TYPE_CHECKING: + from dzgui.controllers.mc import Controller + from dzgui.views.base import OuterWindow + from dzgui.views.mixins.context_mixin import ContextMixin + from gi.repository import GdkPixbuf + +# TODO: randomize/mock PII distance values + + +class IdleScreenshot: + def __init__( + self, controller: "Controller", window: "OuterWindow", scrot_path: Path + ) -> None: + self.window = window + self.controller = controller + self.scrot_path = scrot_path + + clock = window.get_frame_clock() + if clock is None: + raise TypeError("Widget not realized, FrameClock not initialized") + clock.connect("after-paint", self.callback) + + def callback(self, clock: Gdk.FrameClock) -> None: + GLib.timeout_add(1000, self.check_idle) + + def check_idle(self) -> bool: + tv = self.controller.get_active_treeview() + loaded = tv.is_loaded() + if loaded is True: + self.emit_key_press(tv) + GLib.timeout_add(1000, self.save_and_quit) + return True + return False + + def emit_key_press(self, widget: "ContextMixin") -> None: + window = widget.get_parent_window() + if window is None: + raise TypeError(f"Failed to find a valid parent window for {widget}") + event = Gdk.EventKey.new(Gdk.EventType.KEY_PRESS) + # NOTE: GTK introspection returns Gdk.Event type + event_key = cast(Gdk.EventKey, event) + event_key.keyval = Gdk.KEY_l + event_key.state = Gdk.ModifierType.CONTROL_MASK + event_key.window = window + event_key.time = Gdk.CURRENT_TIME + event_key.send_event = True + # TODO: suppress errors from synthetic key events + widget.present_menu(widget, event) + + def save_and_quit(self) -> None: + self.save_pixbuf() + self.controller.save_res_and_quit() + + def save_pixbuf(self) -> None: + pixbuf = self.capture_application() + if pixbuf: + pixbuf.savev(str(self.scrot_path), "png", [], []) + + def capture_application(self) -> "GdkPixbuf.Pixbuf": + # NOTE: composited context menu over X11 window + root = Gdk.get_default_root_window() + + xwindow = self.window.get_window() + if xwindow is None: + raise ValueError("Failed to get a valid X11 window") + index, x, y = xwindow.get_origin() + width, height = self.window.get_size() + + application = Gdk.pixbuf_get_from_window(root, x, y, width, height) + if application is None: + raise ValueError("Failed to get a valid GdkPixbuf") + return application diff --git a/dzgui/views/base.py b/dzgui/views/base.py index cbbb3b5..1c3a83b 100644 --- a/dzgui/views/base.py +++ b/dzgui/views/base.py @@ -78,6 +78,15 @@ class OuterWindow(Gtk.ApplicationWindow): MainController.loaded = True MainController.populate_model(MainController.get_active_treeview()) + try: + from dzgui.util.save_screenshot import IdleScreenshot + + scrot = app.get_automation_param("screenshot_path") + self.fullscreen() + IdleScreenshot(MainController, self, scrot) + except KeyError: + pass + def _on_delete_event(self, window: "OuterWindow", event: Gdk.EventKey) -> None: self.halt_proc_and_quit() @@ -291,6 +300,14 @@ class App(ColorAwareApp, Gtk.Application): # type: ignore self._setup_signals() self.connect("activate", self._on_activate) + self.automation_params: dict[str, Any] = {} + + def get_automation_param(self, key: str) -> Any: + return self.automation_params[key] + + def set_automation_param(self, key: str, value: Any) -> None: + self.automation_params[key] = value + def _on_activate(self, app: Self) -> None: self.win = OuterWindow(app)