a6b4d68a1d
The GDBus bindings in gjs have been updated to leverage metaclasses and gobject inheritance, which should result in cleaner and more maintainable code.
241 lines
7.9 KiB
JavaScript
241 lines
7.9 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
const Gio = imports.gi.Gio;
|
|
const GLib = imports.gi.GLib;
|
|
const Lang = imports.lang;
|
|
const St = imports.gi.St;
|
|
|
|
const PanelMenu = imports.ui.panelMenu;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
|
|
const BUS_NAME = 'org.gnome.SettingsDaemon';
|
|
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
|
|
|
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 = <interface name="org.gnome.SettingsDaemon.Power">
|
|
<method name="GetDevices">
|
|
<arg type="a(susdut)" direction="out" />
|
|
</method>
|
|
<method name="GetPrimaryDevice">
|
|
<arg type="(susdut)" direction="out" />
|
|
</method>
|
|
<property name="Icon" type="s" access="read" />
|
|
</interface>;
|
|
|
|
const PowerManagerProxy = new Gio.DBusProxyClass({
|
|
Name: 'PowerManagerProxy',
|
|
Interface: PowerManagerInterface,
|
|
|
|
_init: function() {
|
|
this.parent({ g_bus_type: Gio.BusType.SESSION,
|
|
g_name: BUS_NAME,
|
|
g_object_path: OBJECT_PATH });
|
|
}
|
|
});
|
|
|
|
const Indicator = new Lang.Class({
|
|
Name: 'PowerIndicator',
|
|
Extends: PanelMenu.SystemStatusButton,
|
|
|
|
_init: function() {
|
|
this.parent('battery-missing-symbolic', _("Battery"));
|
|
|
|
this._proxy = new PowerManagerProxy();
|
|
|
|
this._deviceItems = [ ];
|
|
this._hasPrimary = false;
|
|
this._primaryDeviceId = null;
|
|
|
|
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
|
this._primaryPercentage = new St.Label();
|
|
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
|
this.menu.addMenuItem(this._batteryItem);
|
|
|
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
this._otherDevicePosition = 2;
|
|
|
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
|
|
|
this._proxy.connect('g-properties-changed',
|
|
Lang.bind(this, this._devicesChanged));
|
|
this._proxy.init_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(proxy, result) {
|
|
proxy.init_finish(result);
|
|
|
|
this._devicesChanged();
|
|
}));
|
|
},
|
|
|
|
_readPrimaryDevice: function() {
|
|
this._proxy.GetPrimaryDeviceRemote(null, Lang.bind(this, function(proxy, result) {
|
|
let device_id, device_type, icon, percentage, state, seconds;
|
|
try {
|
|
[[device_id, device_type, icon, percentage, state, seconds]] =
|
|
proxy.GetPrimaryDeviceFinish(result);
|
|
} catch(e) {
|
|
this._hasPrimary = false;
|
|
this._primaryDeviceId = null;
|
|
this._batteryItem.actor.hide();
|
|
return;
|
|
}
|
|
|
|
if (device_type == UPDeviceType.BATTERY) {
|
|
this._hasPrimary = true;
|
|
let time = Math.round(seconds / 60);
|
|
if (time == 0) {
|
|
// 0 is reported when UPower does not have enough data
|
|
// to estimate battery life
|
|
this._batteryItem.label.text = _("Estimating...");
|
|
} else {
|
|
let minutes = time % 60;
|
|
let hours = Math.floor(time / 60);
|
|
let timestring;
|
|
if (time > 60) {
|
|
if (minutes == 0) {
|
|
timestring = 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, ngettext("hour", "hours", hours), minutes, ngettext("minute", "minutes", minutes));
|
|
}
|
|
} else
|
|
timestring = ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
|
|
this._batteryItem.label.text = timestring;
|
|
}
|
|
this._primaryPercentage.text = C_("percent of battery remaining", "%d%%").format(Math.round(percentage));
|
|
this._batteryItem.actor.show();
|
|
} else {
|
|
this._hasPrimary = false;
|
|
this._batteryItem.actor.hide();
|
|
}
|
|
|
|
this._primaryDeviceId = device_id;
|
|
}));
|
|
},
|
|
|
|
_readOtherDevices: function() {
|
|
this._proxy.GetDevicesRemote(null, Lang.bind(this, function(proxy, result) {
|
|
this._deviceItems.forEach(function(i) { i.destroy(); });
|
|
this._deviceItems = [];
|
|
|
|
let devices;
|
|
try {
|
|
[devices] = proxy.GetDevicesFinish(result);
|
|
} catch(e) {
|
|
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]);
|
|
this._deviceItems.push(item);
|
|
this.menu.addMenuItem(item, this._otherDevicePosition + position);
|
|
position++;
|
|
}
|
|
}));
|
|
},
|
|
|
|
_syncIcon: function() {
|
|
let icon = this._proxy.Icon;
|
|
let hasIcon = false;
|
|
|
|
if (icon) {
|
|
let gicon = Gio.icon_new_for_string(icon);
|
|
this.setGIcon(gicon);
|
|
hasIcon = true;
|
|
}
|
|
this.mainIcon.visible = hasIcon;
|
|
this.actor.visible = hasIcon;
|
|
},
|
|
|
|
_devicesChanged: function() {
|
|
this._syncIcon();
|
|
this._readPrimaryDevice();
|
|
this._readOtherDevices();
|
|
}
|
|
});
|
|
|
|
const DeviceItem = new Lang.Class({
|
|
Name: 'DeviceItem',
|
|
Extends: PopupMenu.PopupBaseMenuItem,
|
|
|
|
_init: function(device) {
|
|
this.parent({ reactive: false });
|
|
|
|
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: Gio.icon_new_for_string(icon),
|
|
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: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) });
|
|
this.addActor(percentLabel, { align: St.Align.END });
|
|
//FIXME: ideally we would like to expose this._label and percentLabel
|
|
this.actor.label_actor = percentLabel;
|
|
},
|
|
|
|
_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 C_("device", "Unknown");
|
|
}
|
|
}
|
|
});
|