diff --git a/__main__.py b/__main__.py index b8a9ec8..59e31ff 100644 --- a/__main__.py +++ b/__main__.py @@ -13,9 +13,29 @@ from resources import LineState, CLMgrMessage import serial from serial import SerialException +import session_event_listener as sel +import ctypes portName = 'COM3' +global_inactive = True + +# Lock Screen detection +user32 = ctypes.windll.User32 +OpenDesktop = user32.OpenDesktopA +SwitchDesktop = user32.SwitchDesktop +DESKTOP_SWITCHDESKTOP = 0x0100 + +global_screen_locked = False + +def check_screen_locked(): + hDesktop = OpenDesktop ("default", 0, False, DESKTOP_SWITCHDESKTOP) + result = SwitchDesktop (hDesktop) + if result: + return True + else: + return False + def sendSerial(byte): try: ser = serial.Serial(port=portName, baudrate=115200) @@ -31,6 +51,7 @@ class PhoneLineEventHandler(): def OnDispOnLineMgrNotification(self, msg, param, returns=""): + global global_inactive, global_screen_locked if not self.phone_mgr: self.phone_mgr = win32com.client.Dispatch("CLMgr.ClientLineMgr") if not self.phone_mgr: @@ -46,15 +67,21 @@ class PhoneLineEventHandler(): ) if line.DispState != LineState.Inactive: if line.DispState == LineState.Ringing: - sendSerial(b'shc131139131') + sendSerial(b'shc000255000') else: - sendSerial(b'shc255029000') - asyncio.run(try_pause()) + sendSerial(b'shc255000000') + + if not global_screen_locked: + asyncio.run(try_pause()) + global_inactive = False for linenum in range(self.phone_mgr.DispNumberOfLines): line = self.phone_mgr.DispGetLine(linenum) if line.DispState != LineState.Inactive: return True - asyncio.run(try_play()) + + if not global_screen_locked: + asyncio.run(try_play()) + global_inactive = True sendSerial(b'shc000000000') return True @@ -102,8 +129,27 @@ async def try_pause(): if current_session: await current_session.try_pause_async() +def OnSessionEvent(event: sel.SessionEvent): + global global_inactive, global_screen_locked + if event == sel.SessionEvent.SESSION_LOCK: + global_screen_locked = True + print("locked, try_pause") + asyncio.run(try_pause()) + + if event == sel.SessionEvent.SESSION_UNLOCK: + global_screen_locked = False + if global_inactive: + print("unlocked, global inactive, tryplay") + asyncio.run(try_play()) + if __name__ == '__main__': win32com.client.WithEvents("CLMgr.ClientLineMgr", PhoneLineEventHandler) - while True: - pythoncom.PumpWaitingMessages() - time.sleep(0.1) # Don't use up all our CPU checking constantly \ No newline at end of file + m = sel.WorkstationMonitor() + m.register_handler(sel.SessionEvent.ANY, handler=OnSessionEvent) + try: + while True: + pythoncom.PumpWaitingMessages() + m.listen() + time.sleep(0.1) # Don't use up all our CPU checking constantly + except KeyboardInterrupt: + print("exit") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2bcce01..82e7afd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ winrt -pywin32 \ No newline at end of file +pywin32 +python3-tk \ No newline at end of file diff --git a/session_event_listener.py b/session_event_listener.py new file mode 100644 index 0000000..7470e1f --- /dev/null +++ b/session_event_listener.py @@ -0,0 +1,88 @@ +from collections import defaultdict +from enum import Enum +import win32api +import win32con +import win32gui +import win32ts + + +class SessionEvent(Enum): + ANY = 0 + # window messages + CHANGE = 0x2B1 # WM_WTSSESSION_CHANGE + # WM_WTSSESSION_CHANGE events (wparam) + CONSOLE_CONNECT = 0x1 # WTS_CONSOLE_CONNECT + CONSOLE_DISCONNECT = 0x2 # WTS_CONSOLE_DISCONNECT + REMOTE_CONNECT = 0x3 # WTS_REMOTE_CONNECT + REMOTE_DISCONNECT = 0x4 # WTS_REMOTE_DISCONNECT + SESSION_LOGON = 0x5 # WTS_SESSION_LOGON + SESSION_LOGOFF = 0x6 # WTS_SESSION_LOGOFF + SESSION_LOCK = 0x7 # WTS_SESSION_LOCK + SESSION_UNLOCK = 0x8 # WTS_SESSION_UNLOCK + SESSION_REMOTE_CONTROL = 0x9 # WTS_SESSION_REMOTE_CONTROL + + +class WorkstationMonitor: + CLASS_NAME = "WorkstationMonitor" + WINDOW_TITLE = "Workstation Event Monitor" + + def __init__(self): + self.window_handle = None + self.event_handlers = defaultdict(list) + self._register_listener() + + def _register_listener(self): + wc = win32gui.WNDCLASS() + wc.hInstance = handle_instance = win32api.GetModuleHandle(None) + wc.lpszClassName = self.CLASS_NAME + wc.lpfnWndProc = self._window_procedure + window_class = win32gui.RegisterClass(wc) + + style = 0 + self.window_handle = win32gui.CreateWindow(window_class, + self.WINDOW_TITLE, + style, + 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, + 0, 0, handle_instance, None) + win32gui.UpdateWindow(self.window_handle) + + # scope = win32ts.NOTIFY_FOR_THIS_SESSION + scope = win32ts.NOTIFY_FOR_ALL_SESSIONS + win32ts.WTSRegisterSessionNotification(self.window_handle, scope) + + def listen(self): + win32gui.PumpMessages() + + def stop(self): + exit_code = 0 + win32gui.PostQuitMessage(exit_code) + + def _window_procedure(self, window_handle: int, message: int, event_id, session_id): + """ + # WindowProc callback function + + https://msdn.microsoft.com/en-us/library/ms633573(v=VS.85).aspx + """ + if message == SessionEvent.CHANGE.value: + self._handle_session_change(SessionEvent(event_id), session_id) + elif message == win32con.WM_CLOSE: + win32gui.DestroyWindow(window_handle) + elif message == win32con.WM_DESTROY: + win32gui.PostQuitMessage(0) + elif message == win32con.WM_QUERYENDSESSION: + return True + + def _handle_session_change(self, event: SessionEvent, session_id: int): + for handler in self.event_handlers[event]: + handler(event) + for handler in self.event_handlers[SessionEvent.ANY]: + handler(event) + + def register_handler(self, event: SessionEvent, handler: callable): + self.event_handlers[event].append(handler) + + +if __name__ == '__main__': + m = WorkstationMonitor() + m.register_handler(SessionEvent.ANY, handler=print) + m.listen()