from talon import Context, Module, actions, app, clip, cron, ctrl, imgui, settings, ui from talon_plugins import eye_zoom_mouse key = actions.key self = actions.self scroll_amount = 0 click_job = None scroll_job = None gaze_job = None cancel_scroll_on_pop = True control_mouse_forced = False hiss_scroll_up = False mod = Module() ctx = Context() mod.list( "mouse_button", desc="List of mouse button words to mouse_click index parameter", ) mod.setting( "mouse_enable_pop_click", type=int, default=0, desc="Pop noise clicks left mouse button. 0 = off, 1 = on with eyetracker but not with zoom mouse mode, 2 = on but not with zoom mouse mode", ) mod.setting( "mouse_enable_pop_stops_scroll", type=bool, default=False, desc="When enabled, pop stops continuous scroll modes (wheel upper/downer/gaze)", ) mod.setting( "mouse_enable_pop_stops_drag", type=bool, default=False, desc="When enabled, pop stops mouse drag", ) mod.setting( "mouse_enable_hiss_scroll", type=bool, default=False, desc="Hiss noise scrolls down when enabled", ) mod.setting( "mouse_wake_hides_cursor", type=bool, default=False, desc="When enabled, mouse wake will hide the cursor. mouse_wake enables zoom mouse.", ) mod.setting( "mouse_hide_mouse_gui", type=bool, default=False, desc="When enabled, the 'Scroll Mouse' GUI will not be shown.", ) mod.setting( "mouse_continuous_scroll_amount", type=int, default=80, desc="The default amount used when scrolling continuously", ) mod.setting( "mouse_wheel_down_amount", type=int, default=120, desc="The amount to scroll up/down (equivalent to mouse wheel on Windows by default)", ) mod.setting( "mouse_wheel_horizontal_amount", type=int, default=40, desc="The amount to scroll left/right", ) continuous_scroll_mode = "" @imgui.open(x=700, y=0) def gui_wheel(gui: imgui.GUI): gui.text(f"Scroll mode: {continuous_scroll_mode}") gui.line() if gui.button("Wheel Stop [stop scrolling]"): actions.user.mouse_scroll_stop() @mod.action_class class Actions: def zoom_close(): """Closes an in-progress zoom. Talon will move the cursor position but not click.""" if eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_OVERLAY: actions.tracking.zoom_cancel() def mouse_wake(): """Enable control mouse, zoom mouse, and disables cursor""" actions.tracking.control_zoom_toggle(True) if settings.get("user.mouse_wake_hides_cursor"): actions.user.mouse_cursor_hide() def mouse_drag(button: int): """Press and hold/release a specific mouse button for dragging""" # Clear any existing drags actions.user.mouse_drag_end() # Start drag ctrl.mouse_click(button=button, down=True) def mouse_drag_end(): """Releases any held mouse buttons""" for button in ctrl.mouse_buttons_down(): ctrl.mouse_click(button=button, up=True) def mouse_drag_toggle(button: int): """If the button is held down, release the button, else start dragging""" if button in list(ctrl.mouse_buttons_down()): ctrl.mouse_click(button=button, up=True) else: actions.user.mouse_drag(button=button) def mouse_sleep(): """Disables control mouse, zoom mouse, and re-enables cursor""" actions.tracking.control_zoom_toggle(False) actions.tracking.control_toggle(False) actions.tracking.control1_toggle(False) actions.user.mouse_cursor_show() stop_scroll() actions.user.mouse_drag_end() def mouse_scroll_down(amount: float = 1): """Scrolls down""" mouse_scroll(amount * settings.get("user.mouse_wheel_down_amount"))() def mouse_scroll_down_continuous(): """Scrolls down continuously""" global continuous_scroll_mode continuous_scroll_mode = "scroll down continuous" mouse_scroll(settings.get("user.mouse_continuous_scroll_amount"))() if scroll_job is None: start_scroll() if not settings.get("user.mouse_hide_mouse_gui"): gui_wheel.show() def mouse_scroll_up(amount: float = 1): """Scrolls up""" mouse_scroll(-amount * settings.get("user.mouse_wheel_down_amount"))() def mouse_scroll_up_continuous(): """Scrolls up continuously""" global continuous_scroll_mode continuous_scroll_mode = "scroll up continuous" mouse_scroll(-settings.get("user.mouse_continuous_scroll_amount"))() if scroll_job is None: start_scroll() if not settings.get("user.mouse_hide_mouse_gui"): gui_wheel.show() def mouse_scroll_left(amount: float = 1): """Scrolls left""" actions.mouse_scroll( 0, -amount * settings.get("user.mouse_wheel_horizontal_amount") ) def mouse_scroll_right(amount: float = 1): """Scrolls right""" actions.mouse_scroll( 0, amount * settings.get("user.mouse_wheel_horizontal_amount") ) def mouse_scroll_stop(): """Stops scrolling""" stop_scroll() def mouse_gaze_scroll(): """Starts gaze scroll""" global continuous_scroll_mode # this calls stop_scroll, which resets continuous_scroll_mode start_cursor_scrolling() continuous_scroll_mode = "gaze scroll" if not settings.get("user.mouse_hide_mouse_gui"): gui_wheel.show() # enable 'control mouse' if eye tracker is present and not enabled already global control_mouse_forced if not actions.tracking.control_enabled(): actions.tracking.control_toggle(True) control_mouse_forced = True def mouse_gaze_scroll_toggle(): """If not scrolling, start gaze scroll, else stop scrolling.""" if continuous_scroll_mode == "": actions.user.mouse_gaze_scroll() else: actions.user.mouse_scroll_stop() def copy_mouse_position(): """Copy the current mouse position coordinates""" position = ctrl.mouse_pos() clip.set_text(repr(position)) def mouse_move_center_active_window(): """move the mouse cursor to the center of the currently active window""" rect = ui.active_window().rect ctrl.mouse_move(rect.left + (rect.width / 2), rect.top + (rect.height / 2)) def hiss_scroll_up(): """Change mouse hiss scroll direction to up""" global hiss_scroll_up hiss_scroll_up = True def hiss_scroll_down(): """Change mouse hiss scroll direction to down""" global hiss_scroll_up hiss_scroll_up = False @ctx.action_class("user") class UserActions: def noise_trigger_pop(): if ( settings.get("user.mouse_enable_pop_stops_drag") and ctrl.mouse_buttons_down() ): actions.user.mouse_drag_end() elif settings.get("user.mouse_enable_pop_stops_scroll") and ( gaze_job or scroll_job ): # Allow pop to stop scroll stop_scroll() else: # Otherwise respect the mouse_enable_pop_click setting setting_val = settings.get("user.mouse_enable_pop_click") is_using_eye_tracker = ( actions.tracking.control_zoom_enabled() or actions.tracking.control_enabled() or actions.tracking.control1_enabled() ) should_click = ( setting_val == 2 and not actions.tracking.control_zoom_enabled() ) or ( setting_val == 1 and is_using_eye_tracker and not actions.tracking.control_zoom_enabled() ) if should_click: ctrl.mouse_click(button=0, hold=16000) def noise_trigger_hiss(active: bool): if settings.get("user.mouse_enable_hiss_scroll"): if active: if hiss_scroll_up: actions.user.mouse_scroll_up_continuous() else: actions.user.mouse_scroll_down_continuous() else: actions.user.mouse_scroll_stop() def mouse_scroll(amount): def scroll(): global scroll_amount if continuous_scroll_mode: if (scroll_amount >= 0) == (amount >= 0): scroll_amount += amount else: scroll_amount = amount actions.mouse_scroll(y=int(amount)) return scroll def scroll_continuous_helper(): global scroll_amount # print("scroll_continuous_helper") if scroll_amount and (eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_IDLE): actions.mouse_scroll(by_lines=False, y=int(scroll_amount / 10)) def start_scroll(): global scroll_job scroll_job = cron.interval("60ms", scroll_continuous_helper) def gaze_scroll(): # print("gaze_scroll") if ( eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_IDLE ): # or eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_SLEEP: x, y = ctrl.mouse_pos() # the rect for the window containing the mouse rect = None # on windows, check the active_window first since ui.windows() is not z-ordered if app.platform == "windows" and ui.active_window().rect.contains(x, y): rect = ui.active_window().rect else: windows = ui.windows() for w in windows: if w.rect.contains(x, y): rect = w.rect break if rect is None: # print("no window found!") return midpoint = rect.y + rect.height / 2 amount = int(((y - midpoint) / (rect.height / 10)) ** 3) actions.mouse_scroll(by_lines=False, y=amount) # print(f"gaze_scroll: {midpoint} {rect.height} {amount}") def stop_scroll(): global scroll_amount, scroll_job, gaze_job, continuous_scroll_mode scroll_amount = 0 if scroll_job: cron.cancel(scroll_job) if gaze_job: cron.cancel(gaze_job) global control_mouse_forced if control_mouse_forced: actions.tracking.control_toggle(False) control_mouse_forced = False scroll_job = None gaze_job = None gui_wheel.hide() continuous_scroll_mode = "" def start_cursor_scrolling(): global scroll_job, gaze_job stop_scroll() gaze_job = cron.interval("60ms", gaze_scroll)