|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: UTF-8 -*-
|
|
|
|
"""
|
|
|
|
Copyright 2006-2007 Sebastian Kügler, Canonical Ltd, Luka Renko
|
|
|
|
|
|
|
|
Authors:
|
|
|
|
Sebastian Kügler <sebas@kde.org>
|
|
|
|
Jonathan Riddell <jriddell@ubuntu.com>
|
|
|
|
Luka Renko <lure@kubuntu.org>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
"""
|
|
|
|
A frontend to HAL's power features for KDE.
|
|
|
|
Supports screen brightness, battery level, plugged/unplugged notices, laptop lid closing actions
|
|
|
|
Specification at https://wiki.kubuntu.org/KubuntuPowerManagement
|
|
|
|
|
|
|
|
Issues:
|
|
|
|
- We have to keep polling HAL rather than listening for signals because the Python DBUS bindings
|
|
|
|
don't have Qt mainloop integration
|
|
|
|
- Written in Python so will be slow to load up, will probably port to C++ Qt 4.2 in future
|
|
|
|
- Should also handle UPS and bluetooth batteries
|
|
|
|
- systray applet should be hidden if no battery, but then how do you suspend if no battery?
|
|
|
|
(ksmserver integration please)
|
|
|
|
- Needs lots more testing
|
|
|
|
- Use KUniqueApplication again as soon as dcop problem is sorted outc
|
|
|
|
- dcop calls need patch to dcopexport.py, already submitted upstream
|
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import subprocess
|
|
|
|
import dbus
|
|
|
|
|
|
|
|
from qt import *
|
|
|
|
from tdecore import *
|
|
|
|
from tdeui import *
|
|
|
|
|
|
|
|
from dcopext import DCOPClient, DCOPApp # used to lock the screen
|
|
|
|
from dcopexport import DCOPExObj
|
|
|
|
|
|
|
|
from guidance_power_manager_ui import PowerManagerUI
|
|
|
|
from notify import NotifyWidget
|
|
|
|
from tooltip import ToolTip
|
|
|
|
|
|
|
|
from powermanage import *
|
|
|
|
|
|
|
|
POLL_INTERVAL = 5000 # in milliseconds
|
|
|
|
|
|
|
|
class Notify(NotifyWidget):
|
|
|
|
""" Pop up a passive notication windows. """
|
|
|
|
|
|
|
|
def __init__(self,parent,msg,icon,caption=None):
|
|
|
|
NotifyWidget.__init__(self,parent,"notify")
|
|
|
|
self.setIcon(icon)
|
|
|
|
self.setText(msg)
|
|
|
|
if caption:
|
|
|
|
self.Caption(caption)
|
|
|
|
|
|
|
|
def setIcon(self,pixmap):
|
|
|
|
""" Set an icon to be displayed in the notification. """
|
|
|
|
if pixmap:
|
|
|
|
self.Icon.setPixmap(pixmap)
|
|
|
|
|
|
|
|
def setCaption(self,caption):
|
|
|
|
""" Text to show in bold letters. """
|
|
|
|
self.Caption.setText(QString("<b>")+caption+QString("</b>"))
|
|
|
|
|
|
|
|
def setText(self,msg):
|
|
|
|
"""" Set actual notification message. """
|
|
|
|
self.Text.setText(msg)
|
|
|
|
|
|
|
|
|
|
|
|
class PowerManager(PowerManagerUI):
|
|
|
|
""" Our configuration dialog. """
|
|
|
|
|
|
|
|
def __init__ (self, parent, name):
|
|
|
|
PowerManagerUI.__init__(self, parent, name)
|
|
|
|
TDEGlobal.iconLoader().addAppDir("guidance")
|
|
|
|
|
|
|
|
# The systray icon should show and hide the KDialogBase, not only this widget,
|
|
|
|
# therefore, it gets our parent as parent.
|
|
|
|
self.systray = KSystemTray(parent)
|
|
|
|
self.icon = "battery-charging-100"
|
|
|
|
self.systray.setPixmap(QPixmap(UserIcon(self.icon)))
|
|
|
|
self.connect(self.systray, SIGNAL("quitSelected()"), self.quit)
|
|
|
|
|
|
|
|
# Configuration filename
|
|
|
|
self.config = TDEConfig("power-managerrc")
|
|
|
|
|
|
|
|
self.powermanager = PowerManage()
|
|
|
|
|
|
|
|
def prepare(self):
|
|
|
|
""" Prepare UI. """
|
|
|
|
self._initBrightness()
|
|
|
|
self._initLid()
|
|
|
|
self._initBattery()
|
|
|
|
self.lastidlesec = 0
|
|
|
|
|
|
|
|
self._initConfigKeywords()
|
|
|
|
|
|
|
|
self._initUI(self.parent())
|
|
|
|
|
|
|
|
self.configToUi()
|
|
|
|
|
|
|
|
# Polling: evil. can't receive signals in python-dbus unless we have a glib mainloop,
|
|
|
|
# so we need to poll
|
|
|
|
self.pollTimer = QTimer(self)
|
|
|
|
self.connect(self.pollTimer, SIGNAL("timeout()"), self.poll)
|
|
|
|
self.pollTimer.start(POLL_INTERVAL) # 5 second poll, maybe make this configurable
|
|
|
|
self.poll(False)
|
|
|
|
|
|
|
|
# check CPU freq policy and notify if it was changed
|
|
|
|
msg = self.checkCpuFreq()
|
|
|
|
if msg != "":
|
|
|
|
self.notify(msg)
|
|
|
|
|
|
|
|
self.systray.show()
|
|
|
|
|
|
|
|
def _initBrightness(self):
|
|
|
|
""" Check for brightness support and disable widgets if it's not there. """
|
|
|
|
if not self.powermanager.hasBrightness:
|
|
|
|
self.PoweredBrightnessLabel.hide()
|
|
|
|
self.PoweredBrightnessSlider.hide()
|
|
|
|
self.BatteryBrightnessLabel.hide()
|
|
|
|
self.BatteryBrightnessSlider.hide()
|
|
|
|
|
|
|
|
def _initLid(self):
|
|
|
|
""" Check for lid support and disable widgets if it's not there. """
|
|
|
|
if not self.powermanager.hasLid:
|
|
|
|
self.LaptopLidRadios.setEnabled(False)
|
|
|
|
|
|
|
|
def _initCB(self, combo, options, values):
|
|
|
|
""" Initialize QComboBox with proper values from provided options. """
|
|
|
|
combo.clear()
|
|
|
|
for option in options:
|
|
|
|
combo.insertItem(values[option])
|
|
|
|
|
|
|
|
def _getCB(self, combo, options):
|
|
|
|
""" Get current item from QComboBox from config file (string) value. """
|
|
|
|
try:
|
|
|
|
return options[combo.currentItem()]
|
|
|
|
except IndexError:
|
|
|
|
return ""
|
|
|
|
|
|
|
|
def _setCB(self, combo, options, default, value):
|
|
|
|
""" Set current item in QComboBox from string value. """
|
|
|
|
try:
|
|
|
|
num = options.index(value)
|
|
|
|
except ValueError:
|
|
|
|
num = default
|
|
|
|
pass
|
|
|
|
combo.setCurrentItem(num)
|
|
|
|
|
|
|
|
def _getRB(self, radios, options):
|
|
|
|
""" Get current item from QRadioButton from config file (string) value. """
|
|
|
|
try:
|
|
|
|
return options[radios.selectedId()]
|
|
|
|
except IndexError:
|
|
|
|
return ""
|
|
|
|
|
|
|
|
def _setRB(self, radios, options, default, value):
|
|
|
|
""" Set current item in QRadioButton from string value. """
|
|
|
|
try:
|
|
|
|
num = options.index(value)
|
|
|
|
except ValueError:
|
|
|
|
num = default
|
|
|
|
pass
|
|
|
|
radios.setButton(num)
|
|
|
|
|
|
|
|
def _checkOldConfig(self, value, blank):
|
|
|
|
""" Convert old numerical values to keywords. """
|
|
|
|
try:
|
|
|
|
num_val = int(value)
|
|
|
|
except ValueError:
|
|
|
|
return value
|
|
|
|
if blank:
|
|
|
|
if num_val == 0: return 'nothing'
|
|
|
|
if num_val == 1: return 'blank'
|
|
|
|
if num_val == 2: return 'suspend'
|
|
|
|
if num_val == 3: return 'hibernate'
|
|
|
|
if num_val == 4: return 'shutdown'
|
|
|
|
else:
|
|
|
|
if num_val == 0: return 'nothing'
|
|
|
|
if num_val == 1: return 'suspend'
|
|
|
|
if num_val == 2: return 'hibernate'
|
|
|
|
if num_val == 3: return 'shutdown'
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
def _initConfigKeywords(self):
|
|
|
|
""" Define helper maps used with config file keywords. """
|
|
|
|
# map action keyword to displayed name (l10n)
|
|
|
|
self.act_name = {}
|
|
|
|
self.act_name['nothing'] = i18n("Do nothing")
|
|
|
|
self.act_name['blank'] = i18n("Blank screen")
|
|
|
|
self.act_name['suspend'] = i18n("Suspend")
|
|
|
|
self.act_name['hibernate'] = i18n("Hibernate")
|
|
|
|
self.act_name['shutdown'] = i18n("Shutdown")
|
|
|
|
|
|
|
|
# map action keyword to action methods
|
|
|
|
self.act_call = {}
|
|
|
|
self.act_call['nothing'] = None
|
|
|
|
self.act_call['blank'] = self.blankScreen
|
|
|
|
self.act_call['suspend'] = self.suspend
|
|
|
|
self.act_call['hibernate'] = self.hibernate
|
|
|
|
self.act_call['shutdown'] = self.shutdown
|
|
|
|
|
|
|
|
# map action keyword to notification description (l10n)
|
|
|
|
self.act_notify = {}
|
|
|
|
self.act_notify['nothing'] = i18n("doing nothing")
|
|
|
|
self.act_notify['blank'] = i18n("blanking screen")
|
|
|
|
self.act_notify['suspend'] = i18n("suspending")
|
|
|
|
self.act_notify['hibernate'] = i18n("hibernating")
|
|
|
|
self.act_notify['shutdown'] = i18n("shutting down")
|
|
|
|
|
|
|
|
# map action keyword to action icon used in notification window
|
|
|
|
self.act_icon = {}
|
|
|
|
self.act_icon['nothing'] = None
|
|
|
|
self.act_icon['blank'] = None
|
|
|
|
self.act_icon['suspend'] = SmallIcon("suspend")
|
|
|
|
self.act_icon['hibernate'] = SmallIcon("hibernate")
|
|
|
|
self.act_icon['shutdown'] = SmallIcon("exit")
|
|
|
|
|
|
|
|
# map policy keyword to displayed name (l10n)
|
|
|
|
self.freq_name = {}
|
|
|
|
self.freq_name['dynamic'] = i18n("Dynamic")
|
|
|
|
self.freq_name['powersave'] = i18n("Powersave")
|
|
|
|
self.freq_name['performance'] = i18n("Performance")
|
|
|
|
|
|
|
|
# map policy keyword to policy change methods
|
|
|
|
self.freq_call = {}
|
|
|
|
self.freq_call['dynamic'] = self.setCpuPolicyDynamic
|
|
|
|
self.freq_call['powersave'] = self.setCpuPolicyPowersave
|
|
|
|
self.freq_call['performance'] = self.setCpuPolicyPerformance
|
|
|
|
|
|
|
|
|
|
|
|
def _initUI(self, parent):
|
|
|
|
""" Build dynamic parts of the UI: context menu and tooltip. """
|
|
|
|
self.canSuspend = self.powermanager.canSuspend and not self.config.readBoolEntry("disableSuspend", False)
|
|
|
|
self.canHibernate = self.powermanager.canHibernate and not self.config.readBoolEntry("disableHibernate", False)
|
|
|
|
|
|
|
|
# Connect some signals. Updates in the dialogue apply instantly
|
|
|
|
self.connect(self.PoweredBrightnessSlider, SIGNAL("valueChanged(int)"), self.changePoweredBrightness)
|
|
|
|
self.connect(self.BatteryBrightnessSlider, SIGNAL("valueChanged(int)"), self.changeBatteryBrightness)
|
|
|
|
|
|
|
|
#Add a blank tooltip, the tooltipgroup signals are then used for our KPassivePopup
|
|
|
|
toolTipGroup = QToolTipGroup(self.systray)
|
|
|
|
QToolTip.add(self.systray, "", toolTipGroup, "blah")
|
|
|
|
self.connect(toolTipGroup, SIGNAL("showTip(const QString&)"), self.showTip)
|
|
|
|
self.connect(toolTipGroup, SIGNAL("removeTip()"), self.hideTip)
|
|
|
|
|
|
|
|
# Popup tooltip showing battery level
|
|
|
|
self.popup = KPassivePopup(self.systray)
|
|
|
|
|
|
|
|
self.tooltip = ToolTip(self.popup)
|
|
|
|
|
|
|
|
self._addBatteryWidgets()
|
|
|
|
|
|
|
|
self._addCpuWidgets()
|
|
|
|
self.popup.setView(self.tooltip)
|
|
|
|
|
|
|
|
# fill actions for LID
|
|
|
|
self.lid_act = ['nothing', 'blank', 'suspend', 'hibernate', 'shutdown']
|
|
|
|
self.lid_act_def = 0
|
|
|
|
# hide LID close actions that are not supported
|
|
|
|
if not self.canSuspend:
|
|
|
|
self.laptopClosedSuspend.hide()
|
|
|
|
if not self.canHibernate:
|
|
|
|
self.laptopClosedHibernate.hide()
|
|
|
|
|
|
|
|
# fill in only CPU policies that are supported by HW
|
|
|
|
self.cb_freq = [] # list of supported cpu freq policies
|
|
|
|
self.cb_freq_def = 0 # always use first policy as default
|
|
|
|
if self.powermanager.hasCpuFreqGovernors:
|
|
|
|
self.cb_freq = self.powermanager.getSupportedCpuPolicies()
|
|
|
|
if len(self.cb_freq) > 0:
|
|
|
|
self._initCB(self.PoweredFreqCombo, self.cb_freq, self.freq_name)
|
|
|
|
self._initCB(self.BatteryFreqCombo, self.cb_freq, self.freq_name)
|
|
|
|
else:
|
|
|
|
self.PoweredFreqLabel.hide()
|
|
|
|
self.PoweredFreqCombo.hide()
|
|
|
|
self.BatteryFreqLabel.hide()
|
|
|
|
self.BatteryFreqCombo.hide()
|
|
|
|
|
|
|
|
# fill actions in Idle/Critical battery combo boxes
|
|
|
|
self.cb_act = ['nothing'] # list of supported actions (keywords)
|
|
|
|
self.cb_act_def_critical = 0 # default action when critical battery
|
|
|
|
if self.canSuspend:
|
|
|
|
self.cb_act.append('suspend')
|
|
|
|
if self.canHibernate:
|
|
|
|
self.cb_act.append('hibernate')
|
|
|
|
self.cb_act_def_critical = len(self.cb_act) - 1 # hibernate
|
|
|
|
self.cb_act.append('shutdown')
|
|
|
|
if self.cb_act_def_critical == 0:
|
|
|
|
self.cb_act_def_critical = len(self.cb_act) - 1 # shutdown
|
|
|
|
self._initCB(self.PoweredIdleCombo, self.cb_act, self.act_name)
|
|
|
|
self._initCB(self.BatteryIdleCombo, self.cb_act, self.act_name)
|
|
|
|
self._initCB(self.BatteryCriticalCombo, self.cb_act, self.act_name)
|
|
|
|
|
|
|
|
self.connect(self.PoweredIdleCombo,SIGNAL("activated(int)"),self.slotPoweredIdleActivated)
|
|
|
|
self.connect(self.BatteryIdleCombo,SIGNAL("activated(int)"),self.slotBatteryIdleActivated)
|
|
|
|
self.connect(self.BatteryCriticalCombo,SIGNAL("activated(int)"),self.slotBatteryCriticalActivated)
|
|
|
|
|
|
|
|
# add suspend/hibernate to tray's context menu
|
|
|
|
menu = self.systray.contextMenu()
|
|
|
|
if self.canSuspend:
|
|
|
|
action = TDEAction( i18n("Suspend"), TDEShortcut(), self.suspend,
|
|
|
|
self.systray.actionCollection(), "suspend")
|
|
|
|
action.setIcon("suspend")
|
|
|
|
action.plug(menu)
|
|
|
|
if self.canHibernate:
|
|
|
|
action = TDEAction( i18n("Hibernate"), TDEShortcut(), self.hibernate,
|
|
|
|
self.systray.actionCollection(), "hibernate")
|
|
|
|
action.setIcon("hibernate")
|
|
|
|
action.plug(menu)
|
|
|
|
|
|
|
|
# add list of governators
|
|
|
|
if self.powermanager.hasCpuFreqGovernors and len(self.cb_freq) > 0:
|
|
|
|
submenu = TDEPopupMenu(menu)
|
|
|
|
for policy in self.cb_freq:
|
|
|
|
action = TDERadioAction(self.freq_name[policy], TDEShortcut(),
|
|
|
|
self.freq_call[policy],
|
|
|
|
self.systray.actionCollection(), policy)
|
|
|
|
action.setExclusiveGroup("freqs")
|
|
|
|
action.plug(submenu)
|
|
|
|
|
|
|
|
policy = self.powermanager.getCpuPolicy()
|
|
|
|
if policy in self.cb_freq:
|
|
|
|
self.systray.actionCollection().action(policy).setChecked(True);
|
|
|
|
menu.insertItem(i18n("CPU policy"), submenu)
|
|
|
|
|
|
|
|
|
|
|
|
# TDEGlobalAccel crashes the application in pytde
|
|
|
|
# see http://mats.gmd.de/pipermail/pytde/2006-May/013224.html
|
|
|
|
#self.globalActions = TDEGlobalAccel(self)
|
|
|
|
#self.suspendShortcut = TDEShortcut("XF86Sleep")
|
|
|
|
#self.hibernateShortcut = TDEShortcut("XF86Standby")
|
|
|
|
#self.hshutdownShortcut = TDEShortcut("XF86PowerOff")
|
|
|
|
#self.globalActions.insert("suspend", i18n("Suspend"), i18n("what's this?"), self.suspendShortcut, #self.suspendShortcut, self.suspend)
|
|
|
|
#self.globalActions.updateConnections()
|
|
|
|
|
|
|
|
def _initBattery(self):
|
|
|
|
""" Remove non-battery-related widgets if there's no battery bay. """
|
|
|
|
if not self.powermanager.hasBattery:
|
|
|
|
# Disable the Batterybox in the config dialogue,
|
|
|
|
self.BatteryBox.setEnabled(False)
|
|
|
|
# And change the icon in the systray, remove the restore option
|
|
|
|
# This way, we're basically becoming a systray applet, you can
|
|
|
|
# hibernate and suspend from
|
|
|
|
self.systray.setPixmap(QPixmap(UserIcon(self.icon)))
|
|
|
|
if self.powermanager.hasAC:
|
|
|
|
self.wasOnBattery = self.powermanager.onBattery()
|
|
|
|
|
|
|
|
def configToUi(self):
|
|
|
|
""" Setup the the values from the config file in the UI."""
|
|
|
|
# brightness.
|
|
|
|
if self.powermanager.hasBrightness:
|
|
|
|
brightness_high = self.powermanager.brightness_levels
|
|
|
|
self.BatteryBrightnessSlider.setMaxValue(self.powermanager.brightness_levels-1)
|
|
|
|
self.PoweredBrightnessSlider.setMaxValue(self.powermanager.brightness_levels-1)
|
|
|
|
self.BatteryBrightnessSlider.setValue(self.config.readNumEntry("batteryBrightness", int(brightness_high/2))) #default middle
|
|
|
|
self.PoweredBrightnessSlider.setValue(self.config.readNumEntry("poweredBrightness", brightness_high)) #default highest
|
|
|
|
|
|
|
|
tt_text = "Every step increases or decreases the brightness by %i%%" % int(100/brightness_high)
|
|
|
|
QToolTip.add(self.BatteryBrightnessSlider, tt_text)
|
|
|
|
QToolTip.add(self.PoweredBrightnessSlider, tt_text)
|
|
|
|
|
|
|
|
self.lockScreenOnResume.setChecked(self.config.readBoolEntry("lockOnResume", True))
|
|
|
|
|
|
|
|
# Idletime-related configuration
|
|
|
|
self._setCB(self.PoweredIdleCombo, self.cb_act, 0, str(self.config.readEntry("poweredIdleAction")))
|
|
|
|
self.PoweredIdleTime.setValue(self.config.readNumEntry("poweredIdleTime", 60))
|
|
|
|
self._setCB(self.BatteryIdleCombo, self.cb_act, 0, str(self.config.readEntry("batteryIdleAction")))
|
|
|
|
self.BatteryIdleTime.setValue(self.config.readNumEntry("batteryIdleTime", 10))
|
|
|
|
|
|
|
|
self._setCB(self.PoweredFreqCombo, self.cb_freq, self.cb_freq_def, str(self.config.readEntry("poweredFreqPolicy")))
|
|
|
|
self._setCB(self.BatteryFreqCombo, self.cb_freq, self.cb_freq_def, str(self.config.readEntry("batteryFreqPolicy")))
|
|
|
|
|
|
|
|
self.BatteryIdleTime.setValue(self.config.readNumEntry("batteryIdleTime", 10)) # default Do nothing
|
|
|
|
# battery critical and lid actions.
|
|
|
|
self._setCB(self.BatteryCriticalCombo, self.cb_act, self.cb_act_def_critical, self._checkOldConfig(self.config.readEntry("batteryCriticalAction", ""), False))
|
|
|
|
self._setRB(self.LaptopLidRadios, self.lid_act, self.lid_act_def, self._checkOldConfig(self.config.readEntry("laptopLidAction", ""), True))
|
|
|
|
self.CriticalRemainTime.setValue(self.config.readNumEntry("criticalRemainTime", BATTERY_CRITICAL_MINUTES))
|
|
|
|
self.criticalLevel = self.CriticalRemainTime.value()
|
|
|
|
|
|
|
|
# Call some slots to disable various spinboxes if necessary
|
|
|
|
self.slotBatteryCriticalActivated()
|
|
|
|
self.slotPoweredIdleActivated()
|
|
|
|
self.slotBatteryIdleActivated()
|
|
|
|
|
|
|
|
|
|
|
|
def uiToConfig(self):
|
|
|
|
""" Read all values from the UI and write them to the config file. """
|
|
|
|
self.config.writeEntry("poweredBrightness", self.PoweredBrightnessSlider.value())
|
|
|
|
self.config.writeEntry("batteryBrightness", self.BatteryBrightnessSlider.value())
|
|
|
|
|
|
|
|
self.config.writeEntry("poweredIdleTime", self.PoweredIdleTime.value())
|
|
|
|
self.config.writeEntry("poweredIdleAction", self._getCB(self.PoweredIdleCombo, self.cb_act))
|
|
|
|
self.config.writeEntry("batteryIdleTime", self.BatteryIdleTime.value())
|
|
|
|
self.config.writeEntry("batteryIdleAction", self._getCB(self.BatteryIdleCombo, self.cb_act))
|
|
|
|
self.config.writeEntry("poweredFreqPolicy", self._getCB(self.PoweredFreqCombo, self.cb_freq))
|
|
|
|
self.config.writeEntry("batteryFreqPolicy", self._getCB(self.BatteryFreqCombo, self.cb_freq))
|
|
|
|
|
|
|
|
self.config.writeEntry("batteryCriticalAction", self._getCB(self.BatteryCriticalCombo, self.cb_act))
|
|
|
|
self.config.writeEntry("criticalRemainTime", self.CriticalRemainTime.value())
|
|
|
|
|
|
|
|
self.config.writeEntry("laptopLidAction", self._getRB(self.LaptopLidRadios, self.lid_act))
|
|
|
|
self.config.writeEntry("lockOnResume", self.lockScreenOnResume.isChecked())
|
|
|
|
|
|
|
|
self.criticalLevel = self.CriticalRemainTime.value()
|
|
|
|
|
|
|
|
self.config.sync()
|
|
|
|
|
|
|
|
def quit(self):
|
|
|
|
""" Quit application. """
|
|
|
|
kapp.quit()
|
|
|
|
|
|
|
|
def showTip(self, text=""):
|
|
|
|
""" Pop up the tooltip showing battery data and CPU frequencies. """
|
|
|
|
self.popup.show()
|
|
|
|
|
|
|
|
|
|
|
|
def showBrightnessPopup(self):
|
|
|
|
if self.powermanager.onBattery():
|
|
|
|
value=self.BatteryBrightnessSlider.value()*100/self.BatteryBrightnessSlider.maxValue()
|
|
|
|
else:
|
|
|
|
value=self.PoweredBrightnessSlider.value()*100/self.PoweredBrightnessSlider.maxValue()
|
|
|
|
self.brightnessPopup = KPassivePopup.message('<b>Brightness:</b> '+str(value)+'%', self.systray)
|
|
|
|
"""pop.setTimeout(3000)"""
|
|
|
|
self.brightnessPopup.show()
|
|
|
|
|
|
|
|
def setBrightnessUp(self):
|
|
|
|
"""Increments slider value by 10%"""
|
|
|
|
if self.powermanager.onBattery():
|
|
|
|
self.BatteryBrightnessSlider.setValue(float(self.BatteryBrightnessSlider.value())+max(float(self.BatteryBrightnessSlider.maxValue())/float(10),1))
|
|
|
|
else:
|
|
|
|
self.PoweredBrightnessSlider.setValue(float(self.PoweredBrightnessSlider.value())+max(float(self.PoweredBrightnessSlider.maxValue())/float(10),1))
|
|
|
|
self.showBrightnessPopup()
|
|
|
|
|
|
|
|
def setBrightnessDown(self):
|
|
|
|
"""Decrements slider value by 10%"""
|
|
|
|
if self.powermanager.onBattery():
|
|
|
|
self.BatteryBrightnessSlider.setValue(float(self.BatteryBrightnessSlider.value())-max(float(self.BatteryBrightnessSlider.maxValue())/float(10),1))
|
|
|
|
else:
|
|
|
|
self.PoweredBrightnessSlider.setValue(float(self.PoweredBrightnessSlider.value())-max(float(self.PoweredBrightnessSlider.maxValue())/float(10),1))
|
|
|
|
self.showBrightnessPopup()
|
|
|
|
|
|
|
|
def getBrightness(self):
|
|
|
|
"""Work with percentages - it's a bit nicer"""
|
|
|
|
if self.powermanager.onBattery():
|
|
|
|
value=self.BatteryBrightnessSlider.value()*100/self.BatteryBrightnessSlider.maxValue()
|
|
|
|
else:
|
|
|
|
value=self.PoweredBrightnessSlider.value()*100/self.PoweredBrightnessSlider.maxValue()
|
|
|
|
return QString(str(value))
|
|
|
|
|
|
|
|
def hideTip(self):
|
|
|
|
""" Hide the tooltip."""
|
|
|
|
self.popup.hide()
|
|
|
|
|
|
|
|
def lockScreen(self):
|
|
|
|
""" locks the screen using kdesktop """
|
|
|
|
# create a new DCOP-Client:
|
|
|
|
client = DCOPClient()
|
|
|
|
# connect the client to the local DCOP-server:
|
|
|
|
client.attach()
|
|
|
|
# create a DCOP-Application-Object to talk to kdesktop:
|
|
|
|
kdesktop = DCOPApp('kdesktop', client)
|
|
|
|
# call a DCOP-function:
|
|
|
|
try:
|
|
|
|
ok, foo = kdesktop.KScreensaverIface.lock()
|
|
|
|
except:
|
|
|
|
print "Unable to lock the screen. The KDE Screensaver does not seem to be running."
|
|
|
|
def suspend(self):
|
|
|
|
""" Lock the screen and initiate a suspend to RAM (S3). """
|
|
|
|
if self.config.readBoolEntry("lockOnResume", True):
|
|
|
|
self.lockScreen()
|
|
|
|
try:
|
|
|
|
self.warningPopup.hide()
|
|
|
|
except AttributeError:
|
|
|
|
pass # No warningpopup, that's OK.
|
|
|
|
self.powermanager.suspend()
|
|
|
|
self.powermanager.resetIdleSeconds()
|
|
|
|
|
|
|
|
def hibernate(self):
|
|
|
|
""" Lock the screen and initiate a suspend to disk (S4). """
|
|
|
|
if self.config.readBoolEntry("lockOnResume", True):
|
|
|
|
self.lockScreen()
|
|
|
|
try:
|
|
|
|
self.warningPopup.hide()
|
|
|
|
except AttributeError:
|
|
|
|
pass # No warningpopup, that's OK.
|
|
|
|
self.powermanager.hibernate()
|
|
|
|
self.powermanager.resetIdleSeconds()
|
|
|
|
|
|
|
|
def shutdown(self):
|
|
|
|
""" Perform system shutdown. """
|
|
|
|
self.powermanager.shutdown()
|
|
|
|
|
|
|
|
def setCpuPolicyDynamic(self):
|
|
|
|
"""Change frequ for all cpu"""
|
|
|
|
self.powermanager.setCpuPolicy('dynamic')
|
|
|
|
self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['dynamic']))
|
|
|
|
|
|
|
|
def setCpuPolicyPerformance(self):
|
|
|
|
"""Change frequ for all cpu"""
|
|
|
|
self.powermanager.setCpuPolicy('performance')
|
|
|
|
self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['performance']))
|
|
|
|
|
|
|
|
def setCpuPolicyPowersave(self):
|
|
|
|
"""Change frequ for all cpu"""
|
|
|
|
self.powermanager.setCpuPolicy('powersave')
|
|
|
|
self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['powersave']))
|
|
|
|
|
|
|
|
def trySuspend(self):
|
|
|
|
""" If supported, lock the screen and initiate a suspend to RAM (S3). """
|
|
|
|
if self.canSuspend:
|
|
|
|
self.suspend()
|
|
|
|
else:
|
|
|
|
print "Warning: DCOP suspend() called, but not supported."
|
|
|
|
|
|
|
|
def tryHibernate(self):
|
|
|
|
""" If supported, lock the screen and initiate a suspend to disk (S4). """
|
|
|
|
if self.canHibernate:
|
|
|
|
self.hibernate()
|
|
|
|
else:
|
|
|
|
print "Warning: DCOP hibernate() called, but not supported."
|
|
|
|
|
|
|
|
def blankScreen(self):
|
|
|
|
""" Lock and blank screen. """
|
|
|
|
if self.config.readBoolEntry("lockOnResume", True):
|
|
|
|
self.lockScreen()
|
|
|
|
self.powermanager.blankScreen()
|
|
|
|
|
|
|
|
def _getIcon(self):
|
|
|
|
""" Set systray icon depending on battery status/level. """
|
|
|
|
if self.powermanager.hasBattery:
|
|
|
|
if self.batt_state == "not present":
|
|
|
|
self.icon = "ac-adapter"
|
|
|
|
if self.batt_state == "charged":
|
|
|
|
self.icon = "battery-charging-100"
|
|
|
|
elif self.batt_state == "discharging":
|
|
|
|
if self.batt_level >= 95:
|
|
|
|
self.icon = "battery-discharging-100"
|
|
|
|
elif self.batt_level < 95 and self.batt_level >= 85:
|
|
|
|
self.icon = "battery-discharging-090"
|
|
|
|
elif self.batt_level < 85 and self.batt_level >= 75:
|
|
|
|
self.icon = "battery-discharging-070"
|
|
|
|
elif self.batt_level < 75 and self.batt_level >= 60:
|
|
|
|
self.icon = "battery-discharging-060"
|
|
|
|
elif self.batt_level < 65 and self.batt_level >= 45:
|
|
|
|
self.icon = "battery-discharging-050"
|
|
|
|
elif self.batt_level < 45 and self.batt_level >= 30:
|
|
|
|
self.icon = "battery-discharging-040"
|
|
|
|
elif self.batt_level < 30 and self.batt_level >= 20:
|
|
|
|
self.icon = "battery-discharging-030"
|
|
|
|
elif self.batt_level < 20 and self.batt_level >= 10:
|
|
|
|
self.icon = "battery-discharging-020"
|
|
|
|
elif self.batt_level < 10 and self.batt_level >= 5:
|
|
|
|
self.icon = "battery-discharging-010"
|
|
|
|
else:
|
|
|
|
self.icon = "battery-discharging-000"
|
|
|
|
elif self.batt_state == "charging":
|
|
|
|
if self.batt_level >= 95:
|
|
|
|
self.icon = "battery-charging-100"
|
|
|
|
elif self.batt_level < 95 and self.batt_level >= 85:
|
|
|
|
self.icon = "battery-charging-090"
|
|
|
|
elif self.batt_level < 85 and self.batt_level >= 75:
|
|
|
|
self.icon = "battery-charging-070"
|
|
|
|
elif self.batt_level < 75 and self.batt_level >= 60:
|
|
|
|
self.icon = "battery-charging-060"
|
|
|
|
elif self.batt_level < 65 and self.batt_level >= 45:
|
|
|
|
self.icon = "battery-charging-050"
|
|
|
|
elif self.batt_level < 45 and self.batt_level >= 30:
|
|
|
|
self.icon = "battery-charging-040"
|
|
|
|
elif self.batt_level < 30 and self.batt_level >= 20:
|
|
|
|
self.icon = "battery-charging-030"
|
|
|
|
elif self.batt_level < 20 and self.batt_level >= 10:
|
|
|
|
self.icon = "battery-charging-020"
|
|
|
|
elif self.batt_level < 10 and self.batt_level >= 5:
|
|
|
|
self.icon = "battery-charging-010"
|
|
|
|
else:
|
|
|
|
self.icon = "battery-charging-000"
|
|
|
|
else:
|
|
|
|
self.icon = "ac-adapter"
|
|
|
|
return self.icon
|
|
|
|
|
|
|
|
def getIcon(self):
|
|
|
|
""" Return current icon."""
|
|
|
|
return UserIcon(self.icon)
|
|
|
|
|
|
|
|
def setIcon(self):
|
|
|
|
""" Change the systray/tooltip icon."""
|
|
|
|
oldIcon = self.icon
|
|
|
|
self.icon = self._getIcon()
|
|
|
|
if self.icon != oldIcon:
|
|
|
|
self.systray.setPixmap(QPixmap(UserIcon(self.icon)))
|
|
|
|
self.BattPixmap.setPixmap(QPixmap(UserIcon(self.icon)))
|
|
|
|
|
|
|
|
def notify(self, msg, icon=None):
|
|
|
|
""" Send a notification popup. """
|
|
|
|
if icon:
|
|
|
|
icon = QPixmap(icon)
|
|
|
|
else:
|
|
|
|
icon = QPixmap(SmallIcon("messagebox_info"))
|
|
|
|
try:
|
|
|
|
del self.warningPopup
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
self.warningPopup = KPassivePopup(self.systray)
|
|
|
|
label = Notify(self.warningPopup, msg, icon)
|
|
|
|
self.warningPopup.setView(label)
|
|
|
|
position = QPoint(5,5)
|
|
|
|
self.warningPopup.show(position)
|
|
|
|
|
|
|
|
def poll(self,notify=True):
|
|
|
|
""" Check for changes in plugged in status, battery status and laptop lid closed status. """
|
|
|
|
debug( "------------ POLL ---------------")
|
|
|
|
|
|
|
|
self.powermanager.checkHAL()
|
|
|
|
# Battery stuff:
|
|
|
|
# check for last state, and run plugged / unplugged message if the state changed.
|
|
|
|
if self.powermanager.hasBattery:
|
|
|
|
plugged_num = 0
|
|
|
|
self.batt_state = "not present" # unknown yet
|
|
|
|
self.batt_level = self.batt_remain = 0
|
|
|
|
self.batt_rate = self.batt_charge = self.batt_full = 0
|
|
|
|
for batt in self.powermanager.batteries:
|
|
|
|
state, level, remain, rate, current, full = self.powermanager.getBatteryState(batt)
|
|
|
|
self._updateBatteryWidget(batt, state, level, remain, rate)
|
|
|
|
|
|
|
|
## notify plugged/unplugged batteries
|
|
|
|
if state == "not present":
|
|
|
|
if self.powermanager.batteryIsPresent[batt]:
|
|
|
|
self.notify(i18n("The battery has been removed."))
|
|
|
|
self.powermanager.batteryIsPresent[batt] = False
|
|
|
|
else: # battery present
|
|
|
|
if not self.powermanager.batteryIsPresent[batt]:
|
|
|
|
self.notify(i18n("The battery has been inserted."))
|
|
|
|
self.powermanager.batteryIsPresent[batt] = True
|
|
|
|
|
|
|
|
## get cumulative charge levels/rate
|
|
|
|
self.batt_rate += rate
|
|
|
|
self.batt_charge += current
|
|
|
|
self.batt_full += full
|
|
|
|
|
|
|
|
## calculate overall level (average of present batteries)
|
|
|
|
self.batt_remain += remain
|
|
|
|
self.batt_level += level
|
|
|
|
plugged_num += 1
|
|
|
|
|
|
|
|
## calculate overall state (charging/discharging/charged)
|
|
|
|
if state in ("charging","discharging"):
|
|
|
|
self.batt_state = state
|
|
|
|
elif not self.batt_state in ("charging, discharging"):
|
|
|
|
self.batt_state = state
|
|
|
|
|
|
|
|
# if we know charge and full -> recalculate overall level
|
|
|
|
if self.batt_full > 0 and self.batt_charge > 0:
|
|
|
|
self.batt_level = 100 * self.batt_charge / self.batt_full
|
|
|
|
else:
|
|
|
|
# if more than one battery present, we need to calculate average level
|
|
|
|
if plugged_num > 1:
|
|
|
|
self.batt_level /= plugged_num
|
|
|
|
|
|
|
|
# if rate is reported, calculate remaining time on our own
|
|
|
|
if self.batt_rate > 0:
|
|
|
|
if self.batt_state == "charging":
|
|
|
|
self.batt_remain = 3600 * (float(self.batt_full - self.batt_charge) / self.batt_rate)
|
|
|
|
if self.batt_state == "discharging":
|
|
|
|
self.batt_remain = 3600 * (float(self.batt_charge) / self.batt_rate)
|
|
|
|
|
|
|
|
remain_h = self.batt_remain/3600
|
|
|
|
remain_m = (self.batt_remain/60)%60
|
|
|
|
|
|
|
|
blabel = i18n("<b>Battery:</b>")
|
|
|
|
if self.batt_state == "charged":
|
|
|
|
blabel += i18n(" fully charged")
|
|
|
|
elif self.batt_state == "charging":
|
|
|
|
blabel += i18n(" %i:%02ih to charge" % (remain_h,remain_m))
|
|
|
|
elif self.batt_state == "discharging":
|
|
|
|
blabel += i18n(" %i:%02ih remaining" % (remain_h,remain_m))
|
|
|
|
self.BattMainLabel.setText(blabel)
|
|
|
|
|
|
|
|
# update tray icon if needed
|
|
|
|
self.setIcon()
|
|
|
|
|
|
|
|
# check battery state
|
|
|
|
self.checkBatteryCritical()
|
|
|
|
|
|
|
|
# check Idletime
|
|
|
|
self.checkIdletime()
|
|
|
|
|
|
|
|
# CPU stuff
|
|
|
|
self._updateCpuWidgets()
|
|
|
|
|
|
|
|
if self.powermanager.hasBattery:
|
|
|
|
on_battery = self.powermanager.onBattery()
|
|
|
|
if self.powermanager.wasOnBattery != on_battery:
|
|
|
|
self.powermanager.wasOnBattery = on_battery
|
|
|
|
debug("poll: states differ")
|
|
|
|
if not on_battery:
|
|
|
|
debug("poll: Now on AC")
|
|
|
|
if notify:
|
|
|
|
self.powerHasBeenPlugged()
|
|
|
|
else:
|
|
|
|
debug("poll: Now on battery")
|
|
|
|
if notify:
|
|
|
|
self.powerHasBeenUnplugged()
|
|
|
|
else:
|
|
|
|
debug("poll: state is the same")
|
|
|
|
|
|
|
|
# Lid stuff
|
|
|
|
if self.powermanager.hasLid:
|
|
|
|
if self.powermanager.getLidClosedState():
|
|
|
|
if not self.powermanager.lidClosedState:
|
|
|
|
self.powermanager.lidClosedState = True
|
|
|
|
|
|
|
|
action = self._getRB(self.LaptopLidRadios, self.lid_act)
|
|
|
|
if not self.act_name.has_key(action):
|
|
|
|
action = self.act_name[self.lid_act_def]
|
|
|
|
|
|
|
|
if self.act_call[action] != None:
|
|
|
|
note = i18n("Laptop lid is closed, %1 now.").arg(self.act_notify[action])
|
|
|
|
self.notify(note, self.act_icon[action])
|
|
|
|
QTimer.singleShot(2000, self.act_call[action])
|
|
|
|
else:
|
|
|
|
self.powermanager.lidClosedState = False
|
|
|
|
|
|
|
|
def _addBatteryWidgets(self):
|
|
|
|
""" Adds progressbars to show battery status to the tooltip."""
|
|
|
|
BattLayout = QHBoxLayout(None,0,6,"BattLayout")
|
|
|
|
|
|
|
|
self.BattPixmap = QLabel(self.tooltip,"BattLabLayout")
|
|
|
|
self.BattPixmap.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.BattPixmap.sizePolicy().hasHeightForWidth()))
|
|
|
|
self.BattPixmap.setPixmap(QPixmap(UserIcon(self.icon)))
|
|
|
|
self.BattPixmap.setScaledContents(1)
|
|
|
|
BattLayout.addWidget(self.BattPixmap)
|
|
|
|
self.BattMainLabel = QLabel(self.tooltip,"BattMainLabel")
|
|
|
|
self.BattMainLabel.setText(i18n("<b>Battery:</b>"))
|
|
|
|
BattLayout.addWidget(self.BattMainLabel)
|
|
|
|
|
|
|
|
# Add to tooltip
|
|
|
|
self.tooltip.layout().addLayout(BattLayout)
|
|
|
|
|
|
|
|
# Create a progressbar and a label for every battery found, and add it to tooltip
|
|
|
|
self.BattLabel = {}
|
|
|
|
self.BattLayout = {}
|
|
|
|
self.BattProgress = {}
|
|
|
|
i = 1
|
|
|
|
for batt in self.powermanager.batteries:
|
|
|
|
self.BattLayout[batt] = QHBoxLayout(None,0,6,"BattBarLayout")
|
|
|
|
self.BattLabel[batt] = QLabel(self.tooltip,"BattLabel")
|
|
|
|
if len(self.powermanager.batteries) > 1:
|
|
|
|
self.BattLabel[batt].setText(i18n("Battery %i" % i))
|
|
|
|
self.BattLayout[batt].addWidget(self.BattLabel[batt])
|
|
|
|
self.BattProgress[batt] = KProgress(self.tooltip,"BattProgress")
|
|
|
|
self.BattProgress[batt].setMinimumSize(QSize(200,0))
|
|
|
|
self.BattLayout[batt].addWidget(self.BattProgress[batt])
|
|
|
|
self.tooltip.layout().addLayout(self.BattLayout[batt])
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
|
|
|
|
def _updateBatteryWidget(self, batt, state, level, remain, rate):
|
|
|
|
""" Retrieve battery information and update the related widgets accordingly. """
|
|
|
|
self.BattProgress[batt].setEnabled(True)
|
|
|
|
self.BattProgress[batt].setTotalSteps(100)
|
|
|
|
self.BattProgress[batt].setProgress(level)
|
|
|
|
if state == "not present":
|
|
|
|
self.BattProgress[batt].setFormat(i18n("not present"))
|
|
|
|
elif state == "charging":
|
|
|
|
self.BattProgress[batt].setFormat(i18n("Charging (%p%)"))
|
|
|
|
elif state == "discharging":
|
|
|
|
if rate > 0:
|
|
|
|
showrate = rate/1000
|
|
|
|
self.BattProgress[batt].setFormat(i18n("Discharging (%p%)") + " - %.d W" % showrate)
|
|
|
|
else:
|
|
|
|
self.BattProgress[batt].setFormat(i18n("Discharging (%p%)"))
|
|
|
|
else:
|
|
|
|
self.BattProgress[batt].setFormat("%p%")
|
|
|
|
|
|
|
|
def _addCpuWidgets(self):
|
|
|
|
""" Adds progressbars to show CPU frequencies to the tooltip."""
|
|
|
|
if not SHOW_CPUFREQ:
|
|
|
|
return
|
|
|
|
if len(self.powermanager.cpus) == 0:
|
|
|
|
return
|
|
|
|
|
|
|
|
LabelLayout = QHBoxLayout(None,0,6,"layout5")
|
|
|
|
|
|
|
|
self.CpuPixmap = QLabel(self.tooltip,"CpuPixmap")
|
|
|
|
self.CpuPixmap.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.CpuPixmap.sizePolicy().hasHeightForWidth()))
|
|
|
|
self.CpuPixmap.setPixmap(QPixmap(UserIcon("processor")))
|
|
|
|
self.CpuPixmap.setScaledContents(1)
|
|
|
|
LabelLayout.addWidget(self.CpuPixmap)
|
|
|
|
self.CpuMainLabel = QLabel(self.tooltip,"CpuMainLabel")
|
|
|
|
self.CpuMainLabel.setText(i18n("<b>CPU Frequency:</b>"))
|
|
|
|
LabelLayout.addWidget(self.CpuMainLabel)
|
|
|
|
|
|
|
|
# Add to tooltip
|
|
|
|
self.tooltip.layout().addLayout(LabelLayout)
|
|
|
|
|
|
|
|
# Create a progressbar and a label for every CPU found, and add it to tooltip
|
|
|
|
self.CpuLabel = {}
|
|
|
|
self.CpuLayout = {}
|
|
|
|
self.CpuProgress = {}
|
|
|
|
i = 1
|
|
|
|
for cpu in self.powermanager.cpus:
|
|
|
|
self.CpuLayout[cpu] = QHBoxLayout(None,0,6,"layout2")
|
|
|
|
self.CpuLabel[cpu] = QLabel(self.tooltip,"CpuLabel")
|
|
|
|
if len(self.powermanager.cpus) > 1:
|
|
|
|
self.CpuLabel[cpu].setText(i18n("Processor %i" % i))
|
|
|
|
self.CpuLayout[cpu].addWidget(self.CpuLabel[cpu])
|
|
|
|
self.CpuProgress[cpu] = KProgress(self.tooltip,"CpuProgress")
|
|
|
|
self.CpuProgress[cpu].setFormat("%v MHz")
|
|
|
|
self.CpuLayout[cpu].addWidget(self.CpuProgress[cpu])
|
|
|
|
self.tooltip.layout().addLayout(self.CpuLayout[cpu])
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
def slotPoweredIdleActivated(self, index=False):
|
|
|
|
""" Signal slot for activated powered idle action. """
|
|
|
|
if not index:
|
|
|
|
index = self.PoweredIdleCombo.currentItem()
|
|
|
|
self.PoweredIdleTime.setEnabled(index != 0)
|
|
|
|
|
|
|
|
def slotBatteryIdleActivated(self, index=False):
|
|
|
|
""" Signal slot for activated battery idle action. """
|
|
|
|
if not index:
|
|
|
|
index = self.BatteryIdleCombo.currentItem()
|
|
|
|
self.BatteryIdleTime.setEnabled(index != 0)
|
|
|
|
|
|
|
|
def slotBatteryCriticalActivated(self, index=False):
|
|
|
|
""" Signal slot for activated battery critical action. """
|
|
|
|
if not index:
|
|
|
|
index = self.BatteryCriticalCombo.currentItem()
|
|
|
|
self.CriticalRemainTime.setEnabled(index != 0)
|
|
|
|
|
|
|
|
def _updateCpuWidgets(self):
|
|
|
|
""" Retrieve CPU freq information and update the related widgets accordingly. """
|
|
|
|
if not SHOW_CPUFREQ:
|
|
|
|
return
|
|
|
|
if len(self.powermanager.cpus) == 0:
|
|
|
|
return
|
|
|
|
|
|
|
|
clabel = i18n("<b>CPU Frequency:</b>") + " "
|
|
|
|
policy = self.powermanager.getCpuPolicy()
|
|
|
|
if self.freq_name.has_key(policy):
|
|
|
|
clabel += self.freq_name[policy] # get l10n name
|
|
|
|
else:
|
|
|
|
clabel += policy
|
|
|
|
self.CpuMainLabel.setText(clabel)
|
|
|
|
|
|
|
|
for cpu in self.powermanager.cpus:
|
|
|
|
cpustate = self.powermanager.getCpuState(cpu)
|
|
|
|
if not cpustate['online']:
|
|
|
|
self.CpuProgress[cpu].setEnabled(False)
|
|
|
|
else:
|
|
|
|
self.CpuProgress[cpu].setEnabled(True)
|
|
|
|
self.CpuProgress[cpu].setTotalSteps(cpustate['max'])
|
|
|
|
self.CpuProgress[cpu].setProgress(cpustate['cur'])
|
|
|
|
if policy != "":
|
|
|
|
self.systray.actionCollection().action(policy).setChecked(True)
|
|
|
|
if policy in self.cb_freq:
|
|
|
|
self.systray.actionCollection().action(policy).setChecked(True)
|
|
|
|
|
|
|
|
def changePoweredBrightness(self, level=None):
|
|
|
|
""" Mains-powered brigthness slider has been moved. """
|
|
|
|
# Check if the state applies and adjust brightness immediately.
|
|
|
|
if not self.powermanager.onBattery() and self.powermanager.hasBrightness:
|
|
|
|
if not level:
|
|
|
|
level = self.PoweredBrightnessSlider.value()
|
|
|
|
self.powermanager.adjustBrightness(level)
|
|
|
|
|
|
|
|
def changeBatteryBrightness(self, level=None):
|
|
|
|
""" Battery-powered brigthness slider has been moved. """
|
|
|
|
# Check if the state applies and adjust brightness immediately.
|
|
|
|
if self.powermanager.onBattery() and self.powermanager.hasBrightness:
|
|
|
|
if not level:
|
|
|
|
level = self.BatteryBrightnessSlider.value()
|
|
|
|
self.powermanager.adjustBrightness(level)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def checkCpuFreq(self):
|
|
|
|
""" Adjust CPU frequency policy according to current state """
|
|
|
|
if not self.powermanager.hasCpuFreqGovernors:
|
|
|
|
return ""
|
|
|
|
|
|
|
|
if self.powermanager.onBattery():
|
|
|
|
policy = str(self.config.readEntry("batteryFreqPolicy"))
|
|
|
|
else:
|
|
|
|
policy = str(self.config.readEntry("poweredFreqPolicy"))
|
|
|
|
if policy == "":
|
|
|
|
policy = 'dynamic'
|
|
|
|
|
|
|
|
# check if specified policy is supported by HW
|
|
|
|
if not policy in self.cb_freq:
|
|
|
|
print "Warning: policy from config file not supported: ", policy
|
|
|
|
return ""
|
|
|
|
|
|
|
|
current_policy = self.powermanager.getCpuPolicy()
|
|
|
|
if current_policy != policy:
|
|
|
|
debug("Switching CPU policy from %s to %s." % (current_policy, policy))
|
|
|
|
self.powermanager.setCpuPolicy(policy)
|
|
|
|
return i18n("CPU frequency policy changed to %1.").arg(self.freq_name[policy])
|
|
|
|
elif current_policy == 'dynamic':
|
|
|
|
debug("Dynamic policy -> update policy (conservative/ondemand)")
|
|
|
|
self.powermanager.setCpuPolicy(policy)
|
|
|
|
|
|
|
|
debug("CPU policy will stay %s" % current_policy)
|
|
|
|
return ""
|
|
|
|
|
|
|
|
def powerHasBeenUnplugged(self):
|
|
|
|
""" Actions to perform when the plug has been pulled."""
|
|
|
|
if self.powermanager.hasBrightness:
|
|
|
|
self.powermanager.adjustBrightness(self.BatteryBrightnessSlider.value())
|
|
|
|
self.powermanager.setPowerSave(True)
|
|
|
|
self.checkBatteryCritical()
|
|
|
|
self.changeBatteryBrightness()
|
|
|
|
self.powermanager.setScreensaverBlankOnly(True)
|
|
|
|
self.powermanager.resetIdleSeconds()
|
|
|
|
msg = self.checkCpuFreq()
|
|
|
|
if self.powermanager.hasAC:
|
|
|
|
self.notify(i18n("The AC adapter has been unplugged, switching to battery mode.")+"\n"+msg, self.getIcon())
|
|
|
|
|
|
|
|
def powerHasBeenPlugged(self):
|
|
|
|
""" Actions to perform when AC adapter has been plugged in. """
|
|
|
|
if self.powermanager.hasBrightness:
|
|
|
|
self.powermanager.adjustBrightness(self.PoweredBrightnessSlider.value())
|
|
|
|
self.powermanager.setPowerSave(False)
|
|
|
|
self.changePoweredBrightness()
|
|
|
|
self.powermanager.setScreensaverBlankOnly(False)
|
|
|
|
msg = self.checkCpuFreq()
|
|
|
|
self.powermanager.resetIdleSeconds()
|
|
|
|
self.notify(i18n("The AC adapter has been plugged in, switching to AC mode.")+"\n"+msg, self.getIcon())
|
|
|
|
|
|
|
|
def checkBatteryCritical(self):
|
|
|
|
""" Check for warning and critical battery label and notify-warn or
|
|
|
|
initiate the configured action. """
|
|
|
|
|
|
|
|
if not self.powermanager.hasBattery:
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.batt_state == "discharging":
|
|
|
|
currentLevel = int(self.batt_remain/60)
|
|
|
|
|
|
|
|
warningLevel = self.criticalLevel + 5 # warn five minutes before critical
|
|
|
|
criticalLevel = self.criticalLevel
|
|
|
|
|
|
|
|
debug("CurrentBat: %i, WarningBat: %i, CriticalBat: %i" % (currentLevel, warningLevel, criticalLevel))
|
|
|
|
# We only want to suspend if the chargelevel is above a certain threshold,
|
|
|
|
# it sometimes takes some time for HAL to report remaining time correctly
|
|
|
|
if currentLevel <= criticalLevel and self.batt_level < CHARGE_LEVEL_THRESHOLD:
|
|
|
|
if not self.powermanager.criticalBatteryState and self.powermanager.onBattery():
|
|
|
|
self.powermanager.criticalBatteryState = True
|
|
|
|
|
|
|
|
action = str(self.config.readEntry("batteryCriticalAction"))
|
|
|
|
if not self.act_name.has_key(action):
|
|
|
|
action = self.act_name[self.cb_act_def_critical]
|
|
|
|
|
|
|
|
note = i18n("You are about to run out of battery power, %1 now.").arg(self.act_notify[action])
|
|
|
|
self.notify(note, self.act_icon[action])
|
|
|
|
if self.act_call[action] != None:
|
|
|
|
QTimer.singleShot(2000, self.act_call[action])
|
|
|
|
else:
|
|
|
|
self.powermanager.criticalBatteryState = False
|
|
|
|
if currentLevel <= warningLevel and self.batt_level < CHARGE_LEVEL_THRESHOLD:
|
|
|
|
if not self.powermanager.warningBatteryState:
|
|
|
|
self.powermanager.warningBatteryState = True
|
|
|
|
self.notify(i18n("You are low on battery power."), self.getIcon())
|
|
|
|
else:
|
|
|
|
self.powermanager.warningBatteryState = False
|
|
|
|
|
|
|
|
def checkIdletime(self):
|
|
|
|
""" Reads the idle time and does some action. """
|
|
|
|
idlesec = round(self.powermanager.getIdleSeconds()/60, 2)
|
|
|
|
if self.powermanager.onBattery():
|
|
|
|
idleTime = self.config.readNumEntry("batteryIdleTime", 10)
|
|
|
|
action = str(self.config.readEntry("batteryIdleAction"))
|
|
|
|
else:
|
|
|
|
idleTime = self.config.readNumEntry("poweredIdleTime", 60)
|
|
|
|
action = str(self.config.readEntry("poweredIdleAction"))
|
|
|
|
if not self.act_name.has_key(action):
|
|
|
|
action = 'nothing'
|
|
|
|
|
|
|
|
if idlesec - self.lastidlesec > 100:
|
|
|
|
debug("last: %u" % (idlesec - self.lastidlesec))
|
|
|
|
return # probably bogus idleseconds right after suspend
|
|
|
|
self.lastidlesec = idlesec
|
|
|
|
if self.act_call[action] == None:
|
|
|
|
return # doing nothing anyway
|
|
|
|
if idlesec > idleTime:
|
|
|
|
note = i18n("System idle for at least %1 minutes, %2 now.").arg(idleTime).arg(self.act_notify[action])
|
|
|
|
self.notify(note, self.act_icon[action])
|
|
|
|
QTimer.singleShot(2000, self.act_call[action])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def doDcop(kapp):
|
|
|
|
""" Register kvandale in dcop, so it can be controlled from outside. """
|
|
|
|
my_dcop = kapp.dcopClient()
|
|
|
|
#my_dcop.attach()
|
|
|
|
#my_dcop.registerAs("power-manager")
|
|
|
|
|
|
|
|
|
|
|
|
class DcopIface (DCOPExObj):
|
|
|
|
""" Add some interface so we can use powermanager from the outside. """
|
|
|
|
def __init__ (self, app, id='power-manager'):
|
|
|
|
DCOPExObj.__init__ (self, id)
|
|
|
|
# addMethod (<signature>, <Python method>)
|
|
|
|
#self.addMethod ('QString getQuery()', gvd.getZoekbegrip)
|
|
|
|
|
|
|
|
# PM related.
|
|
|
|
self.addMethod ('void suspend ()', app.trySuspend)
|
|
|
|
self.addMethod ('void hibernate ()', app.tryHibernate)
|
|
|
|
self.addMethod ('void shutdown ()', app.shutdown)
|
|
|
|
|
|
|
|
# UI related.
|
|
|
|
self.addMethod ('void showTip ()', app.showTip)
|
|
|
|
#self.addMethod ('void show ()', app.parent().show)
|
|
|
|
#self.addMethod ('void hide ()', app.parent().hide)
|
|
|
|
|
|
|
|
#self.addMethod ('void plugged ()', app.powerHasBeenPlugged)
|
|
|
|
#self.addMethod ('void unplugged ()', app.powerHasBeenUnplugged)
|
|
|
|
self.addMethod ('bool onBattery ()', app.powermanager.onBattery)
|
|
|
|
|
|
|
|
self.addMethod('void brightnessUp ()', app.setBrightnessUp)
|
|
|
|
self.addMethod('void brightnessDown ()', app.setBrightnessDown)
|
|
|
|
self.addMethod('QString getBrightness ()', app.getBrightness)
|
|
|
|
|
|
|
|
#self.addMethod ('QString getCurrentResult()', gvd.getRawResult)
|
|
|
|
|
|
|
|
|
|
|
|
class PowermanagerApp(KDialogBase):
|
|
|
|
""" The KDialog providing the OK, Apply and Cancel buttons."""
|
|
|
|
|
|
|
|
def __init__(self,parent=None,name=None):
|
|
|
|
""" Initialise dialog and set mainwidget. """
|
|
|
|
TDEGlobal.locale().insertCatalogue("guidance")
|
|
|
|
TDEGlobal.iconLoader().addAppDir("guidance")
|
|
|
|
|
|
|
|
# We would like to use a KUniqueApplication, but that breaks dcop due to some
|
|
|
|
# strange bug. The following line is the revenge code for this bug, it is
|
|
|
|
# intentionally ugly.
|
|
|
|
if len(os.popen("dcop |grep guidance-").readlines()) > 1:
|
|
|
|
print "There is already an instance of power manager running. Exiting."
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
# Which buttons do we want?
|
|
|
|
KDialogBase.__init__(self,KJanusWidget.Swallow,i18n("Power Manager"),
|
|
|
|
KDialogBase.Ok|KDialogBase.Apply|KDialogBase.Cancel|KDialogBase.User1, KDialogBase.Close)
|
|
|
|
self.pmwidget = PowerManager(self,name)
|
|
|
|
self.setButtonText(KDialogBase.User1, i18n("About"))
|
|
|
|
|
|
|
|
if not self.pmwidget.powermanager.isLaptop():
|
|
|
|
print "This is not a laptop, quitting ... "
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
self.pmwidget.prepare()
|
|
|
|
|
|
|
|
self.setMainWidget(self.pmwidget)
|
|
|
|
self.aboutus = TDEAboutApplication(self)
|
|
|
|
|
|
|
|
def slotOk(self):
|
|
|
|
""" The OK button has been pressed, save configuration and pass on do whatever
|
|
|
|
needs to be done by KDialog. """
|
|
|
|
self.pmwidget.uiToConfig()
|
|
|
|
self.pmwidget.checkCpuFreq()
|
|
|
|
KDialogBase.slotOk(self)
|
|
|
|
|
|
|
|
def slotApply(self):
|
|
|
|
""" The Apply button has been pressed, save configuration and pass on do whatever
|
|
|
|
needs to be done by KDialog. """
|
|
|
|
self.pmwidget.uiToConfig()
|
|
|
|
self.pmwidget.checkCpuFreq()
|
|
|
|
KDialogBase.slotApply(self)
|
|
|
|
|
|
|
|
def slotCancel(self):
|
|
|
|
""" The Cancel button has been pressed, reset some values and hide dialogue. """
|
|
|
|
# In case brightness has changed, we reset it to the configured value.
|
|
|
|
if self.pmwidget.powermanager.hasBrightness:
|
|
|
|
brightness_high = self.pmwidget.powermanager.brightness_levels
|
|
|
|
if not self.pmwidget.powermanager.onBattery():
|
|
|
|
level = self.pmwidget.config.readNumEntry("poweredBrightness", brightness_high)
|
|
|
|
else:
|
|
|
|
level = self.pmwidget.config.readNumEntry("batteryBrightness", int(brightness_high/2))
|
|
|
|
self.pmwidget.powermanager.adjustBrightness(level)
|
|
|
|
self.pmwidget.configToUi()
|
|
|
|
KDialogBase.slotCancel(self)
|
|
|
|
|
|
|
|
def slotUser1(self):
|
|
|
|
self.aboutus.show()
|
|
|
|
|
|
|
|
# There's a bug in KUniqueApplication that shows the pid in the dcop name,
|
|
|
|
# this fugly hack works around it.
|
|
|
|
class PMApp(TDEApplication):
|
|
|
|
|
|
|
|
def name(self):
|
|
|
|
return "power-manager"
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
aboutdata = TDEAboutData("guidance", "Power Manager", "0.8.0",
|
|
|
|
"Handles battery, display and suspend modes for your computer.", TDEAboutData.License_GPL,
|
|
|
|
"(C) 2006-2007 Sebastian Kügler, Canonical Ltd, Luka Renko",
|
|
|
|
None, None, "jriddell@ubuntu.com")
|
|
|
|
aboutdata.addAuthor("Sebastian Kügler", "Developer", "sebas@kde.org","http://vizZzion.org")
|
|
|
|
aboutdata.addAuthor("Jonathan Riddell", "Developer", "jriddell@ubuntu.com")
|
|
|
|
aboutdata.addAuthor("Luka Renko", "Developer", "lure@kubuntu.org")
|
|
|
|
aboutdata.setProgramLogo(QImage("power-manager.png"))
|
|
|
|
TDECmdLineArgs.init(sys.argv, aboutdata)
|
|
|
|
#kapp = KUniqueApplication(True, True, False)
|
|
|
|
#kapp = TDEApplication()
|
|
|
|
kapp = PMApp(True, True)
|
|
|
|
mainWindow = PowermanagerApp(None, "main window")
|
|
|
|
doDcop(kapp)
|
|
|
|
dcop_iface = DcopIface(mainWindow.pmwidget)
|
|
|
|
"""Start helper module / button listener"""
|
|
|
|
try:
|
|
|
|
helperPid = os.spawnl(os.P_NOWAIT, os.path.dirname(__file__)+'/gpmhelper.py', 'gpmhelper.py')
|
|
|
|
except:
|
|
|
|
"""Non-fatal if this fails"""
|
|
|
|
print "Unable to start button-listener"
|
|
|
|
|
|
|
|
kapp.exec_loop()
|
|
|
|
|
|
|
|
"""Kill helper module / button listener"""
|
|
|
|
os.system('kill '+str(helperPid))
|