mouse.py (12087B)
1 import os 2 import pathlib 3 import subprocess 4 5 from talon import ( 6 Context, 7 Module, 8 actions, 9 app, 10 cron, 11 ctrl, 12 clip, 13 imgui, 14 noise, 15 settings, 16 ui, 17 ) 18 from talon_plugins import eye_mouse, eye_zoom_mouse, speech 19 from talon_plugins.eye_mouse import config, toggle_camera_overlay, toggle_control 20 21 key = actions.key 22 self = actions.self 23 scroll_amount = 0 24 click_job = None 25 scroll_job = None 26 gaze_job = None 27 cancel_scroll_on_pop = True 28 control_mouse_forced = False 29 30 default_cursor = { 31 "AppStarting": r"%SystemRoot%\Cursors\aero_working.ani", 32 "Arrow": r"%SystemRoot%\Cursors\aero_arrow.cur", 33 "Hand": r"%SystemRoot%\Cursors\aero_link.cur", 34 "Help": r"%SystemRoot%\Cursors\aero_helpsel.cur", 35 "No": r"%SystemRoot%\Cursors\aero_unavail.cur", 36 "NWPen": r"%SystemRoot%\Cursors\aero_pen.cur", 37 "Person": r"%SystemRoot%\Cursors\aero_person.cur", 38 "Pin": r"%SystemRoot%\Cursors\aero_pin.cur", 39 "SizeAll": r"%SystemRoot%\Cursors\aero_move.cur", 40 "SizeNESW": r"%SystemRoot%\Cursors\aero_nesw.cur", 41 "SizeNS": r"%SystemRoot%\Cursors\aero_ns.cur", 42 "SizeNWSE": r"%SystemRoot%\Cursors\aero_nwse.cur", 43 "SizeWE": r"%SystemRoot%\Cursors\aero_ew.cur", 44 "UpArrow": r"%SystemRoot%\Cursors\aero_up.cur", 45 "Wait": r"%SystemRoot%\Cursors\aero_busy.ani", 46 "Crosshair": "", 47 "IBeam": "", 48 } 49 50 # todo figure out why notepad++ still shows the cursor sometimes. 51 hidden_cursor = os.path.join( 52 os.path.dirname(os.path.realpath(__file__)), r"Resources\HiddenCursor.cur" 53 ) 54 55 mod = Module() 56 mod.list( 57 "mouse_button", desc="List of mouse button words to mouse_click index parameter" 58 ) 59 setting_mouse_enable_pop_click = mod.setting( 60 "mouse_enable_pop_click", 61 type=int, 62 default=0, 63 desc="Enable pop to click when control mouse is enabled.", 64 ) 65 setting_mouse_enable_pop_stops_scroll = mod.setting( 66 "mouse_enable_pop_stops_scroll", 67 type=int, 68 default=0, 69 desc="When enabled, pop stops continuous scroll modes (wheel upper/downer/gaze)", 70 ) 71 setting_mouse_wake_hides_cursor = mod.setting( 72 "mouse_wake_hides_cursor", 73 type=int, 74 default=0, 75 desc="When enabled, mouse wake will hide the cursor. mouse_wake enables zoom mouse.", 76 ) 77 setting_mouse_hide_mouse_gui = mod.setting( 78 "mouse_hide_mouse_gui", 79 type=int, 80 default=0, 81 desc="When enabled, the 'Scroll Mouse' GUI will not be shown.", 82 ) 83 setting_mouse_continuous_scroll_amount = mod.setting( 84 "mouse_continuous_scroll_amount", 85 type=int, 86 default=80, 87 desc="The default amount used when scrolling continuously", 88 ) 89 setting_mouse_wheel_down_amount = mod.setting( 90 "mouse_wheel_down_amount", 91 type=int, 92 default=120, 93 desc="The amount to scroll up/down (equivalent to mouse wheel on Windows by default)", 94 ) 95 96 continuous_scoll_mode = "" 97 98 99 @imgui.open(x=700, y=0) 100 def gui_wheel(gui: imgui.GUI): 101 gui.text("Scroll mode: {}".format(continuous_scoll_mode)) 102 gui.line() 103 if gui.button("Wheel Stop [stop scrolling]"): 104 actions.user.mouse_scroll_stop() 105 106 107 @mod.action_class 108 class Actions: 109 def mouse_show_cursor(): 110 """Shows the cursor""" 111 show_cursor_helper(True) 112 113 def mouse_hide_cursor(): 114 """Hides the cursor""" 115 show_cursor_helper(False) 116 117 def mouse_wake(): 118 """Enable control mouse, zoom mouse, and disables cursor""" 119 eye_zoom_mouse.toggle_zoom_mouse(True) 120 # eye_mouse.control_mouse.enable() 121 if setting_mouse_wake_hides_cursor.get() >= 1: 122 show_cursor_helper(False) 123 124 def mouse_calibrate(): 125 """Start calibration""" 126 eye_mouse.calib_start() 127 128 def mouse_toggle_control_mouse(): 129 """Toggles control mouse""" 130 toggle_control(not config.control_mouse) 131 132 def mouse_toggle_camera_overlay(): 133 """Toggles camera overlay""" 134 toggle_camera_overlay(not config.show_camera) 135 136 def mouse_toggle_zoom_mouse(): 137 """Toggles zoom mouse""" 138 eye_zoom_mouse.toggle_zoom_mouse(not eye_zoom_mouse.zoom_mouse.enabled) 139 140 def mouse_cancel_zoom_mouse(): 141 """Cancel zoom mouse if pending""" 142 if ( 143 eye_zoom_mouse.zoom_mouse.enabled 144 and eye_zoom_mouse.zoom_mouse.state != eye_zoom_mouse.STATE_IDLE 145 ): 146 eye_zoom_mouse.zoom_mouse.cancel() 147 148 def mouse_trigger_zoom_mouse(): 149 """Trigger zoom mouse if enabled""" 150 if eye_zoom_mouse.zoom_mouse.enabled: 151 eye_zoom_mouse.zoom_mouse.on_pop(eye_zoom_mouse.zoom_mouse.state) 152 153 def mouse_drag(): 154 """(TEMPORARY) Press and hold/release button 0 depending on state for dragging""" 155 # todo: fixme temporary fix for drag command 156 button_down = len(list(ctrl.mouse_buttons_down())) > 0 157 print(str(ctrl.mouse_buttons_down())) 158 if not button_down: 159 # print("start drag...") 160 ctrl.mouse_click(button=0, down=True) 161 # app.notify("drag started") 162 else: 163 # print("end drag...") 164 ctrl.mouse_click(button=0, up=True) 165 166 # app.notify("drag stopped") 167 168 def mouse_sleep(): 169 """Disables control mouse, zoom mouse, and re-enables cursor""" 170 eye_zoom_mouse.toggle_zoom_mouse(False) 171 toggle_control(False) 172 show_cursor_helper(True) 173 stop_scroll() 174 175 # todo: fixme temporary fix for drag command 176 button_down = len(list(ctrl.mouse_buttons_down())) > 0 177 if button_down: 178 ctrl.mouse_click(button=0, up=True) 179 180 def mouse_scroll_down(): 181 """Scrolls down""" 182 mouse_scroll(setting_mouse_wheel_down_amount.get())() 183 184 def mouse_scroll_down_continuous(): 185 """Scrolls down continuously""" 186 global continuous_scoll_mode 187 continuous_scoll_mode = "scroll down continuous" 188 mouse_scroll(setting_mouse_continuous_scroll_amount.get())() 189 190 if scroll_job is None: 191 start_scroll() 192 193 if setting_mouse_hide_mouse_gui.get() == 0: 194 gui_wheel.show() 195 196 def mouse_scroll_up(): 197 """Scrolls up""" 198 mouse_scroll(-setting_mouse_wheel_down_amount.get())() 199 200 def mouse_scroll_up_continuous(): 201 """Scrolls up continuously""" 202 global continuous_scoll_mode 203 continuous_scoll_mode = "scroll up continuous" 204 mouse_scroll(-setting_mouse_continuous_scroll_amount.get())() 205 206 if scroll_job is None: 207 start_scroll() 208 if setting_mouse_hide_mouse_gui.get() == 0: 209 gui_wheel.show() 210 211 def mouse_scroll_stop(): 212 """Stops scrolling""" 213 stop_scroll() 214 215 def mouse_gaze_scroll(): 216 """Starts gaze scroll""" 217 global continuous_scoll_mode 218 continuous_scoll_mode = "gaze scroll" 219 220 start_cursor_scrolling() 221 if setting_mouse_hide_mouse_gui.get() == 0: 222 gui_wheel.show() 223 224 # enable 'control mouse' if eye tracker is present and not enabled already 225 global control_mouse_forced 226 if eye_mouse.tracker is not None and not config.control_mouse: 227 toggle_control(True) 228 control_mouse_forced = True 229 230 def copy_mouse_position(): 231 """Copy the current mouse position coordinates""" 232 position = ctrl.mouse_pos() 233 clip.set_text((repr(position))) 234 235 def mouse_move_center_active_window(): 236 """move the mouse cursor to the center of the currently active window""" 237 rect = ui.active_window().rect 238 ctrl.mouse_move(rect.left + (rect.width / 2), rect.top + (rect.height / 2)) 239 240 241 def show_cursor_helper(show): 242 """Show/hide the cursor""" 243 if app.platform == "windows": 244 import ctypes 245 import winreg 246 247 import win32con 248 249 try: 250 Registrykey = winreg.OpenKey( 251 winreg.HKEY_CURRENT_USER, r"Control Panel\Cursors", 0, winreg.KEY_WRITE 252 ) 253 254 for value_name, value in default_cursor.items(): 255 if show: 256 winreg.SetValueEx( 257 Registrykey, value_name, 0, winreg.REG_EXPAND_SZ, value 258 ) 259 else: 260 winreg.SetValueEx( 261 Registrykey, value_name, 0, winreg.REG_EXPAND_SZ, hidden_cursor 262 ) 263 264 winreg.CloseKey(Registrykey) 265 266 ctypes.windll.user32.SystemParametersInfoA( 267 win32con.SPI_SETCURSORS, 0, None, 0 268 ) 269 270 except WindowsError: 271 print("Unable to show_cursor({})".format(str(show))) 272 else: 273 ctrl.cursor_visible(show) 274 275 276 def on_pop(active): 277 if setting_mouse_enable_pop_stops_scroll.get() >= 1 and (gaze_job or scroll_job): 278 stop_scroll() 279 elif ( 280 not eye_zoom_mouse.zoom_mouse.enabled 281 and eye_mouse.mouse.attached_tracker is not None 282 ): 283 if setting_mouse_enable_pop_click.get() >= 1: 284 ctrl.mouse_click(button=0, hold=16000) 285 286 287 noise.register("pop", on_pop) 288 289 290 def mouse_scroll(amount): 291 def scroll(): 292 global scroll_amount 293 if (scroll_amount >= 0) == (amount >= 0): 294 scroll_amount += amount 295 else: 296 scroll_amount = amount 297 actions.mouse_scroll(y=int(amount)) 298 299 return scroll 300 301 302 def scroll_continuous_helper(): 303 global scroll_amount 304 # print("scroll_continuous_helper") 305 if scroll_amount and ( 306 eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_IDLE 307 ): # or eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_SLEEP): 308 actions.mouse_scroll(by_lines=False, y=int(scroll_amount / 10)) 309 310 311 def start_scroll(): 312 global scroll_job 313 scroll_job = cron.interval("60ms", scroll_continuous_helper) 314 # if eye_zoom_mouse.zoom_mouse.enabled and eye_mouse.mouse.attached_tracker is not None: 315 # eye_zoom_mouse.zoom_mouse.sleep(True) 316 317 318 def gaze_scroll(): 319 # print("gaze_scroll") 320 if ( 321 eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_IDLE 322 ): # or eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_SLEEP: 323 x, y = ctrl.mouse_pos() 324 325 # the rect for the window containing the mouse 326 rect = None 327 328 # on windows, check the active_window first since ui.windows() is not z-ordered 329 if app.platform == "windows" and ui.active_window().rect.contains(x, y): 330 rect = ui.active_window().rect 331 else: 332 windows = ui.windows() 333 for w in windows: 334 if w.rect.contains(x, y): 335 rect = w.rect 336 break 337 338 if rect is None: 339 # print("no window found!") 340 return 341 342 midpoint = rect.y + rect.height / 2 343 amount = int(((y - midpoint) / (rect.height / 10)) ** 3) 344 actions.mouse_scroll(by_lines=False, y=amount) 345 346 # print(f"gaze_scroll: {midpoint} {rect.height} {amount}") 347 348 349 def stop_scroll(): 350 global scroll_amount, scroll_job, gaze_job 351 scroll_amount = 0 352 if scroll_job: 353 cron.cancel(scroll_job) 354 355 if gaze_job: 356 cron.cancel(gaze_job) 357 358 global control_mouse_forced 359 if control_mouse_forced and config.control_mouse: 360 toggle_control(False) 361 control_mouse_forced = False 362 363 scroll_job = None 364 gaze_job = None 365 gui_wheel.hide() 366 367 # if eye_zoom_mouse.zoom_mouse.enabled and eye_mouse.mouse.attached_tracker is not None: 368 # eye_zoom_mouse.zoom_mouse.sleep(False) 369 370 371 def start_cursor_scrolling(): 372 global scroll_job, gaze_job 373 stop_scroll() 374 gaze_job = cron.interval("60ms", gaze_scroll) 375 # if eye_zoom_mouse.zoom_mouse.enabled and eye_mouse.mouse.attached_tracker is not None: 376 # eye_zoom_mouse.zoom_mouse.sleep(True) 377 378 379 if app.platform == "mac": 380 from talon import tap 381 382 def on_move(e): 383 if not config.control_mouse: 384 buttons = ctrl.mouse_buttons_down() 385 # print(str(ctrl.mouse_buttons_down())) 386 if not e.flags & tap.DRAG and buttons: 387 e.flags |= tap.DRAG 388 # buttons is a set now 389 e.button = list(buttons)[0] 390 e.modify() 391 392 tap.register(tap.MMOVE | tap.HOOK, on_move)