summaryrefslogtreecommitdiffstats
path: root/js/ui/endSessionDialog.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/ui/endSessionDialog.js')
-rw-r--r--js/ui/endSessionDialog.js798
1 files changed, 798 insertions, 0 deletions
diff --git a/js/ui/endSessionDialog.js b/js/ui/endSessionDialog.js
new file mode 100644
index 0000000..ca24d06
--- /dev/null
+++ b/js/ui/endSessionDialog.js
@@ -0,0 +1,798 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported init, EndSessionDialog */
+/*
+ * Copyright 2010-2016 Red Hat, Inc
+ *
+ * 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+const {
+ AccountsService, Clutter, Gio, GLib, GObject,
+ Pango, Polkit, Shell, St, UPowerGlib: UPower,
+} = imports.gi;
+
+const CheckBox = imports.ui.checkBox;
+const Dialog = imports.ui.dialog;
+const GnomeSession = imports.misc.gnomeSession;
+const LoginManager = imports.misc.loginManager;
+const ModalDialog = imports.ui.modalDialog;
+const UserWidget = imports.ui.userWidget;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const _ITEM_ICON_SIZE = 64;
+
+const LOW_BATTERY_THRESHOLD = 30;
+
+const EndSessionDialogIface = loadInterfaceXML('org.gnome.SessionManager.EndSessionDialog');
+
+const logoutDialogContent = {
+ subjectWithUser: C_("title", "Log Out %s"),
+ subject: C_("title", "Log Out"),
+ descriptionWithUser(user, seconds) {
+ return ngettext(
+ '%s will be logged out automatically in %d second.',
+ '%s will be logged out automatically in %d seconds.',
+ seconds).format(user, seconds);
+ },
+ description(seconds) {
+ return ngettext(
+ 'You will be logged out automatically in %d second.',
+ 'You will be logged out automatically in %d seconds.',
+ seconds).format(seconds);
+ },
+ showBatteryWarning: false,
+ confirmButtons: [{
+ signal: 'ConfirmedLogout',
+ label: C_('button', 'Log Out'),
+ }],
+ showOtherSessions: false,
+};
+
+const shutdownDialogContent = {
+ subject: C_("title", "Power Off"),
+ subjectWithUpdates: C_("title", "Install Updates & Power Off"),
+ description(seconds) {
+ return ngettext(
+ 'The system will power off automatically in %d second.',
+ 'The system will power off automatically in %d seconds.',
+ seconds).format(seconds);
+ },
+ checkBoxText: C_("checkbox", "Install pending software updates"),
+ showBatteryWarning: true,
+ confirmButtons: [{
+ signal: 'ConfirmedShutdown',
+ label: C_('button', 'Power Off'),
+ }],
+ iconName: 'system-shutdown-symbolic',
+ showOtherSessions: true,
+};
+
+const restartDialogContent = {
+ subject: C_("title", "Restart"),
+ subjectWithUpdates: C_('title', 'Install Updates & Restart'),
+ description(seconds) {
+ return ngettext(
+ 'The system will restart automatically in %d second.',
+ 'The system will restart automatically in %d seconds.',
+ seconds).format(seconds);
+ },
+ checkBoxText: C_('checkbox', 'Install pending software updates'),
+ showBatteryWarning: true,
+ confirmButtons: [{
+ signal: 'ConfirmedReboot',
+ label: C_('button', 'Restart'),
+ }],
+ iconName: 'view-refresh-symbolic',
+ showOtherSessions: true,
+};
+
+const restartUpdateDialogContent = {
+
+ subject: C_("title", "Restart & Install Updates"),
+ description(seconds) {
+ 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);
+ },
+ showBatteryWarning: true,
+ confirmButtons: [{
+ signal: 'ConfirmedReboot',
+ label: C_('button', 'Restart &amp; Install'),
+ }],
+ unusedFutureButtonForTranslation: C_("button", "Install &amp; Power Off"),
+ unusedFutureCheckBoxForTranslation: C_("checkbox", "Power off after updates are installed"),
+ iconName: 'view-refresh-symbolic',
+ showOtherSessions: true,
+};
+
+const restartUpgradeDialogContent = {
+
+ subject: C_("title", "Restart & Install Upgrade"),
+ upgradeDescription(distroName, distroVersion) {
+ /* Translators: This is the text displayed for system upgrades in the
+ shut down dialog. First %s gets replaced with the distro name and
+ second %s with the distro version to upgrade to */
+ 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);
+ },
+ disableTimer: true,
+ showBatteryWarning: false,
+ confirmButtons: [{
+ signal: 'ConfirmedReboot',
+ label: C_('button', 'Restart &amp; Install'),
+ }],
+ iconName: 'view-refresh-symbolic',
+ showOtherSessions: true,
+};
+
+const DialogType = {
+ 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,
+ UPGRADE_RESTART: 4,
+};
+
+const DialogContent = {
+ 0 /* DialogType.LOGOUT */: logoutDialogContent,
+ 1 /* DialogType.SHUTDOWN */: shutdownDialogContent,
+ 2 /* DialogType.RESTART */: restartDialogContent,
+ 3 /* DialogType.UPDATE_RESTART */: restartUpdateDialogContent,
+ 4 /* DialogType.UPGRADE_RESTART */: restartUpgradeDialogContent,
+};
+
+var MAX_USERS_IN_SESSION_DIALOG = 5;
+
+const LogindSessionIface = loadInterfaceXML('org.freedesktop.login1.Session');
+const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
+
+const PkOfflineIface = loadInterfaceXML('org.freedesktop.PackageKit.Offline');
+const PkOfflineProxy = Gio.DBusProxy.makeProxyWrapper(PkOfflineIface);
+
+const UPowerIface = loadInterfaceXML('org.freedesktop.UPower.Device');
+const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface);
+
+function findAppFromInhibitor(inhibitor) {
+ let desktopFile;
+ try {
+ [desktopFile] = inhibitor.GetAppIdSync();
+ } catch (e) {
+ // XXX -- sometimes JIT inhibitors generated by gnome-session
+ // get removed too soon. Don't fail in this case.
+ log(`gnome-session gave us a dead inhibitor: ${inhibitor.get_object_path()}`);
+ return null;
+ }
+
+ if (!GLib.str_has_suffix(desktopFile, '.desktop'))
+ desktopFile += '.desktop';
+
+ return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile);
+}
+
+// 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;
+}
+
+function _setCheckBoxLabel(checkBox, text) {
+ let label = checkBox.getLabelActor();
+
+ if (text) {
+ label.set_text(text);
+ checkBox.show();
+ } else {
+ label.set_text('');
+ checkBox.hide();
+ }
+}
+
+function init() {
+ // This always returns the same singleton object
+ // By instantiating it initially, we register the
+ // bus object, etc.
+ new EndSessionDialog();
+}
+
+var EndSessionDialog = GObject.registerClass(
+class EndSessionDialog extends ModalDialog.ModalDialog {
+ _init() {
+ super._init({
+ styleClass: 'end-session-dialog',
+ destroyOnClose: false,
+ });
+
+ this._loginManager = LoginManager.getLoginManager();
+ this._canRebootToBootLoaderMenu = false;
+ this._getCanRebootToBootLoaderMenu();
+
+ this._userManager = AccountsService.UserManager.get_default();
+ this._user = this._userManager.get_user(GLib.get_user_name());
+ this._updatesPermission = null;
+
+ this._pkOfflineProxy = new PkOfflineProxy(Gio.DBus.system,
+ 'org.freedesktop.PackageKit',
+ '/org/freedesktop/PackageKit',
+ this._onPkOfflineProxyCreated.bind(this));
+
+ this._powerProxy = new UPowerProxy(Gio.DBus.system,
+ 'org.freedesktop.UPower',
+ '/org/freedesktop/UPower/devices/DisplayDevice',
+ (proxy, error) => {
+ if (error) {
+ log(error.message);
+ return;
+ }
+ this._powerProxy.connect('g-properties-changed',
+ this._sync.bind(this));
+ this._sync();
+ });
+
+ this._secondsLeft = 0;
+ this._totalSecondsToStayOpen = 0;
+ this._applications = [];
+ this._sessions = [];
+ this._capturedEventId = 0;
+ this._rebootButton = null;
+ this._rebootButtonAlt = null;
+
+ this.connect('opened',
+ this._onOpened.bind(this));
+
+ this._user.connectObject(
+ 'notify::is-loaded', this._sync.bind(this),
+ 'changed', this._sync.bind(this), this);
+
+ this._messageDialogContent = new Dialog.MessageDialogContent();
+
+ this._checkBox = new CheckBox.CheckBox();
+ this._checkBox.connect('clicked', this._sync.bind(this));
+ this._messageDialogContent.add_child(this._checkBox);
+
+ this._batteryWarning = new St.Label({
+ style_class: 'end-session-dialog-battery-warning',
+ text: _('Low battery power: please plug in before installing updates.'),
+ });
+ this._batteryWarning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this._batteryWarning.clutter_text.line_wrap = true;
+ this._messageDialogContent.add_child(this._batteryWarning);
+
+ this.contentLayout.add_child(this._messageDialogContent);
+
+ 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'),
+ });
+ this.contentLayout.add_child(this._sessionSection);
+
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
+ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
+ }
+
+ async _getCanRebootToBootLoaderMenu() {
+ const {canRebootToBootLoaderMenu} = await this._loginManager.canRebootToBootLoaderMenu();
+ this._canRebootToBootLoaderMenu = canRebootToBootLoaderMenu;
+ }
+
+ async _onPkOfflineProxyCreated(proxy, error) {
+ 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.
+ try {
+ this._updatesPermission = await Polkit.Permission.new(
+ 'org.freedesktop.packagekit.trigger-offline-update', null, null);
+ } catch (e) {
+ log(`No permission to trigger offline updates: ${e}`);
+ }
+ }
+
+ _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;
+ }
+
+ _sync() {
+ let open = this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED;
+ if (!open)
+ return;
+
+ let dialogContent = DialogContent[this._type];
+
+ let subject = dialogContent.subject;
+
+ // Use different title when we are installing updates
+ if (dialogContent.subjectWithUpdates && this._checkBox.checked)
+ subject = dialogContent.subjectWithUpdates;
+
+ this._batteryWarning.visible = this._shouldShowLowBatteryWarning(dialogContent);
+
+ let description;
+ let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
+ this._secondsLeft,
+ 10);
+
+ 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);
+ }
+ }
+
+ // Use a different description when we are installing a system upgrade
+ // if the PackageKit proxy is available (i.e. PackageKit is available).
+ if (dialogContent.upgradeDescription) {
+ const { name, version } = this._updateInfo.PreparedUpgrade;
+ if (name != null && version != null)
+ description = dialogContent.upgradeDescription(name, version);
+ }
+
+ // Fall back to regular description
+ if (!description)
+ description = dialogContent.description(displayTime);
+
+ this._messageDialogContent.title = subject;
+ this._messageDialogContent.description = description;
+
+ let hasApplications = this._applications.length > 0;
+ let hasSessions = this._sessions.length > 0;
+
+ this._applicationSection.visible = hasApplications;
+ this._sessionSection.visible = hasSessions;
+ }
+
+ _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;
+ }
+
+ _updateButtons() {
+ this.clearButtons();
+
+ this.addButton({
+ action: this.cancel.bind(this),
+ label: _('Cancel'),
+ key: Clutter.KEY_Escape,
+ });
+
+ let dialogContent = DialogContent[this._type];
+ for (let i = 0; i < dialogContent.confirmButtons.length; i++) {
+ let signal = dialogContent.confirmButtons[i].signal;
+ let label = dialogContent.confirmButtons[i].label;
+ let button = this.addButton({
+ action: () => {
+ this.close(true);
+ let signalId = this.connect('closed', () => {
+ this.disconnect(signalId);
+ this._confirm(signal);
+ });
+ },
+ label,
+ });
+
+ // 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;
+ this._capturedEventId = this.connect('captured-event',
+ this._onCapturedEvent.bind(this));
+ }
+ }
+ }
+
+ _stopAltCapture() {
+ if (this._capturedEventId > 0) {
+ global.stage.disconnect(this._capturedEventId);
+ this._capturedEventId = 0;
+ }
+ this._rebootButton = null;
+ this._rebootButtonAlt = null;
+ }
+
+ close(skipSignal) {
+ super.close();
+
+ if (!skipSignal)
+ this._dbusImpl.emit_signal('Closed', null);
+ }
+
+ cancel() {
+ this._stopTimer();
+ this._stopAltCapture();
+ this._dbusImpl.emit_signal('Canceled', null);
+ this.close();
+ }
+
+ _confirmRebootToBootLoaderMenu() {
+ this._loginManager.setRebootToBootLoaderMenu();
+ this._confirm('ConfirmedReboot');
+ }
+
+ 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();
+ }
+ }
+
+ this._fadeOutDialog();
+ this._stopTimer();
+ this._stopAltCapture();
+ this._dbusImpl.emit_signal(signal, null);
+ }
+
+ _onOpened() {
+ this._sync();
+ }
+
+ async _triggerOfflineUpdateReboot() {
+ // Handle this gracefully if PackageKit is not available.
+ if (!this._pkOfflineProxy)
+ return;
+
+ try {
+ await this._pkOfflineProxy.TriggerAsync('reboot');
+ } catch (error) {
+ log(error.message);
+ }
+ }
+
+ async _triggerOfflineUpdateShutdown() {
+ // Handle this gracefully if PackageKit is not available.
+ if (!this._pkOfflineProxy)
+ return;
+
+ try {
+ await this._pkOfflineProxy.TriggerAsync('power-off');
+ } catch (error) {
+ log(error.message);
+ }
+ }
+
+ async _triggerOfflineUpdateCancel() {
+ // Handle this gracefully if PackageKit is not available.
+ if (!this._pkOfflineProxy)
+ return;
+
+ try {
+ await this._pkOfflineProxy.CancelAsync();
+ } catch (error) {
+ log(error.message);
+ }
+ }
+
+ _startTimer() {
+ let startTime = GLib.get_monotonic_time();
+ this._secondsLeft = this._totalSecondsToStayOpen;
+
+ this._timerId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
+ let currentTime = GLib.get_monotonic_time();
+ let secondsElapsed = (currentTime - startTime) / 1000000;
+
+ this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
+ if (this._secondsLeft > 0) {
+ this._sync();
+ return GLib.SOURCE_CONTINUE;
+ }
+
+ let dialogContent = DialogContent[this._type];
+ let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
+ this._confirm(button.signal);
+ this._timerId = 0;
+
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._timerId, '[gnome-shell] this._confirm');
+ }
+
+ _stopTimer() {
+ if (this._timerId > 0) {
+ GLib.source_remove(this._timerId);
+ this._timerId = 0;
+ }
+
+ this._secondsLeft = 0;
+ }
+
+ _onInhibitorLoaded(inhibitor) {
+ if (!this._applications.includes(inhibitor)) {
+ // Stale inhibitor
+ return;
+ }
+
+ let app = findAppFromInhibitor(inhibitor);
+ const [flags] = app ? inhibitor.GetFlagsSync() : [0];
+
+ if (app && flags & GnomeSession.InhibitFlags.LOGOUT) {
+ 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);
+ } else {
+ // inhibiting app is a service (not an application) or is not
+ // inhibiting logout/shutdown
+ this._applications.splice(this._applications.indexOf(inhibitor), 1);
+ }
+
+ this._sync();
+ }
+
+ 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}`);
+ }
+
+ 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);
+
+ if (proxy.Class !== 'user')
+ continue;
+
+ if (proxy.State === 'closing')
+ continue;
+
+ if (proxy.Id === sessionId)
+ continue;
+
+ 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();
+ }
+
+ 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) {
+ let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
+ this._totalSecondsToStayOpen = totalSecondsToStayOpen;
+ this._type = type;
+
+ try {
+ this._updateInfo = await this._getUpdateInfo();
+ } catch (e) {
+ if (this._pkOfflineProxy !== null)
+ log(`Failed to get update info from PackageKit: ${e.message}`);
+
+ this._updateInfo = {
+ UpdateTriggered: false,
+ UpdatePrepared: false,
+ UpgradeTriggered: false,
+ PreparedUpgrade: {},
+ };
+ }
+
+ // Only consider updates and upgrades if PackageKit is available.
+ if (this._pkOfflineProxy && this._type == DialogType.RESTART) {
+ if (this._updateInfo.UpdateTriggered)
+ this._type = DialogType.UPDATE_RESTART;
+ else if (this._updateInfo.UpgradeTriggered)
+ this._type = DialogType.UPGRADE_RESTART;
+ }
+
+ this._applications = [];
+ this._applicationSection.list.destroy_all_children();
+
+ this._sessions = [];
+ this._sessionSection.list.destroy_all_children();
+
+ if (!(this._type in DialogContent)) {
+ invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
+ "Unknown dialog type requested");
+ return;
+ }
+
+ let dialogContent = DialogContent[this._type];
+
+ for (let i = 0; i < inhibitorObjectPaths.length; i++) {
+ let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], proxy => {
+ this._onInhibitorLoaded(proxy);
+ });
+
+ this._applications.push(inhibitor);
+ }
+
+ if (dialogContent.showOtherSessions)
+ this._loadSessions();
+
+ let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
+
+ _setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText || '');
+ 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);
+
+ this._updateButtons();
+
+ if (!this.open(timestamp)) {
+ invocation.return_dbus_error('org.gnome.Shell.ModalDialog.GrabError',
+ "Cannot grab pointer and keyboard");
+ return;
+ }
+
+ if (!dialogContent.disableTimer)
+ this._startTimer();
+
+ this._sync();
+
+ let signalId = this.connect('opened', () => {
+ invocation.return_value(null);
+ this.disconnect(signalId);
+ });
+ }
+
+ Close(_parameters, _invocation) {
+ this.close();
+ }
+});