Skip to content

Commit ed81d01

Browse files
committed
screensaver: Clear cinnamon modal before activating.
We can't do anything about other toolkit grabs in x11, besides what we already do with libxdo, but we can close our own things to avoid failed grabs and locking delays. - Add a dismiss callback for pushModal (optional). - Add safe outs for users of pushModal - close popup menus, cancel expo, overview, close dialogs.
1 parent b3965ee commit ed81d01

8 files changed

Lines changed: 91 additions & 13 deletions

File tree

js/ui/expo.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ var Expo = GObject.registerClass({
261261
return;
262262
this.beforeShow();
263263
// Do this manually instead of using _syncInputMode, to handle failure
264-
if (!Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.EXPO))
264+
if (!Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.EXPO,
265+
() => this._dismissGrab()))
265266
return;
266267
this._modal = true;
267268
this._shown = true;
@@ -353,6 +354,15 @@ var Expo = GObject.registerClass({
353354
this._syncInputMode();
354355
}
355356

357+
// onDismiss handler for Main.dismissInternalModals().
358+
_dismissGrab() {
359+
this.hide();
360+
if (this._modal) {
361+
Main.popModal(this._group);
362+
this._modal = false;
363+
}
364+
}
365+
356366
toggle() {
357367
if (this._shown)
358368
this.hide();
@@ -369,7 +379,8 @@ var Expo = GObject.registerClass({
369379

370380
if (this._shown) {
371381
if (!this._modal) {
372-
if (Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.EXPO))
382+
if (Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.EXPO,
383+
() => this._dismissGrab()))
373384
this._modal = true;
374385
else
375386
this.hide();

js/ui/lookingGlass.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,8 @@ var Inspector = GObject.registerClass({
285285
reactive: true,
286286
});
287287
this._eventHandler = eventHandler;
288-
Main.pushModal(this._eventHandler, undefined, undefined, Cinnamon.ActionMode.LOOKING_GLASS);
288+
Main.pushModal(this._eventHandler, undefined, undefined, Cinnamon.ActionMode.LOOKING_GLASS,
289+
() => this._close());
289290
this.add_child(eventHandler);
290291
this._displayText = new St.Label({ style: 'text-align: center;' });
291292
eventHandler.add(this._displayText, { expand: true });

js/ui/main.js

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,7 +1388,7 @@ function _findModal(actor) {
13881388
return -1;
13891389
}
13901390

1391-
function _completeModalSetup(actor, mode) {
1391+
function _completeModalSetup(actor, mode, onDismiss) {
13921392
_modifierOnlyAction = 0;
13931393

13941394
if (modalCount == 0)
@@ -1409,7 +1409,8 @@ function _completeModalSetup(actor, mode) {
14091409
actor: actor,
14101410
focus: global.stage.get_key_focus(),
14111411
destroyId: actorDestroyId,
1412-
actionMode: mode
1412+
actionMode: mode,
1413+
onDismiss: onDismiss || null
14131414
};
14141415
if (record.focus != null) {
14151416
record.focusDestroyId = record.focus.connect('destroy', function() {
@@ -1431,6 +1432,8 @@ function _completeModalSetup(actor, mode) {
14311432
* @options (Meta.ModalOptions): (optional) flags to indicate that the pointer
14321433
* is already grabbed
14331434
* @mode (Cinnamon.ActionMode): (optional) action mode, defaults to SYSTEM_MODAL
1435+
* @onDismiss (function): (optional) callback invoked by dismissInternalModals()
1436+
* to cleanly release this grab.
14341437
*
14351438
* Ensure we are in a mode where all keyboard and mouse input goes to
14361439
* the stage, and focus @actor. Multiple calls to this function act in
@@ -1450,7 +1453,7 @@ function _completeModalSetup(actor, mode) {
14501453
*
14511454
* Returns (boolean): true iff we successfully acquired a grab or already had one
14521455
*/
1453-
function pushModal(actor, timestamp, options, mode) {
1456+
function pushModal(actor, timestamp, options, mode, onDismiss) {
14541457
if (timestamp == undefined)
14551458
timestamp = global.get_current_time();
14561459

@@ -1464,7 +1467,7 @@ function pushModal(actor, timestamp, options, mode) {
14641467
}
14651468
}
14661469

1467-
_completeModalSetup(actor, mode);
1470+
_completeModalSetup(actor, mode, onDismiss);
14681471
return true;
14691472
}
14701473

@@ -1590,6 +1593,52 @@ function popModal(actor, timestamp) {
15901593
Meta.enable_unredirect_for_display(global.display);
15911594
}
15921595

1596+
/**
1597+
* dismissInternalModals:
1598+
*
1599+
* Cleanly release every Cinnamon-internal modal grab currently on the stack.
1600+
* Used by the internal screensaver and called over dbus by cinnamon-screensaver
1601+
* -command for cinnamon-screensaver or custom-command mode.
1602+
*/
1603+
function dismissInternalModals() {
1604+
let guard = modalActorFocusStack.length * 2 + 4;
1605+
1606+
while (modalActorFocusStack.length > 0 && guard-- > 0) {
1607+
let record = modalActorFocusStack[modalActorFocusStack.length - 1];
1608+
let actor = record.actor;
1609+
1610+
if (typeof record.onDismiss === 'function') {
1611+
try {
1612+
record.onDismiss();
1613+
} catch (e) {
1614+
global.logError(`dismissInternalModals: onDismiss threw: ${e.message}`);
1615+
}
1616+
} else {
1617+
global.logWarning('dismissInternalModals: modal actor has no onDismiss; force-popping');
1618+
}
1619+
1620+
let idx = _findModal(actor);
1621+
if (idx !== -1) {
1622+
try {
1623+
popModal(actor);
1624+
} catch (e) {
1625+
global.logError(`dismissInternalModals: force-pop failed: ${e.message}`);
1626+
modalActorFocusStack.splice(idx, 1);
1627+
modalCount = Math.max(0, modalCount - 1);
1628+
}
1629+
}
1630+
}
1631+
1632+
if (modalActorFocusStack.length > 0 || modalCount > 0) {
1633+
global.logError('dismissInternalModals: stack non-empty after walk, forcing reset');
1634+
modalActorFocusStack.length = 0;
1635+
modalCount = 0;
1636+
global.end_modal(global.get_current_time());
1637+
global.set_stage_input_mode(Cinnamon.StageInputMode.NORMAL);
1638+
actionMode = Cinnamon.ActionMode.NORMAL;
1639+
}
1640+
}
1641+
15931642
/**
15941643
* createLookingGlass:
15951644
*

js/ui/modalDialog.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ class ModalDialog extends BaseDialog.BaseDialog {
200200
pushModal(timestamp, mode) {
201201
if (this._hasModal)
202202
return true;
203-
if (!Main.pushModal(this, timestamp, undefined, mode))
203+
if (!Main.pushModal(this, timestamp, undefined, mode, () => this.close()))
204204
return false;
205205

206206
this._hasModal = true;

js/ui/overview.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ var Overview = GObject.registerClass({
221221
if (this._shown || this.animationInProgress)
222222
return;
223223
// Do this manually instead of using _syncInputMode, to handle failure
224-
if (!Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.OVERVIEW))
224+
if (!Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.OVERVIEW,
225+
() => this._dismissGrab()))
225226
return;
226227
this._modal = true;
227228
this._shown = true;
@@ -295,6 +296,15 @@ var Overview = GObject.registerClass({
295296
this._buttonPressId = 0;
296297
}
297298

299+
// onDismiss handler for Main.dismissInternalModals().
300+
_dismissGrab() {
301+
this.hide();
302+
if (this._modal) {
303+
Main.popModal(this._group);
304+
this._modal = false;
305+
}
306+
}
307+
298308
toggle() {
299309
if (this._shown)
300310
this.hide();
@@ -311,7 +321,8 @@ var Overview = GObject.registerClass({
311321

312322
if (this._shown) {
313323
if (!this._modal) {
314-
if (Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.OVERVIEW))
324+
if (Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.OVERVIEW,
325+
() => this._dismissGrab()))
315326
this._modal = true;
316327
else
317328
this.hide();

js/ui/popupMenu.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2767,7 +2767,8 @@ var PopupMenuManager = class PopupMenuManager {
27672767
}
27682768

27692769
_grab() {
2770-
if (!Main.pushModal(this._owner.actor, undefined, undefined, Cinnamon.ActionMode.POPUP)) {
2770+
if (!Main.pushModal(this._owner.actor, undefined, undefined, Cinnamon.ActionMode.POPUP,
2771+
() => this._closeMenu())) {
27712772
return;
27722773
}
27732774
this._signals.connect(global.stage, 'captured-event', this._onEventCapture, this);

js/ui/screensaver/screenShield.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,9 @@ var ScreenShield = GObject.registerClass({
423423
this._allowFloating = this._settings.get_boolean('floating-widgets');
424424

425425
this._activationPending = true;
426+
427+
Main.dismissInternalModals();
428+
426429
_log('ScreenShield: requesting screensaver modal grab');
427430
Main.pushScreensaverModal(this, global.get_current_time(), Cinnamon.ActionMode.LOCK_SCREEN,
428431
(success) => {

js/ui/screenshot.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@ class SelectArea {
255255
}
256256

257257
show() {
258-
if (!Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.SYSTEM_MODAL))
258+
if (!Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.SYSTEM_MODAL,
259+
() => this._ungrab()))
259260
return;
260261
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
261262

@@ -465,7 +466,8 @@ class PickColor {
465466
}
466467

467468
show() {
468-
if (!Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.SYSTEM_MODAL))
469+
if (!Main.pushModal(this._group, undefined, undefined, Cinnamon.ActionMode.SYSTEM_MODAL,
470+
() => this._ungrab()))
469471
return;
470472
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
471473

0 commit comments

Comments
 (0)