[hamster-applet/windows] Implemented idle and screenlock detection



commit daf93241486ad9ca73f93b5623bb4c95dfba3d11
Author: Matthew Howle <matthew howle org>
Date:   Sun Mar 27 04:34:35 2011 -0400

    Implemented idle and screenlock detection

 src/hamster-time-tracker |    9 ++-
 src/hamster/idle.py      |  220 +++++++++++++++++++++-------------------------
 2 files changed, 107 insertions(+), 122 deletions(-)
---
diff --git a/src/hamster-time-tracker b/src/hamster-time-tracker
index a138481..f77ca76 100755
--- a/src/hamster-time-tracker
+++ b/src/hamster-time-tracker
@@ -58,6 +58,10 @@ class ProjectHamster(object):
         self.get_widget("today_box").add(self.treeview)
         self.new_name.grab_focus()
 
+        #idle listener 
+        self.IdleListener = idle.IdleListener()
+        self.IdleListener.connect('idle-changed', self.on_idle_changed)
+
         # configuration
         self.timeout_enabled = conf.get("enable_timeout")
         self.notify_on_idle = conf.get("notify_on_idle")
@@ -285,15 +289,14 @@ class ProjectHamster(object):
 
     def on_idle_changed(self, event, state):
         # state values: 0 = active, 1 = idle
-
+        
         # refresh when we are out of idle
         # for example, instantly after coming back from suspend
         if state == 0:
             self.refresh_hamster()
         elif self.timeout_enabled and self.last_activity and \
              self.last_activity.end_time is None:
-                 #TODO: need a Windows way to get idle time
-                 runtime.storage.stop_tracking(end_time = dt.datetime.now())
+                 runtime.storage.stop_tracking(end_time = self.IdleListener.get_idle_from())
 
     def on_workspace_changed(self, screen, previous_workspace):
         if not previous_workspace:
diff --git a/src/hamster/idle.py b/src/hamster/idle.py
index f305db7..9aee8ac 100644
--- a/src/hamster/idle.py
+++ b/src/hamster/idle.py
@@ -17,122 +17,104 @@
 # You should have received a copy of the GNU General Public License
 # along with Project Hamster.  If not, see <http://www.gnu.org/licenses/>.
 #
