From fd36bbbc106b173cc1c0deaa36573a7c70289e91 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 29 Jun 2018 17:35:39 +0200 Subject: [PATCH] inputMethod: Handle IBusInputContext::forward-key-press The input method may hint that certain keycodes should be pressed/released besides the textual information in ::commit. An example is hitting space in some IMs to commit text, where both ::commit happens, and an space is visibly inserted. In order to handle this properly, we must honor ::forward-key-press. In order to cater for the case that a keypress is forwarded while handling that same keypress in a physical keyboard, check the current event being handled and just forward it as-is if it matches. This is necessary to prevent state from being doubly set, and the second event silenced away. https://gitlab.gnome.org/GNOME/gnome-shell/issues/275 Closes: #275 --- js/misc/inputMethod.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/js/misc/inputMethod.js b/js/misc/inputMethod.js index 621483243..59b3d78d6 100644 --- a/js/misc/inputMethod.js +++ b/js/misc/inputMethod.js @@ -15,6 +15,8 @@ var InputMethod = new Lang.Class({ this._purpose = 0; this._enabled = true; this._currentFocus = null; + this._currentEvent = null; + this._doForwardEvent = false; this._ibus = IBus.Bus.new_async(); this._ibus.connect('connected', this._onConnected.bind(this)); this._ibus.connect('disconnected', this._clear.bind(this)); @@ -25,6 +27,9 @@ var InputMethod = new Lang.Class({ this._onSourceChanged.bind(this)); this._currentSource = this._inputSourceManager.currentSource; + let deviceManager = Clutter.DeviceManager.get_default(); + this._virtualDevice = deviceManager.create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE); + if (this._ibus.is_connected()) this._onConnected(); }, @@ -64,6 +69,7 @@ var InputMethod = new Lang.Class({ this._context.connect('commit-text', this._onCommitText.bind(this)); this._context.connect('delete-surrounding-text', this._onDeleteSurroundingText.bind(this)); this._context.connect('update-preedit-text', this._onUpdatePreeditText.bind(this)); + this._context.connect('forward-key-event', this._onForwardKeyEvent.bind(this)); this._updateCapabilities(); }, @@ -96,6 +102,24 @@ var InputMethod = new Lang.Class({ this.set_preedit_text(str, pos); }, + _onForwardKeyEvent(context, keyval, keycode, state) { + let press = (state & IBus.ModifierType.RELEASE_MASK) == 0; + + if (this._currentEvent) { + // If we are handling this same event in filter_key_press(), + // just let it go through, sending the same event again will + // be silenced away because the key counts as pressed. + if (this._currentEvent.get_key_symbol() == keyval && + (this._currentEvent.type() == Clutter.EventType.KEY_PRESS) == press) { + this._doForwardEvent = true; + return; + } + } + + this._virtualDevice.notify_key(Clutter.get_current_event_time(), keycode, + press ? Clutter.KeyState.PRESSED : Clutter.KeyState.RELEASED); + }, + vfunc_focus_in(focus) { this._currentFocus = focus; if (this._context) { @@ -197,13 +221,23 @@ var InputMethod = new Lang.Class({ if (event.type() == Clutter.EventType.KEY_RELEASE) state |= IBus.ModifierType.RELEASE_MASK; + + this._currentEvent = event; + this._doForwardEvent = false; + this._context.process_key_event_async(event.get_key_symbol(), event.get_key_code() - 8, // Convert XKB keycodes to evcodes state, -1, null, (context, res) => { try { let retval = context.process_key_event_async_finish(res); + + if (this._doForwardEvent) + retval = false; + this.notify_key_event(event, retval); + this._doForwardEvent = false; + this._currentEvent = null; } catch (e) { log('Error processing key on IM: ' + e.message); }