From f9d480cfe50ca1d7a0f0b5a2b8bb9932962bfbe7 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 17:07:22 +0200 Subject: Adding upstream version 3.38.6. Signed-off-by: Daniel Baumann --- js/misc/ibusManager.js | 276 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 js/misc/ibusManager.js (limited to 'js/misc/ibusManager.js') diff --git a/js/misc/ibusManager.js b/js/misc/ibusManager.js new file mode 100644 index 0000000..d9a9a9d --- /dev/null +++ b/js/misc/ibusManager.js @@ -0,0 +1,276 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* exported getIBusManager */ + +const { Gio, GLib, IBus, Meta } = imports.gi; +const Signals = imports.signals; + +const IBusCandidatePopup = imports.ui.ibusCandidatePopup; + +Gio._promisify(IBus.Bus.prototype, + 'list_engines_async', 'list_engines_async_finish'); +Gio._promisify(IBus.Bus.prototype, + 'request_name_async', 'request_name_async_finish'); +Gio._promisify(IBus.Bus.prototype, + 'get_global_engine_async', 'get_global_engine_async_finish'); +Gio._promisify(IBus.Bus.prototype, + 'set_global_engine_async', 'set_global_engine_async_finish'); + +// Ensure runtime version matches +_checkIBusVersion(1, 5, 2); + +let _ibusManager = null; + +function _checkIBusVersion(requiredMajor, requiredMinor, requiredMicro) { + if ((IBus.MAJOR_VERSION > requiredMajor) || + (IBus.MAJOR_VERSION == requiredMajor && IBus.MINOR_VERSION > requiredMinor) || + (IBus.MAJOR_VERSION == requiredMajor && IBus.MINOR_VERSION == requiredMinor && + IBus.MICRO_VERSION >= requiredMicro)) + return; + + throw "Found IBus version %d.%d.%d but required is %d.%d.%d" + .format(IBus.MAJOR_VERSION, IBus.MINOR_VERSION, IBus.MINOR_VERSION, + requiredMajor, requiredMinor, requiredMicro); +} + +function getIBusManager() { + if (_ibusManager == null) + _ibusManager = new IBusManager(); + return _ibusManager; +} + +var IBusManager = class { + constructor() { + IBus.init(); + + // This is the longest we'll keep the keyboard frozen until an input + // source is active. + this._MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms + this._PRELOAD_ENGINES_DELAY_TIME = 30; // sec + + + this._candidatePopup = new IBusCandidatePopup.CandidatePopup(); + + this._panelService = null; + this._engines = new Map(); + this._ready = false; + this._registerPropertiesId = 0; + this._currentEngineName = null; + this._preloadEnginesId = 0; + + this._ibus = IBus.Bus.new_async(); + this._ibus.connect('connected', this._onConnected.bind(this)); + this._ibus.connect('disconnected', this._clear.bind(this)); + // Need to set this to get 'global-engine-changed' emitions + this._ibus.set_watch_ibus_signal(true); + this._ibus.connect('global-engine-changed', this._engineChanged.bind(this)); + + this._spawn(Meta.is_wayland_compositor() ? [] : ['--xim']); + } + + _spawn(extraArgs = []) { + try { + let cmdLine = ['ibus-daemon', '--panel', 'disable', ...extraArgs]; + let launcher = Gio.SubprocessLauncher.new(Gio.SubprocessFlags.NONE); + // Forward the right X11 Display for ibus-x11 + let display = GLib.getenv('GNOME_SETUP_DISPLAY'); + if (display) + launcher.setenv('DISPLAY', display, true); + launcher.spawnv(cmdLine); + } catch (e) { + log(`Failed to launch ibus-daemon: ${e.message}`); + } + } + + restartDaemon(extraArgs = []) { + this._spawn(['-r', ...extraArgs]); + } + + _clear() { + if (this._cancellable) { + this._cancellable.cancel(); + this._cancellable = null; + } + + if (this._preloadEnginesId) { + GLib.source_remove(this._preloadEnginesId); + this._preloadEnginesId = 0; + } + + if (this._panelService) + this._panelService.destroy(); + + this._panelService = null; + this._candidatePopup.setPanelService(null); + this._engines.clear(); + this._ready = false; + this._registerPropertiesId = 0; + this._currentEngineName = null; + + this.emit('ready', false); + } + + _onConnected() { + this._cancellable = new Gio.Cancellable(); + this._initEngines(); + this._initPanelService(); + } + + async _initEngines() { + try { + const enginesList = + await this._ibus.list_engines_async(-1, this._cancellable); + for (let i = 0; i < enginesList.length; ++i) { + let name = enginesList[i].get_name(); + this._engines.set(name, enginesList[i]); + } + this._updateReadiness(); + } catch (e) { + if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + return; + + logError(e); + this._clear(); + } + } + + async _initPanelService() { + try { + await this._ibus.request_name_async(IBus.SERVICE_PANEL, + IBus.BusNameFlag.REPLACE_EXISTING, -1, this._cancellable); + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { + logError(e); + this._clear(); + } + return; + } + + this._panelService = new IBus.PanelService({ + connection: this._ibus.get_connection(), + object_path: IBus.PATH_PANEL, + }); + this._candidatePopup.setPanelService(this._panelService); + this._panelService.connect('update-property', this._updateProperty.bind(this)); + this._panelService.connect('set-cursor-location', (ps, x, y, w, h) => { + let cursorLocation = { x, y, width: w, height: h }; + this.emit('set-cursor-location', cursorLocation); + }); + this._panelService.connect('focus-in', (panel, path) => { + if (!GLib.str_has_suffix(path, '/InputContext_1')) + this.emit('focus-in'); + }); + this._panelService.connect('focus-out', () => this.emit('focus-out')); + + try { + // IBus versions older than 1.5.10 have a bug which + // causes spurious set-content-type emissions when + // switching input focus that temporarily lose purpose + // and hints defeating its intended semantics and + // confusing users. We thus don't use it in that case. + _checkIBusVersion(1, 5, 10); + this._panelService.connect('set-content-type', this._setContentType.bind(this)); + } catch (e) { + } + this._updateReadiness(); + + try { + // If an engine is already active we need to get its properties + const engine = + await this._ibus.get_global_engine_async(-1, this._cancellable); + this._engineChanged(this._ibus, engine.get_name()); + } catch (e) { + } + } + + _updateReadiness() { + this._ready = this._engines.size > 0 && this._panelService != null; + this.emit('ready', this._ready); + } + + _engineChanged(bus, engineName) { + if (!this._ready) + return; + + this._currentEngineName = engineName; + + if (this._registerPropertiesId != 0) + return; + + this._registerPropertiesId = + this._panelService.connect('register-properties', (p, props) => { + if (!props.get(0)) + return; + + this._panelService.disconnect(this._registerPropertiesId); + this._registerPropertiesId = 0; + + this.emit('properties-registered', this._currentEngineName, props); + }); + } + + _updateProperty(panel, prop) { + this.emit('property-updated', this._currentEngineName, prop); + } + + _setContentType(panel, purpose, hints) { + this.emit('set-content-type', purpose, hints); + } + + activateProperty(key, state) { + this._panelService.property_activate(key, state); + } + + getEngineDesc(id) { + if (!this._ready || !this._engines.has(id)) + return null; + + return this._engines.get(id); + } + + async setEngine(id, callback) { + // Send id even if id == this._currentEngineName + // because 'properties-registered' signal can be emitted + // while this._ibusSources == null on a lock screen. + if (!this._ready) { + if (callback) + callback(); + return; + } + + try { + await this._ibus.set_global_engine_async(id, + this._MAX_INPUT_SOURCE_ACTIVATION_TIME, + this._cancellable); + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + logError(e); + } + if (callback) + callback(); + } + + preloadEngines(ids) { + if (!this._ibus || ids.length == 0) + return; + + if (this._preloadEnginesId != 0) { + GLib.source_remove(this._preloadEnginesId); + this._preloadEnginesId = 0; + } + + this._preloadEnginesId = + GLib.timeout_add_seconds( + GLib.PRIORITY_DEFAULT, + this._PRELOAD_ENGINES_DELAY_TIME, + () => { + this._ibus.preload_engines_async( + ids, + -1, + this._cancellable, + null); + this._preloadEnginesId = 0; + return GLib.SOURCE_REMOVE; + }); + } +}; +Signals.addSignalMethods(IBusManager.prototype); -- cgit v1.2.3