-#import logging
-#import dbus
-#import datetime as dt
-#import gobject
-# TODO: Create a Windows alternative
-#class DbusIdleListener(gobject.GObject):
-#    """
-#    Listen for idleness coming from org.gnome.ScreenSaver
-#
-#    Monitors org.gnome.ScreenSaver for idleness. There are two types,
-#    implicit (due to inactivity) and explicit (lock screen), that need to be
-#    handled differently. An implicit idle state should subtract the
-#    time-to-become-idle (as specified in the gconf) from the last activity,
-#    but an explicit idle state should not.
-#
-#    The signals are inspected for the "ActiveChanged" and "Lock"
-#    members coming from the org.gnome.ScreenSaver interface and the
-#    and is_screen_locked members are updated appropriately.
-#    """
-#    __gsignals__ = {
-#        "idle-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
-#    }
-#    def __init__(self):
-#        gobject.GObject.__init__(self)
-#
-#        self.screensaver_uri = "org.gnome.ScreenSaver"
-#        self.screen_locked = False
-#        self.idle_from = None
-#        self.timeout_minutes = 0 # minutes after session is considered idle
-#        self.idle_was_there = False # a workaround variable for pre 2.26
-#
-#        try:
-#            self.bus = dbus.SessionBus()
-#        except:
-#            return 0
-#        # Listen for chatter on the screensaver interface.
-#        # We cannot just add additional match strings to narrow down
-#        # what we hear because match strings are ORed together.
-#        # E.g., if we were to make the match string
-#        # "interface='org.gnome.ScreenSaver', type='method_call'",
-#        # we would not get only screensaver's method calls, rather
-#        # we would get anything on the screensaver interface, as well
-#        # as any method calls on *any* interface. Therefore the
-#        # bus_inspector needs to do some additional filtering.
-#        self.bus.add_match_string_non_blocking("interface='%s'" %
-#                                                           self.screensaver_uri)
-#        self.bus.add_message_filter(self.bus_inspector)
-#
-#
-#    def bus_inspector(self, bus, message):
-#        """
-#        Inspect the bus for screensaver messages of interest
-#        """
-#
-#        # We only care about stuff on this interface.  We did filter
-#        # for it above, but even so we still hear from ourselves
-#        # (hamster messages).
-#        if message.get_interface() != self.screensaver_uri:
-#            return True
-#
-#        member = message.get_member()
-#
-#        if member in ("SessionIdleChanged", "ActiveChanged"):
-#            logging.debug("%s -> %s" % (member, message.get_args_list()))
-#
-#            idle_state = message.get_args_list()[0]
-#            if idle_state:
-#                self.idle_from = dt.datetime.now()
-#
-#                # from gnome screensaver 2.24 to 2.28 they have switched
-#                # configuration keys and signal types.
-#                # luckily we can determine key by signal type
-#                if member == "SessionIdleChanged":
-#                    delay_key = "/apps/gnome-screensaver/idle_delay"
-#                else:
-#                    delay_key = "/desktop/gnome/session/idle_delay"
-#
-#                client = gconf.client_get_default()
-#                self.timeout_minutes = client.get_int(delay_key)
-#
-#            else:
-#                self.screen_locked = False
-#                self.idle_from = None
-#
-#            if member == "ActiveChanged":
-#                # ActiveChanged comes before SessionIdleChanged signal
-#                # as a workaround for pre 2.26, we will wait a second - maybe
-#                # SessionIdleChanged signal kicks in
-#                def dispatch_active_changed(idle_state):
-#                    if not self.idle_was_there:
-#                        self.emit('idle-changed', idle_state)
-#                    self.idle_was_there = False
-#
-#                gobject.timeout_add_seconds(1, dispatch_active_changed, idle_state)
-#
-#            else:
-#                # dispatch idle status change to interested parties
-#                self.idle_was_there = True
-#                self.emit('idle-changed', idle_state)
-#
-#        elif member == "Lock":
-#            # in case of lock, lock signal will be sent first, followed by
-#            # ActiveChanged and SessionIdle signals
-#            logging.debug("Screen Lock Requested")
-#            self.screen_locked = True
-#
-#        return
-#
-#
-#    def getIdleFrom(self):
-#        if not self.idle_from:
-#            return dt.datetime.now()
-#
-#        if self.screen_locked:
-#            return self.idle_from
-#        else:
-#            # Only subtract idle time from the running task when
-#            # idleness is due to time out, not a screen lock.
-#            return self.idle_from - dt.timedelta(minutes = self.timeout_minutes)
+import logging
+import datetime as dt
+import gobject
+import ctypes, ctypes.wintypes
+try:
+    import _winreg as winreg
+except ImportError:
+    try:
+        import winreg
+    except ImportError:
+        winreg = None
+
+class IdleListener(gobject.GObject):
+    """
+    Listen for idleness
+
+    Monitors the system for idleness.  There are two types, implicit (due to
+    inactivity) and explicit (locked screen), that need to be handled differently.
+    An implicit idle state should subtract the time-to-become-idle (as specified
+    in the configuration) from the last activity but an explicit idle state should
+    not.
+    """
+
+    __gsignals__ = {
+        "idle-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
+    }
+    def __init__(self):
+        gobject.GObject.__init__(self)
+
+        self.screen_locked = False
+        self.idle_from = None
+        self.timeout = self.get_screensaver_timeout()
+        gobject.timeout_add_seconds(5, self.check_idle)
+
+    def check_idle(self):
+        # Check idleness or screen lock
+        idletime = self.get_idle_time()
+        self.screen_locked = self.is_screenlocked()
+        if idletime >= self.timeout or self.screen_locked:
+            if self.idle_from is None:
+                logging.debug("idle/screenlock detected")
+                self.idle_from = dt.datetime.now()
+                self.emit('idle-changed', 1)
+        elif self.idle_from is not None: # user is back
+            self.idle_from = None
+            self.emit('idle-changed', 0)
+
+        return True
+        
+
+    def get_idle_time(self):
+        """ Returns the idle time in seconds """
+
+        class LASTINPUTINFO(ctypes.Structure):
+            """ LastInputInfo struct (http://msdn.microsoft.com/en-us/library/ms646272%28v=VS.85%29.aspx) """
+            _fields_ = [
+                ("cbSize", ctypes.wintypes.UINT),
+                ("dwTime",  ctypes.wintypes.DWORD),
+            ]
+
+        lastinput = LASTINPUTINFO()
+        lastinput.cbSize = ctypes.sizeof(lastinput) # this member must be set to sizeof(LASTINPUTINFO)
+    
+        ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastinput))
+        ticks = ctypes.windll.kernel32.GetTickCount() - lastinput.dwTime
+    
+        return ticks / 1000.0  # convert milliseconds to seconds
+
+    def get_idle_from(self):
+        """ Return time when the user went idle """
+        if not self.idle_from:
+            return dt.datetime.now()
+
+        if self.screen_locked:
+            return self.idle_from
+        else:
+            return self.idle_from - dt.timedelta(seconds = self.timeout)
+
+    def get_screensaver_timeout(self):
+        """ Returns the Windows screensaver timeout (in seconds) """
+
+        if winreg == None: return 600  # default to 10 minutes
+
+        try:
+            key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Control Panel\Desktop")
+            value = winreg.QueryValueEx(key, "ScreenSaveTimeOut")[0]
+            key.Close()
+            return int(value)
+        except:
+            logging.warn("WARNING - Failed to get screensaver timeout")
+            return 600
+        
+        
+    def is_screenlocked(self):
+        # if screen is locked, GetForegroundWindow() returns 0
+        if ctypes.windll.user32.GetForegroundWindow() == 0:
+            return True
+        else:
+            return False
+
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]