sleep.py

changeset 76
4b08fca3ce34
parent 69
8705e296c7a0
child 77
e8773133bf79
--- a/sleep.py	Sat Jan 27 12:19:39 2018 +0000
+++ b/sleep.py	Sun Jan 28 00:11:18 2018 +0000
@@ -4,34 +4,199 @@
 
 import Foundation
 import AppKit
+import platform
+import time
+import threading
 import borgend
-import platform
+import weakref
+import datetime
 
 logger=borgend.logger.getChild(__name__)
 
+_dreamtime_monitor=[None]
+
+#
+# Support classes for dealing with different times
+#
+
+def dreamtime():
+    if _dreamtime_monitor[0]:
+        return max(0, time.monotonic()-_dreamtime_monitor[0].diff())
+    else:
+        return time.monotonic()
+
+class Time:
+    def realtime(self):
+        raise NotImplementedError
+
+    def monotonic(self):
+        raise NotImplementedError
+
+    @staticmethod
+    def _now():
+        raise NotImplementedError
+
+    @classmethod
+    def now(cls):
+        return cls(cls._now())
+
+    @classmethod
+    def from_realtime(cls, realtime):
+        return cls(realtime-time.time()+cls._now())
+
+    @classmethod
+    def from_monotonic(cls, monotonic):
+        return cls(monotonic-time.monotonic()+cls._now())
+
+    @classmethod
+    def after(cls, seconds):
+        return cls(cls._now()+seconds)
+
+    def datetime(self):
+        return datetime.datetime.fromtimestamp(self.realtime())
+
+    def seconds_to(self):
+        return self._value-self._now()
+
+    def __lt__(self, other):
+        return self.monotonic() < other.monotonic()
+
+    def __gt__(self, other):
+        return self.monotonic() > other.monotonic()
+
+    def __le__(self, other):
+        return self.monotonic() <= other.monotonic()
+
+    def __ge__(self, other):
+        return self.monotonic() >= other.monotonic()
+
+    def __eq__(self, other):
+        return self.monotonic() == other.realtime()
+
+class RealTime(Time):
+    def __init__(self, when):
+        self._value=when
+
+    def realtime(self):
+        return self._value
+
+    def monotonic(self):
+        return self._value+(time.monotonic()-time.time())
+
+    @staticmethod
+    def _now():
+        return time.time()
+
+class MonotonicTime(Time):
+    def __init__(self, when):
+        self._value=when
+
+    def realtime(self):
+        return self._value+(time.time()-time.monotonic())
+
+    def monotonic(self):
+        return self._value
+
+    @staticmethod
+    def _now():
+        return time.monotonic()
+
+class DreamTime(Time):
+    def __init__(self, when):
+        self._value=when
+
+    def realtime(self):
+        return self._value+(time.time()-dreamtime())
+
+    def monotonic(self):
+        return self._value+(time.monotonic()-dreamtime())
+
+    @staticmethod
+    def _now():
+        return dreamtime()
+
+#
+# Wake up / sleep handling
+#
+
 class SleepHandler(Foundation.NSObject):
     """ Handle wake/sleep notifications """
 
-    def handleSleepNotification(self, aNotification):
+    def init(self):
+        self.__sleeptime=None
+        self.__slept=0
+        self.__epoch=time.monotonic()
+        self._lock=threading.Lock()
+        self._callbacks=weakref.WeakKeyDictionary()
+
+        return self
+
+    def handleSleepNotification_(self, aNotification):
         logger.info("System going to sleep")
+        now=time.monotonic()
+        with self._lock:
+            self.__sleeptime=now
 
-    def handleWakeNotification(self, aNotification):
+    def handleWakeNotification_(self, aNotification):
         logger.info("System waking up from sleep")
+        try:
+            now=time.monotonic()
+            with self._lock:
+                if self.__sleeptime:
+                    slept=max(0, now-self.__sleeptime)
+                    logger.info("Slept %f seconds" % slept)
+                    self.__slept=self.__slept+slept
+                    self.__sleeptime=None
+                callbacks=self._callbacks.copy()
+        except:
+            logger.exception("Bug in wakeup handler")
 
-if platform.system()=='Darwin':
-    workspace = AppKit.NSWorkspace.sharedWorkspace()
-    notification_center = workspace.notificationCenter()
-    sleep_handler = SleepHandler.new()
+        for callback in callbacks.values():
+            try:
+                callback()
+            except Exception:
+                logger.exception("Error in wake notification callback")
 
-    notification_center.addObserver_selector_name_object_(
-        sleep_handler,
-        "handleSleepNotification",
-        AppKit.NSWorkspaceWillSleepNotification,
-        None)
+    # Return difference to time.monotonic()
+    def diff(self):
+        with self._lock:
+            diff=self.__epoch+self.__slept
+        return diff
+
+    # Obj-C hates this
+    # def add_callback(self, obj, callback):
+    #     with self.lock:
+    #         self.__callbacks[obj]=callback
+
+# obj is to use a a key in a weak key dictionary
+def add_callback(obj, callback):
+    monitor=_dreamtime_monitor[0]
+    if not monitor:
+        raise Exception("Dreamtime monitor not started")
+    else:
+        #monitor.add_my_callback(obj, callback)
+        with monitor._lock:
+            monitor._callbacks[obj]=callback
 
-    notification_center.addObserver_selector_name_object_(
-        sleep_handler,
-        "handleWakeNotification",
-        AppKit.NSWorkspaceDidWakeNotification,
-        None)
+def start_monitoring():
+    if platform.system()=='Darwin':
+        logger.debug("Starting to monitor system sleep")
+        workspace = AppKit.NSWorkspace.sharedWorkspace()
+        notification_center = workspace.notificationCenter()
+        _dreamtime_monitor[0] = SleepHandler.new()
 
+        notification_center.addObserver_selector_name_object_(
+            _dreamtime_monitor[0],
+            "handleSleepNotification:",
+            AppKit.NSWorkspaceWillSleepNotification,
+            None)
+
+        notification_center.addObserver_selector_name_object_(
+            _dreamtime_monitor[0],
+            "handleWakeNotification:",
+            AppKit.NSWorkspaceDidWakeNotification,
+            None)
+    else:
+        logger.warning(("No system sleep monitor implemented for '%s'"
+                       % platform.system()))
+

mercurial