diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js index 6c5018006..3294d6556 100644 --- a/js/gdm/loginDialog.js +++ b/js/gdm/loginDialog.js @@ -1289,7 +1289,7 @@ var LoginDialog = GObject.registerClass({ this.opacity = 0; - Main.pushModal(global.stage, { actionMode: Shell.ActionMode.LOGIN_SCREEN }); + this._grab = Main.pushModal(global.stage, { actionMode: Shell.ActionMode.LOGIN_SCREEN }); this.ease({ opacity: 255, @@ -1301,7 +1301,8 @@ var LoginDialog = GObject.registerClass({ } close() { - Main.popModal(global.stage); + Main.popModal(this._grab); + this._grab = null; Main.ctrlAltTabManager.removeGroup(this); } diff --git a/js/ui/dnd.js b/js/ui/dnd.js index f6324f14e..d6a1f07c8 100644 --- a/js/ui/dnd.js +++ b/js/ui/dnd.js @@ -117,7 +117,6 @@ var _Draggable = class _Draggable { this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting). this._dragCancellable = true; - this._eventsGrabbed = false; this._capturedEventId = 0; } @@ -208,18 +207,22 @@ var _Draggable = class _Draggable { } _grabEvents(device, touchSequence) { - if (!this._eventsGrabbed) { - this._eventsGrabbed = Main.pushModal(_getEventHandlerActor()); - if (this._eventsGrabbed) + if (!this._eventsGrab) { + let grab = Main.pushModal(_getEventHandlerActor()); + if ((grab.get_seat_state() & Clutter.GrabState.POINTER) !== 0) { this._grabDevice(_getEventHandlerActor(), device, touchSequence); + this._eventsGrab = grab; + } else { + Main.popModal(grab); + } } } _ungrabEvents() { - if (this._eventsGrabbed) { + if (this._eventsGrab) { this._ungrabDevice(); - Main.popModal(_getEventHandlerActor()); - this._eventsGrabbed = false; + Main.popModal(this._eventsGrab); + this._eventsGrab = null; } } diff --git a/js/ui/grabHelper.js b/js/ui/grabHelper.js index 89e1bee32..47a332a24 100644 --- a/js/ui/grabHelper.js +++ b/js/ui/grabHelper.js @@ -181,9 +181,13 @@ var GrabHelper = class GrabHelper { _takeModalGrab() { let firstGrab = this._modalCount == 0; if (firstGrab) { - if (!Main.pushModal(this._owner, this._modalParams)) + let grab = Main.pushModal(this._owner, this._modalParams); + if (grab.get_seat_state() === Clutter.GrabState.NONE) { + Main.popModal(grab); return false; + } + this._grab = grab; this._capturedEventId = this._owner.connect('captured-event', (actor, event) => { return this.onCapturedEvent(event); @@ -202,7 +206,8 @@ var GrabHelper = class GrabHelper { this._owner.disconnect(this._capturedEventId); this._ignoreUntilRelease = false; - Main.popModal(this._owner); + Main.popModal(this._grab); + this._grab = null; } // ignoreRelease: diff --git a/js/ui/lookingGlass.js b/js/ui/lookingGlass.js index 93d08f3fa..ed217a4db 100644 --- a/js/ui/lookingGlass.js +++ b/js/ui/lookingGlass.js @@ -1560,9 +1560,13 @@ class LookingGlass extends St.BoxLayout { if (this._open) return; - if (!Main.pushModal(this, { actionMode: Shell.ActionMode.LOOKING_GLASS })) + let grab = Main.pushModal(this, { actionMode: Shell.ActionMode.LOOKING_GLASS }); + if (grab.get_seat_state() === Clutter.GrabState.NONE) { + Main.popModal(grab); return; + } + this._grab = grab; this._notebook.selectIndex(0); this.show(); this._open = true; @@ -1602,7 +1606,8 @@ class LookingGlass extends St.BoxLayout { duration, mode: Clutter.AnimationMode.EASE_OUT_QUAD, onComplete: () => { - Main.popModal(this); + Main.popModal(this._grab); + this._grab = null; this.hide(); }, }); diff --git a/js/ui/main.js b/js/ui/main.js index 11ac073bb..14a541f0a 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -503,9 +503,9 @@ function notifyError(msg, details) { notify(msg, details); } -function _findModal(actor) { +function _findModal(grab) { for (let i = 0; i < modalActorFocusStack.length; i++) { - if (modalActorFocusStack[i].actor == actor) + if (modalActorFocusStack[i].grab === grab) return i; } return -1; @@ -537,7 +537,7 @@ function _findModal(actor) { * global keybindings; the default of NONE will filter * out all keybindings * - * @returns {bool}: true iff we successfully acquired a grab or already had one + * @returns {Clutter.Grab}: the grab handle created */ function pushModal(actor, params) { params = Params.parse(params, { timestamp: global.get_current_time(), @@ -551,9 +551,9 @@ function pushModal(actor, params) { modalCount += 1; let actorDestroyId = actor.connect('destroy', () => { - let index = _findModal(actor); + let index = _findModal(grab); if (index >= 0) - popModal(actor); + popModal(grab); }); let prevFocus = global.stage.get_key_focus(); @@ -577,14 +577,13 @@ function pushModal(actor, params) { actionMode = params.actionMode; global.stage.set_key_focus(actor); - return true; + return grab; } /** * popModal: - * @param {Clutter.Actor} actor: the actor passed to original invocation - * of pushModal() - * @param {number=} timestamp: optional timestamp + * @param {Clutter.Grab} grab - the grab given by pushModal() + * @param {number=} timestamp - optional timestamp * * Reverse the effect of pushModal(). If this invocation is undoing * the topmost invocation, then the focus will be restored to the @@ -594,11 +593,11 @@ function pushModal(actor, params) { * initiated event. If not provided then the value of * global.get_current_time() is assumed. */ -function popModal(actor, timestamp) { +function popModal(grab, timestamp) { if (timestamp == undefined) timestamp = global.get_current_time(); - let focusIndex = _findModal(actor); + let focusIndex = _findModal(grab); if (focusIndex < 0) { global.stage.set_key_focus(null); actionMode = Shell.ActionMode.NORMAL; @@ -611,8 +610,7 @@ function popModal(actor, timestamp) { let record = modalActorFocusStack[focusIndex]; record.actor.disconnect(record.destroyId); - let grab = record.grab; - grab.dismiss(); + record.grab.dismiss(); if (focusIndex == modalActorFocusStack.length - 1) { if (record.prevFocus) diff --git a/js/ui/modalDialog.js b/js/ui/modalDialog.js index 4b3078ae5..259a9c6bb 100644 --- a/js/ui/modalDialog.js +++ b/js/ui/modalDialog.js @@ -204,7 +204,8 @@ var ModalDialog = GObject.registerClass({ this._savedKeyFocus = focus; else this._savedKeyFocus = null; - Main.popModal(this, timestamp); + Main.popModal(this._grab, timestamp); + this._grab = null; this._hasModal = false; if (!this._shellReactive) @@ -218,9 +219,13 @@ var ModalDialog = GObject.registerClass({ let params = { actionMode: this._actionMode }; if (timestamp) params['timestamp'] = timestamp; - if (!Main.pushModal(this, params)) + let grab = Main.pushModal(this, params); + if (grab.get_seat_state() === Clutter.GrabState.NONE) { + Main.popModal(grab); return false; + } + this._grab = grab; Main.layoutManager.emit('system-modal-opened'); this._hasModal = true; diff --git a/js/ui/overview.js b/js/ui/overview.js index bcfdca344..2a603ba02 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -492,9 +492,12 @@ var Overview = class { let shouldBeModal = !this._inXdndDrag; if (shouldBeModal && !this._modal) { let actionMode = Shell.ActionMode.OVERVIEW; - if (Main.pushModal(global.stage, { actionMode })) { + let grab = Main.pushModal(global.stage, { actionMode }); + if (grab.get_seat_state() !== Clutter.GrabState.NONE) { + this._grab = grab; this._modal = true; } else { + Main.popModal(grab); this.hide(); return false; } @@ -502,7 +505,8 @@ var Overview = class { } else { // eslint-disable-next-line no-lonely-if if (this._modal) { - Main.popModal(global.stage); + Main.popModal(this._grab); + this._grab = false; this._modal = false; } } diff --git a/js/ui/padOsd.js b/js/ui/padOsd.js index 9de5b4927..8362b101d 100644 --- a/js/ui/padOsd.js +++ b/js/ui/padOsd.js @@ -723,7 +723,7 @@ var PadOsd = GObject.registerClass({ buttonBox.add_actor(this._editButton); this._syncEditionMode(); - Main.pushModal(this); + this._grab = Main.pushModal(this); } _updatePadChooser() { @@ -919,7 +919,8 @@ var PadOsd = GObject.registerClass({ } _onDestroy() { - Main.popModal(this); + Main.popModal(this._grab); + this._grab = null; this._actionEditor.close(); let seat = Clutter.get_default_backend().get_default_seat(); diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index 3a0e53c91..a0c00067d 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -1329,8 +1329,10 @@ var PopupMenuManager = class { } removeMenu(menu) { - if (menu == this.activeMenu) - Main.popModal(menu.actor); + if (menu === this.activeMenu) { + Main.popModal(this._grab); + this._grab = null; + } let position = this._findMenu(menu); if (position == -1) // not a menu we manage @@ -1353,12 +1355,13 @@ var PopupMenuManager = class { if (open) { if (this.activeMenu) this.activeMenu.close(BoxPointer.PopupAnimation.FADE); - Main.pushModal(menu.actor, this._grabParams); + this._grab = Main.pushModal(menu.actor, this._grabParams); this.activeMenu = menu; } else { if (this.activeMenu === menu) this.activeMenu = null; - Main.popModal(menu.actor); + Main.popModal(this._grab); + this._grab = null; } } diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js index f18314b64..3d1a6eb1a 100644 --- a/js/ui/screenShield.js +++ b/js/ui/screenShield.js @@ -192,14 +192,15 @@ var ScreenShield = class { if (this._isModal) return true; - this._isModal = Main.pushModal(this.actor, { actionMode: Shell.ActionMode.LOCK_SCREEN }); - if (this._isModal) - return true; + let grab = Main.pushModal(this.actor, { actionMode: Shell.ActionMode.LOCK_SCREEN }); + + // We expect at least a keyboard grab here + this._isModal = (grab.get_seat_state() & Clutter.GrabState.KEYBOARD) !== 0; + if (this._isModal) + this._grab = grab; + else + Main.popModal(grab); - // We failed to get a pointer grab, it means that - // something else has it. Try with a keyboard grab only - this._isModal = Main.pushModal(this.actor, { options: Meta.ModalOptions.POINTER_ALREADY_GRABBED, - actionMode: Shell.ActionMode.LOCK_SCREEN }); return this._isModal; } @@ -550,7 +551,8 @@ var ScreenShield = class { this._dialog.popModal(); if (this._isModal) { - Main.popModal(this.actor); + Main.popModal(this._grab); + this._grab = null; this._isModal = false; } diff --git a/js/ui/switcherPopup.js b/js/ui/switcherPopup.js index ccc8b0fb0..17c3fcaa3 100644 --- a/js/ui/switcherPopup.js +++ b/js/ui/switcherPopup.js @@ -1,7 +1,7 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /* exported SwitcherPopup, SwitcherList */ -const { Clutter, GLib, GObject, Meta, St } = imports.gi; +const { Clutter, GLib, GObject, St } = imports.gi; const Main = imports.ui.main; @@ -100,11 +100,13 @@ var SwitcherPopup = GObject.registerClass({ if (this._items.length == 0) return false; - if (!Main.pushModal(this)) { - // Probably someone else has a pointer grab, try again with keyboard only - if (!Main.pushModal(this, { options: Meta.ModalOptions.POINTER_ALREADY_GRABBED })) - return false; + let grab = Main.pushModal(this); + // We expect at least a keyboard grab here + if ((grab.get_seat_state() & Clutter.GrabState.KEYBOARD) === 0) { + Main.popModal(grab); + return false; } + this._grab = grab; this._haveModal = true; this._modifierMask = primaryModifier(mask); @@ -306,7 +308,8 @@ var SwitcherPopup = GObject.registerClass({ _popModal() { if (this._haveModal) { - Main.popModal(this); + Main.popModal(this._grab); + this._grab = null; this._haveModal = false; } } diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js index b33da66a1..7fa307aa9 100644 --- a/js/ui/unlockDialog.js +++ b/js/ui/unlockDialog.js @@ -904,9 +904,13 @@ var UnlockDialog = GObject.registerClass({ timestamp, actionMode: Shell.ActionMode.UNLOCK_SCREEN, }; - if (!Main.pushModal(this, modalParams)) + let grab = Main.pushModal(this, modalParams); + if (grab.get_seat_state() !== Clutter.GrabState.ALL) { + Main.popModal(grab); return false; + } + this._grab = grab; this._isModal = true; return true; @@ -918,7 +922,8 @@ var UnlockDialog = GObject.registerClass({ popModal(timestamp) { if (this._isModal) { - Main.popModal(this, timestamp); + Main.popModal(this._grab, timestamp); + this._grab = null; this._isModal = false; } }