fix(ibus): sync XWayland layout from GNOME after scoped inject (#742)
Super+Space during recording on GNOME Wayland updates Mutter and IBus but can leave XWayland on the previous map. Scoped inject restores the IBus engine and skips setxkbmap (#474), so X apps keep the old layout. After engine restore, copy GNOME's current XKB source onto XWayland when DISPLAY is set. Do not replay an empty capture and do not call restore_xkb_layout for this. Sessions with no X display are left alone.
This commit is contained in:
@@ -1056,6 +1056,101 @@ class TestIBusTextInjector(unittest.TestCase):
|
||||
self.assertTrue(injector.inject_text("hello"))
|
||||
mock_restore_xkb.assert_not_called()
|
||||
|
||||
@patch("vocalinux.text_injection.ibus_engine.sync_xwayland_layout_from_gnome")
|
||||
@patch("vocalinux.text_injection.ibus_engine.restore_xkb_layout")
|
||||
@patch(
|
||||
"vocalinux.text_injection.ibus_engine.get_current_xkb_layout",
|
||||
return_value=("", "", ""),
|
||||
)
|
||||
@patch("vocalinux.text_injection.ibus_engine.get_current_engine", return_value="xkb:us::eng")
|
||||
@patch("vocalinux.text_injection.ibus_engine.is_engine_active", return_value=False)
|
||||
@patch("vocalinux.text_injection.ibus_engine.switch_engine", return_value=True)
|
||||
@patch("socket.socket")
|
||||
@patch("vocalinux.text_injection.ibus_engine.SOCKET_PATH")
|
||||
@patch("vocalinux.text_injection.ibus_engine.IBUS_AVAILABLE", True)
|
||||
@patch("vocalinux.text_injection.ibus_engine.ensure_ibus_dir")
|
||||
def test_inject_text_syncs_xwayland_from_gnome_when_layout_empty(
|
||||
self,
|
||||
mock_ensure_dir,
|
||||
mock_socket_path,
|
||||
mock_socket_cls,
|
||||
mock_switch,
|
||||
mock_is_active,
|
||||
mock_get_current,
|
||||
mock_get_xkb,
|
||||
mock_restore_xkb,
|
||||
mock_sync,
|
||||
):
|
||||
"""Wayland/empty capture syncs XWayland from GNOME after engine restore (#738)."""
|
||||
from vocalinux.text_injection.ibus_engine import ENGINE_NAME, IBusTextInjector
|
||||
|
||||
mock_socket_path.exists.return_value = True
|
||||
mock_sock = MagicMock()
|
||||
mock_sock.__enter__.return_value = mock_sock
|
||||
mock_sock.__exit__.return_value = None
|
||||
mock_sock.recv.return_value = b"OK"
|
||||
mock_socket_cls.return_value = mock_sock
|
||||
|
||||
parent = MagicMock()
|
||||
parent.attach_mock(mock_get_xkb, "get_xkb")
|
||||
parent.attach_mock(mock_switch, "switch")
|
||||
parent.attach_mock(mock_restore_xkb, "restore_xkb")
|
||||
parent.attach_mock(mock_sync, "sync")
|
||||
|
||||
injector = IBusTextInjector(auto_activate=False)
|
||||
self.assertTrue(injector.inject_text("hello"))
|
||||
|
||||
mock_restore_xkb.assert_not_called()
|
||||
mock_sync.assert_called_once_with()
|
||||
self.assertEqual(
|
||||
[call.args[0] for call in mock_switch.call_args_list],
|
||||
[ENGINE_NAME, "xkb:us::eng"],
|
||||
)
|
||||
self.assertEqual(
|
||||
[c[0] for c in parent.mock_calls],
|
||||
["get_xkb", "switch", "switch", "sync"],
|
||||
)
|
||||
|
||||
@patch("vocalinux.text_injection.ibus_engine.sync_xwayland_layout_from_gnome")
|
||||
@patch("vocalinux.text_injection.ibus_engine.restore_xkb_layout")
|
||||
@patch(
|
||||
"vocalinux.text_injection.ibus_engine.get_current_xkb_layout",
|
||||
return_value=("br", "", ""),
|
||||
)
|
||||
@patch("vocalinux.text_injection.ibus_engine.get_current_engine", return_value="xkb:br::por")
|
||||
@patch("vocalinux.text_injection.ibus_engine.is_engine_active", return_value=False)
|
||||
@patch("vocalinux.text_injection.ibus_engine.switch_engine", return_value=True)
|
||||
@patch("socket.socket")
|
||||
@patch("vocalinux.text_injection.ibus_engine.SOCKET_PATH")
|
||||
@patch("vocalinux.text_injection.ibus_engine.IBUS_AVAILABLE", True)
|
||||
@patch("vocalinux.text_injection.ibus_engine.ensure_ibus_dir")
|
||||
def test_inject_text_does_not_sync_xwayland_when_xkb_was_captured(
|
||||
self,
|
||||
mock_ensure_dir,
|
||||
mock_socket_path,
|
||||
mock_socket_cls,
|
||||
mock_switch,
|
||||
mock_is_active,
|
||||
mock_get_current,
|
||||
mock_get_xkb,
|
||||
mock_restore_xkb,
|
||||
mock_sync,
|
||||
):
|
||||
"""X11 captured layouts still go through restore_xkb_layout, not the Wayland sync."""
|
||||
from vocalinux.text_injection.ibus_engine import IBusTextInjector
|
||||
|
||||
mock_socket_path.exists.return_value = True
|
||||
mock_sock = MagicMock()
|
||||
mock_sock.__enter__.return_value = mock_sock
|
||||
mock_sock.__exit__.return_value = None
|
||||
mock_sock.recv.return_value = b"OK"
|
||||
mock_socket_cls.return_value = mock_sock
|
||||
|
||||
injector = IBusTextInjector(auto_activate=False)
|
||||
self.assertTrue(injector.inject_text("ola"))
|
||||
mock_restore_xkb.assert_called_once_with("br", "", "")
|
||||
mock_sync.assert_not_called()
|
||||
|
||||
@patch("vocalinux.text_injection.ibus_engine.get_current_engine", return_value="xkb:de::deu")
|
||||
@patch("vocalinux.text_injection.ibus_engine.is_engine_active", return_value=False)
|
||||
@patch("vocalinux.text_injection.ibus_engine.switch_engine", return_value=True)
|
||||
|
||||
Reference in New Issue
Block a user