From e783fb14a02ef2fdf7c47656a2afc3ba9ed7315c Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 14 Jan 2025 13:49:33 +0700 Subject: [PATCH] #4449 use setting-change message for ibus layouts this prevents the hello packet from growing too large --- xpra/client/gtk3/tray_menu.py | 26 ++++++++++++++++---------- xpra/client/gui/keyboard_helper.py | 1 - xpra/client/gui/ui_client_base.py | 3 ++- xpra/server/mixins/input.py | 21 +++++++++++++++++++++ xpra/server/source/input.py | 14 ++++++++------ 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/xpra/client/gtk3/tray_menu.py b/xpra/client/gtk3/tray_menu.py index 7114fa476f..9b1914b6a5 100644 --- a/xpra/client/gtk3/tray_menu.py +++ b/xpra/client/gtk3/tray_menu.py @@ -1176,19 +1176,21 @@ def make_keyboardmenuitem(self) -> Gtk.Menu: def make_layoutsmenuitem(self) -> Gtk.ImageMenuItem: keyboard = self.menuitem("Layout", "keyboard.png", "Select your keyboard layout") set_sensitive(keyboard, False) - self.layout_submenu = Gtk.Menu() - keyboard.set_submenu(self.layout_submenu) self.keyboard_layout_item = keyboard self.after_handshake(self.populate_keyboard_layouts) return keyboard def populate_keyboard_layouts(self) -> None: + # by default, use keyboard helper values: + self.populate_keyboard_helper_layouts() set_sensitive(self.keyboard_layout_item, True) - kh = self.client.keyboard_helper - if kh.server_ibus and kh.layout and PREFER_IBUS_LAYOUTS: - self.populate_ibus_keyboard_layouts() - else: - self.populate_keyboard_helper_layouts() + + if PREFER_IBUS_LAYOUTS: + def got_ibus_layouts(setting: str, ibus_layouts) -> None: + kh = self.client.keyboard_helper + if ibus_layouts and kh.layout and PREFER_IBUS_LAYOUTS: + self.populate_ibus_keyboard_layouts(ibus_layouts) + self.client.on_server_setting_changed("ibus-layouts", got_ibus_layouts) def kbitem(self, title: str, layout: str, variant: str, backend="", name="", active=False) -> Gtk.CheckMenuItem: @@ -1225,14 +1227,16 @@ def set_layout(item) -> None: l.keyboard_variant = variant return l - def populate_ibus_keyboard_layouts(self) -> None: + def populate_ibus_keyboard_layouts(self, ibus_layouts: dict) -> None: + self.layout_submenu = Gtk.Menu() + self.keyboard_layout_item.set_submenu(self.layout_submenu) kh = self.client.keyboard_helper # use csv and back to get every single layout string: layouts = uniq((kh.layout_option or csv(kh.layouts)).split(",")) log(f"populate_ibus_keyboard_layouts() {layouts}") # find the "engines" matching the layouts: engines = {} - for i, engine in enumerate(kh.server_ibus.get("engines", ())): + for i, engine in enumerate(ibus_layouts.get("engines", ())): layout = engine.get("layout", "") if not layout or layout not in layouts: continue @@ -1242,11 +1246,13 @@ def populate_ibus_keyboard_layouts(self) -> None: engine = engines[rank] layout = engine.get("layout", "") name = engine.get("name", layout) - descr = engine.get("description", layout) + descr = engine.get("description", layout).split("\n")[0] variant = engine.get("variant", "") self.layout_submenu.append(self.kbitem(descr, layout, variant, "ibus", name)) def populate_keyboard_helper_layouts(self) -> None: + self.layout_submenu = Gtk.Menu() + self.keyboard_layout_item.set_submenu(self.layout_submenu) def disable(message: str) -> None: self.keyboard_layout_item.set_tooltip_text(message) diff --git a/xpra/client/gui/keyboard_helper.py b/xpra/client/gui/keyboard_helper.py index 7864b67838..5234d3256b 100644 --- a/xpra/client/gui/keyboard_helper.py +++ b/xpra/client/gui/keyboard_helper.py @@ -36,7 +36,6 @@ def __init__(self, net_send: Callable, keyboard_sync=True, self.shortcut_modifiers: list[str] = [] self.key_shortcuts_strs = key_shortcuts self.key_shortcuts: dict[str, list[str]] = {} - self.server_ibus = {} # command line overrides: self.raw = raw self.model_option = model diff --git a/xpra/client/gui/ui_client_base.py b/xpra/client/gui/ui_client_base.py index 527a45fb46..f9788de3e9 100644 --- a/xpra/client/gui/ui_client_base.py +++ b/xpra/client/gui/ui_client_base.py @@ -522,7 +522,6 @@ def process_ui_capabilities(self, caps: typedict) -> None: modifier_keycodes = caps.dictget("modifier_keycodes", {}) if modifier_keycodes: self.keyboard_helper.set_modifier_mappings(modifier_keycodes) - self.keyboard_helper.server_ibus = caps.dictget("ibus") self.key_repeat_delay, self.key_repeat_interval = caps.intpair("key_repeat", (-1, -1)) self.handshake_complete() @@ -599,6 +598,7 @@ def _process_setting_change(self, packet: PacketType) -> None: "start-new-commands", "client-shutdown", "webcam", "bandwidth-limit", "clipboard-limits", "xdg-menu", "monitors", + "ibus-layouts", ): setattr(self, "server_%s" % setting.replace("-", "_"), value) else: @@ -679,6 +679,7 @@ def get_keyboard_caps(self) -> dict[str, Any]: caps["keyboard"] = False else: caps["keyboard"] = True + caps["ibus"] = True caps["modifiers"] = self.get_current_modifiers() skip = ("keycodes", "x11_keycodes") if DELAY_KEYBOARD_DATA else () caps["keymap"] = kh.get_keymap_properties(skip) diff --git a/xpra/server/mixins/input.py b/xpra/server/mixins/input.py index ecd29c1c05..555b3428df 100644 --- a/xpra/server/mixins/input.py +++ b/xpra/server/mixins/input.py @@ -5,6 +5,7 @@ # later version. See the file COPYING for details. # pylint: disable-msg=E1101 +import threading from time import monotonic from typing import Any @@ -23,6 +24,8 @@ INPUT_SEQ_NO = envbool("XPRA_INPUT_SEQ_NO", False) +EXPOSE_IBUS_LAYOUTS = envbool("XPRA_EXPOSE_IBUS_LAYOUTS", True) + class InputServer(StubServerMixin): """ @@ -51,6 +54,14 @@ def __init__(self): self.key_repeat_timer = 0 self.last_mouse_user = None + self.ibus_layouts = {} + + def get_ibus_layouts(self): + if EXPOSE_IBUS_LAYOUTS and not self.ibus_layouts: + from xpra.keyboard.ibus import query_ibus + self.ibus_layouts = dict((k, v) for k, v in query_ibus().items() if k.startswith("engine")) + keylog.info("loaded ibus layouts from %s: %s", threading.current_thread(), self.ibus_layouts) + return self.ibus_layouts def init(self, opts) -> None: props = typedict() @@ -95,6 +106,14 @@ def parse_hello(self, ss, caps: typedict, send_ui: bool) -> None: if send_ui: self.parse_hello_ui_keyboard(ss, caps) + def send_initial_data(self, ss, caps, send_ui: bool, share_count: int) -> None: + if not send_ui: + return + ibus_layouts = self.get_ibus_layouts() + if ibus_layouts and hasattr(ss, "send_ibus_layouts"): + keylog("calling %s", ss.send_ibus_layouts) + ss.send_ibus_layouts(ibus_layouts) + def watch_keymap_changes(self) -> None: """ GTK servers will start listening for the 'keys-changed' signal """ @@ -130,6 +149,8 @@ def get_keyboard_info(self) -> dict[str, Any]: kc = self.keyboard_config if kc: info.update(kc.get_info()) + if EXPOSE_IBUS_LAYOUTS: + info["ibus"] = self.get_ibus_layouts() keylog("get_keyboard_info took %ims", (monotonic() - start) * 1000) return info diff --git a/xpra/server/source/input.py b/xpra/server/source/input.py index 9facb154ca..7112e414b2 100644 --- a/xpra/server/source/input.py +++ b/xpra/server/source/input.py @@ -6,7 +6,7 @@ from typing import Any -from xpra.util.env import envbool +from xpra.util.str_fn import Ellipsizer from xpra.server.source.stub_source_mixin import StubSourceMixin from xpra.keyboard.mask import DEFAULT_MODIFIER_MEANINGS from xpra.util.objects import typedict @@ -14,8 +14,6 @@ log = Logger("keyboard") -EXPOSE_IBUS_LAYOUTS = envbool("XPRA_EXPOSE_IBUS_LAYOUTS", True) - class InputMixin(StubSourceMixin): """ @@ -32,6 +30,7 @@ def is_needed(cls, caps: typedict) -> bool: def init_state(self) -> None: self.keyboard_config = None + self.ibus = False self.double_click_time: int = -1 self.double_click_distance: tuple[int, int] | None = None # mouse echo: @@ -51,6 +50,7 @@ def parse_client_caps(self, c: typedict): self.double_click_time = c.intget("double_click.time") self.double_click_distance = c.intpair("double_click.distance") self.mouse_last_position = c.intpair("mouse.initial-position") + self.ibus = c.boolget("ibus") def get_info(self) -> dict[str, Any]: dc_info: dict[str, Any] = {} @@ -75,11 +75,13 @@ def get_caps(self) -> dict[str, Any]: mck = getattr(self.keyboard_config, "modifier_client_keycodes", None) if mck: caps["modifier_keycodes"] = mck - if EXPOSE_IBUS_LAYOUTS: - from xpra.keyboard.ibus import query_ibus - caps["ibus"] = dict((k, v) for k, v in query_ibus().items() if k.startswith("engine")) return caps + def send_ibus_layouts(self, ibus_layouts: dict) -> None: + log(f"send_ibus_layouts(%s) {self.ibus=}", Ellipsizer(ibus_layouts)) + if self.ibus and ibus_layouts: + self.send_setting_change("ibus-layouts", ibus_layouts) + def set_layout(self, layout: str, variant: str, options): if not self.keyboard_config: return