@@ -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 *
0 commit comments