Blame SOURCES/0001-magnifier-Request-window-relative-coordinates-for-fo.patch

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