From 8b3f05b6e3993aa51a230fd546de17062e7b07ee Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Mon, 15 Nov 2010 22:45:17 +0100 Subject: [PATCH] Status area: introduce battery & power indicator Add an indicator for battery charge and power indication in the system status area, using the newly exported DBus API from gnome-power-manager. https://bugzilla.gnome.org/show_bug.cgi?id=622451 --- data/theme/gnome-shell.css | 4 + js/Makefile.am | 1 + js/ui/panel.js | 1 + js/ui/status/power.js | 230 +++++++++++++++++++++++++ tools/build/gnome-shell-build-setup.sh | 5 +- tools/build/gnome-shell.modules | 58 ++++++- 6 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 js/ui/status/power.js 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 @@ +