44from  dataclasses  import  dataclass 
55from  collections  import  deque   # for storing x, y time series 
66import  numpy  as  np   # for smoothing moving average 
7- import  ctypes   # for windows mouse 
7+ import  ctypes   # for windows mouse POINT struct  
88import  socket   # udp networking 
99import  struct   # binary unpacking 
10+ # import keyboard # keyboard shortcuts 
11+ 
12+ from  pynput  import  keyboard 
13+ 
14+ 
15+ 
16+ 
17+ # Collect events until released 
18+ # with keyboard.Listener( 
19+ #         on_press=on_press, 
20+ #         on_release=on_release) as listener: 
21+ #     listener.join() 
22+ 
23+ # ...or, in a non-blocking fashion: 
24+ 
25+ 
26+ enabled  =  True 
27+ 
28+ def  toggle ():
29+     global  enabled 
30+     logging .info ("\n Toggled PhilNav on/off\n " )
31+     enabled  =  not  enabled 
32+ 
33+ # https://pynput.readthedocs.io/en/latest/keyboard.html#global-hotkeys 
34+ 
35+ def  for_canonical (f ):
36+     return  lambda  k : f (listener .canonical (k ))
37+ 
38+ hotkey  =  keyboard .HotKey (
39+     [keyboard .Key .shift , keyboard .Key .f7 .value ],
40+     toggle )
41+ 
42+ listener  =  keyboard .Listener (
43+         on_press = for_canonical (hotkey .press ),
44+         on_release = for_canonical (hotkey .release ))
45+ 
46+ listener .start ()
47+ 
48+ # keyboard.add_hotkey('space', toggle) 
49+ # keyboard.add_hotkey('space', toggle) 
50+ 
51+ # keyboard.write('The quick brown fox jumps over the lazy dog.') 
52+ # keyboard.add_hotkey('space', lambda: print('space was pressed!')) 
53+ 
54+ # returned from ctypes.windll.user32.GetCursorPos 
55+ # simple point.x, point.y 
56+ class  POINT (ctypes .Structure ):
57+     _fields_  =  [("x" , ctypes .c_long ), ("y" , ctypes .c_long )]
58+ 
59+ import  platform 
1060# Done: Windows 
1161# TODO: Mac, Linux 
62+ match  platform .system ():
63+     case  "Darwin" : # macOS 
64+         from  mouse_mac  import  getCursorPos , setCursorPos 
65+ 
1266
1367print ("\n \n CLIENT: Starting PhilNav\n " )
1468
4397    logging .info (" Logging verbosely\n " )
4498
4599
46- # returned from ctypes.windll.user32.GetCursorPos 
47- # simple point.x, point.y 
48- class  POINT (ctypes .Structure ):
49-     _fields_  =  [("x" , ctypes .c_long ), ("y" , ctypes .c_long )]
50100
51101
52102# initialize networking 
@@ -65,8 +115,7 @@ class POINT(ctypes.Structure):
65115# would allow discovery, but when you are *in public* - like at a coffee shop - 
66116# you don't want strangers to access your PC. 
67117text_listening  =  (
68-     f"Listening on {  
69-         sock .getsockname ()}   for mouse data from Raspberry Pi server..."
118+     f"Listening on { sock .getsockname ()}  
70119)
71120print (ctime () +  " - "  +  text_listening )
72121print ("\n Press Ctrl-C to exit\n " )
@@ -137,16 +186,15 @@ def smooth(q):
137186            y_smooth  =  y 
138187
139188        # The Magic Happens Now! eg. move mouse cursor =P 
140-         pt  =  POINT ()
141-         ctypes .windll .user32 .GetCursorPos (
142-             ctypes .byref (pt )
143-         )  # get current mouse position by reference (C++ thing) 
189+         x_cur , y_cur  =  getCursorPos ()
190+         # get current mouse position by reference (C++ thing) 
144191        # I'm moving the Y axis slightly faster because looking left and right 
145192        # is easier than nodding up and down. Also, monitors are wider than they 
146193        # are tall. 
147-         x_new  =  round (pt .x  +  x_smooth  *  args .speed )
148-         y_new  =  round (pt .y  +  y_smooth  *  args .speed  *  1.25 )
149-         ctypes .windll .user32 .SetCursorPos (x_new , y_new )  # move mouse cursor 
194+         x_new  =  round (x_cur  +  x_smooth  *  args .speed )
195+         y_new  =  round (y_cur  +  y_smooth  *  args .speed  *  1.25 )
196+         if  enabled :
197+             setCursorPos (x_new , y_new )  # move mouse cursor 
150198
151199        # I'm trying to measure the total time from capturing the frame on the 
152200        # camera to moving the mouse cursor on my PC. This isn't super accurate. 
@@ -161,10 +209,8 @@ def smooth(q):
161209            # display legend every 5 seconds 
162210            if  PhilNav .debug_num  %  5  ==  1 :
163211                logging .info (
164-                     f" { ctime ()} { 'x_diff' :>8} { 'y_diff' :>8} {  
165-                         'n/a' :>8}  ,{ 'n/a' :>8} { 'loc ns' :>8} { 'net ms' :>8} 
212+                     f" { ctime ()} { 'x_diff' :>8} { 'y_diff' :>8} { 'n/a' :>8} { 'n/a' :>8} { 'loc ns' :>8} { 'net ms' :>8}  
166213                )
167214            logging .info (
168-                 f" { ctime ()} { x :> 8.2f} { y :> 8.2f} { z :> 8.2f} { pitch :> 8.2f} {  
169-                     (time () -  PhilNav .msg_time_start )* 1000 :> 8.2f}  ,{ time_diff_ms :> 8} 
215+                 f" { ctime ()} { x :> 8.2f} { y :> 8.2f} { z :> 8.2f} { pitch :> 8.2f} { (time () -  PhilNav .msg_time_start )* 1000 :> 8.2f} { time_diff_ms :> 8}  
170216            )
0 commit comments