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