diff --git a/mac-agent/kb-focus.py b/mac-agent/kb-focus.py index f2bcb74..7917649 100644 --- a/mac-agent/kb-focus.py +++ b/mac-agent/kb-focus.py @@ -1,6 +1,6 @@ -import signal, sys +import signal, sys, objc from AppKit import NSWorkspace -from Foundation import NSRunLoop, NSDate +from Foundation import NSRunLoop, NSDate, NSObject import hid VID, PID = 0x3384, 0x0009 @@ -20,47 +20,60 @@ def send_app_name(dev, name): dev.write(data) dev.read(32, timeout=1000) +class AppObserver(NSObject): + def init(self): + self = objc.super(AppObserver, self).init() + self.dev = None + self.last_app = None + self.current_app = NSWorkspace.sharedWorkspace().frontmostApplication().localizedName() + return self + + def appChanged_(self, notification): + app = notification.userInfo()["NSWorkspaceApplicationKey"].localizedName() + self.current_app = app + if self.dev is not None: + self.last_app = app + print(f"App: {app}") + try: + send_app_name(self.dev, app) + except Exception: + print("Keyboard disconnected") + self.dev = None + self.last_app = None + def main(): signal.signal(signal.SIGINT, lambda *_: sys.exit(0)) ws = NSWorkspace.sharedWorkspace() - dev = None - last_app = None + + observer = AppObserver.alloc().init() + ws.notificationCenter().addObserver_selector_name_object_( + observer, "appChanged:", "NSWorkspaceDidActivateApplicationNotification", None + ) while True: connected = find_keyboard_path() is not None # Detect disconnect - if dev is not None and not connected: + if observer.dev is not None and not connected: print("Keyboard disconnected") - dev = None - last_app = None + observer.dev = None + observer.last_app = None # Connect and send current app - if dev is None and connected: + if observer.dev is None and connected: try: - dev = hid.Device(path=find_keyboard_path()) + observer.dev = hid.Device(path=find_keyboard_path()) print("Keyboard connected") - last_app = ws.frontmostApplication().localizedName() - print(f"App: {last_app}") - send_app_name(dev, last_app) + observer.last_app = observer.current_app + print(f"App: {observer.current_app}") + send_app_name(observer.dev, observer.current_app) except Exception: - dev = None - - # App change - if dev is not None: - app = ws.frontmostApplication().localizedName() - if app != last_app: - print(f"App: {app}") - last_app = app - try: - send_app_name(dev, app) - except Exception: - print("Keyboard disconnected") - dev = None - last_app = None + observer.dev = None + # Pump run loop — notifications fire here and send immediately; + # 2s is enough for connect/disconnect detection NSRunLoop.currentRunLoop().runUntilDate_( - NSDate.dateWithTimeIntervalSinceNow_(0.25) + NSDate.dateWithTimeIntervalSinceNow_(2.0) ) if __name__ == "__main__":