diff --git a/keyhint/app.py b/keyhint/app.py
index 41d04d9..80030f9 100644
--- a/keyhint/app.py
+++ b/keyhint/app.py
@@ -35,7 +35,7 @@ class Application(Adw.Application):
def __init__(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003
"""Initialize application with command line options."""
kwargs.update(
- application_id="eu.dynobo.keyhint",
+ application_id="com.github.dynobo.keyhint",
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
)
super().__init__(
diff --git a/keyhint/context.py b/keyhint/context.py
index 3fdc8c0..5ad6e28 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")
@@ -63,16 +66,16 @@ def get_kde_version() -> str:
Returns:
Version string or '(n/a)'.
"""
- if not shutil.which("plasma-desktop"):
+ if not shutil.which("plasmashell"):
return "(n/a)"
try:
output = subprocess.check_output(
- ["plasma-desktop", "--version"], # noqa: S607
+ ["plasmashell", "--version"], # noqa: S607
shell=False, # noqa: S603
text=True,
)
- if result := re.search(r"Platform:\s+([\d+\.]+)", output.strip()):
+ if result := re.search(r"([\d+\.]+)", output.strip()):
kde_version = result.groups()[0]
except Exception as e:
logger.warning("Exception when trying to get kde version from cli %s", e)
@@ -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,74 @@ 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("""
+ console.info("keyhint test");
+ client = workspace.activeClient;
+ title = client.caption;
+ wm_class = client.resourceClass;
+ console.info(`keyhint_out: wm_class=${wm_class}, window_title=${title}`);
+ """)
+
+ with tempfile.NamedTemporaryFile(suffix=".js", delete=False) 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 '{fh.name}'"
+ )
+ logger.debug("cmd_load: %s", cmd_load)
+ stdout = subprocess.check_output(cmd_load, shell=True).decode() # noqa: S602
+
+ logger.debug("loadScript output: %s", stdout)
+ script_id = stdout.strip().strip("()").split(",")[0]
+
+ since = str(datetime.now())
+
+ cmd_run = (
+ "gdbus call --session --dest org.kde.KWin "
+ f"--object-path /{script_id} "
+ "--method org.kde.kwin.Script.run"
+ )
+ subprocess.check_output(cmd_run, shell=True) # noqa: S602
+
+ cmd_unload = (
+ "gdbus call --session --dest org.kde.KWin "
+ "--object-path /Scripting "
+ f"--method org.kde.kwin.Scripting.unloadScript {script_id}"
+ )
+ subprocess.check_output(cmd_unload, shell=True) # noqa: S602
+
+ # Unfortunately, we can read script output from stdout, because of a KDE bug:
+ # https://bugs.kde.org/show_bug.cgi?id=445058
+ # The output has to be read through journalctl instead. A timestamp for
+ # filtering speeds up the process.
+ log_lines = (
+ subprocess.check_output(
+ f'journalctl --user -o cat --since "{since}"',
+ shell=True, # noqa: S602
+ )
+ .decode()
+ .split("\n")
+ )
+ logger.debug("Journal message: %s", log_lines)
+ result_line = [m for m in log_lines if "keyhint_out" in m][-1]
+ match = re.search(r"keyhint_out: wm_class=(.+), window_title=(.+)", result_line)
+ 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/resources/headerbar.ui b/keyhint/resources/headerbar.ui
index 757ed7c..86d1bb6 100644
--- a/keyhint/resources/headerbar.ui
+++ b/keyhint/resources/headerbar.ui
@@ -168,7 +168,7 @@
diff --git a/keyhint/window.py b/keyhint/window.py
index d7d2f42..e496c2d 100644
--- a/keyhint/window.py
+++ b/keyhint/window.py
@@ -86,7 +86,6 @@ def __init__(self, cli_args: dict) -> None:
display=self.get_display(), css_file=RESOURCE_PATH / "style.css"
)
self.zoom_css_provider = css.new_provider(display=self.get_display())
- self.set_icon_name("keyhint")
self.set_titlebar(self.headerbars.normal)
self.container.prepend(self.headerbars.fullscreen)
@@ -133,7 +132,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 True, "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()
@@ -591,7 +591,7 @@ def on_create_new_sheet(self, _: Gio.SimpleAction, __: None) -> None:
pad = 26 - len(title)
template = f"""\
id = "{title}"{" " * pad } # Unique ID, used e.g. in cheatsheet dropdown
- url = "" # (Optional) URL to keybinding docs
+ url = "" # (Optional) URL to keybinding docs
[match]
regex_wmclass = "{self.wm_class}"
@@ -834,6 +834,5 @@ def get_debug_info_text(self) -> str:
Wayland: {context.is_using_wayland()}
Python: {platform.python_version()}
Keyhint: v{__version__}
- Flatpak: {context.is_flatpak_package()}\
- """
+ Flatpak: {context.is_flatpak_package()}"""
)