1
0
Fork 0
gnome-shell-extensions-extra/extensions/46/hibernate-status/extension.js
Daniel Baumann b96d6292ac
Adding upstream version 20240414.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-02-09 23:17:40 +01:00

573 lines
19 KiB
JavaScript

import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import St from 'gi://St';
import Clutter from 'gi://Clutter';
import * as LoginManager from 'resource:///org/gnome/shell/misc/loginManager.js';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as StatusSystem from 'resource:///org/gnome/shell/ui/status/system.js';
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
import * as ExtensionSystem from 'resource:///org/gnome/shell/ui/extensionSystem.js';
import * as ModalDialog from 'resource:///org/gnome/shell/ui/modalDialog.js';
import * as Dialog from 'resource:///org/gnome/shell/ui/dialog.js';
import * as CheckBoxImport from 'resource:///org/gnome/shell/ui/checkBox.js';
import {loadInterfaceXML} from 'resource:///org/gnome/shell/misc/fileUtils.js';
const CheckBox = CheckBoxImport.CheckBox;
// Use __ () and N__() for the extension gettext domain, and reuse
// the shell domain with the default _() and N_()
import {Extension, gettext as __} from 'resource:///org/gnome/shell/extensions/extension.js';
export {__};
const N__ = function (e) {
return e;
};
const HIBERNATE_CHECK_TIMEOUT = 20000;
export default class HibernateButtonExtension extends Extension {
_loginManagerCanHibernate(asyncCallback) {
if (this._loginManager._proxy) {
// systemd path
this._loginManager._proxy.call(
'CanHibernate',
null,
Gio.DBusCallFlags.NONE,
-1,
null,
function (proxy, asyncResult) {
let result, error;
try {
result = proxy.call_finish(asyncResult).deep_unpack();
} catch (e) {
error = e;
}
if (error) asyncCallback(false);
else asyncCallback(!['no', 'na'].includes(result[0]));
}
);
} else {
this.can_hibernate_sourceID = GLib.idle_add(() => {
asyncCallback(false);
return false;
});
}
}
_loginManagerHibernate() {
if (this._setting.get_boolean('hibernate-works-check')) {
this._hibernateStarted = new Date();
this.hibernate_sourceID = GLib.timeout_add(
GLib.PRIORITY_DEFAULT,
HIBERNATE_CHECK_TIMEOUT,
() => this._checkDidHibernate()
);
}
if (this._loginManager._proxy) {
// systemd path
this._loginManager._proxy.call(
'Hibernate',
GLib.Variant.new('(b)', [true]),
Gio.DBusCallFlags.NONE,
-1,
null,
null
);
} else {
// Can't do in ConsoleKit
this._loginManager.emit('prepare-for-sleep', true);
this._loginManager.emit('prepare-for-sleep', false);
}
}
_loginManagerCanHybridSleep(asyncCallback) {
if (this._loginManager._proxy) {
// systemd path
this._loginManager._proxy.call(
'CanHybridSleep',
null,
Gio.DBusCallFlags.NONE,
-1,
null,
function (proxy, asyncResult) {
let result, error;
try {
result = proxy.call_finish(asyncResult).deep_unpack();
} catch (e) {
error = e;
}
if (error) asyncCallback(false);
else asyncCallback(!['no', 'na'].includes(result[0]));
}
);
} else {
this.can_hybrid_sleep_sourceID = GLib.idle_add(() => {
asyncCallback(false);
return false;
});
}
}
_loginManagerHybridSleep() {
if (this._loginManager._proxy) {
// systemd path
this._loginManager._proxy.call(
'HybridSleep',
GLib.Variant.new('(b)', [true]),
Gio.DBusCallFlags.NONE,
-1,
null,
null
);
} else {
// Can't do in ConsoleKit
this._loginManager.emit('prepare-for-sleep', true);
this._loginManager.emit('prepare-for-sleep', false);
}
}
_loginManagerCanSuspendThenHibernate(asyncCallback) {
if (this._loginManager._proxy) {
// systemd path
this._loginManager._proxy.call(
'CanSuspendThenHibernate',
null,
Gio.DBusCallFlags.NONE,
-1,
null,
function (proxy, asyncResult) {
let result, error;
try {
result = proxy.call_finish(asyncResult).deep_unpack();
} catch (e) {
error = e;
}
if (error) asyncCallback(false);
else asyncCallback(!['no', 'na'].includes(result[0]));
}
);
} else {
this.can_suspend_then_hibernate_sourceID = GLib.idle_add(() => {
asyncCallback(false);
return false;
});
}
}
_loginManagerSuspendThenHibernate() {
if (this._loginManager._proxy) {
// systemd path
this._loginManager._proxy.call(
'SuspendThenHibernate',
GLib.Variant.new('(b)', [true]),
Gio.DBusCallFlags.NONE,
-1,
null,
null
);
} else {
// Can't do in ConsoleKit
this._loginManager.emit('prepare-for-sleep', true);
this._loginManager.emit('prepare-for-sleep', false);
}
}
_updateHaveHibernate() {
this._loginManagerCanHibernate(result => {
log(`Able to hibernate: ${result}`);
this._haveHibernate = result;
this._updateHibernate();
});
}
_updateHibernate() {
this._hibernateMenuItem.visible =
this._haveHibernate && !Main.sessionMode.isLocked && this._setting.get_boolean('show-hibernate');
}
_updateHaveHybridSleep() {
this._loginManagerCanHybridSleep(result => {
log(`Able to hybrid-sleep: ${result}`);
this._haveHybridSleep = result;
this._updateHybridSleep();
});
}
_updateHybridSleep() {
this._hybridSleepMenuItem.visible =
this._haveHybridSleep && !Main.sessionMode.isLocked && this._setting.get_boolean('show-hybrid-sleep');
}
_updateHaveSuspendThenHibernate() {
this._loginManagerCanSuspendThenHibernate(result => {
log(`Able to suspend then hibernate: ${result}`);
this._haveSuspendThenHibernate = result;
this._updateSuspendThenHibernate();
});
}
_updateSuspendThenHibernate() {
this._suspendThenHibernateMenuItem.visible =
this._haveSuspendThenHibernate && !Main.sessionMode.isLocked && this._setting.get_boolean('show-suspend-then-hibernate');
}
_updateDefaults() {
console.log("Update defaults");
}
_onHibernateClicked() {
this.systemMenu._systemItem.menu.itemActivated();
if (this._setting.get_boolean('show-hibernate-dialog')) {
let HibernateDialogContent = {
subject: C_('title', __('Hibernate')),
description: __('Do you really want to hibernate the system?'),
confirmButtons: [
{
signal: 'Cancel',
label: C_('button', __('Cancel')),
key: Clutter.Escape,
},
{
signal: 'ConfirmedHibernate',
label: C_('button', __('Hibernate')),
default: true,
},
],
};
this._dialog = new ConfirmDialog(
HibernateDialogContent
);
this._dialog.connect('ConfirmedHibernate', () =>
this._loginManagerHibernate()
);
this._dialog.open();
} else {
this._loginManagerHibernate()
}
}
_onHybridSleepClicked() {
this.systemMenu._systemItem.menu.itemActivated();
this._loginManagerHybridSleep();
}
_onSuspendThenHibernateClicked() {
this.systemMenu._systemItem.menu.itemActivated();
this._loginManagerSuspendThenHibernate();
}
_disableExtension() {
Main.extensionManager.disableExtension('hibernate-status@dromi')
console.log('Disabled')
}
_cancelDisableExtension(notAgain) {
if (notAgain) this.setHibernateWorksCheckEnabled(false);
}
_checkRequirements() {
if (GLib.access('/run/systemd/seats', 0) < 0) {
let SystemdMissingDialogContent = {
subject: C_('title', __('Hibernate button: Systemd Missing')),
description: __('Systemd seems to be missing and is required.'),
confirmButtons: [
{
signal: 'Cancel',
label: C_('button', __('Cancel')),
key: Clutter.Escape,
},
{
signal: 'DisableExtension',
label: C_('button', __('Disable Extension')),
default: true,
},
],
iconName: 'document-save-symbolic',
iconStyleClass: 'end-session-dialog-shutdown-icon',
};
this._dialog = new ConfirmDialog(
SystemdMissingDialogContent
);
this._dialog.connect('DisableExtension', this._disableExtension);
this._dialog.open();
}
}
_checkDidHibernate() {
/* This function is called HIBERNATE_CHECK_TIMEOUT ms after
* hibernate started. If it is successful, at that point the GS
* process is already frozen; so when this function is actually
* called, way more than HIBERNATE_CHECK_TIMEOUT ms are passed*/
if (
new Date() - this._hibernateStarted >
HIBERNATE_CHECK_TIMEOUT + 5000
) {
// hibernate succeeded
return;
}
// hibernate failed
let HibernateFailedDialogContent = {
subject: C_('title', __('Hibernate button: Hibernate failed')),
description: __(
'Looks like hibernation failed. On some linux distributions hibernation is disabled ' +
'because not all hardware supports it well; ' +
'please check your distribution documentation ' +
'on how to enable it.'
),
checkBox: __("You are wrong, don't check this anymore!"),
confirmButtons: [
{
signal: 'Cancel',
label: C_('button', __('Cancel')),
key: Clutter.Escape,
},
{
signal: 'DisableExtension',
label: C_('button', __('Disable Extension')),
default: true,
},
],
iconName: 'document-save-symbolic',
iconStyleClass: 'end-session-dialog-shutdown-icon',
}
this._dialog = new ConfirmDialog(
HibernateFailedDialogContent
);
this._dialog.connect('DisableExtension', this._disableExtension);
this._dialog.connect('Cancel', this._cancelDisableExtension);
this._dialog.open();
}
setHibernateWorksCheckEnabled(enabled) {
let key = 'hibernate-works-check';
if (this._setting.is_writable(key)) {
if (this._setting.set_boolean(key, enabled)) {
Gio.Settings.sync();
} else {
throw this._errorSet(key);
}
} else {
throw this._errorWritable(key);
}
}
_modifySystemItem() {
this._setting = this.getSettings()
this._checkRequirements();
this._loginManager = LoginManager.getLoginManager();
this.systemMenu = Main.panel.statusArea.quickSettings._system;
this._hibernateMenuItem = new PopupMenu.PopupMenuItem(__('Hibernate'));
this._hibernateMenuItemId = this._hibernateMenuItem.connect(
'activate',
() => this._onHibernateClicked()
);
this._hybridSleepMenuItem = new PopupMenu.PopupMenuItem(
__('Hybrid Sleep')
);
this._hybridSleepMenuItemId = this._hybridSleepMenuItem.connect(
'activate',
() => this._onHybridSleepClicked()
);
this._suspendThenHibernateMenuItem = new PopupMenu.PopupMenuItem(
__('Suspend then Hibernate')
);
this._suspendThenHibernateMenuItemId = this._suspendThenHibernateMenuItem.connect(
'activate',
() => this._onSuspendThenHibernateClicked()
);
let afterSuspendPosition =
this.systemMenu._systemItem.menu.numMenuItems - 5;
this.systemMenu._systemItem.menu.addMenuItem(
this._hybridSleepMenuItem,
afterSuspendPosition
);
this.systemMenu._systemItem.menu.addMenuItem(
this._hibernateMenuItem,
afterSuspendPosition
);
this.systemMenu._systemItem.menu.addMenuItem(
this._suspendThenHibernateMenuItem,
afterSuspendPosition
);
this._menuOpenStateChangedId = this.systemMenu._systemItem.menu.connect(
'open-state-changed',
(menu, open) => {
if (!open) return;
this._updateDefaults();
this._updateHaveHibernate();
this._updateHaveHybridSleep();
this._updateHaveSuspendThenHibernate();
}
);
}
_queueModifySystemItem() {
this.sourceId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
if (!Main.panel.statusArea.quickSettings._system)
return GLib.SOURCE_CONTINUE;
this._modifySystemItem();
return GLib.SOURCE_REMOVE;
});
}
enable() {
if (!Main.panel.statusArea.quickSettings._system) {
this._queueModifySystemItem();
} else {
this._modifySystemItem();
}
}
disable() {
this._setting = null;
if (this._menuOpenStateChangedId) {
this.systemMenu._systemItem.menu.disconnect(
this._menuOpenStateChangedId
);
this._menuOpenStateChangedId = 0;
}
if (this._suspendThenHibernateMenuItemId) {
this._suspendThenHibernateMenuItem.disconnect(this._suspendThenHibernateMenuItemId);
this._suspendThenHibernateMenuItemId = 0;
}
if (this._hybridSleepMenuItemId) {
this._hybridSleepMenuItem.disconnect(this._hybridSleepMenuItemId);
this._hybridSleepMenuItemId = 0;
}
if (this._hibernateMenuItemId) {
this._hibernateMenuItem.disconnect(this._hibernateMenuItemId);
this._hibernateMenuItemId = 0;
}
if (this._suspendThenHibernateMenuItem) {
this._suspendThenHibernateMenuItem.destroy();
this._suspendThenHibernateMenuItem = 0;
}
if (this._hybridSleepMenuItem) {
this._hybridSleepMenuItem.destroy();
this._hybridSleepMenuItem = 0;
}
if (this._hibernateMenuItem) {
this._hibernateMenuItem.destroy();
this._hibernateMenuItem = 0;
}
if (this.sourceId) {
GLib.Source.remove(this.sourceId);
this.sourceId = null;
}
if (this.can_suspend_then_hibernate_sourceID) {
GLib.Source.remove(this.can_suspend_then_hibernate_sourceID);
this.can_suspend_then_hibernate_sourceID = null;
}
if (this.can_hybrid_sleep_sourceID) {
GLib.Source.remove(this.can_hybrid_sleep_sourceID);
this.can_hybrid_sleep_sourceID = null;
}
if (this.can_hibernate_sourceID) {
GLib.Source.remove(this.can_hibernate_sourceID);
this.can_hibernate_sourceID = null;
}
if (this.hibernate_sourceID) {
GLib.Source.remove(this.hibernate_sourceID);
this.hibernate_sourceID = null;
}
};
}
var ConfirmDialog = GObject.registerClass(
{
Signals: {
ConfirmedHibernate: {param_types: [GObject.TYPE_BOOLEAN]},
DisableExtension: {param_types: [GObject.TYPE_BOOLEAN]},
Cancel: {param_types: [GObject.TYPE_BOOLEAN]},
},
},
class ConfirmDialog extends ModalDialog.ModalDialog {
_init(dialog) {
super._init({
styleClass: 'end-session-dialog',
destroyOnClose: true,
});
this._messageDialogContent = new Dialog.MessageDialogContent();
this._messageDialogContent.description = dialog.description;
this._messageDialogContent.title = dialog.subject;
if (dialog.iconName) {
this._icon = new St.Icon({
icon_name: dialog.iconName,
icon_size: _DIALOG_ICON_SIZE,
style_class: dialog.iconStyleClass,
});
}
if (dialog.checkBox) {
this._checkBox = new CheckBox(dialog.checkBox);
this._messageDialogContent.add(this._checkBox.actor);
}
this.contentLayout.add_child(this._messageDialogContent);
let buttons = [];
for (let i = 0; i < dialog.confirmButtons.length; i++) {
let signal = dialog.confirmButtons[i].signal;
let label = dialog.confirmButtons[i].label;
let keys = dialog.confirmButtons[i].key;
buttons.push({
action: () => {
let signalId = this.connect('closed', () => {
this.disconnect(signalId);
this._confirm(signal);
});
this.close();
},
label: label,
key: keys,
});
}
this.setButtons(buttons);
}
_confirm(signal) {
var checked;
if (this._checkBox) checked = this._checkBox.actor.get_checked();
this.emit(signal, checked);
}
cancel() {
this.close();
}
}
);
const _DIALOG_ICON_SIZE = 32;