From df57829ea1c9467b0d684a9d67d4e180e528b9e1 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 25 May 2018 11:35:49 +0200 Subject: [PATCH] keyboard: Implement standalone FocusTracker And stop using FocusCaretTracker for caret position purposes. This new object uses 1) the text-input protocol in wayland and 2) Info from IBusPanelService for X11 (which is meant to work for XIM too). This drops the usage of AtspiEventListener for OSK purposes, which is best to avoid. --- js/misc/ibusManager.js | 5 + js/ui/keyboard.js | 236 +++++++++++++++++++---------------------- 2 files changed, 117 insertions(+), 124 deletions(-) diff --git a/js/misc/ibusManager.js b/js/misc/ibusManager.js index 782b9ad28..6452e492f 100644 --- a/js/misc/ibusManager.js +++ b/js/misc/ibusManager.js @@ -115,6 +115,11 @@ var IBusManager = new Lang.Class({ 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); + }); + try { // IBus versions older than 1.5.10 have a bug which // causes spurious set-content-type emissions when diff --git a/js/ui/keyboard.js b/js/ui/keyboard.js index e13c7b724..5fcdf988a 100644 --- a/js/ui/keyboard.js +++ b/js/ui/keyboard.js @@ -1,6 +1,5 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- -const FocusCaretTracker = imports.ui.focusCaretTracker; const Atspi = imports.gi.Atspi; const Clutter = imports.gi.Clutter; const Gdk = imports.gi.Gdk; @@ -13,6 +12,7 @@ const Signals = imports.signals; const St = imports.gi.St; const InputSourceManager = imports.ui.status.keyboard; +const IBusManager = imports.misc.ibusManager; const BoxPointer = imports.ui.boxpointer; const Layout = imports.ui.layout; const Main = imports.ui.main; @@ -261,6 +261,7 @@ var Key = new Lang.Class({ this._extended_keyboard = null; this._pressTimeoutId = 0; this._capturedPress = false; + this._capturedEventId = 0; this._unmapId = 0; this._longPress = false; @@ -484,6 +485,71 @@ var KeyboardModel = new Lang.Class({ } }); +var FocusTracker = new Lang.Class({ + Name: 'FocusTracker', + + _init() { + this._currentWindow = null; + this._currentWindowPositionId = 0; + + global.screen.get_display().connect('notify::focus-window', () => { + this._setCurrentWindow(global.screen.get_display().focus_window); + this.emit('window-changed', this._currentWindow); + }); + + /* Valid for wayland clients */ + Main.inputMethod.connect('cursor-location-changed', (o, rect) => { + let newRect = { x: rect.get_x(), y: rect.get_y(), width: rect.get_width(), height: rect.get_height() }; + this._setCurrentRect(newRect); + }); + + this._ibusManager = IBusManager.getIBusManager(); + this._ibusManager.connect('set-cursor-location', (manager, rect) => { + /* Valid for X11 clients only */ + if (Main.inputMethod.currentFocus) + return; + + this._setCurrentRect(rect); + }); + }, + + get currentWindow() { + return this._currentWindow; + }, + + _setCurrentWindow(window) { + if (this._currentWindow) + this._currentWindow.disconnect(this._currentWindowPositionId); + + this._currentWindow = window; + if (window) { + this._currentWindowPositionId = this._currentWindow.connect('position-changed', () => { + if (global.display.get_grab_op() == Meta.GrabOp.NONE) + this.emit('position-changed'); + else + this.emit('reset'); + }); + } + }, + + _setCurrentRect(rect) { + let frameRect = this._currentWindow.get_frame_rect(); + rect.x -= frameRect.x; + rect.y -= frameRect.y; + + this._rect = rect; + this.emit('position-changed'); + }, + + getCurrentRect() { + let frameRect = this._currentWindow.get_frame_rect(); + let rect = { x: this._rect.x + frameRect.x, y: this._rect.y + frameRect.y, width: this._rect.width, height: this._rect.height }; + + return rect; + } +}); +Signals.addSignalMethods(FocusTracker.prototype); + var Keyboard = new Lang.Class({ Name: 'Keyboard', @@ -491,15 +557,10 @@ var Keyboard = new Lang.Class({ this.actor = null; this._focusInExtendedKeys = false; - this._focusCaretTracker = new FocusCaretTracker.FocusCaretTracker(); - this._focusCaretTracker.connect('focus-changed', this._onFocusChanged.bind(this)); - this._focusCaretTracker.connect('caret-moved', this._onCaretMoved.bind(this)); this._languagePopup = null; - this._currentAccessible = null; - this._caretTrackingEnabled = false; - this._updateCaretPositionId = 0; this._currentFocusWindow = null; - this._originalWindowY = null; + this._animFocusedWindow = null; + this._delayedAnimFocusWindow = null; this._enableKeyboard = false; // a11y settings value this._enabled = false; // enabled state (by setting or device type) @@ -510,6 +571,14 @@ var Keyboard = new Lang.Class({ this._lastDeviceId = null; this._suggestions = null; + this._focusTracker = new FocusTracker(); + this._focusTracker.connect('position-changed', this._onFocusPositionChanged.bind(this)); + this._focusTracker.connect('reset', () => { + this._delayedAnimFocusWindow = null; + this._animFocusedWindow = null; + this._oskFocusWindow = null; + }); + Meta.get_backend().connect('last-device-changed', (backend, deviceId) => { let manager = Clutter.DeviceManager.get_default(); @@ -532,102 +601,15 @@ var Keyboard = new Lang.Class({ this._keyboardRestingId = 0; Main.layoutManager.connect('monitors-changed', this._relayout.bind(this)); - //Main.inputMethod.connect('cursor-location-changed', (o, rect) => { - // if (this._keyboardVisible) { - // let currentWindow = global.screen.get_display().focus_window; - // this.setCursorLocation(currentWindow, rect.get_x(), rect.get_y(), - // rect.get_width(), rect.get_height()); - // } - //}); }, get visible() { return this._keyboardVisible; }, - _setCaretTrackerEnabled(enabled) { - if (this._caretTrackingEnabled == enabled) - return; - - this._caretTrackingEnabled = enabled; - - if (enabled) { - this._focusCaretTracker.registerFocusListener(); - this._focusCaretTracker.registerCaretListener(); - } else { - this._focusCaretTracker.deregisterFocusListener(); - this._focusCaretTracker.deregisterCaretListener(); - } - }, - - _updateCaretPosition(accessible) { - if (this._updateCaretPositionId) - GLib.source_remove(this._updateCaretPositionId); - if (!this._keyboardRequested) - return; - this._updateCaretPositionId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - this._updateCaretPositionId = 0; - - let currentWindow = global.screen.get_display().focus_window; - if (!currentWindow) { - this.setCursorLocation(null); - return GLib.SOURCE_REMOVE; - } - - let windowRect = currentWindow.get_frame_rect(); - let text = accessible.get_text_iface(); - let component = accessible.get_component_iface(); - - try { - let caretOffset = text.get_caret_offset(); - let caretRect = text.get_character_extents(caretOffset, Atspi.CoordType.WINDOW); - let focusRect = component.get_extents(Atspi.CoordType.WINDOW); - - if (caretRect.width == 0 && caretRect.height == 0) - caretRect = focusRect; - - this.setCursorLocation(currentWindow, caretRect.x, caretRect.y, caretRect.width, caretRect.height); - } catch (e) { - log('Error updating caret position for OSK: ' + e.message); - } - - return GLib.SOURCE_REMOVE; - }); - - GLib.Source.set_name_by_id(this._updateCaretPositionId, '[gnome-shell] this._updateCaretPosition'); - }, - - _focusIsTextEntry(accessible) { - try { - let role = accessible.get_role(); - let stateSet = accessible.get_state_set(); - return stateSet.contains(Atspi.StateType.EDITABLE) || role == Atspi.Role.TERMINAL; - } catch (e) { - log('Error determining accessible role: ' + e.message); - return false; - } - }, - - _onFocusChanged(caretTracker, event) { - let accessible = event.source; - if (!this._focusIsTextEntry(accessible)) - return; - - let focused = event.detail1 != 0; - if (focused) { - this._currentAccessible = accessible; - this._updateCaretPosition(accessible); - this.show(Main.layoutManager.focusIndex); - } else if (this._currentAccessible == accessible) { - this._currentAccessible = null; - this.hide(); - } - }, - - _onCaretMoved(caretTracker, event) { - let accessible = event.source; - if (this._currentAccessible == accessible) - this._updateCaretPosition(accessible); + _onFocusPositionChanged(focusTracker) { + let rect = focusTracker.getCurrentRect(); + this.setCursorLocation(focusTracker.currentWindow, rect.x, rect.y, rect.width, rect.height); }, _lastDeviceIsTouchscreen() { @@ -650,8 +632,6 @@ var Keyboard = new Lang.Class({ if (!this._enabled && !this._keyboardController) return; - this._setCaretTrackerEnabled(this._enabled); - if (this._enabled && !this._keyboardController) this._setupKeyboard(); else if (!this._enabled) @@ -1027,11 +1007,14 @@ var Keyboard = new Lang.Class({ if (!this._keyboardRequested) return; - if (this._currentAccessible) - this._updateCaretPosition(this._currentAccessible); Main.layoutManager.keyboardIndex = monitor; this._relayout(); Main.layoutManager.showKeyboard(); + + if (this._delayedAnimFocusWindow) { + this._setAnimationWindow(this._delayedAnimFocusWindow); + this._delayedAnimFocusWindow = null; + } }, hide() { @@ -1102,8 +1085,9 @@ var Keyboard = new Lang.Class({ window.move_frame(true, frameRect.x, frameRect.y); }, - _animateWindow(window, show, deltaY) { + _animateWindow(window, show) { let windowActor = window.get_compositor_private(); + let deltaY = Main.layoutManager.keyboardBox.height; if (!windowActor) return; @@ -1124,35 +1108,39 @@ var Keyboard = new Lang.Class({ } }, - setCursorLocation(window, x, y , w, h) { - if (window == this._oskFocusWindow) + _setAnimationWindow(window) { + if (this._animFocusedWindow == window) return; - if (this._oskFocusWindow) { - let display = global.screen.get_display(); + if (this._animFocusedWindow) + this._animateWindow(this._animFocusedWindow, false); + if (window) + this._animateWindow(window, true); - if (display.get_grab_op() == Meta.GrabOp.NONE || - display.get_focus_window() != this._oskFocusWindow) - this._animateWindow(this._oskFocusWindow, false, this._oskFocusWindowDelta); + this._animFocusedWindow = window; + }, - this._oskFocusWindow = null; - this._oskFocusWindowDelta = null; - } + setCursorLocation(window, x, y , w, h) { + let monitor = Main.layoutManager.keyboardMonitor; - if (window) { - let monitor = Main.layoutManager.keyboardMonitor; + if (window && monitor) { let keyboardHeight = Main.layoutManager.keyboardBox.height; - let frameRect = window.get_frame_rect(); - let windowActor = window.get_compositor_private(); - let delta = 0; + let focusObscured = false; - if (frameRect.y + y + h >= monitor.height - keyboardHeight) - delta = keyboardHeight; - - this._animateWindow(window, true, delta); - this._oskFocusWindow = window; - this._oskFocusWindowDelta = delta; + if (y + h >= monitor.y + monitor.height - keyboardHeight) { + if (this._keyboardVisible) + this._setAnimationWindow(window); + else + this._delayedAnimFocusWindow = window; + } else if (y < keyboardHeight) { + this._delayedAnimFocusWindow = null; + this._setAnimationWindow(null); + } + } else { + this._setAnimationWindow(null); } + + this._oskFocusWindow = window; }, });