3
# Copyright (C) 2008 Patryk Zawadzki <patrys at pld-linux.org>
5
# This file is part of Project Hamster.
7
# Project Hamster is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
12
# Project Hamster is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with Project Hamster. If not, see <http://www.gnu.org/licenses/>.
22
from dbus.lowlevel import Message
27
class DbusIdleListener(object):
29
Listen for idleness coming from org.gnome.ScreenSaver
31
Monitors org.gnome.ScreenSaver for idleness. There are two types,
32
implicit (due to inactivity) and explicit (lock screen), that need to be
33
handled differently. An implicit idle state should subtract the
34
time-to-become-idle (as specified in the gconf) from the last activity,
35
but an explicit idle state should not.
37
The signals are inspected for the "ActiveChanged" and "Lock"
38
members coming from the org.gnome.ScreenSaver interface and the
39
and is_screen_locked members are updated appropriately.
41
def __init__(self, dispatcher):
42
self.screensaver_uri = "org.gnome.ScreenSaver"
44
self.dispatcher = dispatcher
45
self.screen_locked = False
47
self.timeout_minutes = 0 # minutes after session is considered idle
48
self.idle_was_there = False # a workaround variable for pre 2.26
51
self.bus = dbus.SessionBus()
54
# Listen for chatter on the screensaver interface.
55
# We cannot just add additional match strings to narrow down
56
# what we hear because match strings are ORed together.
57
# E.g., if we were to make the match string
58
# "interface='org.gnome.ScreenSaver', type='method_call'",
59
# we would not get only screensaver's method calls, rather
60
# we would get anything on the screensaver interface, as well
61
# as any method calls on *any* interface. Therefore the
62
# bus_inspector needs to do some additional filtering.
63
self.bus.add_match_string_non_blocking("interface='%s'" %
65
self.bus.add_message_filter(self.bus_inspector)
68
def bus_inspector(self, bus, message):
70
Inspect the bus for screensaver messages of interest
73
# We only care about stuff on this interface. We did filter
74
# for it above, but even so we still hear from ourselves
76
if message.get_interface() != self.screensaver_uri:
79
member = message.get_member()
81
if member in ("SessionIdleChanged", "ActiveChanged"):
82
logging.debug("%s -> %s" % (member, message.get_args_list()))
84
idle_state = message.get_args_list()[0]
86
self.idle_from = dt.datetime.now()
88
# from gnome screensaver 2.24 to 2.28 they have switched
89
# configuration keys and signal types.
90
# luckily we can determine key by signal type
91
if member == "SessionIdleChanged":
92
delay_key = "/apps/gnome-screensaver/idle_delay"
94
delay_key = "/desktop/gnome/session/idle_delay"
96
client = gconf.client_get_default()
97
self.timeout_minutes = client.get_int(delay_key)
100
self.screen_locked = False
101
self.idle_from = None
103
if member == "ActiveChanged":
104
# ActiveChanged comes before SessionIdleChanged signal
105
# as a workaround for pre 2.26, we will wait a second - maybe
106
# SessionIdleChanged signal kicks in
107
def dispatch_active_changed(idle_state):
108
if not self.idle_was_there:
109
self.dispatcher.dispatch('active_changed', idle_state)
110
self.idle_was_there = False
112
gobject.timeout_add_seconds(1, dispatch_active_changed, idle_state)
115
# dispatch idle status change to interested parties
116
self.idle_was_there = True
117
self.dispatcher.dispatch('active_changed', idle_state)
119
elif member == "Lock":
120
# in case of lock, lock signal will be sent first, followed by
121
# ActiveChanged and SessionIdle signals
122
logging.debug("Screen Lock Requested")
123
self.screen_locked = True
128
def getIdleFrom(self):
129
if not self.idle_from:
130
return dt.datetime.now()
132
if self.screen_locked:
133
return self.idle_from
135
# Only subtract idle time from the running task when
136
# idleness is due to time out, not a screen lock.
137
return self.idle_from - dt.timedelta(minutes = self.timeout_minutes)