Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions aw_watcher_afk/afk.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@

if system == "Windows":
# noreorder
from .windows import seconds_since_last_input # fmt: skip
from .windows import seconds_since_last_input, is_screen_locked # fmt: skip
elif system == "Darwin":
# noreorder
from .macos import seconds_since_last_input # fmt: skip
from .macos import seconds_since_last_input, is_screen_locked # fmt: skip
elif system == "Linux":
# noreorder
from .unix import seconds_since_last_input # fmt: skip
from .unix import seconds_since_last_input, is_screen_locked # fmt: skip
else:
raise Exception(f"Unsupported platform: {system}")

Expand Down Expand Up @@ -84,19 +84,26 @@ def heartbeat_loop(self):

now = datetime.now(timezone.utc)
seconds_since_input = seconds_since_last_input()
locked = is_screen_locked()
last_input = now - timedelta(seconds=seconds_since_input)
logger.debug(f"Seconds since last input: {seconds_since_input}")
logger.debug(f"Screen locked: {locked}")

# Screen lock means the user is away even if HID idle time is
# low (e.g. mouse jiggle, background audio). While locked we
# must not transition back to not-afk based on synthetic input.
# If no longer AFK
if afk and seconds_since_input < self.settings.timeout:
if afk and not locked and seconds_since_input < self.settings.timeout:
logger.info("No longer AFK")
self.ping(afk, timestamp=last_input)
afk = False
# ping with timestamp+1ms with the next event (to ensure the latest event gets retrieved by get_event)
self.ping(afk, timestamp=last_input + td1ms)
# If becomes AFK
elif not afk and seconds_since_input >= self.settings.timeout:
logger.info("Became AFK")
elif not afk and (
seconds_since_input >= self.settings.timeout or locked
):
logger.info("Became AFK" + (" (screen locked)" if locked else ""))
self.ping(afk, timestamp=last_input)
afk = True
# ping with timestamp+1ms with the next event (to ensure the latest event gets retrieved by get_event)
Expand Down
29 changes: 24 additions & 5 deletions aw_watcher_afk/macos.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
from Quartz.CoreGraphics import (CGEventSourceSecondsSinceLastEventType,
kCGEventSourceStateHIDSystemState,
kCGAnyInputEventType)
from Quartz.CoreGraphics import (
CGEventSourceSecondsSinceLastEventType,
CGSessionCopyCurrentDictionary,
kCGAnyInputEventType,
kCGEventSourceStateHIDSystemState,
)


def is_screen_locked() -> bool:
"""Return True if the macOS screen/session is locked.

Uses the CGSSessionScreenIsLocked key from the current CoreGraphics
session dictionary. That key is only present when the screen is locked
(e.g. loginwindow after Cmd-Ctrl-Q or after the lock delay).
"""
session = CGSessionCopyCurrentDictionary()
if not session:
return False
return bool(session.get("CGSSessionScreenIsLocked", False))


def seconds_since_last_input() -> float:
return CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateHIDSystemState, kCGAnyInputEventType)
return CGEventSourceSecondsSinceLastEventType(
kCGEventSourceStateHIDSystemState, kCGAnyInputEventType
)


if __name__ == "__main__":
from time import sleep

while True:
sleep(1)
print(seconds_since_last_input())
print(seconds_since_last_input(), is_screen_locked())
7 changes: 6 additions & 1 deletion aw_watcher_afk/unix.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ def seconds_since_last_input():
return _last_input_unix.seconds_since_last_input()


def is_screen_locked() -> bool:
# TODO: Linux implementation (e.g. logind LockedHint / DBus screensaver)
return False


if __name__ == "__main__":
while True:
sleep(1)
print(seconds_since_last_input())
print(seconds_since_last_input(), is_screen_locked())
7 changes: 6 additions & 1 deletion aw_watcher_afk/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ def _getTickCount() -> int:
return c_GetTickCount()


def is_screen_locked() -> bool:
# TODO: Windows implementation (e.g. WTSQuerySessionInformation)
return False


def seconds_since_last_input():
seconds_since_input = (_getTickCount() - _getLastInputTick()) / 1000
return seconds_since_input
Expand All @@ -34,4 +39,4 @@ def seconds_since_last_input():
if __name__ == "__main__":
while True:
time.sleep(1)
print(seconds_since_last_input())
print(seconds_since_last_input(), is_screen_locked())
Loading