2011-09-28 13:16:26 +00:00
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/ *
2016-03-15 17:46:40 +00:00
* Copyright 2010 - 2016 Red Hat , Inc
2011-01-06 15:30:15 +00:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2014-01-07 21:32:37 +00:00
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2011-01-06 15:30:15 +00:00
* /
2023-07-10 09:53:00 +00:00
import AccountsService from 'gi://AccountsService' ;
import Clutter from 'gi://Clutter' ;
import Gio from 'gi://Gio' ;
import GLib from 'gi://GLib' ;
import GObject from 'gi://GObject' ;
import Pango from 'gi://Pango' ;
import Polkit from 'gi://Polkit' ;
import Shell from 'gi://Shell' ;
import St from 'gi://St' ;
import UPower from 'gi://UPowerGlib' ;
import * as CheckBox from './checkBox.js' ;
import * as Dialog from './dialog.js' ;
import * as GnomeSession from '../misc/gnomeSession.js' ;
import * as LoginManager from '../misc/loginManager.js' ;
import * as ModalDialog from './modalDialog.js' ;
import * as UserWidget from './userWidget.js' ;
import { loadInterfaceXML } from '../misc/fileUtils.js' ;
2018-09-06 00:55:20 +00:00
2019-10-18 21:18:10 +00:00
const _ITEM _ICON _SIZE = 64 ;
2011-01-06 15:30:15 +00:00
2020-07-25 00:08:16 +00:00
const LOW _BATTERY _THRESHOLD = 30 ;
2018-09-06 00:55:20 +00:00
const EndSessionDialogIface = loadInterfaceXML ( 'org.gnome.SessionManager.EndSessionDialog' ) ;
2011-01-06 15:30:15 +00:00
const logoutDialogContent = {
2023-08-06 22:34:20 +00:00
subjectWithUser : C _ ( 'title' , 'Log Out %s' ) ,
subject : C _ ( 'title' , 'Log Out' ) ,
2017-10-31 00:03:21 +00:00
descriptionWithUser ( user , seconds ) {
2020-07-01 14:44:16 +00:00
return ngettext (
'%s will be logged out automatically in %d second.' ,
'%s will be logged out automatically in %d seconds.' ,
seconds ) . format ( user , seconds ) ;
2011-09-07 11:34:48 +00:00
} ,
2017-10-31 00:03:21 +00:00
description ( seconds ) {
2020-07-01 14:44:16 +00:00
return ngettext (
'You will be logged out automatically in %d second.' ,
'You will be logged out automatically in %d seconds.' ,
seconds ) . format ( seconds ) ;
2011-09-07 11:34:48 +00:00
} ,
2014-02-19 18:59:47 +00:00
showBatteryWarning : false ,
2020-07-01 14:44:16 +00:00
confirmButtons : [ {
signal : 'ConfirmedLogout' ,
label : C _ ( 'button' , 'Log Out' ) ,
} ] ,
2013-08-30 11:40:40 +00:00
showOtherSessions : false ,
2011-01-06 15:30:15 +00:00
} ;
const shutdownDialogContent = {
2023-08-06 22:34:20 +00:00
subject : C _ ( 'title' , 'Power Off' ) ,
subjectWithUpdates : C _ ( 'title' , 'Install Updates & Power Off' ) ,
2017-10-31 00:03:21 +00:00
description ( seconds ) {
2020-07-01 14:44:16 +00:00
return ngettext (
'The system will power off automatically in %d second.' ,
'The system will power off automatically in %d seconds.' ,
seconds ) . format ( seconds ) ;
2011-09-07 11:34:48 +00:00
} ,
2023-08-06 22:34:20 +00:00
checkBoxText : C _ ( 'checkbox' , 'Install pending software updates' ) ,
2014-02-19 18:59:47 +00:00
showBatteryWarning : true ,
2020-07-01 14:44:16 +00:00
confirmButtons : [ {
signal : 'ConfirmedShutdown' ,
label : C _ ( 'button' , 'Power Off' ) ,
} ] ,
2012-05-30 13:58:37 +00:00
iconName : 'system-shutdown-symbolic' ,
2013-08-30 11:40:40 +00:00
showOtherSessions : true ,
2011-01-06 15:30:15 +00:00
} ;
const restartDialogContent = {
2023-08-06 22:34:20 +00:00
subject : C _ ( 'title' , 'Restart' ) ,
2020-07-01 14:49:17 +00:00
subjectWithUpdates : C _ ( 'title' , 'Install Updates & Restart' ) ,
2017-10-31 00:03:21 +00:00
description ( seconds ) {
2020-07-01 14:44:16 +00:00
return ngettext (
'The system will restart automatically in %d second.' ,
'The system will restart automatically in %d seconds.' ,
seconds ) . format ( seconds ) ;
2011-09-07 11:34:48 +00:00
} ,
2020-07-01 14:49:17 +00:00
checkBoxText : C _ ( 'checkbox' , 'Install pending software updates' ) ,
showBatteryWarning : true ,
2020-07-01 14:44:16 +00:00
confirmButtons : [ {
signal : 'ConfirmedReboot' ,
label : C _ ( 'button' , 'Restart' ) ,
} ] ,
2012-11-01 16:18:32 +00:00
iconName : 'view-refresh-symbolic' ,
2013-08-30 11:40:40 +00:00
showOtherSessions : true ,
2011-01-06 15:30:15 +00:00
} ;
2016-03-14 22:52:14 +00:00
const restartUpdateDialogContent = {
2013-08-26 04:52:41 +00:00
2023-08-06 22:34:20 +00:00
subject : C _ ( 'title' , 'Restart & Install Updates' ) ,
2017-10-31 00:03:21 +00:00
description ( seconds ) {
2020-07-01 14:44:16 +00:00
return ngettext (
'The system will automatically restart and install updates in %d second.' ,
'The system will automatically restart and install updates in %d seconds.' ,
seconds ) . format ( seconds ) ;
2013-08-26 04:52:41 +00:00
} ,
2014-02-19 18:59:47 +00:00
showBatteryWarning : true ,
2020-07-01 14:44:16 +00:00
confirmButtons : [ {
signal : 'ConfirmedReboot' ,
label : C _ ( 'button' , 'Restart & Install' ) ,
} ] ,
2023-08-06 22:34:20 +00:00
unusedFutureButtonForTranslation : C _ ( 'button' , 'Install & Power Off' ) ,
unusedFutureCheckBoxForTranslation : C _ ( 'checkbox' , 'Power off after updates are installed' ) ,
2013-08-26 04:52:41 +00:00
iconName : 'view-refresh-symbolic' ,
2013-08-30 11:40:40 +00:00
showOtherSessions : true ,
2013-08-26 04:52:41 +00:00
} ;
2016-03-15 17:46:40 +00:00
const restartUpgradeDialogContent = {
2023-08-06 22:34:20 +00:00
subject : C _ ( 'title' , 'Restart & Install Upgrade' ) ,
2017-10-31 00:03:21 +00:00
upgradeDescription ( distroName , distroVersion ) {
2016-03-15 17:46:40 +00:00
/ * T r a n s l a t o r s : T h i s i s t h e t e x t d i s p l a y e d f o r s y s t e m u p g r a d e s i n t h e
shut down dialog . First % s gets replaced with the distro name and
second % s with the distro version to upgrade to * /
2023-08-06 22:34:20 +00:00
return _ ( '%s %s will be installed after restart. Upgrade installation can take a long time: ensure that you have backed up and that the computer is plugged in.' ) . format ( distroName , distroVersion ) ;
2016-03-15 17:46:40 +00:00
} ,
disableTimer : true ,
showBatteryWarning : false ,
2020-07-01 14:44:16 +00:00
confirmButtons : [ {
signal : 'ConfirmedReboot' ,
label : C _ ( 'button' , 'Restart & Install' ) ,
} ] ,
2016-03-15 17:46:40 +00:00
iconName : 'view-refresh-symbolic' ,
showOtherSessions : true ,
} ;
2014-09-11 13:55:07 +00:00
const DialogType = {
2019-01-29 19:36:54 +00:00
LOGOUT : 0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */ ,
SHUTDOWN : 1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */ ,
RESTART : 2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */ ,
UPDATE _RESTART : 3 ,
2019-08-20 21:43:54 +00:00
UPGRADE _RESTART : 4 ,
2014-09-11 13:55:07 +00:00
} ;
2011-01-06 15:30:15 +00:00
const DialogContent = {
2014-09-11 16:04:36 +00:00
0 /* DialogType.LOGOUT */ : logoutDialogContent ,
1 /* DialogType.SHUTDOWN */ : shutdownDialogContent ,
2 /* DialogType.RESTART */ : restartDialogContent ,
2016-03-15 17:46:40 +00:00
3 /* DialogType.UPDATE_RESTART */ : restartUpdateDialogContent ,
2019-08-20 21:43:54 +00:00
4 /* DialogType.UPGRADE_RESTART */ : restartUpgradeDialogContent ,
2011-01-06 15:30:15 +00:00
} ;
2023-07-10 09:53:00 +00:00
const MAX _USERS _IN _SESSION _DIALOG = 5 ;
2013-08-22 18:35:28 +00:00
2018-09-06 00:55:20 +00:00
const LogindSessionIface = loadInterfaceXML ( 'org.freedesktop.login1.Session' ) ;
2013-08-22 18:35:28 +00:00
const LogindSession = Gio . DBusProxy . makeProxyWrapper ( LogindSessionIface ) ;
2018-09-06 00:55:20 +00:00
const PkOfflineIface = loadInterfaceXML ( 'org.freedesktop.PackageKit.Offline' ) ;
2014-09-09 11:16:25 +00:00
const PkOfflineProxy = Gio . DBusProxy . makeProxyWrapper ( PkOfflineIface ) ;
2020-07-25 00:08:16 +00:00
const UPowerIface = loadInterfaceXML ( 'org.freedesktop.UPower.Device' ) ;
2014-02-19 18:59:47 +00:00
const UPowerProxy = Gio . DBusProxy . makeProxyWrapper ( UPowerIface ) ;
2011-01-06 15:30:15 +00:00
function findAppFromInhibitor ( inhibitor ) {
2013-08-22 20:14:14 +00:00
let desktopFile ;
try {
[ desktopFile ] = inhibitor . GetAppIdSync ( ) ;
2019-01-29 01:26:39 +00:00
} catch ( e ) {
2013-08-22 20:14:14 +00:00
// XXX -- sometimes JIT inhibitors generated by gnome-session
// get removed too soon. Don't fail in this case.
2022-02-07 14:14:06 +00:00
log ( ` gnome-session gave us a dead inhibitor: ${ inhibitor . get _object _path ( ) } ` ) ;
2013-08-22 20:14:14 +00:00
return null ;
}
2011-01-06 15:30:15 +00:00
if ( ! GLib . str _has _suffix ( desktopFile , '.desktop' ) )
2012-03-17 02:13:37 +00:00
desktopFile += '.desktop' ;
2011-01-06 15:30:15 +00:00
2012-03-17 02:16:30 +00:00
return Shell . AppSystem . get _default ( ) . lookup _heuristic _basename ( desktopFile ) ;
2011-01-06 15:30:15 +00:00
}
// The logout timer only shows updates every 10 seconds
// until the last 10 seconds, then it shows updates every
// second. This function takes a given time and returns
// what we should show to the user for that time.
function _roundSecondsToInterval ( totalSeconds , secondsLeft , interval ) {
let time ;
time = Math . ceil ( secondsLeft ) ;
// Final count down is in decrements of 1
if ( time <= interval )
return time ;
// Round up higher than last displayable time interval
time += interval - 1 ;
// Then round down to that time interval
if ( time > totalSeconds )
time = Math . ceil ( totalSeconds ) ;
else
time -= time % interval ;
return time ;
}
2014-02-19 18:58:50 +00:00
function _setCheckBoxLabel ( checkBox , text ) {
let label = checkBox . getLabelActor ( ) ;
if ( text ) {
label . set _text ( text ) ;
2019-07-16 09:24:13 +00:00
checkBox . show ( ) ;
2014-02-19 18:58:50 +00:00
} else {
label . set _text ( '' ) ;
2019-07-16 09:24:13 +00:00
checkBox . hide ( ) ;
2014-02-19 18:58:50 +00:00
}
}
2023-07-10 09:53:00 +00:00
export function init ( ) {
2011-01-06 15:30:15 +00:00
// This always returns the same singleton object
// By instantiating it initially, we register the
// bus object, etc.
2019-08-19 19:38:51 +00:00
new EndSessionDialog ( ) ;
2011-01-06 15:30:15 +00:00
}
2023-07-10 09:53:00 +00:00
const EndSessionDialog = GObject . registerClass (
2019-05-23 20:45:44 +00:00
class EndSessionDialog extends ModalDialog . ModalDialog {
_init ( ) {
2020-03-29 21:51:13 +00:00
super . _init ( {
styleClass : 'end-session-dialog' ,
destroyOnClose : false ,
} ) ;
2011-01-06 15:30:15 +00:00
2013-08-22 18:35:28 +00:00
this . _loginManager = LoginManager . getLoginManager ( ) ;
2022-06-23 13:45:44 +00:00
this . _canRebootToBootLoaderMenu = false ;
2023-03-15 19:11:39 +00:00
this . _getCanRebootToBootLoaderMenu ( ) . catch ( logError ) ;
2018-08-15 13:03:56 +00:00
2013-08-22 18:35:28 +00:00
this . _userManager = AccountsService . UserManager . get _default ( ) ;
this . _user = this . _userManager . get _user ( GLib . get _user _name ( ) ) ;
2019-07-08 06:51:36 +00:00
this . _updatesPermission = null ;
2011-01-06 15:30:15 +00:00
2014-09-09 11:16:25 +00:00
this . _pkOfflineProxy = new PkOfflineProxy ( Gio . DBus . system ,
'org.freedesktop.PackageKit' ,
'/org/freedesktop/PackageKit' ,
2019-07-08 06:51:36 +00:00
this . _onPkOfflineProxyCreated . bind ( this ) ) ;
2014-02-19 18:59:47 +00:00
this . _powerProxy = new UPowerProxy ( Gio . DBus . system ,
'org.freedesktop.UPower' ,
2020-07-25 00:08:16 +00:00
'/org/freedesktop/UPower/devices/DisplayDevice' ,
2017-10-31 00:38:18 +00:00
( proxy , error ) => {
2014-02-19 18:59:47 +00:00
if ( error ) {
log ( error . message ) ;
return ;
}
this . _powerProxy . connect ( 'g-properties-changed' ,
2017-12-02 00:27:35 +00:00
this . _sync . bind ( this ) ) ;
2014-02-19 18:59:47 +00:00
this . _sync ( ) ;
2017-10-31 00:38:18 +00:00
} ) ;
2014-02-19 18:59:47 +00:00
2011-01-06 15:30:15 +00:00
this . _secondsLeft = 0 ;
this . _totalSecondsToStayOpen = 0 ;
2013-08-23 16:13:35 +00:00
this . _applications = [ ] ;
2013-08-22 18:35:28 +00:00
this . _sessions = [ ] ;
2018-08-15 13:03:56 +00:00
this . _capturedEventId = 0 ;
this . _rebootButton = null ;
this . _rebootButtonAlt = null ;
2011-01-06 15:30:15 +00:00
this . connect ( 'opened' ,
2017-12-02 00:27:35 +00:00
this . _onOpened . bind ( this ) ) ;
2011-01-06 15:30:15 +00:00
2021-08-15 22:36:59 +00:00
this . _user . connectObject (
'notify::is-loaded' , this . _sync . bind ( this ) ,
'changed' , this . _sync . bind ( this ) , this ) ;
2011-01-06 15:30:15 +00:00
2019-10-18 21:18:10 +00:00
this . _messageDialogContent = new Dialog . MessageDialogContent ( ) ;
2011-01-06 15:30:15 +00:00
2014-02-19 18:58:50 +00:00
this . _checkBox = new CheckBox . CheckBox ( ) ;
2019-07-16 09:24:13 +00:00
this . _checkBox . connect ( 'clicked' , this . _sync . bind ( this ) ) ;
2019-10-18 21:18:10 +00:00
this . _messageDialogContent . add _child ( this . _checkBox ) ;
2014-02-19 18:58:50 +00:00
2019-10-18 21:18:10 +00:00
this . _batteryWarning = new St . Label ( {
style _class : 'end-session-dialog-battery-warning' ,
2020-07-25 00:08:16 +00:00
text : _ ( 'Low battery power: please plug in before installing updates.' ) ,
2019-10-18 21:18:10 +00:00
} ) ;
2014-02-19 18:59:47 +00:00
this . _batteryWarning . clutter _text . ellipsize = Pango . EllipsizeMode . NONE ;
this . _batteryWarning . clutter _text . line _wrap = true ;
2019-10-18 21:18:10 +00:00
this . _messageDialogContent . add _child ( this . _batteryWarning ) ;
this . contentLayout . add _child ( this . _messageDialogContent ) ;
2014-02-19 18:59:47 +00:00
2020-01-17 13:07:57 +00:00
this . _applicationSection = new Dialog . ListSection ( {
title : _ ( 'Some applications are busy or have unsaved work' ) ,
} ) ;
this . contentLayout . add _child ( this . _applicationSection ) ;
this . _sessionSection = new Dialog . ListSection ( {
title : _ ( 'Other users are logged in' ) ,
2019-10-21 18:44:00 +00:00
} ) ;
2020-01-17 13:07:57 +00:00
this . contentLayout . add _child ( this . _sessionSection ) ;
2011-08-16 12:24:39 +00:00
2019-07-08 06:51:36 +00:00
this . _dbusImpl = Gio . DBusExportedObject . wrapJSObject ( EndSessionDialogIface , this ) ;
this . _dbusImpl . export ( Gio . DBus . session , '/org/gnome/SessionManager/EndSessionDialog' ) ;
}
2022-06-23 13:45:44 +00:00
async _getCanRebootToBootLoaderMenu ( ) {
2022-07-08 14:23:39 +00:00
const { canRebootToBootLoaderMenu } = await this . _loginManager . canRebootToBootLoaderMenu ( ) ;
2022-06-23 13:45:44 +00:00
this . _canRebootToBootLoaderMenu = canRebootToBootLoaderMenu ;
}
2019-12-19 19:50:37 +00:00
async _onPkOfflineProxyCreated ( proxy , error ) {
2019-07-08 06:51:36 +00:00
if ( error ) {
log ( error . message ) ;
return ;
}
// Creating a D-Bus proxy won't propagate SERVICE_UNKNOWN or NAME_HAS_NO_OWNER
// errors if PackageKit is not available, but the GIO implementation will make
// sure in that case that the proxy's g-name-owner is set to null, so check that.
if ( this . _pkOfflineProxy . g _name _owner === null ) {
this . _pkOfflineProxy = null ;
return ;
}
// It only makes sense to check for this permission if PackageKit is available.
2019-12-19 19:50:37 +00:00
try {
this . _updatesPermission = await Polkit . Permission . new (
'org.freedesktop.packagekit.trigger-offline-update' , null , null ) ;
} catch ( e ) {
2022-02-07 14:14:06 +00:00
log ( ` No permission to trigger offline updates: ${ e } ` ) ;
2019-12-19 19:50:37 +00:00
}
2017-10-31 01:19:44 +00:00
}
2011-01-06 15:30:15 +00:00
2020-07-25 00:08:16 +00:00
_isDischargingBattery ( ) {
return this . _powerProxy . IsPresent &&
this . _powerProxy . State !== UPower . DeviceState . CHARGING &&
this . _powerProxy . State !== UPower . DeviceState . FULLY _CHARGED ;
}
_isBatteryLow ( ) {
return this . _isDischargingBattery ( ) && this . _powerProxy . Percentage < LOW _BATTERY _THRESHOLD ;
}
_shouldShowLowBatteryWarning ( dialogContent ) {
if ( ! dialogContent . showBatteryWarning )
return false ;
if ( ! this . _isBatteryLow ( ) )
return false ;
if ( this . _checkBox . checked )
return true ;
// Show the warning if updates have already been triggered, but
// the user doesn't have enough permissions to cancel them.
let updatesAllowed = this . _updatesPermission && this . _updatesPermission . allowed ;
return this . _updateInfo . UpdatePrepared && this . _updateInfo . UpdateTriggered && ! updatesAllowed ;
}
2017-10-31 00:03:21 +00:00
_sync ( ) {
2019-08-19 19:38:51 +00:00
let open = this . state == ModalDialog . State . OPENING || this . state == ModalDialog . State . OPENED ;
2013-08-22 18:48:28 +00:00
if ( ! open )
2011-01-06 15:30:15 +00:00
return ;
let dialogContent = DialogContent [ this . _type ] ;
let subject = dialogContent . subject ;
2014-02-19 18:58:50 +00:00
// Use different title when we are installing updates
2019-07-16 09:24:13 +00:00
if ( dialogContent . subjectWithUpdates && this . _checkBox . checked )
2014-02-19 18:58:50 +00:00
subject = dialogContent . subjectWithUpdates ;
2020-07-25 00:08:16 +00:00
this . _batteryWarning . visible = this . _shouldShowLowBatteryWarning ( dialogContent ) ;
2014-02-19 18:59:47 +00:00
2013-08-22 18:48:28 +00:00
let description ;
2013-08-23 16:30:30 +00:00
let displayTime = _roundSecondsToInterval ( this . _totalSecondsToStayOpen ,
this . _secondsLeft ,
10 ) ;
2011-01-06 15:30:15 +00:00
2013-08-23 16:30:30 +00:00
if ( this . _user . is _loaded ) {
let realName = this . _user . get _real _name ( ) ;
if ( realName != null ) {
if ( dialogContent . subjectWithUser )
subject = dialogContent . subjectWithUser . format ( realName ) ;
if ( dialogContent . descriptionWithUser )
description = dialogContent . descriptionWithUser ( realName , displayTime ) ;
}
2011-01-06 15:30:15 +00:00
}
2016-03-15 17:46:40 +00:00
// Use a different description when we are installing a system upgrade
2019-07-08 06:51:36 +00:00
// if the PackageKit proxy is available (i.e. PackageKit is available).
2020-03-18 01:56:33 +00:00
if ( dialogContent . upgradeDescription ) {
const { name , version } = this . _updateInfo . PreparedUpgrade ;
2016-03-15 17:46:40 +00:00
if ( name != null && version != null )
description = dialogContent . upgradeDescription ( name , version ) ;
}
// Fall back to regular description
2013-08-23 16:30:30 +00:00
if ( ! description )
description = dialogContent . description ( displayTime ) ;
2019-10-18 21:18:10 +00:00
this . _messageDialogContent . title = subject ;
this . _messageDialogContent . description = description ;
2013-08-23 16:13:35 +00:00
let hasApplications = this . _applications . length > 0 ;
let hasSessions = this . _sessions . length > 0 ;
2020-01-17 13:07:57 +00:00
this . _applicationSection . visible = hasApplications ;
this . _sessionSection . visible = hasSessions ;
2017-10-31 01:19:44 +00:00
}
2012-06-05 09:51:36 +00:00
2018-08-15 13:03:56 +00:00
_onCapturedEvent ( actor , event ) {
let altEnabled = false ;
let type = event . type ( ) ;
if ( type !== Clutter . EventType . KEY _PRESS && type !== Clutter . EventType . KEY _RELEASE )
return Clutter . EVENT _PROPAGATE ;
let key = event . get _key _symbol ( ) ;
if ( key !== Clutter . KEY _Alt _L && key !== Clutter . KEY _Alt _R )
return Clutter . EVENT _PROPAGATE ;
if ( type === Clutter . EventType . KEY _PRESS )
altEnabled = true ;
this . _rebootButton . visible = ! altEnabled ;
this . _rebootButtonAlt . visible = altEnabled ;
return Clutter . EVENT _PROPAGATE ;
}
2017-10-31 00:03:21 +00:00
_updateButtons ( ) {
2018-08-15 12:26:19 +00:00
this . clearButtons ( ) ;
2020-03-29 21:51:13 +00:00
this . addButton ( {
action : this . cancel . bind ( this ) ,
label : _ ( 'Cancel' ) ,
key : Clutter . KEY _Escape ,
} ) ;
2011-03-21 20:12:30 +00:00
2018-08-15 12:26:19 +00:00
let dialogContent = DialogContent [ this . _type ] ;
2011-03-21 20:12:30 +00:00
for ( let i = 0 ; i < dialogContent . confirmButtons . length ; i ++ ) {
let signal = dialogContent . confirmButtons [ i ] . signal ;
let label = dialogContent . confirmButtons [ i ] . label ;
2018-08-15 12:26:19 +00:00
let button = this . addButton ( {
2019-02-12 14:02:09 +00:00
action : ( ) => {
let signalId = this . connect ( 'closed' , ( ) => {
this . disconnect ( signalId ) ;
2023-03-15 19:11:39 +00:00
this . _confirm ( signal ) . catch ( logError ) ;
2019-02-12 14:02:09 +00:00
} ) ;
2023-03-06 01:00:42 +00:00
this . close ( true ) ;
2019-02-12 14:02:09 +00:00
} ,
2019-08-19 19:06:04 +00:00
label ,
2019-02-12 14:02:09 +00:00
} ) ;
2018-08-15 13:03:56 +00:00
// Add Alt "Boot Options" option to the Reboot button
if ( this . _canRebootToBootLoaderMenu && signal === 'ConfirmedReboot' ) {
this . _rebootButton = button ;
this . _rebootButtonAlt = this . addButton ( {
action : ( ) => {
this . close ( true ) ;
let signalId = this . connect ( 'closed' , ( ) => {
this . disconnect ( signalId ) ;
this . _confirmRebootToBootLoaderMenu ( ) ;
} ) ;
} ,
label : C _ ( 'button' , 'Boot Options' ) ,
} ) ;
this . _rebootButtonAlt . visible = false ;
2022-02-01 13:12:17 +00:00
this . _capturedEventId = this . connect ( 'captured-event' ,
2018-08-15 13:03:56 +00:00
this . _onCapturedEvent . bind ( this ) ) ;
}
}
}
_stopAltCapture ( ) {
if ( this . _capturedEventId > 0 ) {
global . stage . disconnect ( this . _capturedEventId ) ;
this . _capturedEventId = 0 ;
2011-03-21 20:12:30 +00:00
}
2018-08-15 13:03:56 +00:00
this . _rebootButton = null ;
this . _rebootButtonAlt = null ;
2017-10-31 01:19:44 +00:00
}
2011-01-06 15:30:15 +00:00
2017-10-31 00:03:21 +00:00
close ( skipSignal ) {
2017-10-31 01:19:44 +00:00
super . close ( ) ;
2013-02-20 18:30:26 +00:00
if ( ! skipSignal )
this . _dbusImpl . emit _signal ( 'Closed' , null ) ;
2017-10-31 01:19:44 +00:00
}
2011-03-22 16:50:56 +00:00
2017-10-31 00:03:21 +00:00
cancel ( ) {
2011-01-06 15:30:15 +00:00
this . _stopTimer ( ) ;
2018-08-15 13:03:56 +00:00
this . _stopAltCapture ( ) ;
2011-08-16 12:24:39 +00:00
this . _dbusImpl . emit _signal ( 'Canceled' , null ) ;
2013-02-20 18:12:42 +00:00
this . close ( ) ;
2017-10-31 01:19:44 +00:00
}
2011-01-06 15:30:15 +00:00
2018-08-15 13:03:56 +00:00
_confirmRebootToBootLoaderMenu ( ) {
this . _loginManager . setRebootToBootLoaderMenu ( ) ;
2023-03-15 19:11:39 +00:00
this . _confirm ( 'ConfirmedReboot' ) . catch ( logError ) ;
2018-08-15 13:03:56 +00:00
}
2022-06-23 12:53:29 +00:00
async _confirm ( signal ) {
if ( this . _checkBox . visible ) {
// Trigger the offline update as requested
if ( this . _checkBox . checked ) {
switch ( signal ) {
case 'ConfirmedReboot' :
await this . _triggerOfflineUpdateReboot ( ) ;
break ;
case 'ConfirmedShutdown' :
// To actually trigger the offline update, we need to
// reboot to do the upgrade. When the upgrade is complete,
// the computer will shut down automatically.
signal = 'ConfirmedReboot' ;
await this . _triggerOfflineUpdateShutdown ( ) ;
break ;
default :
break ;
}
} else {
await this . _triggerOfflineUpdateCancel ( ) ;
2014-02-19 18:58:50 +00:00
}
}
2022-06-23 12:53:29 +00:00
this . _fadeOutDialog ( ) ;
this . _stopTimer ( ) ;
this . _stopAltCapture ( ) ;
this . _dbusImpl . emit _signal ( signal , null ) ;
2017-10-31 01:19:44 +00:00
}
2011-01-06 15:30:15 +00:00
2017-10-31 00:03:21 +00:00
_onOpened ( ) {
2013-08-22 18:48:28 +00:00
this . _sync ( ) ;
2017-10-31 01:19:44 +00:00
}
2011-01-06 15:30:15 +00:00
2022-06-23 12:53:29 +00:00
async _triggerOfflineUpdateReboot ( ) {
2019-07-08 06:51:36 +00:00
// Handle this gracefully if PackageKit is not available.
2022-06-23 12:53:29 +00:00
if ( ! this . _pkOfflineProxy )
2019-07-08 06:51:36 +00:00
return ;
2014-02-19 18:58:50 +00:00
2022-06-23 12:53:29 +00:00
try {
await this . _pkOfflineProxy . TriggerAsync ( 'reboot' ) ;
} catch ( error ) {
log ( error . message ) ;
}
2017-10-31 01:19:44 +00:00
}
2014-02-19 18:58:50 +00:00
2022-06-23 12:53:29 +00:00
async _triggerOfflineUpdateShutdown ( ) {
2019-07-08 06:51:36 +00:00
// Handle this gracefully if PackageKit is not available.
2022-06-23 12:53:29 +00:00
if ( ! this . _pkOfflineProxy )
2019-07-08 06:51:36 +00:00
return ;
2014-02-19 18:58:50 +00:00
2022-06-23 12:53:29 +00:00
try {
await this . _pkOfflineProxy . TriggerAsync ( 'power-off' ) ;
} catch ( error ) {
log ( error . message ) ;
}
2017-10-31 01:19:44 +00:00
}
2014-02-19 18:58:50 +00:00
2022-06-23 12:53:29 +00:00
async _triggerOfflineUpdateCancel ( ) {
2019-07-08 06:51:36 +00:00
// Handle this gracefully if PackageKit is not available.
2022-06-23 12:53:29 +00:00
if ( ! this . _pkOfflineProxy )
2019-07-08 06:51:36 +00:00
return ;
2014-02-19 18:58:50 +00:00
2022-06-23 12:53:29 +00:00
try {
await this . _pkOfflineProxy . CancelAsync ( ) ;
} catch ( error ) {
log ( error . message ) ;
}
2017-10-31 01:19:44 +00:00
}
2014-02-19 18:58:50 +00:00
2017-10-31 00:03:21 +00:00
_startTimer ( ) {
2013-01-26 08:01:41 +00:00
let startTime = GLib . get _monotonic _time ( ) ;
2011-01-06 15:30:15 +00:00
this . _secondsLeft = this . _totalSecondsToStayOpen ;
2013-01-26 08:01:41 +00:00
2019-08-19 18:50:33 +00:00
this . _timerId = GLib . timeout _add _seconds ( GLib . PRIORITY _DEFAULT , 1 , ( ) => {
2017-10-31 00:38:18 +00:00
let currentTime = GLib . get _monotonic _time ( ) ;
2019-08-19 19:38:51 +00:00
let secondsElapsed = ( currentTime - startTime ) / 1000000 ;
2013-01-26 08:01:41 +00:00
2017-10-31 00:38:18 +00:00
this . _secondsLeft = this . _totalSecondsToStayOpen - secondsElapsed ;
if ( this . _secondsLeft > 0 ) {
this . _sync ( ) ;
return GLib . SOURCE _CONTINUE ;
}
2013-01-26 08:01:41 +00:00
2017-10-31 00:38:18 +00:00
let dialogContent = DialogContent [ this . _type ] ;
let button = dialogContent . confirmButtons [ dialogContent . confirmButtons . length - 1 ] ;
2023-03-15 19:11:39 +00:00
this . _confirm ( button . signal ) . catch ( logError ) ;
2017-10-31 00:38:18 +00:00
this . _timerId = 0 ;
2013-01-26 08:01:41 +00:00
2017-10-31 00:38:18 +00:00
return GLib . SOURCE _REMOVE ;
} ) ;
2014-04-10 17:26:52 +00:00
GLib . Source . set _name _by _id ( this . _timerId , '[gnome-shell] this._confirm' ) ;
2017-10-31 01:19:44 +00:00
}
2011-01-06 15:30:15 +00:00
2017-10-31 00:03:21 +00:00
_stopTimer ( ) {
2013-08-22 20:17:52 +00:00
if ( this . _timerId > 0 ) {
2019-08-19 18:50:33 +00:00
GLib . source _remove ( this . _timerId ) ;
2013-01-26 08:01:41 +00:00
this . _timerId = 0 ;
}
2011-01-06 15:30:15 +00:00
this . _secondsLeft = 0 ;
2017-10-31 01:19:44 +00:00
}
2011-01-06 15:30:15 +00:00
2017-10-31 00:03:21 +00:00
_onInhibitorLoaded ( inhibitor ) {
2018-07-14 20:56:22 +00:00
if ( ! this . _applications . includes ( inhibitor ) ) {
2011-01-06 15:30:15 +00:00
// Stale inhibitor
return ;
}
let app = findAppFromInhibitor ( inhibitor ) ;
2020-08-30 16:57:19 +00:00
const [ flags ] = app ? inhibitor . GetFlagsSync ( ) : [ 0 ] ;
2011-01-06 15:30:15 +00:00
2020-08-30 16:57:19 +00:00
if ( app && flags & GnomeSession . InhibitFlags . LOGOUT ) {
2020-01-17 13:07:57 +00:00
let [ description ] = inhibitor . GetReasonSync ( ) ;
let listItem = new Dialog . ListSectionItem ( {
icon _actor : app . create _icon _texture ( _ITEM _ICON _SIZE ) ,
title : app . get _name ( ) ,
description ,
} ) ;
this . _applicationSection . list . add _child ( listItem ) ;
2011-01-06 15:30:15 +00:00
} else {
2020-08-30 16:57:19 +00:00
// inhibiting app is a service (not an application) or is not
// inhibiting logout/shutdown
2013-08-23 16:13:35 +00:00
this . _applications . splice ( this . _applications . indexOf ( inhibitor ) , 1 ) ;
2011-01-06 15:30:15 +00:00
}
2013-08-22 18:48:28 +00:00
this . _sync ( ) ;
2017-10-31 01:19:44 +00:00
}
2011-01-06 15:30:15 +00:00
2022-06-23 13:45:44 +00:00
async _loadSessions ( ) {
let sessionId = GLib . getenv ( 'XDG_SESSION_ID' ) ;
if ( ! sessionId ) {
const currentSessionProxy = await this . _loginManager . getCurrentSessionProxy ( ) ;
sessionId = currentSessionProxy . Id ;
log ( ` endSessionDialog: No XDG_SESSION_ID, fetched from logind: ${ sessionId } ` ) ;
}
2013-08-22 18:35:28 +00:00
2022-06-23 13:45:44 +00:00
const sessions = await this . _loginManager . listSessions ( ) ;
for ( const [ id _ , uid _ , userName , seat _ , sessionPath ] of sessions ) {
let proxy = new LogindSession ( Gio . DBus . system , 'org.freedesktop.login1' , sessionPath ) ;
2013-08-22 18:35:28 +00:00
2022-06-23 13:45:44 +00:00
if ( proxy . Class !== 'user' )
continue ;
2013-08-22 18:35:28 +00:00
2022-06-23 13:45:44 +00:00
if ( proxy . State === 'closing' )
continue ;
2013-08-22 18:35:28 +00:00
2022-06-23 13:45:44 +00:00
if ( proxy . Id === sessionId )
continue ;
2013-08-22 18:35:28 +00:00
2022-06-23 13:45:44 +00:00
const session = {
user : this . _userManager . get _user ( userName ) ,
username : userName ,
type : proxy . Type ,
remote : proxy . Remote ,
} ;
const nSessions = this . _sessions . push ( session ) ;
let userAvatar = new UserWidget . Avatar ( session . user , {
iconSize : _ITEM _ICON _SIZE ,
} ) ;
userAvatar . update ( ) ;
const displayUserName =
session . user . get _real _name ( ) ? ? session . username ;
let userLabelText ;
if ( session . remote )
/* Translators: Remote here refers to a remote session, like a ssh login */
userLabelText = _ ( '%s (remote)' ) . format ( displayUserName ) ;
else if ( session . type === 'tty' )
/* Translators: Console here refers to a tty like a VT console */
userLabelText = _ ( '%s (console)' ) . format ( displayUserName ) ;
else
userLabelText = userName ;
let listItem = new Dialog . ListSectionItem ( {
icon _actor : userAvatar ,
title : userLabelText ,
} ) ;
this . _sessionSection . list . add _child ( listItem ) ;
// limit the number of entries
if ( nSessions === MAX _USERS _IN _SESSION _DIALOG )
break ;
}
this . _sync ( ) ;
2017-10-31 01:19:44 +00:00
}
2013-08-22 18:35:28 +00:00
2020-03-18 01:56:33 +00:00
async _getUpdateInfo ( ) {
const connection = this . _pkOfflineProxy . get _connection ( ) ;
const reply = await connection . call (
this . _pkOfflineProxy . g _name ,
this . _pkOfflineProxy . g _object _path ,
'org.freedesktop.DBus.Properties' ,
'GetAll' ,
new GLib . Variant ( '(s)' , [ this . _pkOfflineProxy . g _interface _name ] ) ,
null ,
Gio . DBusCallFlags . NONE ,
- 1 ,
null ) ;
const [ info ] = reply . recursiveUnpack ( ) ;
return info ;
}
async OpenAsync ( parameters , invocation ) {
2011-08-16 12:24:39 +00:00
let [ type , timestamp , totalSecondsToStayOpen , inhibitorObjectPaths ] = parameters ;
2011-01-06 15:30:15 +00:00
this . _totalSecondsToStayOpen = totalSecondsToStayOpen ;
this . _type = type ;
2020-03-18 01:56:33 +00:00
try {
this . _updateInfo = await this . _getUpdateInfo ( ) ;
} catch ( e ) {
if ( this . _pkOfflineProxy !== null )
2022-02-07 14:14:06 +00:00
log ( ` Failed to get update info from PackageKit: ${ e . message } ` ) ;
2020-03-18 01:56:33 +00:00
this . _updateInfo = {
UpdateTriggered : false ,
UpdatePrepared : false ,
UpgradeTriggered : false ,
PreparedUpgrade : { } ,
} ;
}
2019-07-08 06:51:36 +00:00
// Only consider updates and upgrades if PackageKit is available.
if ( this . _pkOfflineProxy && this . _type == DialogType . RESTART ) {
2020-03-18 01:56:33 +00:00
if ( this . _updateInfo . UpdateTriggered )
2016-03-15 17:46:40 +00:00
this . _type = DialogType . UPDATE _RESTART ;
2020-03-18 01:56:33 +00:00
else if ( this . _updateInfo . UpgradeTriggered )
2016-03-15 17:46:40 +00:00
this . _type = DialogType . UPGRADE _RESTART ;
}
2014-02-18 21:17:58 +00:00
2013-08-23 16:13:35 +00:00
this . _applications = [ ] ;
2020-01-17 13:07:57 +00:00
this . _applicationSection . list . destroy _all _children ( ) ;
2013-08-23 16:13:35 +00:00
this . _sessions = [ ] ;
2020-01-17 13:07:57 +00:00
this . _sessionSection . list . destroy _all _children ( ) ;
2013-08-23 16:13:35 +00:00
2011-08-16 12:24:39 +00:00
if ( ! ( this . _type in DialogContent ) ) {
2012-02-28 23:45:33 +00:00
invocation . return _dbus _error ( 'org.gnome.Shell.ModalDialog.TypeError' ,
2023-08-06 22:34:20 +00:00
'Unknown dialog type requested' ) ;
2011-08-16 12:24:39 +00:00
return ;
}
2011-01-06 15:30:15 +00:00
2014-02-19 18:58:50 +00:00
let dialogContent = DialogContent [ this . _type ] ;
2011-01-06 15:30:15 +00:00
for ( let i = 0 ; i < inhibitorObjectPaths . length ; i ++ ) {
2019-02-04 11:30:53 +00:00
let inhibitor = new GnomeSession . Inhibitor ( inhibitorObjectPaths [ i ] , proxy => {
2011-08-16 12:24:39 +00:00
this . _onInhibitorLoaded ( proxy ) ;
2017-10-31 00:38:18 +00:00
} ) ;
2011-01-06 15:30:15 +00:00
2013-08-23 16:13:35 +00:00
this . _applications . push ( inhibitor ) ;
2011-01-06 15:30:15 +00:00
}
2014-02-19 18:58:50 +00:00
if ( dialogContent . showOtherSessions )
2023-03-15 19:11:39 +00:00
this . _loadSessions ( ) . catch ( logError ) ;
2013-08-22 18:35:28 +00:00
2014-02-19 18:58:50 +00:00
let updatesAllowed = this . _updatesPermission && this . _updatesPermission . allowed ;
2018-09-25 17:01:36 +00:00
_setCheckBoxLabel ( this . _checkBox , dialogContent . checkBoxText || '' ) ;
2020-07-25 00:08:16 +00:00
this . _checkBox . visible = dialogContent . checkBoxText && this . _updateInfo . UpdatePrepared && updatesAllowed ;
if ( this . _type === DialogType . UPGRADE _RESTART )
this . _checkBox . checked = this . _checkBox . visible && this . _updateInfo . UpdateTriggered && ! this . _isDischargingBattery ( ) ;
else
this . _checkBox . checked = this . _checkBox . visible && ! this . _isBatteryLow ( ) ;
this . _batteryWarning . visible = this . _shouldShowLowBatteryWarning ( dialogContent ) ;
2014-02-19 18:59:47 +00:00
2011-04-06 16:54:47 +00:00
this . _updateButtons ( ) ;
2011-08-16 12:24:39 +00:00
if ( ! this . open ( timestamp ) ) {
2012-02-28 23:45:33 +00:00
invocation . return _dbus _error ( 'org.gnome.Shell.ModalDialog.GrabError' ,
2023-08-06 22:34:20 +00:00
'Cannot grab pointer and keyboard' ) ;
2011-08-16 12:24:39 +00:00
return ;
}
2011-01-06 15:30:15 +00:00
2016-03-15 17:46:40 +00:00
if ( ! dialogContent . disableTimer )
this . _startTimer ( ) ;
2013-08-22 18:48:28 +00:00
this . _sync ( ) ;
2011-01-06 15:30:15 +00:00
2017-10-31 00:38:18 +00:00
let signalId = this . connect ( 'opened' , ( ) => {
invocation . return _value ( null ) ;
this . disconnect ( signalId ) ;
} ) ;
2017-10-31 01:19:44 +00:00
}
2012-12-04 23:32:59 +00:00
2019-01-31 14:08:10 +00:00
Close ( _parameters , _invocation ) {
2012-12-04 23:32:59 +00:00
this . close ( ) ;
2011-01-06 15:30:15 +00:00
}
2019-05-23 20:45:44 +00:00
} ) ;