fix(text-injection): restore KDE Wayland IBus path (#502)
* fix(text-injection): restore bridged Wayland IBus path * fix(text-injection): keep GNOME xkb fallback
This commit is contained in:
@@ -242,21 +242,31 @@ class TextInjector:
|
||||
# Prefer IBus on both X11 and Wayland - it sends Unicode directly,
|
||||
# bypassing keyboard layout issues entirely
|
||||
if is_ibus_available():
|
||||
ibus_active = is_ibus_active_input_method()
|
||||
kde_wayland = (
|
||||
self.environment == DesktopEnvironment.WAYLAND and _is_kde_plasma_session()
|
||||
)
|
||||
gtk_im = os.environ.get("GTK_IM_MODULE", "").lower()
|
||||
qt_im = os.environ.get("QT_IM_MODULE", "").lower()
|
||||
xmodifiers = os.environ.get("XMODIFIERS", "").lower()
|
||||
explicit_non_ibus_im = (
|
||||
(gtk_im and "ibus" not in gtk_im)
|
||||
or (qt_im and "ibus" not in qt_im)
|
||||
or (xmodifiers and "@im=" in xmodifiers and "@im=ibus" not in xmodifiers)
|
||||
)
|
||||
kde_wayland_ibus = kde_wayland and not explicit_non_ibus_im
|
||||
|
||||
# Check if IBus is the active input method (not just installed)
|
||||
# This is important because IBus may be installed but not being used,
|
||||
# e.g., when the user has configured ydotool or Fcitx instead
|
||||
if not is_ibus_active_input_method():
|
||||
if self.environment == DesktopEnvironment.WAYLAND and _is_kde_plasma_session():
|
||||
logger.warning(
|
||||
"IBus is installed but is not active for KDE Plasma Wayland. "
|
||||
f"{_kde_wayland_ibus_hint()} Falling back to alternative "
|
||||
"text injection method."
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
"IBus is installed but not the active input method. "
|
||||
"Falling back to alternative text injection method."
|
||||
)
|
||||
# e.g., when the user has configured ydotool or Fcitx instead. KDE
|
||||
# Plasma Wayland can still route the Vocalinux engine through IBus
|
||||
# Wayland when its current engine is only an xkb:* layout; GNOME and
|
||||
# other desktops must keep the real-engine guard from issue #478.
|
||||
if not ibus_active and not kde_wayland_ibus:
|
||||
logger.info(
|
||||
"IBus is installed but not the active input method. "
|
||||
"Falling back to alternative text injection method."
|
||||
)
|
||||
# Check if ibus-daemon is running before attempting setup
|
||||
elif not is_ibus_daemon_running():
|
||||
logger.info(
|
||||
@@ -275,6 +285,11 @@ class TextInjector:
|
||||
)
|
||||
else:
|
||||
try:
|
||||
if kde_wayland_ibus and not ibus_active:
|
||||
logger.info(
|
||||
"KDE Plasma Wayland detected with ibus-daemon running; "
|
||||
"using IBus Wayland despite the inactive input-method heuristic"
|
||||
)
|
||||
self._ibus_injector = IBusTextInjector(auto_activate=False)
|
||||
ibus_requested = True
|
||||
except Exception as e:
|
||||
|
||||
+100
-27
@@ -150,39 +150,112 @@ class TestCheckDependencies(unittest.TestCase):
|
||||
with self.assertRaises(RuntimeError):
|
||||
obj._check_dependencies()
|
||||
|
||||
def test_kde_wayland_inactive_ibus_logs_virtual_keyboard_hint(self):
|
||||
def test_kde_wayland_uses_ibus_when_daemon_runs_with_xkb_engine(self):
|
||||
from vocalinux.text_injection.text_injector import DesktopEnvironment
|
||||
|
||||
obj = _make_injector(DesktopEnvironment.WAYLAND)
|
||||
mock_ibus = MagicMock()
|
||||
|
||||
with (
|
||||
patch.dict(
|
||||
os.environ,
|
||||
{"XDG_SESSION_TYPE": "wayland", "XDG_CURRENT_DESKTOP": "KDE"},
|
||||
clear=True,
|
||||
),
|
||||
patch(
|
||||
"vocalinux.text_injection.text_injector.is_ibus_available",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"vocalinux.text_injection.text_injector.is_ibus_active_input_method",
|
||||
return_value=False,
|
||||
),
|
||||
patch(
|
||||
"vocalinux.text_injection.text_injector.is_ibus_daemon_running",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"vocalinux.text_injection.text_injector.IBusTextInjector",
|
||||
return_value=mock_ibus,
|
||||
),
|
||||
patch.object(obj, "_start_ibus_initialization") as mock_start,
|
||||
patch(
|
||||
"shutil.which",
|
||||
side_effect=lambda cmd: "/usr/bin/wtype" if cmd == "wtype" else None,
|
||||
),
|
||||
):
|
||||
obj._check_dependencies()
|
||||
|
||||
self.assertIs(obj._ibus_injector, mock_ibus)
|
||||
mock_start.assert_called_once_with()
|
||||
self.assertEqual(obj.wayland_tool, "wtype")
|
||||
|
||||
def test_gnome_wayland_keeps_xkb_engine_fallback(self):
|
||||
from vocalinux.text_injection.text_injector import DesktopEnvironment
|
||||
|
||||
obj = _make_injector(DesktopEnvironment.WAYLAND)
|
||||
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{"XDG_SESSION_TYPE": "wayland", "XDG_CURRENT_DESKTOP": "KDE"},
|
||||
clear=True,
|
||||
):
|
||||
with patch(
|
||||
"vocalinux.text_injection.text_injector.is_ibus_available",
|
||||
with (
|
||||
patch.dict(
|
||||
os.environ,
|
||||
{"XDG_SESSION_TYPE": "wayland", "XDG_CURRENT_DESKTOP": "GNOME"},
|
||||
clear=True,
|
||||
),
|
||||
patch("vocalinux.text_injection.text_injector.is_ibus_available", return_value=True),
|
||||
patch(
|
||||
"vocalinux.text_injection.text_injector.is_ibus_active_input_method",
|
||||
return_value=False,
|
||||
),
|
||||
patch(
|
||||
"vocalinux.text_injection.text_injector.is_ibus_daemon_running",
|
||||
return_value=True,
|
||||
):
|
||||
with patch(
|
||||
"vocalinux.text_injection.text_injector.is_ibus_active_input_method",
|
||||
return_value=False,
|
||||
):
|
||||
with patch(
|
||||
"shutil.which",
|
||||
side_effect=lambda cmd: "/usr/bin/ydotool" if cmd == "ydotool" else None,
|
||||
):
|
||||
with patch("subprocess.run") as mock_run:
|
||||
mock_run.return_value = MagicMock(returncode=0, stderr="")
|
||||
with self.assertLogs(
|
||||
"vocalinux.text_injection.text_injector",
|
||||
level="WARNING",
|
||||
) as logs:
|
||||
obj._check_dependencies()
|
||||
),
|
||||
patch("vocalinux.text_injection.text_injector.IBusTextInjector") as mock_ibus_class,
|
||||
patch(
|
||||
"shutil.which",
|
||||
side_effect=lambda cmd: "/usr/bin/wtype" if cmd == "wtype" else None,
|
||||
),
|
||||
):
|
||||
obj._check_dependencies()
|
||||
|
||||
log_output = "\n".join(logs.output)
|
||||
self.assertIn("KDE Plasma Wayland", log_output)
|
||||
self.assertIn("Virtual Keyboard", log_output)
|
||||
mock_ibus_class.assert_not_called()
|
||||
self.assertEqual(obj.wayland_tool, "wtype")
|
||||
|
||||
def test_kde_wayland_respects_explicit_non_ibus_input_method(self):
|
||||
from vocalinux.text_injection.text_injector import DesktopEnvironment
|
||||
|
||||
obj = _make_injector(DesktopEnvironment.WAYLAND)
|
||||
|
||||
with (
|
||||
patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"XDG_SESSION_TYPE": "wayland",
|
||||
"XDG_CURRENT_DESKTOP": "KDE",
|
||||
"QT_IM_MODULE": "fcitx",
|
||||
},
|
||||
clear=True,
|
||||
),
|
||||
patch("vocalinux.text_injection.text_injector.is_ibus_available", return_value=True),
|
||||
patch(
|
||||
"vocalinux.text_injection.text_injector.is_ibus_active_input_method",
|
||||
return_value=False,
|
||||
),
|
||||
patch(
|
||||
"vocalinux.text_injection.text_injector.is_ibus_daemon_running",
|
||||
return_value=True,
|
||||
),
|
||||
patch("vocalinux.text_injection.text_injector.IBusTextInjector") as mock_ibus_class,
|
||||
patch(
|
||||
"shutil.which",
|
||||
side_effect=lambda cmd: "/usr/bin/ydotool" if cmd == "ydotool" else None,
|
||||
),
|
||||
patch.object(obj, "_is_ydotoold_running", return_value=True),
|
||||
):
|
||||
obj._check_dependencies()
|
||||
|
||||
mock_ibus_class.assert_not_called()
|
||||
self.assertEqual(obj.wayland_tool, "ydotool")
|
||||
|
||||
def test_kde_wayland_wtype_probe_logs_ibus_hint(self):
|
||||
from vocalinux.text_injection.text_injector import DesktopEnvironment, TextInjector
|
||||
|
||||
Reference in New Issue
Block a user