55 lines
1.9 KiB
Python
55 lines
1.9 KiB
Python
import ctypes
|
|
import time
|
|
from ctypes import POINTER, Structure, c_ulonglong
|
|
from ctypes.wintypes import BOOL, DWORD, UINT
|
|
|
|
ULONGLONG = c_ulonglong
|
|
|
|
|
|
class LastInputInfo(Structure):
|
|
_fields_ = [("cbSize", UINT), ("dwTime", DWORD)]
|
|
|
|
|
|
def _getLastInputTick() -> int:
|
|
prototype = ctypes.WINFUNCTYPE(BOOL, POINTER(LastInputInfo)) # type: ignore[attr-defined]
|
|
paramflags = ((1, "lastinputinfo"),)
|
|
c_GetLastInputInfo = prototype(("GetLastInputInfo", ctypes.windll.user32), paramflags) # type: ignore
|
|
|
|
lastinput = LastInputInfo()
|
|
lastinput.cbSize = ctypes.sizeof(LastInputInfo)
|
|
if 0 == c_GetLastInputInfo(lastinput):
|
|
raise OSError("GetLastInputInfo failed")
|
|
return lastinput.dwTime
|
|
|
|
|
|
def _getTickCount64() -> int:
|
|
"""Use GetTickCount64 to avoid 32-bit overflow after ~49.7 days of uptime."""
|
|
prototype = ctypes.WINFUNCTYPE(ULONGLONG) # type: ignore[attr-defined]
|
|
paramflags = ()
|
|
c_GetTickCount64 = prototype(("GetTickCount64", ctypes.windll.kernel32), paramflags) # type: ignore
|
|
return c_GetTickCount64()
|
|
|
|
|
|
def seconds_since_last_input() -> float:
|
|
tick_count = _getTickCount64()
|
|
last_input_tick = _getLastInputTick() # 32-bit DWORD from GetLastInputInfo
|
|
|
|
# GetLastInputInfo returns a 32-bit DWORD tick count that wraps at 2^32 ms.
|
|
# GetTickCount64 returns 64-bit. To compute the difference correctly, we
|
|
# compare only the lower 32 bits when the values are close (normal case),
|
|
# and handle the wraparound when dwTime > lower 32 bits of tick_count.
|
|
tick_lower32 = tick_count & 0xFFFFFFFF
|
|
if tick_lower32 >= last_input_tick:
|
|
diff_ms = tick_lower32 - last_input_tick
|
|
else:
|
|
# 32-bit wraparound: tick_count's lower bits wrapped past dwTime
|
|
diff_ms = (0x100000000 - last_input_tick) + tick_lower32
|
|
|
|
return diff_ms / 1000
|
|
|
|
|
|
if __name__ == "__main__":
|
|
while True:
|
|
time.sleep(1)
|
|
print(seconds_since_last_input())
|