2 # Wake/sleep detection for scheduling adjustments |
2 # Wake/sleep detection for scheduling adjustments |
3 # |
3 # |
4 |
4 |
5 import Foundation |
5 import Foundation |
6 import AppKit |
6 import AppKit |
|
7 import platform |
|
8 import time |
|
9 import threading |
7 import borgend |
10 import borgend |
8 import platform |
11 import weakref |
|
12 import datetime |
9 |
13 |
10 logger=borgend.logger.getChild(__name__) |
14 logger=borgend.logger.getChild(__name__) |
|
15 |
|
16 _dreamtime_monitor=[None] |
|
17 |
|
18 # |
|
19 # Support classes for dealing with different times |
|
20 # |
|
21 |
|
22 def dreamtime(): |
|
23 if _dreamtime_monitor[0]: |
|
24 return max(0, time.monotonic()-_dreamtime_monitor[0].diff()) |
|
25 else: |
|
26 return time.monotonic() |
|
27 |
|
28 class Time: |
|
29 def realtime(self): |
|
30 raise NotImplementedError |
|
31 |
|
32 def monotonic(self): |
|
33 raise NotImplementedError |
|
34 |
|
35 @staticmethod |
|
36 def _now(): |
|
37 raise NotImplementedError |
|
38 |
|
39 @classmethod |
|
40 def now(cls): |
|
41 return cls(cls._now()) |
|
42 |
|
43 @classmethod |
|
44 def from_realtime(cls, realtime): |
|
45 return cls(realtime-time.time()+cls._now()) |
|
46 |
|
47 @classmethod |
|
48 def from_monotonic(cls, monotonic): |
|
49 return cls(monotonic-time.monotonic()+cls._now()) |
|
50 |
|
51 @classmethod |
|
52 def after(cls, seconds): |
|
53 return cls(cls._now()+seconds) |
|
54 |
|
55 def datetime(self): |
|
56 return datetime.datetime.fromtimestamp(self.realtime()) |
|
57 |
|
58 def seconds_to(self): |
|
59 return self._value-self._now() |
|
60 |
|
61 def __lt__(self, other): |
|
62 return self.monotonic() < other.monotonic() |
|
63 |
|
64 def __gt__(self, other): |
|
65 return self.monotonic() > other.monotonic() |
|
66 |
|
67 def __le__(self, other): |
|
68 return self.monotonic() <= other.monotonic() |
|
69 |
|
70 def __ge__(self, other): |
|
71 return self.monotonic() >= other.monotonic() |
|
72 |
|
73 def __eq__(self, other): |
|
74 return self.monotonic() == other.realtime() |
|
75 |
|
76 class RealTime(Time): |
|
77 def __init__(self, when): |
|
78 self._value=when |
|
79 |
|
80 def realtime(self): |
|
81 return self._value |
|
82 |
|
83 def monotonic(self): |
|
84 return self._value+(time.monotonic()-time.time()) |
|
85 |
|
86 @staticmethod |
|
87 def _now(): |
|
88 return time.time() |
|
89 |
|
90 class MonotonicTime(Time): |
|
91 def __init__(self, when): |
|
92 self._value=when |
|
93 |
|
94 def realtime(self): |
|
95 return self._value+(time.time()-time.monotonic()) |
|
96 |
|
97 def monotonic(self): |
|
98 return self._value |
|
99 |
|
100 @staticmethod |
|
101 def _now(): |
|
102 return time.monotonic() |
|
103 |
|
104 class DreamTime(Time): |
|
105 def __init__(self, when): |
|
106 self._value=when |
|
107 |
|
108 def realtime(self): |
|
109 return self._value+(time.time()-dreamtime()) |
|
110 |
|
111 def monotonic(self): |
|
112 return self._value+(time.monotonic()-dreamtime()) |
|
113 |
|
114 @staticmethod |
|
115 def _now(): |
|
116 return dreamtime() |
|
117 |
|
118 # |
|
119 # Wake up / sleep handling |
|
120 # |
11 |
121 |
12 class SleepHandler(Foundation.NSObject): |
122 class SleepHandler(Foundation.NSObject): |
13 """ Handle wake/sleep notifications """ |
123 """ Handle wake/sleep notifications """ |
14 |
124 |
15 def handleSleepNotification(self, aNotification): |
125 def init(self): |
|
126 self.__sleeptime=None |
|
127 self.__slept=0 |
|
128 self.__epoch=time.monotonic() |
|
129 self._lock=threading.Lock() |
|
130 self._callbacks=weakref.WeakKeyDictionary() |
|
131 |
|
132 return self |
|
133 |
|
134 def handleSleepNotification_(self, aNotification): |
16 logger.info("System going to sleep") |
135 logger.info("System going to sleep") |
17 |
136 now=time.monotonic() |
18 def handleWakeNotification(self, aNotification): |
137 with self._lock: |
|
138 self.__sleeptime=now |
|
139 |
|
140 def handleWakeNotification_(self, aNotification): |
19 logger.info("System waking up from sleep") |
141 logger.info("System waking up from sleep") |
20 |
142 try: |
21 if platform.system()=='Darwin': |
143 now=time.monotonic() |
22 workspace = AppKit.NSWorkspace.sharedWorkspace() |
144 with self._lock: |
23 notification_center = workspace.notificationCenter() |
145 if self.__sleeptime: |
24 sleep_handler = SleepHandler.new() |
146 slept=max(0, now-self.__sleeptime) |
25 |
147 logger.info("Slept %f seconds" % slept) |
26 notification_center.addObserver_selector_name_object_( |
148 self.__slept=self.__slept+slept |
27 sleep_handler, |
149 self.__sleeptime=None |
28 "handleSleepNotification", |
150 callbacks=self._callbacks.copy() |
29 AppKit.NSWorkspaceWillSleepNotification, |
151 except: |
30 None) |
152 logger.exception("Bug in wakeup handler") |
31 |
153 |
32 notification_center.addObserver_selector_name_object_( |
154 for callback in callbacks.values(): |
33 sleep_handler, |
155 try: |
34 "handleWakeNotification", |
156 callback() |
35 AppKit.NSWorkspaceDidWakeNotification, |
157 except Exception: |
36 None) |
158 logger.exception("Error in wake notification callback") |
37 |
159 |
|
160 # Return difference to time.monotonic() |
|
161 def diff(self): |
|
162 with self._lock: |
|
163 diff=self.__epoch+self.__slept |
|
164 return diff |
|
165 |
|
166 # Obj-C hates this |
|
167 # def add_callback(self, obj, callback): |
|
168 # with self.lock: |
|
169 # self.__callbacks[obj]=callback |
|
170 |
|
171 # obj is to use a a key in a weak key dictionary |
|
172 def add_callback(obj, callback): |
|
173 monitor=_dreamtime_monitor[0] |
|
174 if not monitor: |
|
175 raise Exception("Dreamtime monitor not started") |
|
176 else: |
|
177 #monitor.add_my_callback(obj, callback) |
|
178 with monitor._lock: |
|
179 monitor._callbacks[obj]=callback |
|
180 |
|
181 def start_monitoring(): |
|
182 if platform.system()=='Darwin': |
|
183 logger.debug("Starting to monitor system sleep") |
|
184 workspace = AppKit.NSWorkspace.sharedWorkspace() |
|
185 notification_center = workspace.notificationCenter() |
|
186 _dreamtime_monitor[0] = SleepHandler.new() |
|
187 |
|
188 notification_center.addObserver_selector_name_object_( |
|
189 _dreamtime_monitor[0], |
|
190 "handleSleepNotification:", |
|
191 AppKit.NSWorkspaceWillSleepNotification, |
|
192 None) |
|
193 |
|
194 notification_center.addObserver_selector_name_object_( |
|
195 _dreamtime_monitor[0], |
|
196 "handleWakeNotification:", |
|
197 AppKit.NSWorkspaceDidWakeNotification, |
|
198 None) |
|
199 else: |
|
200 logger.warning(("No system sleep monitor implemented for '%s'" |
|
201 % platform.system())) |
|
202 |