Blame SOURCES/0001-desktop-icons-Fix-stuck-grab-issue-with-rubber-bandi.patch

b86695
From b334c8c248f849be996963cdafb1b0b69476bdf1 Mon Sep 17 00:00:00 2001
b86695
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@redhat.com>
b86695
Date: Tue, 2 Nov 2021 09:20:11 +0100
b86695
Subject: [PATCH] desktop-icons: Fix stuck grab issue with rubber banding
b86695
b86695
The desktop icons extension can get into a state where the desktop no longer
b86695
takes mouse input.
b86695
b86695
This happens if a user starts a rubber banding operation and then drags
b86695
the mouse to somewhere on screen that has a pop up menu, and then pops
b86695
the menu up.
b86695
b86695
This commit addresses the bug by limiting the grab actor to the
b86695
backgrounds, and by explicitly ending the rubber banding operation
b86695
when one of the icons own menus is shown.
b86695
b86695
One side effect of limiting the grab actor to the backgrounds, is the
b86695
rubber banding code never gets to see motion outside of the backgrounds
b86695
anymore. In order to keep drag operations feeling fluid when the user moves
b86695
toward the edge of the screen, this commit also overrides the
b86695
grab helpers captured-event handler so those motion events keep coming.
b86695
b86695
We also start to end the rubber band if for any reason the grab it had
b86695
was released.
b86695
---
b86695
 extensions/desktop-icons/desktopGrid.js    |  1 +
b86695
 extensions/desktop-icons/desktopManager.js | 75 ++++++++++++++--------
b86695
 extensions/desktop-icons/fileItem.js       |  1 +
b86695
 3 files changed, 49 insertions(+), 28 deletions(-)
b86695
b86695
diff --git a/extensions/desktop-icons/desktopGrid.js b/extensions/desktop-icons/desktopGrid.js
b86695
index 602fa7f..bd27e2a 100644
b86695
--- a/extensions/desktop-icons/desktopGrid.js
b86695
+++ b/extensions/desktop-icons/desktopGrid.js
b86695
@@ -365,6 +365,7 @@ var DesktopGrid = class {
b86695
     }
b86695
 
b86695
     _openMenu(x, y) {
b86695
+        Extension.desktopManager.endRubberBand();
b86695
         Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0);
b86695
         this.actor._desktopBackgroundMenu.open(BoxPointer.PopupAnimation.NONE);
b86695
         /* Since the handler is in the press event it needs to ignore the release event
b86695
diff --git a/extensions/desktop-icons/desktopManager.js b/extensions/desktop-icons/desktopManager.js
b86695
index a70cd98..c37e1e7 100644
b86695
--- a/extensions/desktop-icons/desktopManager.js
b86695
+++ b/extensions/desktop-icons/desktopManager.js
b86695
@@ -79,6 +79,7 @@ var DesktopManager = GObject.registerClass({
b86695
         this._queryFileInfoCancellable = null;
b86695
         this._unixMode = null;
b86695
         this._writableByOthers = null;
b86695
+        this._rubberBandActive = false;
b86695
 
b86695
         this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', () => this._recreateDesktopIcons());
b86695
         this._rubberBand = new St.Widget({ style_class: 'rubber-band' });
b86695
@@ -86,6 +87,20 @@ var DesktopManager = GObject.registerClass({
b86695
         Main.layoutManager._backgroundGroup.add_child(this._rubberBand);
b86695
         this._grabHelper = new GrabHelper.GrabHelper(global.stage);
b86695
 
b86695
+        let origCapturedEvent = this._grabHelper.onCapturedEvent;
b86695
+        this._grabHelper.onCapturedEvent = (event) => {
b86695
+            if (event.type() === Clutter.EventType.MOTION) {
b86695
+                /* We handle motion events from a captured event handler so we
b86695
+                 * we can see motion over actors that are on other parts of the
b86695
+                 * stage.
b86695
+                 */
b86695
+                this._handleMotion(event);
b86695
+                return Clutter.EVENT_STOP;
b86695
+            }
b86695
+
b86695
+            return origCapturedEvent.bind(this._grabHelper)(event);
b86695
+        };
b86695
+
b86695
         this._addDesktopIcons();
b86695
         this._monitorDesktopFolder();
b86695
 
