Blob Blame History Raw
From dc441b5d71f80e2b6dcdccbb4d6562c5b799294f Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 28 Sep 2020 22:28:08 -0400
Subject: [PATCH 11/13] screenShield: Fix pointer motion signal handler leak

The screen shield code listens for motion events on the stage
so that it can hide the pointer until the user moves the mouse.

Unfortunately, if the user never moves the mouse, the signal
handler connection gets leaked.

This commit makes sure the connection gets disconnected when the
shield goes away.
---
 js/ui/screenShield.js | 31 +++++++++++++++++++++----------
 1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
index ebba6c82a..bbfceba57 100644
--- a/js/ui/screenShield.js
+++ b/js/ui/screenShield.js
@@ -905,91 +905,110 @@ var ScreenShield = new Lang.Class({
         this._completeLockScreenShown();
     },
 
     showDialog() {
         if (!this._becomeModal()) {
             // In the login screen, this is a hard error. Fail-whale
             log('Could not acquire modal grab for the login screen. Aborting login process.');
             Meta.quit(Meta.ExitCode.ERROR);
         }
 
         this.actor.show();
         this._isGreeter = Main.sessionMode.isGreeter;
         this._isLocked = true;
         if (this._ensureUnlockDialog(true, true))
             this._hideLockScreen(false, 0);
     },
 
     _hideLockScreenComplete() {
         if (Main.sessionMode.currentMode == 'lock-screen')
             Main.sessionMode.popMode('lock-screen');
 
         this._lockScreenState = MessageTray.State.HIDDEN;
         this._lockScreenGroup.hide();
 
         if (this._dialog) {
             this._dialog.actor.grab_key_focus();
             this._dialog.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
         }
     },
 
+    _showPointer() {
+        this._cursorTracker.set_pointer_visible(true);
+
+        if (this._motionId) {
+            global.stage.disconnect(this._motionId);
+            this._motionId = 0;
+        }
+    },
+
+    _hidePointerUntilMotion() {
+        this._motionId = global.stage.connect('captured-event', (stage, event) => {
+            if (event.type() == Clutter.EventType.MOTION)
+                this._showPointer();
+
+            return Clutter.EVENT_PROPAGATE;
+        });
+        this._cursorTracker.set_pointer_visible(false);
+    },
+
     _hideLockScreen(animate, velocity) {
         if (this._lockScreenState == MessageTray.State.HIDDEN)
             return;
 
         this._lockScreenState = MessageTray.State.HIDING;
 
         Tweener.removeTweens(this._lockScreenGroup);
 
         if (animate) {
             // Tween the lock screen out of screen
             // if velocity is not specified (i.e. we come here from pressing ESC),
             // use the same speed regardless of original position
             // if velocity is specified, it's in pixels per milliseconds
             let h = global.stage.height;
             let delta = (h + this._lockScreenGroup.y);
             let min_velocity = global.stage.height / (CURTAIN_SLIDE_TIME * 1000);
 
             velocity = Math.max(min_velocity, velocity);
             let time = (delta / velocity) / 1000;
 
             Tweener.addTween(this._lockScreenGroup,
                              { y: -h,
                                time: time,
                                transition: 'easeInQuad',
                                onComplete: this._hideLockScreenComplete.bind(this),
                              });
         } else {
             this._hideLockScreenComplete();
         }
 
-        this._cursorTracker.set_pointer_visible(true);
+        this._showPointer();
     },
 
     _ensureUnlockDialog(onPrimary, allowCancel) {
         if (!this._dialog) {
             let constructor = Main.sessionMode.unlockDialog;
             if (!constructor) {
                 // This session mode has no locking capabilities
                 this.deactivate(true);
                 return false;
             }
 
             this._dialog = new constructor(this._lockDialogGroup);
 
 
             let time = global.get_current_time();
             if (!this._dialog.open(time, onPrimary)) {
                 // This is kind of an impossible error: we're already modal
                 // by the time we reach this...
                 log('Could not open login dialog: failed to acquire grab');
                 this.deactivate(true);
                 return false;
             }
 
             this._dialog.connect('failed', this._onUnlockFailed.bind(this));
         }
 
         this._dialog.allowCancel = allowCancel;
         return true;
     },
 
@@ -1069,69 +1088,61 @@ var ScreenShield = new Lang.Class({
             Mainloop.source_remove(this._arrowAnimationId);
             this._arrowAnimationId = 0;
         }
         if (this._arrowActiveWatchId) {
             this.idleMonitor.remove_watch(this._arrowActiveWatchId);
             this._arrowActiveWatchId = 0;
         }
         if (this._arrowWatchId) {
             this.idleMonitor.remove_watch(this._arrowWatchId);
             this._arrowWatchId = 0;
         }
     },
 
     _checkArrowAnimation() {
         let idleTime = this.idleMonitor.get_idletime();
 
         if (idleTime < ARROW_IDLE_TIME)
             this._startArrowAnimation();
         else
             this._pauseArrowAnimation();
     },
 
     _lockScreenShown(params) {
         if (this._dialog && !this._isGreeter) {
             this._dialog.destroy();
             this._dialog = null;
         }
 
         this._checkArrowAnimation();
 
-        let motionId = global.stage.connect('captured-event', (stage, event) => {
-            if (event.type() == Clutter.EventType.MOTION) {
-                this._cursorTracker.set_pointer_visible(true);
-                global.stage.disconnect(motionId);
-            }
-
-            return Clutter.EVENT_PROPAGATE;
-        });
-        this._cursorTracker.set_pointer_visible(false);
+        this._hidePointerUntilMotion();
 
         this._lockScreenState = MessageTray.State.SHOWN;
         this._lockScreenGroup.fixed_position_set = false;
         this._lockScreenScrollCounter = 0;
 
         if (params.fadeToBlack && params.animateFade) {
             // Take a beat
 
             let id = Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, () => {
                 this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
                 return GLib.SOURCE_REMOVE;
             });
             GLib.Source.set_name_by_id(id, '[gnome-shell] this._activateFade');
         } else {
             if (params.fadeToBlack)
                 this._activateFade(this._shortLightbox, 0);
 
             this._completeLockScreenShown();
         }
     },
 
     _completeLockScreenShown() {
         this._setActive(true);
         this.emit('lock-screen-shown');
     },
 
     // Some of the actors in the lock screen are heavy in
     // resources, so we only create them when needed
     _ensureLockScreen() {
         if (this._hasLockScreen)
-- 
2.25.1