Simple Mouse Gesture and Rocker
Latest version is posted here.
Simple Mouse Gesture and Rocker calls subroutines based on either gestures made when the right(r), middle(m), 4th(x1) or fifth(x2) mouse button is held down; when a rocking action is made using these mouse buttons, or a combination of the two.
The script tracks up (u) down (d) left (l) right (r). There is a tolerance of +/- 35 degrees, so it is quite simple and accurate
Subroutines will be called if labelled with the following syntax:
"m-" is the prefix (m for macro)
the next letter is the mouse button that starts the gesture eg m-r: is a gesture that starts with a right click
If the first mouse button (say RButton) remains down and a different mouse button (say LButton) is pressed, the second button is added. eg m-rl:
If a gesture is made, say up then right (ur), the gesture is enclosed in parentheses eg m-r(ur):
gestures and rocking can be combined eg m-rl(u):
Modifier keys - alt(!), ctrl(^) and shift(+) can be used eg m-^r: or m-!-wheeldown:
for simplicity and accuracy, right click-gesture-left click is the same as right click-left click-gesture
With wheel movements, -wheelup or -wheeldown is appended - eg m-r(u)-wheelup:
If the wheel is used, no further gesturing or rocking is allowed (to avoid movement errors). wheel movements can be repeated while the mouse button is held down
Another thing that isn't allowed is gesturing solely with the LButton (so as not to interfere with mouse dragging). The LButton however is registered when other mouse buttons or the mouse wheel are pressed eg m-rl(u): ; m-l-wheelup:
Middle button clicking is difficult if combined with mouse wheel movements. If one is used the other is blocked.This protection can be turned off by making line 3 "wheelguard:=false"
When rocking, the last button clicked can be repeadedly clicked while the other buttons are kept down. also, A different button can be called
eg m-rl can be changed to m-rm or m-rx or if the right button is kept down
You can rock with more than 2 buttons at a time eg m-rlx
By not allowing diagonals, gestures can be drawn without abrupt angles. eg a circle drawn from the top and clockwise is registered as m-r(rdlu): (right down left up)
if a right mouse button gesture is down (d) and right (r), like an "L", a subroutine called m-r(dr): (for gesture with right mouse button, tracking down then right) will be called
the script also pays attention to the window under the gesture and notes if the gesture is made over the desktop
Thanks for any ideas gathered from other gesture programs, especially lexicos
Code:
demomode:=true ;demonstration mode on/off. change to false when ready to use
mintrack=20 ;minimum length of registered track sector in pixels
wheelguard:=true ;if true, wheel and middle button click can't both be used in the same gesture (recommended)
#MaxHotkeysPerInterval 500 ;prevents problems with rapid wheel movements
winget,windesktop,id,ahk_class Progman ;desktop id, useful for some macros
gosub cleanup
*mbutton:: ;list any mouse buttons that can be uniquely named by first letter here eg. *mbutton is "m"
*lbutton:: ;the asterisk allows for the use of modifier keys
*rbutton::
*xbutton1::
*xbutton2::
lastdown:=thishotkey()
if macrorunning or wheel
return
if not buttonsdown
{
mousegetpos xpos1,ypos1,win1 ;win1 is the window underneath the current gesture
modifier:=(getkeystate("alt","p") ? "!" : "") . (getkeystate("shift","p") ? "+" : "") . (getkeystate("ctrl","p") ? "^" : "")
settimer,tracking,1
}
mouseclick:=buttonsdown.=(not buttonsdown and getkeystate("lbutton","p") ? "l" : "") . lastdown
winget,winactive,id,A ;active window id, useful for some macros
sendinput {lbutton up} ;prevents drag box appearing when left/right rocking and gesturing eg. m-lr(d):
hotkey,*lbutton,on ;*lbutton only tracked when other mouse buttons start tracking
hotkey,*lbutton up,on
return
*mbutton up::
if wheel and wheelguard ;middle button clicks ignored when using wheel (if wheelguard=true)
return
*rbutton up::
*lbutton up::
*xbutton1 up::
*xbutton2 up::
lastup:=thishotkey()
stringreplace,buttonsdown,buttonsdown,%lastup%,,all
buttonsup:=true
settimer,tracking,off ;no more gesture tracking once a mouse button has been released
if not wheel and (lastdown=lastup)
gosub callmacro
if not buttonsdown
gosub cleanup
return
*wheelup::
*wheeldown::
if (instr(buttonsdown,"m") and wheelguard) or macrorunning ;prevents accidental use of mousewheel with middle button gestures
return
if not buttonsdown and getkeystate("lbutton","p")
{
mouseclick:=buttonsdown:="l"
hotkey,*lbutton up,on
}
if (buttonsdown and not buttonsup) or modifier
{
stringtrimleft,wheel,a_thishotkey,1
settimer,tracking,off ;no more tracking once wheel used
goto callmacro
}
stringtrimleft,thishotkey,a_thishotkey,1
if not buttonsdown
sendinput {%thishotkey%}
return
callmacro:
macrorunning:=true
macro:="m-" . modifier . mouseclick . (tracking ? "(" . tracking . ")" : "") . (wheel ? "-" . wheel : "")
if (demomode and macro<>"m-r" and macro<>"m-m")
msgbox,,DEMO MODE,%macro% ,1
else
if islabel(macro)
gosub %macro%
macrorunning:=false
return
cleanup:
if (win1<>windesktop)
win2:=win1 ; used in some gestures, win2 is the window the previous macro acted on
hotkey,*lbutton,off ; restore normal left mouse button function
hotkey,*lbutton up,off
wheel:="",tracking="",buttonsup="",buttonsdown="",mouseclick="",track2="",modifier=""
return
tracking:
mousegetpos xpos2,ypos2
angle:=abs((xpos1-xpos2)/(ypos1-ypos2+0.001)) ;+0.001 avoids divide by zero error
if (angle>0.7 and angle<1.428) or (abs(ypos1-ypos2)<5 and abs(xpos1-xpos2)<5) ;ignores angles within 10 degrees of 45 degree diagonal
return ;ignores movement that is too slow
track1:=(abs(ypos1-ypos2)>abs(xpos1-xpos2)) ? (ypos1>ypos2 ? "u" : "d") : (xpos1>xpos2 ? "l" : "r") ;determines up down left right
if (track1<>track2) ;remembers mouse position when track direction changes
xpos_tack:=xpos2,ypos_tack:=ypos2
if (track1<>SubStr(tracking, 0, 1)) and (track1=track2) and (abs(xpos_tack-xpos2)>=mintrack or abs(ypos_tack-ypos2)>=mintrack)
tracking.=track1 ;track if x or y changing sufficient to register. track needs confirmation (2 tracks same direction)
xpos1:=xpos2,ypos1:=ypos2,track2:=track1
return
thishotkey() ;eg reduces "xbutton1 up" to "x1"
{
stringreplace,thishotkey,a_thishotkey,*,,all
stringreplace,thishotkey,thishotkey,%a_space%up,,all
stringreplace,thishotkey,thishotkey,button,,all
return thishotkey
}
esc::exitapp
m-r: ;normal right click
sendinput {rbutton}
return
m-m: ;normal middle click (optional)
sendinput {mbutton}
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;; Examples ;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; wheel examples ;;;;;;;;;;
m-r-wheelup: ;change volume with wheel - hold right button down and use wheel
Send {Volume_Up}
return
m-r-wheeldown:
Send {Volume_Down}
return
;;;;;;;; rocker examples ;;;;;;;;;;
m-rl:
msgbox,,,if you keep right held down, clicking left will repeat action,5
return
m-lr:
msgbox,,,rocker left then right.,5 ;left click will also fire so some macros not suitable
return ;can be used for browser forward if you don't click over hypertext
m-rl(l):
m-lr(l):
msgbox,,,you combined rocking and gesturing,5
return
;;;;;;;;;;;; gesture examples ;;;;;;;;;;
m-r(u): ;; gesture up ;; maximize window under cursor. maximize last window if cursor over desktop
if (win1=windesktop)
{
if (win2<>"")
winmaximize, ahk_id %win2%
}
else
winmaximize ahk_id %win1%
return
m-r(d): ;minimize window under cursor
if (win1<>windesktop) ;don't minimize the desktop
winminimize, ahk_id %win1%
return
m-r(l): ;browser back
winactivate ahk_id %win1%
sendinput {Browser_Back}
return
m-r(r): ;browser forward
winactivate ahk_id %win1%
sendinput {Browser_Forward}
return
m-r(dr): ;restore last window.
winrestore ahk_id %win2%
return
m-r(ldr): ;close window under cursor (draw a C)
if (win1<>windesktop)
winclose ahk_id %win1%
return