From dc441b5d71f80e2b6dcdccbb4d6562c5b799294f Mon Sep 17 00:00:00 2001 From: Ray Strode 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