From 36a2866f87c0c9bfe96151717ae61db5fa7adab4 Mon Sep 17 00:00:00 2001 From: dynobo Date: Mon, 22 Apr 2024 15:41:30 +0200 Subject: [PATCH] feat: retrieve window information on KDE --- keyhint/context.py | 79 +++++++++++++++++++++++++++++++++++++++++++++- keyhint/window.py | 3 +- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/keyhint/context.py b/keyhint/context.py index 3fdc8c0..ce6a4b1 100644 --- a/keyhint/context.py +++ b/keyhint/context.py @@ -6,6 +6,9 @@ import re import shutil import subprocess +import tempfile +import textwrap +from datetime import datetime logger = logging.getLogger("keyhint") @@ -124,7 +127,7 @@ def has_window_calls_extension() -> bool: def get_active_window_via_window_calls() -> tuple[str, str]: - """Retrieve active window class and active window title on Wayland. + """Retrieve active window class and active window title on Gnome + Wayland. Inspired by https://gist.github.com/rbreaves/257c3edfa301786e66e964d7ac036269 @@ -165,6 +168,80 @@ def _get_cmd_result(cmd: str) -> str: return wm_class, title +def get_active_window_via_kwin() -> tuple[str, str]: + """Retrieve active window class and active window title on KDE + Wayland. + + Returns: + Tuple(str, str): window class, window title + """ + kwin_script = textwrap.dedent(""" + client = workspace.activeClient; + title = client.caption; + wm_class = client.resourceClass; + console.info(`keyhint_out: wm_class=${wm_class}, window_title=${title}`); + """) + logger.debug("kwin_script: %s", kwin_script) + + with tempfile.NamedTemporaryFile(suffix=".js") as fh: + fh.write(kwin_script.encode()) + cmd_load = ( + "gdbus call --session --dest org.kde.KWin " + "--object-path /Scripting " + f"--method org.kde.KWin.Scripting.loadScript 'file://{fh.name}'" + ) + logger.debug("cmd_load: %s", cmd_load) + stdout = subprocess.check_output(cmd_load, shell=True).decode() # noqa: S602 + + script_id = stdout[1:-1].split(", ", 1)[0] + logger.debug("script_id: %s", script_id) + + since = str(datetime.now()) + + cmd_run = ( + "gdbus call --session --dest org.kde.KWin " + "--object-path /Scripting " + f"--method org.kde.KWin.Scripting.run {script_id}" + ) + logger.debug("cmd_run: %s", cmd_run) + subprocess.check_output(cmd_run, shell=True) # noqa: S602 + + cmd_stop = ( + "gdbus call --session --dest org.kde.KWin " + "--object-path /Scripting " + f"--method org.kde.KWin.Scripting.stop {script_id}" + ) + logger.debug("cmd_stop: %s", cmd_stop) + subprocess.check_output(cmd_stop, shell=True) # noqa: S602 + + cmd_unload = ( + "gdbus call --session --dest org.kde.KWin " + "--object-path /Scripting " + f"--method org.kde.KWin.Scripting.unload {script_id}" + ) + logger.debug("cmd_unload: %s", cmd_unload) + subprocess.check_output(cmd_unload, shell=True) # noqa: S602 + + log_messages = ( + subprocess.check_output( + f'journalctl --user -o cat --since "{since}"', + shell=True, # noqa: S602 + ) + .decode() + .split("\n") + ) + logger.debug("log_messages: %s", log_messages) + result_message = next(m for m in log_messages if "keyhint_out" in m) + match = re.match(r"keyhint_out: wm_class=(.+), window_title=(.+)", result_message) + if match: + wm_class = match.group(1) + title = match.group(2) + else: + logger.warning("Could not extract window info from KWin log") + wm_class = title = "" + + return wm_class, title + + def get_active_window_via_xprop() -> tuple[str, str]: """Retrieve active window class and active window title on Xorg desktops. diff --git a/keyhint/window.py b/keyhint/window.py index d7d2f42..2ea3bc3 100644 --- a/keyhint/window.py +++ b/keyhint/window.py @@ -133,7 +133,8 @@ def init_last_active_window_info(self) -> tuple[str, str]: else: self.banner_window_calls.set_revealed(True) logger.error("Window Calls extension not found!") - + case _, "kde": + wm_class, wm_title = context.get_active_window_via_kwin() case False, _: if context.has_xprop(): wm_class, wm_title = context.get_active_window_via_xprop()