b86695
@@ -108,30 +123,15 @@ var DesktopManager = GObject.registerClass({
b86695
         this._initRubberBandColor();
b86695
         this._updateRubberBand(x, y);
b86695
         this._rubberBand.show();
b86695
-        this._grabHelper.grab({ actor: global.stage });
b86695
+        this._rubberBandActive = true;
b86695
+        this._grabHelper.grab({
b86695
+            actor: Main.layoutManager._backgroundGroup,
b86695
+            onUngrab: () => this.endRubberBand(false),
b86695
+        });
b86695
         Extension.lockActivitiesButton = true;
b86695
         this._stageReleaseEventId = global.stage.connect('button-release-event', (actor, event) => {
b86695
             this.endRubberBand();
b86695
         });
b86695
-        this._rubberBandId = global.stage.connect('motion-event', (actor, event) => {
b86695
-            /* In some cases, when the user starts a rubberband selection and ends it
b86695
-             * (by releasing the left button) over a window instead of doing it over
b86695
-             * the desktop, the stage doesn't receive the "button-release" event.
b86695
-             * This happens currently with, at least, Dash to Dock extension, but
b86695
-             * it probably also happens with other applications or extensions.
b86695
-             * To fix this, we also end the rubberband selection if we detect mouse
b86695
-             * motion in the stage without the left button pressed during a
b86695
-             * rubberband selection.
b86695
-             *  */
b86695
-            let button = event.get_state();
b86695
-            if (!(button & Clutter.ModifierType.BUTTON1_MASK)) {
b86695
-                this.endRubberBand();
b86695
-                return;
b86695
-            }
b86695
-            [x, y] = event.get_coords();
b86695
-            this._updateRubberBand(x, y);
b86695
-            this._updateSelection(x, y);
b86695
-        });
b86695
         this._rubberBandTouchId = global.stage.connect('touch-event', (actor, event) => {
b86695
             // Let x11 pointer emulation do the job on X11
b86695
             if (!Meta.is_wayland_compositor())
b86695
@@ -175,14 +175,37 @@ var DesktopManager = GObject.registerClass({
b86695
         }
b86695
     }
b86695
 
b86695
-    endRubberBand() {
b86695
+    _handleMotion(event) {
b86695
+        /* In some cases, when the user starts a rubberband selection and ends it
b86695
+         * (by releasing the left button) over a window instead of doing it over
b86695
+         * the desktop, the stage doesn't receive the "button-release" event.
b86695
+         * This happens currently with, at least, Dash to Dock extension, but
b86695
+         * it probably also happens with other applications or extensions.
b86695
+         * To fix this, we also end the rubberband selection if we detect mouse
b86695
+         * motion in the stage without the left button pressed during a
b86695
+         * rubberband selection.
b86695
+         *  */
b86695
+        let button = event.get_state();
b86695
+        if (!(button & Clutter.ModifierType.BUTTON1_MASK)) {
b86695
+            this.endRubberBand();
b86695
+            return;
b86695
+        }
b86695
+        let [x, y] = event.get_coords();
b86695
+        this._updateRubberBand(x, y);
b86695
+        this._updateSelection(x, y);
b86695
+    }
b86695
+
b86695
+    endRubberBand(ungrab=true) {
b86695
+        if (!this._rubberBandActive)
b86695
+             return;
b86695
+
b86695
+        this._rubberBandActive = false;
b86695
         this._rubberBand.hide();
b86695
         Extension.lockActivitiesButton = false;
b86695
-        this._grabHelper.ungrab();
b86695
-        global.stage.disconnect(this._rubberBandId);
b86695
+        if (ungrab)
b86695
+            this._grabHelper.ungrab();
b86695
         global.stage.disconnect(this._rubberBandTouchId);
b86695
         global.stage.disconnect(this._stageReleaseEventId);
b86695
-        this._rubberBandId = 0;
b86695
         this._rubberBandTouchId = 0;
b86695
         this._stageReleaseEventId = 0;
b86695
 
b86695
@@ -760,10 +783,6 @@ var DesktopManager = GObject.registerClass({
b86695
             global.stage.disconnect(this._stageReleaseEventId);
b86695
         this._stageReleaseEventId = 0;
b86695
 
b86695
-        if (this._rubberBandId)
b86695
-            global.stage.disconnect(this._rubberBandId);
b86695
-        this._rubberBandId = 0;
b86695
-
b86695
         if (this._rubberBandTouchId)
b86695
             global.stage.disconnect(this._rubberBandTouchId);
b86695
         this._rubberBandTouchId = 0;
b86695
diff --git a/extensions/desktop-icons/fileItem.js b/extensions/desktop-icons/fileItem.js
b86695
index 1cb47e8..90f326d 100644
b86695
--- a/extensions/desktop-icons/fileItem.js
b86695
+++ b/extensions/desktop-icons/fileItem.js
b86695
@@ -676,6 +676,7 @@ var FileItem = class {
b86695
     }
b86695
 
b86695
     _onPressButton(actor, event) {
b86695
+        Extension.desktopManager.endRubberBand();
b86695
         this._updateClickState(event);
b86695
         let button = this._eventButton(event);
b86695
         if (button == 3) {
b86695
-- 
b86695
2.31.1
b86695