diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 4307494e9..82b8440f9 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -156,6 +156,10 @@ StTooltip StLabel {
-slider-handle-radius: 0.5em;
}
+.popup-device-menu-item {
+ spacing: .5em;
+}
+
.popup-menu-icon {
icon-size: 1em;
}
diff --git a/js/Makefile.am b/js/Makefile.am
index eca172601..b7632b472 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -43,6 +43,7 @@ nobase_dist_js_DATA = \
ui/statusIconDispatcher.js \
ui/statusMenu.js \
ui/status/accessibility.js \
+ ui/status/power.js \
ui/status/volume.js \
ui/telepathyClient.js \
ui/tweener.js \
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 286699516..690d992c7 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -33,6 +33,7 @@ const STANDARD_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'blue
const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
+ 'battery': imports.ui.status.power.Indicator
};
const CLOCK_FORMAT_KEY = 'format';
diff --git a/js/ui/status/power.js b/js/ui/status/power.js
new file mode 100644
index 000000000..2e028532e
--- /dev/null
+++ b/js/ui/status/power.js
@@ -0,0 +1,230 @@
+/* -*- 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 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
+};
+
+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' },
+ { name: 'Tooltip', 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.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(_("What's using power..."),function() {
+ GLib.spawn_command_line_async('gnome-power-statistics --device wakeups');
+ });
+ this.menu.addAction(_("Power Settings"),function() {
+ GLib.spawn_command_line_async('gnome-control-center power');
+ });
+
+ 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, summary, percentage, state, time] = device;
+ if (device_type == UPDeviceType.BATTERY) {
+ this._hasPrimary = true;
+ this._batteryItem.label.text = summary;
+ 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;
+ }));
+ },
+
+ _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() {
+ let p = new Shell.Process({ args: ['gnome-power-statistics', '--device', device_id] });
+ p.run();
+ });
+ 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._proxy.GetRemote('Tooltip', Lang.bind(this, function(tooltip) {
+ this.setTooltip(tooltip);
+ }));
+ 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$"');
+ GLib.spawn_command_line_async('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, summary, percentage, state, time] = device;
+
+ this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' });
+ this._label = new St.Label({ text: summary });
+
+ let icon;
+ switch (state) {
+ case UPDeviceState.FULLY_CHARGED:
+ icon = 'battery-full-charged';
+ break;
+ case UPDeviceState.UNKNOWN:
+ icon = 'battery-missing';
+ break;
+ default:
+ icon = this._percentageToIcon(percentage) + (state == UPDeviceState.CHARGING ? '-charging' : '');
+ }
+
+ this._icon = new St.Icon({ icon_name: 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 percentBin = new St.Bin({ x_align: St.Align.END });
+ let percentLabel = new St.Label({ text: '%d%%'.format(percentage) });
+ percentBin.child = percentLabel;
+ this.addActor(percentBin);
+ },
+
+ _percentageToIcon: function(p) {
+ if (p > 60)
+ return 'battery-full';
+ if (p > 30)
+ return 'battery-good';
+ if (p > 10)
+ return 'battery-low';
+ if (p > 0)
+ return 'battery-caution';
+ return 'battery-empty';
+ }
+}
diff --git a/tools/build/gnome-shell-build-setup.sh b/tools/build/gnome-shell-build-setup.sh
index 3ffa1ae77..b5d856474 100755
--- a/tools/build/gnome-shell-build-setup.sh
+++ b/tools/build/gnome-shell-build-setup.sh
@@ -61,7 +61,7 @@ fi
# libjasper, libjpeg, libpng, libpulse, libtiff, libwnck,
# libxklavier, libxml2, ORBit2, pam, python, readline,
# spidermonkey ({mozilla,firefox,xulrunner}-js), startup-notification
-# xdamage, icon-naming-utils, libtool-ltdl, libvorbis
+# xdamage, icon-naming-utils, upower, libtool-ltdl, libvorbis
#
# Non-devel packages needed by gnome-shell and its deps:
# glxinfo, gstreamer-plugins-base, gstreamer-plugins-good,
@@ -117,7 +117,8 @@ if test "x$system" = xFedora ; then
libXdamage-devel libcroco-devel libxml2-devel gstreamer-devel
gstreamer-plugins-base gstreamer-plugins-good glx-utils expat-devel
startup-notification-devel xorg-x11-server-Xephyr gnome-terminal zenity
- icon-naming-utils libtool-ltdl-devel libvorbis-devel libxklavier-devel
+ icon-naming-utils upower-devel libtool-ltdl-devel libvorbis-devel
+ libxklavier-devel
"
if expr $version \>= 14 > /dev/null ; then
diff --git a/tools/build/gnome-shell.modules b/tools/build/gnome-shell.modules
index 501077160..044c99787 100644
--- a/tools/build/gnome-shell.modules
+++ b/tools/build/gnome-shell.modules
@@ -191,6 +191,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -214,9 +267,7 @@
change their ~/.jhbuildrc -->
-
-
-
+
@@ -225,6 +276,7 @@
+