213 # |
213 # |
214 |
214 |
215 class SleepHandler(Foundation.NSObject): |
215 class SleepHandler(Foundation.NSObject): |
216 """ Handle wake/sleep notifications """ |
216 """ Handle wake/sleep notifications """ |
217 |
217 |
|
218 # We need to use the actual time.time() to monitor sleep, as |
|
219 # time.monotonic() many not run during sleep. But time.time() |
|
220 # may encounter adjustments, so we also use time.monotonic() to |
|
221 # monitor wake periods. |
|
222 |
218 def init(self): |
223 def init(self): |
219 self.__sleeptime=None |
224 self.__sleeptime=None |
220 self.__slept=0 |
225 self.__dreamtime_last_sleep=0 |
221 self.__epoch=time.monotonic() |
226 self.__monotonic_last_wakeup=time.monotonic() |
|
227 self.__slept=0 # Only used to store the statistic |
222 self.__lock=threading.Lock() |
228 self.__lock=threading.Lock() |
223 self.__callbacks=weakref.WeakKeyDictionary() |
229 self.__callbacks=weakref.WeakKeyDictionary() |
224 |
230 |
225 return self |
231 return self |
226 |
232 |
227 @protect_noreturn |
233 @protect_noreturn |
228 def handleSleepNotification_(self, aNotification): |
234 def handleSleepNotification_(self, aNotification): |
229 logger.info("System going to sleep") |
235 logger.info("System going to sleep") |
230 now=time.monotonic() |
|
231 with self.__lock: |
236 with self.__lock: |
232 self.__sleeptime=now |
237 self.__sleeptime=time.time() |
|
238 now_m=time.monotonic() |
|
239 self.__dreamtime_last_sleep=(self.__dreamtime_last_sleep+now_m |
|
240 -self.__monotonic_last_wakeup) |
233 callbacks=self.__callbacks.copy() |
241 callbacks=self.__callbacks.copy() |
234 do_callbacks(callbacks, False) |
242 do_callbacks(callbacks, False) |
235 |
243 |
236 @protect_noreturn |
244 @protect_noreturn |
237 def handleWakeNotification_(self, aNotification): |
245 def handleWakeNotification_(self, aNotification): |
238 logger.info("System waking up from sleep") |
246 logger.info("System waking up from sleep") |
239 now=time.monotonic() |
|
240 with self.__lock: |
247 with self.__lock: |
241 if self.__sleeptime: |
248 if self.__sleeptime: |
|
249 self.__monotonic_last_wakeup=time.monotonic() |
|
250 now=time.time() |
242 slept=max(0, now-self.__sleeptime) |
251 slept=max(0, now-self.__sleeptime) |
243 logger.info("Slept %f seconds" % slept) |
252 logger.info("Slept %f seconds" % slept) |
244 self.__slept=self.__slept+slept |
253 self.__slept=self.__slept+slept |
245 self.__sleeptime=None |
254 self.__sleeptime=None |
246 callbacks=self.__callbacks.copy() |
255 callbacks=self.__callbacks.copy() |
247 do_callbacks(callbacks, True) |
256 do_callbacks(callbacks, True) |
248 |
257 |
249 # Return dreamtime |
258 # Return dreamtime |
250 def dreamtime_sleeping(self, snapshot=Snapshot()): |
259 def dreamtime_sleeping(self, snapshot=Snapshot()): |
251 sleeping=False |
|
252 with self.__lock: |
260 with self.__lock: |
253 # macOS "sleep" signals / status are complete bollocks: at least |
261 # macOS "sleep" signals / status are complete bollocks: at least |
254 # when plugged in, the system is actually sometimes running all |
262 # when plugged in, the system is actually sometimes running all |
255 # night with the lid closed and sleepNotification delivered. |
263 # night with the lid closed and sleepNotification delivered. |
256 # Therefore, we need our timers to work in a sane manner when |
264 # Therefore, we need our timers to work in a sane manner when |
257 # we should be sleeping! |
265 # we should be sleeping! |
258 if self.__sleeptime is not None: |
266 if self.__sleeptime is not None: |
259 now_monotonic=self.__sleeptime |
|
260 sleeping=True |
267 sleeping=True |
|
268 now_dreamtime=self.__dreamtime_last_sleep |
261 else: |
269 else: |
262 now_monotonic=snapshot.monotonic() |
270 sleeping=False |
263 now_dreamtime=max(0, now_monotonic-self.__epoch-self.__slept) |
271 now=snapshot.monotonic() |
|
272 now_dreamtime=(self.__dreamtime_last_sleep |
|
273 +now-self.__monotonic_last_wakeup); |
264 return now_dreamtime, sleeping |
274 return now_dreamtime, sleeping |
265 |
275 |
266 # Weirdo (Py)ObjC naming to stop it form choking up |
276 # Weirdo (Py)ObjC naming to stop it form choking up |
267 def addForObj_aCallback_(self, obj, callback): |
277 def addForObj_aCallback_(self, obj, callback): |
268 with self.__lock: |
278 with self.__lock: |