Merge pull request #36 from yusufipk/generic-desktop-shortcuts

Stop telling every desktop that it is KDE
This commit is contained in:
Yusuf İpek
2026-08-16 13:43:38 +03:00
committed by GitHub
13 changed files with 360 additions and 85 deletions
+73 -11
View File
@@ -174,38 +174,69 @@ class Bindings(DikteTest):
class Chooser(DikteTest):
"""Which desktop is asked to register the shortcut."""
"""Which mechanism the session gets, and everything keyed off that."""
def setUp(self):
super().setUp()
self.patch_attr(hotkey.sys, "platform", "linux")
self.addCleanup(hotkey._REGISTERED.clear)
@contextlib.contextmanager
def under(self, desktop, has_gsettings=True):
"""A session that says it is this desktop, with or without gsettings."""
def under(self, desktop, tools=True):
"""A session that says it is this desktop, with or without its tools."""
with mock.patch.dict(os.environ, {"XDG_CURRENT_DESKTOP": desktop}), \
mock.patch.object(hotkey.shutil, "which",
return_value="/usr/bin/gsettings"
if has_gsettings else None):
return_value="/usr/bin/tool" if tools else None):
yield
def test_gnome_when_the_session_says_so_and_gsettings_is_there(self):
with self.under("GNOME"):
self.assertEqual(hotkey.backend(), hotkey.GNOME)
self.assertEqual(hotkey.desktop_name(), "GNOME")
def test_kde_otherwise(self):
def test_kde_when_the_session_says_so_and_kwriteconfig_is_there(self):
with self.under("KDE"):
self.assertEqual(hotkey.backend(), hotkey.KDE)
self.assertEqual(hotkey.desktop_name(), "KDE")
def test_a_gnome_session_with_no_gsettings_falls_back(self):
"""Nothing to write the binding with, so KDE's file is the only try."""
with self.under("GNOME", has_gsettings=False):
self.assertEqual(hotkey.desktop_name(), "KDE")
def test_a_desktop_with_no_registry_is_the_listeners(self):
"""The bug this replaced: i3 was told KDE, and KWin was not running."""
for desktop in ("i3", "XFCE", "X-Cinnamon", "sway", "MATE", ""):
with self.subTest(desktop=desktop), self.under(desktop):
self.assertEqual(hotkey.backend(), hotkey.LISTENER)
def test_the_desktop_that_has_no_registry_is_called_by_its_own_name(self):
with self.under("i3"):
self.assertEqual(hotkey.desktop_name(), "i3")
with self.under("XFCE:GNOME-Flashback", tools=False):
self.assertEqual(hotkey.desktop_name(), "XFCE")
with self.under(""):
self.assertEqual(hotkey.desktop_name(), "This desktop")
def test_a_gnome_session_with_no_gsettings_falls_back_to_the_listener(self):
"""Nothing to write the binding with, and KDE's file is not an answer:
KWin is no more running here than it is on i3."""
with self.under("GNOME", tools=False):
self.assertEqual(hotkey.backend(), hotkey.LISTENER)
def test_the_desktop_is_matched_loosely(self):
for desktop in ("GNOME", "ubuntu:GNOME", "gnome"):
with self.subTest(desktop=desktop), self.under(desktop):
self.assertEqual(hotkey.desktop_name(), "GNOME")
self.assertEqual(hotkey.backend(), hotkey.GNOME)
for desktop in ("KDE", "KDE:plasma", "plasma"):
with self.subTest(desktop=desktop), self.under(desktop):
self.assertEqual(hotkey.backend(), hotkey.KDE)
def test_only_a_registry_is_installed_into_and_only_kwin_waits(self):
with self.under("KDE"):
self.assertTrue(hotkey.installs_shortcuts())
self.assertTrue(hotkey.shortcut_needs_restart())
with self.under("GNOME"):
self.assertTrue(hotkey.installs_shortcuts())
self.assertFalse(hotkey.shortcut_needs_restart())
with self.under("i3"):
self.assertFalse(hotkey.installs_shortcuts())
self.assertFalse(hotkey.shortcut_needs_restart())
def test_installing_goes_to_whichever_it_is(self):
with self.under("GNOME"), \
@@ -220,6 +251,20 @@ class Chooser(DikteTest):
hotkey.install_shortcut("Ctrl+Space", "dikte toggle")
kde.assert_called_once()
def test_a_desktop_with_no_registry_installs_nothing_anywhere(self):
with self.under("i3"), \
mock.patch.object(hotkey, "install_kde_shortcut") as kde, \
mock.patch.object(hotkey, "install_gnome_shortcut") as gnome:
ok, message = hotkey.install_shortcut("Ctrl+Space", "dikte toggle")
self.assertEqual(hotkey.shortcut_status(), "Ctrl+Space")
hotkey.remove_shortcut()
self.assertIsNone(hotkey.shortcut_status())
kde.assert_not_called()
gnome.assert_not_called()
self.assertTrue(ok)
self.assertIn("i3", message)
self.assertNotIn("log out", message)
def test_removing_and_reading_back_go_to_the_same_one(self):
with self.under("GNOME"), \
mock.patch.object(hotkey, "remove_gnome_shortcut") as remove, \
@@ -230,6 +275,17 @@ class Chooser(DikteTest):
remove.assert_called_once()
status.assert_called_once()
def test_only_kde_has_a_list_of_conflicts_to_read(self):
"""A leftover kglobalshortcutsrc from a Plasma the user has since left
would otherwise refuse combinations nothing is holding."""
rc = self.path("kglobalshortcutsrc")
rc.write_text(SHORTCUTS_RC, encoding="utf-8")
self.patch_attr(hotkey, "SHORTCUTS_FILE", rc)
with self.under("KDE"):
self.assertTrue(hotkey.conflicting_shortcuts("Meta+W"))
with self.under("i3"):
self.assertEqual(hotkey.conflicting_shortcuts("Meta+W"), [])
@linux_only
class GnomeAccelerator(DikteTest):
@@ -372,6 +428,12 @@ class KdeShortcut(DikteTest):
self.rc = self.path("kglobalshortcutsrc")
self.patch_attr(hotkey, "APPLICATIONS_DIR", self.apps)
self.patch_attr(hotkey, "SHORTCUTS_FILE", self.rc)
# A Plasma session with kwriteconfig6 on it, whatever the machine
# running the suite happens to be logged into.
session = mock.patch.dict(os.environ, {"XDG_CURRENT_DESKTOP": "KDE"})
session.start()
self.addCleanup(session.stop)
self.patch_attr(hotkey.shutil, "which", lambda _name: "/usr/bin/tool")
def test_installing_writes_a_desktop_file_kwin_will_launch(self):
with mock.patch.object(subprocess, "run", return_value=FakeCompleted()):
+66 -2
View File
@@ -6,6 +6,7 @@ save, so a setting added to one half and not the other is silently reset the
next time anybody presses Save. That is the failure this catches.
"""
import os
import sys
import unittest
from typing import ClassVar
@@ -107,13 +108,21 @@ class Settings(DikteTest):
# one. Everything else about the window is the same on both.
changed = CHANGED
platform = "linux"
# A session with no shortcut registry, which is what most Linux desktops
# are. The subclasses below stand on the other two. Pinned rather than
# inherited from whatever the machine running the suite is logged into,
# since half the shortcut tab is built from the answer.
desktop = "i3"
tools: ClassVar[tuple] = ()
def setUp(self):
super().setUp()
# No pactl, no model lists over the network, and no modal dialogue
# waiting for somebody to press OK.
self.enterContext(mock.patch.object(sys, "platform", self.platform))
self.enterContext(only_these_tools())
self.enterContext(only_these_tools(*self.tools))
self.enterContext(mock.patch.dict(
os.environ, {"XDG_CURRENT_DESKTOP": self.desktop}))
self.enterContext(mock.patch.object(QMessageBox, "information"))
self.enterContext(mock.patch.object(settings_ui.SettingsWindow,
"_load_models"))
@@ -254,6 +263,31 @@ class Settings(DikteTest):
window = self.window(cfg.Config())
self.assertEqual(set(window._shortcut_rows), set(hotkey.SHORTCUTS))
def shortcut_tab_text(self, window):
"""Everything the shortcut tab says, as one string."""
return "\n".join(
widget.text() for widget in
window.findChildren(settings_ui.QLabel)
+ window.findChildren(settings_ui.QLineEdit)
+ window.findChildren(settings_ui.QPushButton)
)
def test_the_shortcut_tab_talks_about_this_session_and_no_other(self):
"""A desktop with no registry is told the truth: nothing is installed
anywhere, Dikte is listening, and here is the command to bind if the
desktop should own the keys instead. It used to be promised a KWin that
was not running."""
window = self.window(cfg.Config())
text = self.shortcut_tab_text(window)
self.assertIn("i3 keeps no shortcut registry", text)
self.assertNotIn("KWin", text)
self.assertIn("dikte.py toggle", text)
# Not a choice to offer where it is the only mechanism there is.
self.assertTrue(window.evdev_enabled.isHidden())
self.assertFalse([button for button in
window.findChildren(settings_ui.QPushButton)
if "install" in button.text().lower()])
def test_emptying_a_shortcut_turns_it_off_but_not_the_toggle(self):
"""The application is unusable without the toggle, so that one box
falls back. The rest stay empty, which is how they are switched off.
@@ -415,7 +449,16 @@ class MacSettings(Settings):
def test_the_listener_is_not_offered_as_a_choice(self):
"""It is the whole mechanism there; turning it off would leave nothing."""
window = self.window(cfg.Config())
self.assertFalse(window.evdev_enabled.isVisible())
self.assertTrue(window.evdev_enabled.isHidden())
def test_the_shortcut_tab_talks_about_this_session_and_no_other(self):
"""Carbon holds the keys here, so there is no command to bind and no
/dev/input to be let into."""
window = self.window(cfg.Config())
text = self.shortcut_tab_text(window)
self.assertIn("Dikte asks macOS for these combinations", text)
self.assertNotIn("KWin", text)
self.assertNotIn("dikte.py toggle", text)
def test_the_paste_keys_on_offer_are_the_ones_a_mac_uses(self):
window = self.window(cfg.Config())
@@ -424,6 +467,27 @@ class MacSettings(Settings):
self.assertEqual(offered, paste.MACOS.shortcuts)
class KdeSettings(Settings):
"""The same window on the one desktop that keeps a registry and makes you
wait for it. Nothing here is about KDE: it is the rest of the window,
checked on the platform where Install, Remove and the listener's own
checkbox are all on screen."""
desktop = "KDE"
tools: ClassVar[tuple] = ("kwriteconfig6",)
def test_the_shortcut_tab_talks_about_this_session_and_no_other(self):
window = self.window(cfg.Config())
text = self.shortcut_tab_text(window)
self.assertIn("KWin only reads shortcut settings at startup", text)
self.assertIn("Install as a KDE shortcut", text)
self.assertNotIn("keeps no shortcut registry", text)
self.assertNotIn("dikte.py toggle", text)
# Here it is a choice: the wait for the next login, or the key press
# reaching the focused application as well.
self.assertFalse(window.evdev_enabled.isHidden())
class Overlay(DikteTest):
def overlay(self, **kwargs):
widget = overlay_module.Overlay(**kwargs)