|
|
e52bd3 |
From ea7e7acd45e428cc17306de2bf65730c90d7e118 Mon Sep 17 00:00:00 2001
|
|
|
e52bd3 |
From: Sebastian Keller <skeller@gnome.org>
|
|
|
e52bd3 |
Date: Mon, 23 May 2022 23:01:23 +0200
|
|
|
e52bd3 |
Subject: [PATCH] magnifier: Request window-relative coordinates for
|
|
|
e52bd3 |
focus/caret events
|
|
|
e52bd3 |
|
|
|
e52bd3 |
Absolute screen coordinates are impossible for Wayland clients to
|
|
|
e52bd3 |
provide, because the clients don't know where the window is positioned.
|
|
|
e52bd3 |
Some clients, such as the ones using GTK 3 were providing window
|
|
|
e52bd3 |
relative coordinates even when screen coordinates were requested,
|
|
|
e52bd3 |
while others, such as GTK 4 clients, were just returning an error for
|
|
|
e52bd3 |
caret events or also window-relative coordinates for focus events.
|
|
|
e52bd3 |
|
|
|
e52bd3 |
So for this to work on Wayland we have to request window-relative
|
|
|
e52bd3 |
coordinates and translate them to the current focus window.
|
|
|
e52bd3 |
|
|
|
e52bd3 |
To ensure the correct coordinates, we have to only consider events
|
|
|
e52bd3 |
coming from the current focus window. All other events are filtered out
|
|
|
e52bd3 |
now. As a side effect this also fixes the magnifier always jumping
|
|
|
e52bd3 |
to a terminal cursor whenever there was some output, even if the window
|
|
|
e52bd3 |
was not focused.
|
|
|
e52bd3 |
|
|
|
e52bd3 |
This also needs some special handling for events coming from the shell
|
|
|
e52bd3 |
itself, which should not be translated to the focus window either. As
|
|
|
e52bd3 |
another side effect this fixes another bug that was caused by these
|
|
|
e52bd3 |
events already including scaling and getting scaled again.
|
|
|
e52bd3 |
|
|
|
e52bd3 |
Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5509
|
|
|
e52bd3 |
Part-of:
|
|
|
e52bd3 |
<https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2301>
|
|
|
e52bd3 |
---
|
|
|
e52bd3 |
js/ui/magnifier.js | 77 +++++++++++++++++++++++++++++++++++++++++-----
|
|
|
e52bd3 |
1 file changed, 70 insertions(+), 7 deletions(-)
|
|
|
e52bd3 |
|
|
|
e52bd3 |
diff --git a/js/ui/magnifier.js b/js/ui/magnifier.js
|
|
|
e52bd3 |
index 4c2e88f1a..9813664be 100644
|
|
|
e52bd3 |
--- a/js/ui/magnifier.js
|
|
|
e52bd3 |
+++ b/js/ui/magnifier.js
|
|
|
e52bd3 |
@@ -789,21 +789,81 @@ var ZoomRegion = class ZoomRegion {
|
|
|
e52bd3 |
}
|
|
|
e52bd3 |
}
|
|
|
e52bd3 |
|
|
|
e52bd3 |
+ _convertExtentsToScreenSpace(accessible, extents) {
|
|
|
e52bd3 |
+ const toplevelWindowTypes = new Set([
|
|
|
e52bd3 |
+ Atspi.Role.FRAME,
|
|
|
e52bd3 |
+ Atspi.Role.DIALOG,
|
|
|
e52bd3 |
+ Atspi.Role.WINDOW,
|
|
|
e52bd3 |
+ ]);
|
|
|
e52bd3 |
+
|
|
|
e52bd3 |
+ try {
|
|
|
e52bd3 |
+ let app = null;
|
|
|
e52bd3 |
+ let parentWindow = null;
|
|
|
e52bd3 |
+ let iter = accessible;
|
|
|
e52bd3 |
+ while (iter) {
|
|
|
e52bd3 |
+ if (iter.get_role() === Atspi.Role.APPLICATION) {
|
|
|
e52bd3 |
+ app = iter;
|
|
|
e52bd3 |
+ /* This is the last Accessible we are interested in */
|
|
|
e52bd3 |
+ break;
|
|
|
e52bd3 |
+ } else if (toplevelWindowTypes.has(iter.get_role())) {
|
|
|
e52bd3 |
+ parentWindow = iter;
|
|
|
e52bd3 |
+ }
|
|
|
e52bd3 |
+ iter = iter.get_parent();
|
|
|
e52bd3 |
+ }
|
|
|
e52bd3 |
+
|
|
|
e52bd3 |
+ /* We don't want to translate our own events to the focus window.
|
|
|
e52bd3 |
+ * They are also already scaled by clutter before being sent, so
|
|
|
e52bd3 |
+ * we don't need to do that here either. */
|
|
|
e52bd3 |
+ if (app && app.get_name() === 'gnome-shell')
|
|
|
e52bd3 |
+ return extents;
|
|
|
e52bd3 |
+
|
|
|
e52bd3 |
+ /* Only events from the focused widget of the focused window. Some
|
|
|
e52bd3 |
+ * widgets seem to claim to have focus when the window does not so
|
|
|
e52bd3 |
+ * check both. */
|
|
|
e52bd3 |
+ const windowActive = parentWindow &&
|
|
|
e52bd3 |
+ parentWindow.get_state_set().contains(Atspi.StateType.ACTIVE);
|
|
|
e52bd3 |
+ const accessibleFocused =
|
|
|
e52bd3 |
+ accessible.get_state_set().contains(Atspi.StateType.FOCUSED);
|
|
|
e52bd3 |
+ if (!windowActive || !accessibleFocused)
|
|
|
e52bd3 |
+ return null;
|
|
|
e52bd3 |
+ } catch (e) {
|
|
|
e52bd3 |
+ throw new Error(`Failed to validate parent window: ${e}`);
|
|
|
e52bd3 |
+ }
|
|
|
e52bd3 |
+
|
|
|
e52bd3 |
+ const focusWindowRect = global.display.focus_window?.get_frame_rect();
|
|
|
e52bd3 |
+ if (!focusWindowRect)
|
|
|
e52bd3 |
+ return null;
|
|
|
e52bd3 |
+
|
|
|
e52bd3 |
+ const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
|
|
e52bd3 |
+ const screenSpaceExtents = new Atspi.Rect({
|
|
|
e52bd3 |
+ x: focusWindowRect.x + (scaleFactor * extents.x),
|
|
|
e52bd3 |
+ y: focusWindowRect.y + (scaleFactor * extents.y),
|
|
|
e52bd3 |
+ width: scaleFactor * extents.width,
|
|
|
e52bd3 |
+ height: scaleFactor * extents.height,
|
|
|
e52bd3 |
+ });
|
|
|
e52bd3 |
+
|
|
|
e52bd3 |
+ return screenSpaceExtents;
|
|
|
e52bd3 |
+ }
|
|
|
e52bd3 |
+
|
|
|
e52bd3 |
_updateFocus(caller, event) {
|
|
|
e52bd3 |
let component = event.source.get_component_iface();
|
|
|
e52bd3 |
if (!component || event.detail1 != 1)
|
|
|
e52bd3 |
return;
|
|
|
e52bd3 |
let extents;
|
|
|
e52bd3 |
try {
|
|
|
e52bd3 |
- extents = component.get_extents(Atspi.CoordType.SCREEN);
|
|
|
e52bd3 |
+ extents = component.get_extents(Atspi.CoordType.WINDOW);
|
|
|
e52bd3 |
+ extents = this._convertExtentsToScreenSpace(event.source, extents);
|
|
|
e52bd3 |
+ if (!extents)
|
|
|
e52bd3 |
+ return;
|
|
|
e52bd3 |
} catch (e) {
|
|
|
e52bd3 |
log(`Failed to read extents of focused component: ${e.message}`);
|
|
|
e52bd3 |
return;
|
|
|
e52bd3 |
}
|
|
|
e52bd3 |
|
|
|
e52bd3 |
- let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
|
|
e52bd3 |
- let [xFocus, yFocus] = [(extents.x + (extents.width / 2)) * scaleFactor,
|
|
|
e52bd3 |
- (extents.y + (extents.height / 2)) * scaleFactor];
|
|
|
e52bd3 |
+ const [xFocus, yFocus] = [
|
|
|
e52bd3 |
+ extents.x + (extents.width / 2),
|
|
|
e52bd3 |
+ extents.y + (extents.height / 2),
|
|
|
e52bd3 |
+ ];
|
|
|
e52bd3 |
|
|
|
e52bd3 |
if (this._xFocus !== xFocus || this._yFocus !== yFocus) {
|
|
|
e52bd3 |
[this._xFocus, this._yFocus] = [xFocus, yFocus];
|
|
|
e52bd3 |
@@ -817,14 +877,17 @@ var ZoomRegion = class ZoomRegion {
|
|
|
e52bd3 |
return;
|
|
|
e52bd3 |
let extents;
|
|
|
e52bd3 |
try {
|
|
|
e52bd3 |
- extents = text.get_character_extents(text.get_caret_offset(), 0);
|
|
|
e52bd3 |
+ extents = text.get_character_extents(text.get_caret_offset(),
|
|
|
e52bd3 |
+ Atspi.CoordType.WINDOW);
|
|
|
e52bd3 |
+ extents = this._convertExtentsToScreenSpace(text, extents);
|
|
|
e52bd3 |
+ if (!extents)
|
|
|
e52bd3 |
+ return;
|
|
|
e52bd3 |
} catch (e) {
|
|
|
e52bd3 |
log(`Failed to read extents of text caret: ${e.message}`);
|
|
|
e52bd3 |
return;
|
|
|
e52bd3 |
}
|
|
|
e52bd3 |
|
|
|
e52bd3 |
- let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
|
|
e52bd3 |
- let [xCaret, yCaret] = [extents.x * scaleFactor, extents.y * scaleFactor];
|
|
|
e52bd3 |
+ const [xCaret, yCaret] = [extents.x, extents.y];
|
|
|
e52bd3 |
|
|
|
e52bd3 |
// Ignore event(s) if the caret size is none (0x0). This happens a lot if
|
|
|
e52bd3 |
// the cursor offset can't be translated into a location. This is a work
|
|
|
e52bd3 |
--
|
|
|
e52bd3 |
2.38.1
|
|
|
e52bd3 |
|