8bdfb8df68
Add Util.spawn, Util.spawnCommandLine, and Util.spawnDesktop for spawning a command/argv/.desktop file in the background, automatically handling errors via MessageTray.SystemNotificationSource(), and Util.trySpawn, Util.trySpawnCommandLine, and Utils.trySpawnDesktop that don't do automatic error handling (but do at least clean up the error message in the exception a bit). Update various other bits of code around the shell to use the new methods. https://bugzilla.gnome.org/show_bug.cgi?id=635089
262 lines
8.8 KiB
JavaScript
262 lines
8.8 KiB
JavaScript
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
|
|
const Gio = imports.gi.Gio;
|
|
const GLib = imports.gi.GLib;
|
|
const DBus = imports.dbus;
|
|
const Lang = imports.lang;
|
|
const Mainloop = imports.mainloop;
|
|
const Shell = imports.gi.Shell;
|
|
const St = imports.gi.St;
|
|
|
|
const PanelMenu = imports.ui.panelMenu;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
const Util = imports.misc.util;
|
|
|
|
const Gettext = imports.gettext.domain('gnome-shell');
|
|
const _ = Gettext.gettext;
|
|
|
|
const BUS_NAME = 'org.gnome.PowerManager';
|
|
const OBJECT_PATH = '/org/gnome/PowerManager';
|
|
|
|
const UPDeviceType = {
|
|
UNKNOWN: 0,
|
|
AC_POWER: 1,
|
|
BATTERY: 2,
|
|
UPS: 3,
|
|
MONITOR: 4,
|
|
MOUSE: 5,
|
|
KEYBOARD: 6,
|
|
PDA: 7,
|
|
PHONE: 8,
|
|
MEDIA_PLAYER: 9,
|
|
TABLET: 10,
|
|
COMPUTER: 11
|
|
};
|
|
|
|
const UPDeviceState = {
|
|
UNKNOWN: 0,
|
|
CHARGING: 1,
|
|
DISCHARGING: 2,
|
|
EMPTY: 3,
|
|
FULLY_CHARGED: 4,
|
|
PENDING_CHARGE: 5,
|
|
PENDING_DISCHARGE: 6
|
|
};
|
|
|
|
const PowerManagerInterface = {
|
|
name: 'org.gnome.PowerManager',
|
|
methods: [
|
|
{ name: 'GetDevices', inSignature: '', outSignature: 'a(susbut)' },
|
|
{ name: 'GetPrimaryDevice', inSignature: '', outSignature: '(susbut)' },
|
|
],
|
|
signals: [
|
|
{ name: 'Changed', outSignature: '' },
|
|
],
|
|
properties: [
|
|
{ name: 'Icon', signature: 's', access: 'read' },
|
|
]
|
|
};
|
|
let PowerManagerProxy = DBus.makeProxyClass(PowerManagerInterface);
|
|
|
|
function Indicator() {
|
|
this._init.apply(this, arguments);
|
|
}
|
|
|
|
Indicator.prototype = {
|
|
__proto__: PanelMenu.SystemStatusButton.prototype,
|
|
|
|
_init: function() {
|
|
PanelMenu.SystemStatusButton.prototype._init.call(this, 'battery-missing');
|
|
this._proxy = new PowerManagerProxy(DBus.session, BUS_NAME, OBJECT_PATH);
|
|
|
|
this._deviceItems = [ ];
|
|
this._hasPrimary = false;
|
|
this._primaryDeviceId = null;
|
|
|
|
this._batteryItem = new PopupMenu.PopupMenuItem('');
|
|
this._primaryPercentage = new St.Label();
|
|
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
|
this.menu.addMenuItem(this._batteryItem);
|
|
|
|
this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
|
|
this.menu.addMenuItem(this._deviceSep);
|
|
this._otherDevicePosition = 2;
|
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
|
|
this.menu.addAction(_("Power Settings"),function() {
|
|
Util.spawnDesktop('gnome-power-panel');
|
|
});
|
|
|
|
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
|
|
this._devicesChanged();
|
|
},
|
|
|
|
_readPrimaryDevice: function() {
|
|
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(device, error) {
|
|
if (error) {
|
|
this._checkError(error);
|
|
this._hasPrimary = false;
|
|
this._primaryDeviceId = null;
|
|
this._batteryItem.actor.hide();
|
|
this._deviceSep.actor.hide();
|
|
return;
|
|
}
|
|
let [device_id, device_type, icon, percentage, state, seconds] = device;
|
|
if (device_type == UPDeviceType.BATTERY) {
|
|
this._hasPrimary = true;
|
|
let time = Math.round(seconds / 60);
|
|
let minutes = time % 60;
|
|
let hours = Math.floor(time / 60);
|
|
let timestring;
|
|
if (time > 60) {
|
|
if (minutes == 0) {
|
|
timestring = Gettext.ngettext("%d hour remaining", "%d hours remaining", hours).format(hours);
|
|
} else {
|
|
/* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */
|
|
let template = _("%d %s %d %s remaining");
|
|
|
|
timestring = template.format (hours, Gettext.ngettext("hour", "hours", hours), minutes, Gettext.ngettext("minute", "minutes", minutes));
|
|
}
|
|
} else
|
|
timestring = Gettext.ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
|
|
this._batteryItem.label.text = timestring;
|
|
this._primaryPercentage.text = Math.round(percentage) + '%';
|
|
this._batteryItem.actor.show();
|
|
if (this._deviceItems.length > 0)
|
|
this._deviceSep.actor.show();
|
|
} else {
|
|
this._hasPrimary = false;
|
|
this._batteryItem.actor.hide();
|
|
this._deviceSep.actor.hide();
|
|
}
|
|
|
|
this._primaryDeviceId = device_id;
|
|
if (this._primaryDeviceId) {
|
|
this._batteryItem.actor.reactive = true;
|
|
this._batteryItem.actor.can_focus = true;
|
|
this._batteryItem.connect('activate', function(item) {
|
|
Util.spawn(['gnome-power-statistics', '--device', device_id]);
|
|
});
|
|
} else {
|
|
// virtual device
|
|
this._batteryItem.actor.reactive = false;
|
|
this._batteryItem.actor.can_focus = false;
|
|
}
|
|
}));
|
|
},
|
|
|
|
_readOtherDevices: function() {
|
|
this._proxy.GetDevicesRemote(Lang.bind(this, function(devices, error) {
|
|
this._deviceItems.forEach(function(i) { i.destroy(); });
|
|
this._deviceItems = [];
|
|
|
|
if (error) {
|
|
this._checkError(error);
|
|
this._deviceSep.actor.hide();
|
|
return;
|
|
}
|
|
|
|
let position = 0;
|
|
for (let i = 0; i < devices.length; i++) {
|
|
let [device_id, device_type] = devices[i];
|
|
if (device_type == UPDeviceType.AC_POWER || device_id == this._primaryDeviceId)
|
|
continue;
|
|
|
|
let item = new DeviceItem (devices[i]);
|
|
item.connect('activate', function() {
|
|
Util.spawn(['gnome-power-statistics', '--device', device_id]);
|
|
});
|
|
this._deviceItems.push(item);
|
|
this.menu.addMenuItem(item, this._otherDevicePosition + position);
|
|
position++;
|
|
}
|
|
|
|
if (this._hasPrimary && position > 0)
|
|
this._deviceSep.actor.show();
|
|
else
|
|
this._deviceSep.actor.hide();
|
|
}));
|
|
},
|
|
|
|
_devicesChanged: function() {
|
|
this._proxy.GetRemote('Icon', Lang.bind(this, function(icon, error) {
|
|
if (icon) {
|
|
let gicon = Shell.util_icon_from_string (icon);
|
|
this.setGIcon(gicon);
|
|
this.actor.show();
|
|
} else {
|
|
this._checkError(error);
|
|
this.menu.close();
|
|
this.actor.hide();
|
|
}
|
|
}));
|
|
this._readPrimaryDevice();
|
|
this._readOtherDevices();
|
|
},
|
|
|
|
_checkError: function(error) {
|
|
if (!this._restarted && error && error.message.match(/org\.freedesktop\.DBus\.Error\.(UnknownMethod|InvalidArgs)/)) {
|
|
GLib.spawn_command_line_sync('pkill -f "^gnome-power-manager$"');
|
|
Util.spawn(['gnome-power-manager']);
|
|
this._restarted = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
function DeviceItem() {
|
|
this._init.apply(this, arguments);
|
|
}
|
|
|
|
DeviceItem.prototype = {
|
|
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
|
|
|
|
_init: function(device) {
|
|
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
|
|
|
|
let [device_id, device_type, icon, percentage, state, time] = device;
|
|
|
|
this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' });
|
|
this._label = new St.Label({ text: this._deviceTypeToString(device_type) });
|
|
|
|
this._icon = new St.Icon({ gicon: Shell.util_icon_from_string(icon),
|
|
icon_type: St.IconType.SYMBOLIC,
|
|
style_class: 'popup-menu-icon' });
|
|
|
|
this._box.add_actor(this._icon);
|
|
this._box.add_actor(this._label);
|
|
this.addActor(this._box);
|
|
|
|
let percentLabel = new St.Label({ text: '%d%%'.format(Math.round(percentage)) });
|
|
this.addActor(percentLabel, { align: St.Align.END });
|
|
},
|
|
|
|
_deviceTypeToString: function(type) {
|
|
switch (type) {
|
|
case UPDeviceType.AC_POWER:
|
|
return _("AC adapter");
|
|
case UPDeviceType.BATTERY:
|
|
return _("Laptop battery");
|
|
case UPDeviceType.UPS:
|
|
return _("UPS");
|
|
case UPDeviceType.MONITOR:
|
|
return _("Monitor");
|
|
case UPDeviceType.MOUSE:
|
|
return _("Mouse");
|
|
case UPDeviceType.KEYBOARD:
|
|
return _("Keyboard");
|
|
case UPDeviceType.PDA:
|
|
return _("PDA");
|
|
case UPDeviceType.PHONE:
|
|
return _("Cell phone");
|
|
case UPDeviceType.MEDIA_PLAYER:
|
|
return _("Media player");
|
|
case UPDeviceType.TABLET:
|
|
return _("Tablet");
|
|
case UPDeviceType.COMPUTER:
|
|
return _("Computer");
|
|
default:
|
|
return _("Unknown");
|
|
}
|
|
}
|
|
}
|