Advertisement
Guest User

Untitled

a guest
Jan 21st, 2013
271
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # Copyright © 2009 The Caffeine Developers
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19.  
  20.  
  21. from gi.repository import Gtk, GObject, Gio, Notify
  22. import os
  23. import os.path
  24. import commands
  25. import time
  26. import sys
  27.  
  28. import dbus
  29. import threading
  30.  
  31. import applicationinstance
  32.  
  33. import caffeine
  34. import utils
  35. import procmanager
  36. import caffeinelogging as logging
  37.  
  38. import Xlib.display
  39. #import kaa.metadata
  40.  
  41. os.chdir(os.path.abspath(os.path.dirname(__file__)))
  42. class Caffeine(GObject.GObject):
  43.  
  44. def __init__(self):
  45.  
  46. GObject.GObject.__init__(self)
  47.  
  48.  
  49. ## object to manage processes to activate for.
  50. self.ProcMan = caffeine.get_ProcManager()
  51.  
  52. ## Status string.
  53. self.status_string = ""
  54.  
  55. ## Makes sure that only one instance of Caffeine is run for
  56. ## each user on the system.
  57. self.pid_name = '/tmp/caffeine' + str(os.getuid()) + '.pid'
  58. self.appInstance = applicationinstance.ApplicationInstance( self.pid_name )
  59.  
  60. ## This variable is set to a string describing the type of screensaver and
  61. ## powersaving systems used on this computer. It is detected when the user
  62. ## first attempts to inhibit the screensaver and powersaving, and can be set
  63. ## to one of the following values: "Gnome", "KDE", "XSS+DPMS" or "DPMS".
  64. self.screensaverAndPowersavingType = None
  65.  
  66. # Set to True when the detection routine is in progress
  67. self.attemptingToDetect = False
  68.  
  69. self.dbusDetectionTimer = None
  70. self.dbusDetectionFailures = 0
  71.  
  72. # Set to True when sleep seems to be prevented from the perspective of the user.
  73. # This does not necessarily mean that sleep really is prevented, because the
  74. # detection routine could be in progress.
  75. self.sleepAppearsPrevented = False
  76.  
  77. # Set to True when sleep mode has been successfully inhibited somehow. This should
  78. # match up with "self.sleepAppearsPrevented" most of the time.
  79. self.sleepIsPrevented = False
  80.  
  81. self.preventedForProcess = False
  82. self.preventedForQL = False
  83. self.preventedForFlash = False
  84.  
  85. self.screenSaverCookie = None
  86. self.powerManagementCookie = None
  87. self.timer = None
  88. self.inhibit_id = None
  89.  
  90.  
  91. self.note = None
  92.  
  93. ## check for processes to activate for.
  94. id = GObject.timeout_add(10000, self._check_for_process)
  95.  
  96.  
  97. settings = Gio.Settings.new(caffeine.BASE_KEY)
  98. ## check for Quake Live.
  99. self.ql_id = None
  100. if settings.get_boolean("act-for-quake"):
  101. self.setActivateForQL(True)
  102.  
  103. ## check for Flash video.
  104. self.flash_durations = {}
  105. self.flash_id = None
  106. if settings.get_boolean("act-for-flash"):
  107. self.setActivateForFlash(True)
  108. print self.status_string
  109.  
  110.  
  111.  
  112. def setActivateForFlash(self, do_activate):
  113.  
  114. ## In case caffeine is currently activated for Flash
  115. self._check_for_Flash()
  116.  
  117. if self.flash_id != None:
  118. GObject.source_remove(self.flash_id)
  119.  
  120. self.flash_id = None
  121.  
  122. if do_activate:
  123. self.flash_id = GObject.timeout_add(15000,
  124. self._check_for_Flash)
  125.  
  126.  
  127. def _check_for_Flash(self):
  128.  
  129. class escape(Exception):pass
  130.  
  131. try:
  132. ## look for files opened by flashplayer that begin with 'Flash'
  133.  
  134. output = commands.getoutput("python flash_detect.py")
  135.  
  136. if output == "1":
  137. raise escape
  138.  
  139. elif output.startswith("2\n"):
  140. data = output.split("\n")[-1]
  141. logging.error("Exception: " + str(data))
  142.  
  143. raise escape
  144.  
  145.  
  146.  
  147. parsed = []
  148. for row in output.split("\n"):
  149. try:
  150. iden, length = row.split(" ")
  151. length = int(length)
  152. parsed.append([iden, length])
  153.  
  154. except Exception, data:
  155. logging.error("Exception: " + str(data))
  156.  
  157. for iden, length in parsed:
  158. if iden != "" and iden not in self.flash_durations:
  159. end_time = time.localtime(int(time.time() + length))
  160. self.flash_durations[iden] = end_time
  161.  
  162. idens = [iden for iden, length in parsed]
  163.  
  164. for key in self.flash_durations.keys():
  165. if key not in idens:
  166. self.flash_durations.pop(key)
  167.  
  168. dtimes = []
  169. for t in self.flash_durations.values():
  170.  
  171. dtimes.append(int(time.mktime(t) - time.time()))
  172.  
  173. dtimes.sort(reverse=True)
  174.  
  175. if dtimes == []:
  176. if self.preventedForFlash:
  177. self.setActivated(False, note=False)
  178. raise escape
  179.  
  180. dtime = dtimes[0]
  181. if dtime <= 0:
  182. raise escape
  183.  
  184. if self.preventedForFlash or not self.getActivated():
  185.  
  186. logging.info("Caffeine has detected "+
  187. "that Flash video is playing, "+
  188. "and will activate for "+str(dtime)+
  189. " seconds.")
  190.  
  191. self.status_string = _("Activated for Flash video")
  192. self.timedActivation(dtime, note=False)
  193. self.status_string = _("Activated for Flash video")
  194. self.preventedForFlash = True
  195. else:
  196. logging.info("Caffeine has detected "+
  197. "that Flash video is playing but will "+
  198. "NOT activate because Caffeine is already "+
  199. "activated for a different reason.")
  200.  
  201. return True
  202.  
  203.  
  204. except escape:
  205. pass
  206.  
  207. except Exception, data:
  208.  
  209. logging.error("Exception: " + str(data))
  210.  
  211. if self.preventedForFlash:
  212. self.setActivated(False, note=False)
  213.  
  214. return True
  215.  
  216.  
  217. def setActivateForQL(self, do_activate):
  218.  
  219. ## In case caffeine is currently activated for QL
  220. self._check_for_QL()
  221.  
  222. if self.ql_id != None:
  223. GObject.source_remove(self.ql_id)
  224.  
  225. self.ql_id = None
  226.  
  227. if do_activate:
  228. self.ql_id = GObject.timeout_add(15000, self._check_for_QL)
  229.  
  230.  
  231. def _check_for_QL(self):
  232.  
  233. dsp = None
  234. try:
  235. dsp = Xlib.display.Display()
  236. screen = dsp.screen()
  237. root_win = screen.root
  238.  
  239. activate = False
  240. ## iterate through all of the X windows
  241. for window in root_win.query_tree()._data['children']:
  242. window_name = window.get_wm_name()
  243.  
  244. width = window.get_geometry()._data["width"]
  245. height = window.get_geometry()._data["height"]
  246. if window_name == "QuakeLive":
  247.  
  248. activate = True
  249.  
  250. if self.preventedForQL or not self.getActivated():
  251.  
  252. self.status_string = _("Activated for Quake Live")
  253.  
  254. logging.info("Caffeine has detected that 'QuakeLive' is running, and will auto-activate")
  255.  
  256. self.setActivated(True)
  257. self.preventedForQL = True
  258.  
  259. if not activate and self.preventedForQL:
  260. logging.info("Caffeine had previously auto-activated for QuakeLive, but it is no longer running; deactivating...")
  261. self.setActivated(False)
  262.  
  263. except Exception, data:
  264. logging.error("Exception: " + str(data))
  265.  
  266. finally:
  267. if dsp != None:
  268. dsp.close()
  269.  
  270. return True
  271.  
  272. def _check_for_process(self):
  273. activate = False
  274. for proc in self.ProcMan.get_process_list():
  275. if utils.isProcessRunning(proc):
  276.  
  277. activate = True
  278.  
  279. if self.preventedForProcess or not self.getActivated():
  280.  
  281. logging.info("Caffeine has detected that the process '" + proc + "' is running, and will auto-activate")
  282.  
  283. self.setActivated(True)
  284.  
  285. self.preventedForProcess = True
  286. else:
  287.  
  288. logging.info("Caffeine has detected that the process '"+
  289. proc + "' is running, but will NOT auto-activate"+
  290. " as Caffeine has already been activated for a different"+
  291. " reason.")
  292.  
  293.  
  294. ### No process in the list is running, deactivate.
  295. if not activate and self.preventedForProcess:
  296. logging.info("Caffeine had previously auto-activated for a process, but that process is no longer running; deactivating...")
  297. self.setActivated(False)
  298.  
  299. return True
  300.  
  301. def quit(self):
  302. """Cancels any timer thread running
  303. so the program can quit right away.
  304. """
  305. if self.timer:
  306. self.timer.cancel()
  307.  
  308. if self.dbusDetectionTimer:
  309. self.dbusDetectionTimer.cancel()
  310.  
  311. ## The following four methods deal with adding the correct syntax
  312. ## for plural forms of time units. For example, 1 minute and 2
  313. ## minutes. Will be obsolete once the application is
  314. ## internationalized, as not all languages use "s" for plural form.
  315. def _mconcat(self, base, sep, app):
  316. return (base + sep + app if base else app) if app else base
  317.  
  318. def _spokenConcat(self, ls):
  319. and_str = _(" and ")
  320. txt, n = '', len(ls)
  321. for w in ls[0:n-1]:
  322. txt = self._mconcat(txt, ', ', w)
  323. return self._mconcat(txt, and_str, ls[n-1])
  324.  
  325. def _pluralize(self, name, time):
  326. names = [_('hour'), _('minute')]
  327. if time < 1:
  328. return ""
  329.  
  330. if name == "hour":
  331. if time < 2:
  332. return "%d %s" % (time, _("hour"))
  333. if time >= 2:
  334. return "%d %s" % (time, _("hours"))
  335.  
  336. elif name == "minute":
  337. if time < 2:
  338. return "%d %s" % (time, _("minute"))
  339. if time >= 2:
  340. return "%d %s" % (time, _("minutes"))
  341.  
  342. def _timeDisplay(self, sec):
  343.  
  344. hours = sec/3600
  345. minutes = sec/60 % 60
  346. ls = []
  347. ls.append(self._pluralize("hour", hours))
  348. ls.append(self._pluralize("minute", minutes))
  349.  
  350. string = self._spokenConcat(ls)
  351. if not string:
  352. string = "0 minutes"
  353. return string
  354.  
  355.  
  356. def _notify(self, message, icon, title="Caffeine"):
  357. """Easy way to use pynotify"""
  358. try:
  359.  
  360. Notify.init("Caffeine")
  361. if self.note:
  362. self.note.update(title, message, icon)
  363. else:
  364. self.note = Notify.Notification(title, message, icon)
  365.  
  366. ## Notify OSD doesn't seem to work when sleep is prevented
  367. if self.screenSaverCookie != None and self.sleepIsPrevented:
  368. self.ssProxy.UnInhibit(self.screenSaverCookie)
  369.  
  370. self.note.show()
  371.  
  372. if self.screenSaverCookie != None and self.sleepIsPrevented:
  373. self.screenSaverCookie = self.ssProxy.Inhibit("Caffeine",
  374. "User has requested that Caffeine disable the screen saver")
  375.  
  376. except Exception, e:
  377. logging.error("Exception occurred:\n" + " " + str(e))
  378. logging.error("Exception occurred attempting to display message:\n" + message)
  379. finally:
  380. return False
  381.  
  382.  
  383.  
  384. def getActivated(self):
  385. return self.sleepAppearsPrevented
  386.  
  387. def timedActivation(self, time, note=True):
  388. """Calls toggleActivated after the number of seconds
  389. specified by time has passed.
  390. """
  391. message = (_("Timed activation set; ")+
  392. _("Caffeine will prevent powersaving for the next ") +
  393. self._timeDisplay(time))
  394.  
  395. logging.info("Timed activation set for " + self._timeDisplay(time))
  396.  
  397. if self.status_string == "":
  398.  
  399. self.status_string = _("Activated for ")+self._timeDisplay(time)
  400. self.emit("activation-toggled", self.getActivated(),
  401. self.status_string)
  402.  
  403.  
  404. self.setActivated(True, note)
  405.  
  406. if note:
  407. self._notify(message, caffeine.FULL_ICON_PATH)
  408.  
  409. ## and deactivate after time has passed.
  410. ## Stop already running timer
  411. if self.timer:
  412. logging.info("Previous timed activation cancelled due to a second timed activation request (was set for " +
  413. self._timeDisplay(self.timer.interval) + " or "+
  414. str(time)+" seconds )")
  415. self.timer.cancel()
  416.  
  417. self.timer = threading.Timer(time, self._deactivate, args=[note])
  418. self.timer.name = "Active"
  419. self.timer.start()
  420.  
  421.  
  422. def _deactivate(self, note):
  423. self.timer.name = "Expired"
  424. self.toggleActivated(note=note)
  425.  
  426.  
  427. def setActivated(self, activate, note=True):
  428. if self.getActivated() != activate:
  429. self.toggleActivated(note)
  430.  
  431. def toggleActivated(self, note=True):
  432. """This function toggles the inhibition of the screensaver and powersaving
  433. features of the current computer, detecting the the type of screensaver and powersaving
  434. in use, if it has not been detected already."""
  435.  
  436. self.preventedForProcess = False
  437. self.preventedForQL = False
  438. self.preventedForFlash = False
  439.  
  440. if self.sleepAppearsPrevented:
  441. ### sleep prevention was on now turn it off
  442.  
  443. self.sleepAppearsPrevented = False
  444. logging.info("Caffeine is now dormant; powersaving is re-enabled")
  445. self.status_string = _("Caffeine is dormant; powersaving is enabled")
  446.  
  447. # If the user clicks on the full coffee-cup to disable
  448. # sleep prevention, it should also
  449. # cancel the timer for timed activation.
  450.  
  451. if self.timer != None and self.timer.name != "Expired":
  452.  
  453. message = (_("Timed activation cancelled (was set for ") +
  454. self._timeDisplay(self.timer.interval) + ")")
  455.  
  456. logging.info("Timed activation cancelled (was set for " +
  457. self._timeDisplay(self.timer.interval) + ")")
  458.  
  459. if note:
  460. self._notify(message, caffeine.EMPTY_ICON_PATH)
  461.  
  462. self.timer.cancel()
  463. self.timer = None
  464.  
  465.  
  466.  
  467. elif self.timer != None and self.timer.name == "Expired":
  468.  
  469. message = (self._timeDisplay(self.timer.interval) +
  470. _(" have elapsed; powersaving is re-enabled"))
  471.  
  472. logging.info("Timed activation period (" + self._timeDisplay(self.timer.interval) + ") has elapsed")
  473.  
  474. if note:
  475. self._notify(message, caffeine.EMPTY_ICON_PATH)
  476.  
  477. self.timer = None
  478.  
  479. else:
  480.  
  481. self.sleepAppearsPrevented = True
  482.  
  483.  
  484. self._performTogglingActions()
  485.  
  486. if self.status_string == "":
  487. ### Fixes bug #458847.
  488. if self.screensaverAndPowersavingType != None:
  489. self.status_string = (_("Caffeine is preventing powersaving modes and screensaver activation ")+"("+
  490. self.screensaverAndPowersavingType + ")")
  491.  
  492. self.emit("activation-toggled", self.getActivated(),
  493. self.status_string)
  494. self.status_string = ""
  495.  
  496.  
  497.  
  498. def _detectScreensaverAndPowersavingType(self):
  499. """This method always runs when the first attempt to inhibit the screensaver and
  500. powersaving is made. It detects what screensaver/powersaving software is running.
  501. After detection is complete, it will finish the inhibiting process."""
  502. logging.info("Attempting to detect screensaver/powersaving type... (" + str(self.dbusDetectionFailures) + " dbus failures so far)")
  503. bus = dbus.SessionBus()
  504.  
  505. if 'org.gnome.SessionManager' in bus.list_names() and utils.isProcessRunning("gnome-screensaver"):
  506. self.screensaverAndPowersavingType = "Gnome3"
  507.  
  508. elif 'org.freedesktop.ScreenSaver' in bus.list_names() and \
  509. 'org.freedesktop.PowerManagement.Inhibit' in bus.list_names():
  510. self.screensaverAndPowersavingType = "KDE"
  511. else:
  512. self.dbusDetectionFailures += 1
  513. if self.dbusDetectionFailures <= 3:
  514. self.dbusDetectionTimer = threading.Timer(10, self._detectScreensaverAndPowersavingType)
  515. self.dbusDetectionTimer.start()
  516. return
  517. else:
  518. # At this point, all attempts to connect to the relevant dbus interfaces have failed.
  519. # This user must be using something other than the Gnome or KDE screensaver programs.
  520. if utils.isProcessRunning("xscreensaver"):
  521. self.screensaverAndPowersavingType = "XSS+DPMS"
  522. else:
  523. self.screensaverAndPowersavingType = "DPMS"
  524.  
  525. self.attemptingToDetect = False
  526. self.dbusDetectionFailures = 0
  527. self.dbusDetectionTimer = None
  528.  
  529. logging.info("Successfully detected screensaver and powersaving type: " + str(self.screensaverAndPowersavingType))
  530.  
  531. if self.sleepAppearsPrevented != self.sleepIsPrevented:
  532. self._performTogglingActions()
  533.  
  534. def _performTogglingActions(self):
  535. """This method performs the actions that affect the screensaver and
  536. powersaving."""
  537. if self.screensaverAndPowersavingType == None:
  538. if self.attemptingToDetect == False:
  539. self.attemptingToDetect = True
  540. self._detectScreensaverAndPowersavingType()
  541. return
  542.  
  543. if self.screensaverAndPowersavingType == "Gnome":
  544. self._toggleGnome()
  545. if self.screensaverAndPowersavingType == "Gnome3":
  546. self._toggleGnome3()
  547. elif self.screensaverAndPowersavingType == "KDE":
  548. self._toggleKDE()
  549. elif self.screensaverAndPowersavingType == "XSS+DPMS":
  550. self._toggleXSSAndDPMS()
  551. elif self.screensaverAndPowersavingType == "DPMS":
  552. self._toggleDPMS()
  553.  
  554. if self.sleepIsPrevented == False:
  555. logging.info("Caffeine is now preventing powersaving modes"+
  556. " and screensaver activation (" +
  557. self.screensaverAndPowersavingType + ")")
  558.  
  559. self.sleepIsPrevented = not self.sleepIsPrevented
  560.  
  561.  
  562. def _toggleGnome3(self):
  563. """Toggle the screensaver and powersaving with the interfaces used by Gnome 3."""
  564.  
  565. self._toggleDPMS()
  566. bus = dbus.SessionBus()
  567. self.susuProxy = bus.get_object('org.gnome.SessionManager', '/org/gnome/SessionManager')
  568. if self.sleepIsPrevented:
  569. if self.screenSaverCookie != None:
  570. self.susuProxy.Uninhibit(self.screenSaverCookie)
  571. else:
  572. self.screenSaverCookie = self.susuProxy.Inhibit("Caffeine",dbus.UInt32(0),
  573. "User has requested that Caffeine disable the screen saver",dbus.UInt32(8))
  574.  
  575.  
  576. def _toggleKDE(self):
  577. """Toggle the screensaver and powersaving with the interfaces used by KDE."""
  578.  
  579. self._toggleDPMS()
  580. bus = dbus.SessionBus()
  581. self.ssProxy = bus.get_object(
  582. 'org.freedesktop.ScreenSaver', '/ScreenSaver')
  583. pmProxy = bus.get_object(
  584. 'org.freedesktop.PowerManagement.Inhibit',
  585. '/org/freedesktop/PowerManagement/Inhibit')
  586. if self.sleepIsPrevented:
  587. if self.screenSaverCookie != None:
  588. self.ssProxy.UnInhibit(self.screenSaverCookie)
  589. if self.powerManagementCookie != None:
  590. pmProxy.UnInhibit(self.powerManagementCookie)
  591. else:
  592. self.powerManagementCookie = pmProxy.Inhibit("Caffeine",
  593. "User has requested that Caffeine disable"+
  594. " the powersaving modes")
  595. self.screenSaverCookie = self.ssProxy.Inhibit("Caffeine",
  596. "User has requested that Caffeine disable the screen saver")
  597.  
  598. def _toggleXSSAndDPMS(self):
  599. self._toggleXSS()
  600. self._toggleDPMS()
  601.  
  602. def _toggleDPMS(self):
  603. """Toggle the DPMS powersaving subsystem."""
  604. if self.sleepIsPrevented:
  605. commands.getoutput("xset +dpms")
  606. commands.getoutput("xset s on")
  607. else:
  608. commands.getoutput("xset -dpms")
  609. commands.getoutput("xset s off")
  610.  
  611. def _toggleXSS(self):
  612. """Toggle whether XScreensaver is activated (powersaving is unaffected)"""
  613.  
  614. if self.sleepIsPrevented:
  615. ### sleep prevention was on now turn it off
  616.  
  617. # If the user clicks on the full coffee-cup to disable
  618. # sleep prevention, it should also
  619. # cancel the timer for timed activation.
  620.  
  621. if self.inhibit_id != None:
  622. GObject.source_remove(self.inhibit_id)
  623.  
  624. else:
  625.  
  626. def deactivate():
  627. try:
  628. output = commands.getoutput(
  629. "xscreensaver-command -deactivate")
  630. except Exception, data:
  631. logging.error("Exception occurred:\n" + data)
  632.  
  633. return True
  634.  
  635. # reset the idle timer every 50 seconds.
  636. self.inhibit_id = GObject.timeout_add(50000, deactivate)
  637.  
  638.  
  639. ## register a signal
  640. GObject.signal_new("activation-toggled", Caffeine,
  641. GObject.SignalFlags.RUN_FIRST, None, [bool, str])
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement