Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Lex' Mouse Gestures


  • Please log in to reply
131 replies to this topic
Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Lex' Mouse Gestures
Allows mouse gestures similar to the All-in-One Gestures addon for Firefox, on which it is loosely based. Gestures are triggered by holding the gesture key (default is the right mouse button), drawing a sequence of "strokes," then releasing the gesture key. By default, a stroke can be up, down, left or right.

Features:
  • Allows an arbitrary number of directions ("zones"), though 4 is the most practical and efficient.
  • Allows any number of "strokes" in a gesture (between pressing and releasing the gesture key).
  • Gestures either execute a label or send keystrokes.
  • Wheel gestures (gesture key + wheel up/down.)
  • Various options controlling gesture recognition. See Gestures.ahk for a description of each option.
  • Custom enabled/disabled tray icons.
  • Win + Gesture key enables/disables gestures, and indicates the current status by playing a sound from Wurt Simplicity.
  • Note that it ignores the length of each stroke (excluding m_LowThreshold and m_HighThreshold), so two consecutive strokes cannot be in the same zone. For instance, Gesture_L_L is not valid, since it would be recognized as Gesture_L.
  • Draw visible gestures on-screen by setting the m_PenWidth option. To define the colour, set m_PenColor to a RRGGBB colour code.
Script Structure:
  • Gestures.ahk contains the "gesture engine."
  • Gestures_Default.ahk contains the default scripted gestures.
  • Gestures_User.ahk should be created by the user to contain user-defined gestures, option overrides or any unrelated scripts.
Default Gestures:
  • Default_L (left) - Back - !{Left}, ^-, or Browser_Back depending on the active window.
  • Default_R (right) - Forward.
  • Default_D_R (down, then right) - Close the active window. A three second delay is implemented to allow cancelling by pressing Escape.
  • Default_R_D_L_U (right, down, left, up) - Reload the script.
  • Default_L_D_R_U (left, down, right, up) - Edit Gestures.ini.
  • Default_L_D (left, down) - Minimize active window.
  • Default_R_U (right, up) - Maximize active window.
  • Default_R_L (right, left) or
    Default_L_R (left, right) - Undo recent minimize, restore active window, or restore the "top-most" minimized window.
  • Default_L_D_U (left, down, up) or
    Default_U_L_D_U (up, left, down, up) - Restore the "top-most" minimized window. (Since the above gesture prefers to restore the active window if maximized.)
  • Default_R_L_R_L (right, left, right, left) or
    Default_L_R_L_R (left, right, left, right) - Enter "window move" mode. Click or press Enter to confirm window location. Press Escape to cancel. (This really just activates "Move" from the window's system menu.)
  • Gesture_Default - Pressing the gesture key without moving the mouse executes Gesture_Default. If the gesture key is a mouse button, it simulates a click+drag from the gesture start point to the current mouse location, otherwise it sends a keystroke. Gesture_Default is also executed if the gesture is cancelled via m_Timeout and m_DefaultOnTimeout is non-zero.
Download
(Includes script, tray icons and wurt sounds used by the script.)

Example Customizations
Gestures_User.ahk contains a number of customizations:
  • Additional gestures, defined with Send syntax.
  • Gesture_WheelUp (gesture key + wheel up) - Ctrl+Shift+Tab.
  • Gesture_WheelDown (gesture key + wheel down) - Ctrl+Tab.
  • Gesture_L_U (left, up) - Up one level in Explorer/a file dialog.
  • Gesture_U (up) - Launch_App1 (usually My Computer.)
  • Gesture_D (down) - Launch_App2 (usually Calculator.)
  • Gesture_D_U (down, up) - Browser_Home.
  • Gesture_D_L (down, left) - ^F4 (usually close tab/document.)
  • Gesture_D_R_L (down, right, left) - !F4 (close window without delay.)
  • Enables blue trails.
  • Hides the tray icon while gestures are enabled (use Win+RButton to disable gestures or access the tray menu via AHKControl.)
  • Holding the Gesture Key and pressing XButton1/XButton2 is equivalent to pressing Media_Next/Media_Prev.
  • Holding XButton1 shows the Alt+Tab dialog, equivalent to holding Alt and pressing Tab. Releasing XButton1 dismisses the dialog/switches applications, equivalent to releasing Alt. With the "Aero" Alt+Tab dialog, one can hold XButton1 and click a thumbnail to switch windows (but this may require the blacklisting feature described below).
  • Tapping XButton2 switches forward through documents or tabs (^{Tab}). Pressing it for more than 200ms switches backward (^+{Tab}). I've found 200ms to be long enough (for me) to get the desired result while also being quick.
  • XButton2 & mouse-wheel scrolls through documents or tabs.
  • XButton2 & LButton minimizes the active window.
  • XButton2 & RButton closes the active window. WinClose is used for windows in the WinCloseGroup window group.
  • Disables gestures and a number of hotkeys for any windows in the Blacklist window group. By default, this includes VMware Player, VirtualBox, Remote Desktop and the "Aero" Alt+Tab dialog. Since this feature requires AutoHotkey_L, it is disabled by default. To enable it, find and uncomment this line:
    ; #if m_WaitForRelease || !G_Blacklisted()
Lex' Mouse Gestures is basically public domain.

Andreone
  • Members
  • 257 posts
  • Last active: Mar 19 2008 05:30 PM
  • Joined: 20 Jul 2007
This is a really nice script :D and very useful
Thank you

catweazle
  • Guests
  • Last active:
  • Joined: --
Did you consider rendering "mouse trails" on the screen to show the gesture as it's being composed (as per all-in-one gestures)? I think this would be a useful addition.

Some time ago I added very basic trail support (rendering multiple tiny windows) to one of the earlier mouse gesture scripts (see below), although it could probably be improved by rendering lines directly to the screen.


;
; mg.ahk - Mouse gestures with right button.
;
; Jason Hood, 15 August to 9 September, 2004.
;
; Adapted from mousegesture.ahk from:
;   http://lukewarm.s41.xrea.com/AutoHotkey/myscripts/
;

;#NoTrayIcon
Menu Tray, Tip, Mouse Gestures

SetMouseDelay -1
SetDefaultMouseSpeed 0
mg_active = 0

timeout = 50            ; approx. half a second
sensitivity = 10        ; this many pixels required before being accepted

; Show a tooltip indicating the current gesture; uncomment to hide it.
;mg_showtips = hide
;mg_showtrails = hide
; Ignore unknown gestures (no right click); uncomment to do the click.
;mg_rclick = yes

; Define the recognised gestures (each of these should have an _Action label).
; Any number of directions is allowed, so, for example, an "S" (or "5") gesture
; could be created with "LDRDL".
gestures = -L-R-U-D-UD-DR-RU-DL-DR-LDR-DU-DURD-DLU-

; Window titles to exclude.
excluded = Pixia,Firefox,Virtual Machine Remote Control,Remote Desktop,Potter

; Window titles which automatically disable the gestures (it can't quite handle
; simultaneous left/right presses correctly).
disabled = Minesweeper,Cheat Mine,Potter

; Initialise variables for the actions and exit.
;goto action_init

SetWinDelay,0
CoordMode,Mouse,Screen

; Initialise variables to support mouse pointer trails
trail=50
delay=20
;trans=255
image=C:\4x4_red.bmp

; Create small 4x4 windows to represent mouse pointer trails
Loop,%trail%
{
  Gui,%A_Index%:+Owner +AlwaysOnTop -Resize -SysMenu -MinimizeBox -MaximizeBox +Disabled -Caption -Border -ToolWindow
  Gui,%A_Index%:Margin,0,0
  Gui,%A_Index%:Add,Picture,,%image%
  Gui,%A_Index%:Show,X-50 Y-50 W4 H4 NoActivate,MouseTrail%A_Index%
  ;WinSet,TransColor,FFFFFF %trans%,MouseTrail%A_Index%
  ;trans-=25
}

; Hotkeys for script control.
#g::Suspend
+#g::Reload
^#g::Edit
!#g::ExitApp

;------------------------------------------------------------------------------

DR_Action:
  ; Close the active window.
  WinClose A
  return

DL_Action:
  ; Minimize the active window.
  ; WinMinimize A

;  WinGetClass, GestureInitiatedWindow, ahk_id %winid%
;
;  ;MsgBox, "Active" %GestureInitiatedWindow%
;
;  If GestureInitiatedWindow in Progman
;  {
;;    MsgBox, "Clicked root"
;    If LastMinimized <>
;    {
;      WinRestore
;    }
;  }
;  Else
;  {
;    PostMessage, 0x112, 0xF020,,, A ; 0x112 = WM_SYSCOMMAND, 0xF020 = SC_MINIMIZE
;    LastMinimized = %winid%
;  }

  PostMessage, 0x112, 0xF020,,, A ; 0x112 = WM_SYSCOMMAND, 0xF020 = SC_MINIMIZE
  return

; -----------------------------------------------------------------------------

D_Action:
  WinGetClass, ActiveClass, A
  If ActiveClass in ExploreWClass,CabinetWClass
  {
    ControlGetText, Dir, Edit1, A
    
    ;WinGetTitle, Dir, A
    ;Run, explore %Dir%
    If Dir = My Documents
      Dir = %A_MyDocuments%
    Else If Dir = My Computer
      Dir = %A_MyComputer%
    Else If Dir = Desktop
      Dir = %A_Desktop%
    
    Run, explorer /n `, %Dir%
  }
  return

; -----------------------------------------------------------------------------

U_Action:
  WinGetClass, ActiveClass, A
  If ActiveClass in ExploreWClass,IEFrame,CabinetWClass
  {
    WinGetTitle, Title, A
    Send, {Backspace}
  }
  return

UD_Action:
; Strg+N erzeugt im Explorer oder Dateidialog einen neuen Ordner
~^M:: ; Strg+M (~ = native)
WinGetClass, ActiveClass, A                    ; Klasse des aktiven Fensters
If ActiveClass in ExploreWClass,CabinetWClass  ; Wenn Explorer-Fenster ...
{
  Send, !F
  Send, w
  Send, f
}
IfWinActive, ahk_class #32770                            ; Ist Dateidialog?
{
   PostMessage, 0x111, 40962                                ; Direkten Befehl für neuer Ordner senden
}
return

RU_Action:
  WinGet state, MinMax, A
  If state = 1 ;  maximized (or minimized)
    WinRestore,A
  else ; normal window
  WinMaximize,A
  return

L_Action:
  WinGetClass, ActiveClass, A
  If ActiveClass in ExploreWClass,IEFrame,CabinetWClass,wndclass_desked_gsk
    Send, !{Left}
  return

R_Action:
  WinGetClass, ActiveClass, A
  If ActiveClass in ExploreWClass,IEFrame,CabinetWClass,wndclass_desked_gsk
    Send, !{Right}
  return

LDR_Action:
  Run, C:\
  return

DU_Action:
  if (A_ComputerName = "CATWEAZLE")
    url = http://www.google.co.uk
  else
    url = http://ra/johnc
  Run,%url%
  return

DURD_Action:
  Run,%userprofile%\My Documents
  return

DLU_Action:
  Send, #d
  return


;------------------------------------------------------------------------------

;MButton::
;  mb = M
;goto MouseButton

;LButton::
;  mb = L

;MouseButton:
;    If mg_active = 1
;    {
;        IfEqual mg_showtips,, Tooltip
;            mg_executed = 1
;        mg_str = %mb%B
;        goto %mg_str%_Action
;    }
;    ; I like to use middle button as left double-click.
;    If mb = M
;    {
;        MouseClick L,,, 2
;        return
;    }

; NOTE: This function is needed for excluded windows (e.g., Firefox) to be handled correctly
ButtonWait:
  ;FileAppend mg_active %mg_active% `n, C:\debug.txt
  ;IfEqual,mg_active,0,Return
  MouseClick %mb%,,,,,D
  loop
  {
    GetKeyState st, %mb%Button, P
    IfEqual st, U, break
    Sleep 10
  }
  MouseClick %mb%,,,,,U
  return

;WheelUp::
;  mg_str = WU
;goto Wheel
;
;WheelDown::
;  mg_str = WD
;
;Wheel:
;  If mg_active = 1
;  {
;    IfEqual mg_showtips,, Tooltip
;    mg_executed = 1
;    goto %mg_str%_Action
;  }
;  MouseClick %mg_str%
;return

^+RButton::
  Loop,%trail%
  {
    WinSet,Top,,MouseTrail%index%
  }
  MsgBox, raised gesture windows
  return

;^RButton::
+RButton::
RButton::
;  Loop,%trail%
;  {
;    WinSet,Top,,MouseTrail%index%
;  }

  MouseGetPos,,, id
  WinGetTitle mg_title, ahk_id %id%

  If mg_title contains %disabled%
    Suspend
  else
  {
    If mg_title contains %excluded%
      ;If !GetKeyState("Control")
      If !GetKeyState("LShift")
      {
	mb = R
	goto ButtonWait
      }
    mg_active = 1
    mg_executed = 0
    mg_rheld = 0
    mg_str =
    mg_idletime = 0
    mg_rclick = 
    ;IfEqual mg_showtips,, ToolTip %A_Space%
    MouseGetPos px, py, winid
    px2 = %px%
    py2 = %py%
    loop
    {
      GetKeyState sh, Shift, P
      If sh = D
      {
	mg_rclick = yes
	;MsgBox, shift down
      }

      GetKeyState st, RButton, P
      If st = U ; the button has been released
      {
        IfEqual mg_showtips,, ToolTip
        IfEqual mg_showtrails,, GoSub, ClearTrails
        
        If mg_rheld = 0
        {
          mg_active = 0
          IfEqual mg_showtrails,, GoSub, ClearTrails
          If mg_executed = 0
            If mg_str =
              MouseClick R
          else IfInString gestures, -%mg_str%-
            gosub %mg_str%_Action
          else IfNotEqual mg_rclick,,MouseClick, R
          }
        else IfNotEqual mg_rclick,,MouseClick,R,,,,,U
;      else
;        MouseClick R,,,,,U
          return
        }
      else If mg_idletime < %timeout% 
        ; button still pressed, within timeout since last direction change
        {
          MouseGetPos dx, dy
          dx -= %px2%
          dy -= %py2%
          If dx > %sensitivity%
            mg_dir = R
          else If dx < -%sensitivity%
            mg_dir = L
          else If dy > %sensitivity%
            mg_dir = D
          else If dy < -%sensitivity%
            mg_dir = U
          If mg_dir <>
          {
            StringRight,right,mg_str,1
            IfNotInString, right, %mg_dir%
            {
              mg_str = %mg_str%%mg_dir%
            }
            IfEqual mg_showtips,, ToolTip %mg_str%
            MouseGetPos px2, py2
            mg_idletime = 0
            mg_dir =
          }
          else
            mg_idletime++
        }
      else ; timeout has expired
      {
        IfEqual mg_showtips,,ToolTip Gesture Expired
        If mg_rheld = 0
          If mg_executed = 0
          {
            MouseGetPos px2, py2
            If px2 <> %px%
              If py2 <> %py%
              {
                IfEqual mg_showtips,, ToolTip
                IfEqual mg_showtrails,, GoSub, ClearTrails
		If mg_rclick <>  
		{
                  MouseClick R, %px%, %py%,,,D
                  MouseMove %px2%, %py2%
                }
                mg_rheld = 1
                mg_active = 0
              }
          }
      }
      
      If (mg_active)
        IfEqual mg_showtrails,, GoSub, DrawTrails
      Sleep delay
    }
  }
  mb = R
  goto ButtonWait
  

DrawTrails:
  ;FileAppend index %index% trail %trail% `n, C:\debug.txt
  If (index > trail)
  {
    ;FileAppend greater %index% `n, C:\debug.txt
    index = 1
  }
  ;FileAppend index %index% `n, C:\debug.txt
  
  MouseGetPos,x,y
  x+=5
  y+=5
  WinMove,MouseTrail%index%,,%x%,%y%
  index += 1
  
  Return


ClearTrails:
  Loop,%trail%
  {
    WinMove,MouseTrail%A_Index%,,-10,-10
    ;WinSet,Top,,MouseTrail%index%
  } 
  Return


majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Thank you
Posted Image

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Did you consider rendering "mouse trails" on the screen to show the gesture as it's being composed (as per all-in-one gestures)? I think this would be a useful addition.

I find trails distracting rather than useful. The tactile response of the mouse is effective enough, IMO. Eventually the movements become so natural you don't need to think about which way you're moving the mouse.

juan
  • Guests
  • Last active:
  • Joined: --
thankyou, really nice script, I´m now using it
I added the current gesture to tooltip for one second, just for a litle feedback

bye

juan
  • Guests
  • Last active:
  • Joined: --
I´m a litle newbe to do it my self, what do you think about adding to gestures .ini something like

[mp3 @ C:\.* - xplorer²]
Gesture_D_U = !{F4}

["- Mozilla Firefox"]
Gesture_D_U = {Browser_Home}

[else]
Gesture_D_U = {something_else}

or something better, like using the classname to, it would be great :)

thanks againg, i´m enjoying your script
and sorry about my english

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I've been planning to revise Gestures for a long time, especially for context-sensitive gestures, but since I very rarely edit my gestures, other projects have taken precedence.

Note that you can script any gesture by adding it to either ahk file:
Gesture_D_U:
    SetTitleMatchMode, RegEx
    ifWinActive, mp3 @ C:\\.* \- xplorer²  ; assuming ".*" was meant as a wildcard
        Send !{F4}
    else ifWinActive, \- Mozilla Firefox ahk_class MozillaUIWindowClass
        Send {Browser_Home}
    else
        Send {Launch_App1}
    return
Be sure to remove Gesture_D_U from your ini file, if present.

Juan
  • Guests
  • Last active:
  • Joined: --
thanks lexikos, that´s the way i´m doing it now, i think your script is very good, if you find the time please revise it a litle, I´ll try for my self, thanks again and see you

jpsala
  • Members
  • 9 posts
  • Last active: Apr 30 2010 01:58 PM
  • Joined: 21 Jan 2008
Hi lexikos, I made some modifications, if you find some time have a look and make the necesary conrrections, take a look at gestures.ini

now you can have the same hotkey for diferent windows :)

Gesture_D = doSomething<win>Scite<=>- SciTE
Gesture_D = doSomethingElse<win>Firefox<=>- Mozilla Firefox


;
; AutoHotkey Version: 1.0.46.09 +
; Language:       English
; Platform:       Win XP, 2003, Vista, and probably 2000
; Author:         Steve Gray, aka Lexikos
;
; Script Function:
;	Mouse gestures.
;    - Allows abitrary number of directions (zones).
;    - Allows any number of movements/strokes in a sequence.
;    - Custom script (label) execution OR variable-based key-stroke simulation.
;    - Variable-based key-strokes and options can be set via Gestures.ini
;      (setting a variable-based gesture overrides any associated labels/custom scripts)
;

gosub Gestures_Default_Init
; guardo los nombres de las variables

RunDebugView()

; Read gesture definitions from Gestures.ini
G_LoadGestures( A_ScriptDir . "\Gestures.ini" )
; save the name of the gestures in vars
vars := getVars()

c_PI := 3.141592653589793
c_halfPI := 1.5707963267948965 ; Pi/2
c_Degrees := 57.29578 ; 180/Pi (degrees per radian)


#NoEnv
SendMode Input

#SingleInstance force
#KeyHistory 20

CoordMode, Mouse, Screen
SetTitleMatchMode, 2

; custom tray icon (also called by ToggleGestureSuspend)
G_SetTrayIcon(true)

; Hook "Suspend Hotkeys" messages to update the tray icon.
; Note: This has the odd side-effect of "disabling" the tray menu
;       if the script is paused from the tray menu.
OnMessage(0x111, "WM_COMMAND")

Menu, TRAY, Tip, Mouse Gestures
Menu, TRAY, Add
Menu, TRAY, Add, Edit &Gestures, EditGestures
Menu, TRAY, Add, Edit &Default Gestures, EditGestures2
Menu, TRAY, Add, Edit &this script, EditThisScript

; RAlt or %m_GestureKey%, whichever was used last
m_LastGestureKey := m_GestureKey

; m_Stroke%i% will be set to the actual strokes (starting at m_Stroke1)
m_StrokeCount = 0

m_WaitForRelease := false
m_PassKeyUp := false

m_ClosingWindow := 0

; Use code similar to this to disable gestures on a per-application basis:
;   GroupAdd, Blacklist, Firefox ahk_class MozillaUIWindowClass
;   Hotkey, IfWinNotActive, ahk_group Blacklist

; register the hotkey
Hotkey, %m_GestureKey%, GestureKey_Down
Hotkey, #%m_GestureKey%, ToggleGestureSuspend
; extra key
if ( m_GestureKey2 && m_GestureKey2 != m_GestureKey )
{
    Hotkey, %m_GestureKey2%, GestureKey_Down
    Hotkey, #%m_GestureKey2%, ToggleGestureSuspend
}
if !m_GestureKey2
    m_GestureKey2 := m_GestureKey
; see also: GestureKey_Down for GetKeyState(...)

GroupAdd, WinCloseGroup, ahk_class ConsoleWindowClass
GroupAdd, WinCloseGroup, ahk_class AutoHotkey

return


Gestures_Default_Init:
    #Include %A_ScriptDir%\Gestures Default.ahk
return

EditGestures:
    Run, C:\tools\wscite\SciTE.exe "%A_ScriptDir%\Gestures.ini",, UseErrorLevel
    if ErrorLevel = ERROR
        Run, C:\tools\wscite\SciTE.exe "%A_ScriptDir%\Gestures.ini"
return
EditGestures2:
    Run, C:\tools\wscite\SciTE.exe "%A_ScriptDir%\Gestures Default.ahk",, UseErrorLevel
    if ErrorLevel = ERROR
        Run, C:\tools\wscite\SciTE.exe "%A_ScriptDir%\Gestures Default.ahk"
return
EditThisScript:
    Run, C:\tools\wscite\SciTE.exe %A_ScriptFullPath%,, UseErrorLevel
    if ErrorLevel = ERROR
        Run, C:\tools\wscite\SciTE.exe "%A_ScriptDir%\Gestures Default.ahk"
return


; Wheel Gestures: Gesture key + Wheel sends keystrokes as defined by
; Gesture_WheelUp and Gesture_WheelDown in Gestures.ini.
WheelUp::
WheelDown::
    gesture := c_GesturePrefix ? c_GesturePrefix : "Gesture"
    if (m_WaitForRelease && %gesture%_%A_ThisHotkey%)
    { ; holding gesture button && this wheel gesture has a defined action
        m_ScrolledWheel := true
        m_ExitLoop := true
        Send % %gesture%_%A_ThisHotkey%
    } else
        Send {%A_ThisHotkey%}
    return


/********** XBUTTON HOTKEYS - included in the script for (my) convenience.
 */
XButton2 & LButton::MinimizeActiveWindow() ;WinMinimize, A
XButton2 & RButton::
    ifWinActive, ahk_group WinCloseGroup
        WinClose, A
    else
        Send !{F4}
    return

; Task-switch with XButton1( + Wheel).
XButton1 & WheelUp::AltTab
XButton1 & WheelDown::ShiftAltTab
XButton1::Send !{Tab}

; Document/tab-switch with XButton2( + Wheel)
XButton2 & WheelUp::Send ^+{tab}
XButton2 & WheelDown::Send ^{tab}
XButton2::Send ^{Tab}


MinimizeActiveWindow()
{
    global
    lastMinTime := A_TickCount
    lastMinID   := WinExist("A")
    ; unlike WinMinimize, using WM_SYSCOMMAND, SC_MINIMIZE
    ; causes the system-wide "Minimize" sound to be played
    PostMessage, 0x112, 0xF020
}

; Press Win + Gesture button to enable/disable gestures.
ToggleGestureSuspend:
    Suspend, Toggle

    G_SetTrayIcon(!A_IsSuspended)

    ; wurt from: http://addons.miranda-im.org/details.php?action=viewfile&id=1512
    if A_IsSuspended
        SoundPlay, %A_ScriptDir%\wurt_disabled.wav
    else
        SoundPlay, %A_ScriptDir%\wurt_enabled.wav
return

; Press Escape to cancel the current gesture (before releasing the gesture button.)
CancelGesture:
    Hotkey, Escape, CancelGesture, Off
    m_ExitLoop := true
return

GestureKey_Up:
    m_WaitForRelease := false

    Hotkey, IfWinActive
    Hotkey, *%m_LastGestureKey% Up, GestureKey_Up, Off
    Hotkey, Escape, CancelGesture, Off

    ; record for later use
    MouseGetPos, m_EndX, m_EndY

    if ( m_PassKeyUp )
    {
        Send {%m_LastGestureKey% Up}
        m_PassKeyUp := false
    }
return

GestureKey_Down:

    if ( !GetKeyState(m_GestureKey, "P") && !GetKeyState(m_GestureKey2, "P") )
        return

    if ( m_WaitForRelease )
        return
    m_WaitForRelease := true

    m_ExitLoop := false

    Hotkey, IfWinActive

    m_LastGestureKey := A_ThisHotkey
    Hotkey, *%m_LastGestureKey% Up, GestureKey_Up, On
    Hotkey, Escape, CancelGesture, On

    waitCounter := 0
    startX := -1
    startY := -1
    totalDistance := 0
    zone := 0
    lastZone := -1

    m_StrokeCount := 0

    ; get starting mouse position
    MouseGetPos, lastX, lastY

    ; record for later use
    m_StartX := lastX
    m_StartY := lastY

    Loop
    {
        ; wait for mouse to move
        Sleep, m_Interval

        if ( m_ExitLoop )
        {
            if m_ScrolledWheel
                KeyWait, %m_LastGestureKey%
            return
        }

        ; increment waitCounter by timer interval
        ; (may not be entirely accurate if the script is lagging...)
        waitCounter += m_Interval

        if ( !m_StrokeCount && m_InitialTimeout && waitCounter > m_InitialTimeout )
        {
            if ( GetKeyState(m_LastGestureKey, "P") )
            {
                if m_LastGestureKey in LButton,MButton,RButton
                {
                    ; convert key name to a "button" (silly how MouseClick won't accept "LButton")
                    StringLeft, btn, m_LastGestureKey, 1
                    ; remember position
                    MouseGetPos, m_EndX, m_EndY
                    ; move to point where gesture started, then click
                    MouseClick, %btn%, m_StartX, m_StartY, , 0, D
                    ; move back into place
                    MouseMove, m_EndX, m_EndY, 0
                    btn =
                }
                else
                {
                    Send {%m_LastGestureKey%}
                }
                ; pass GestureKey Up on to active window
                m_PassKeyUp := true
            }
            return
        }

        if ( !GetKeyState(m_GestureKey, "P") && !GetKeyState(m_GestureKey2, "P") )
        { ; use location mouse was released at
            x := m_EndX
            y := m_EndY
        }
        else ; get current mouse position
            MouseGetPos, x, y

        offsetX := x - lastX
        offsetY := y - lastY

        ; check if mouse has moved
        if ( offsetX!=0 || offsetY!=0 )
        {
            ; calculate distance and angle from previous position
            distance := G_GetLength(offsetX, offsetY)

            if ( distance > m_LowThreshold )
            {
                angle := G_GetAngle(offsetX, offsetY)
                angle *= c_Degrees

                lastX := x
                lastY := y

                ; get zone of angle
                if ( m_StrokeCount > 0 || m_InitialZoneCount < 2 )
                    zone := G_GetZone(angle, m_ZoneCount)
                else
                    zone := G_GetZone(angle, m_InitialZoneCount)

                if ( zone == -1 )
                {
                    ;DEBUG
                    ;MsgBox, 64, DEBUG, Gesture stroke went off-course! (Exceeded zone tolerance `%%m_Tolerance%.), 5
                    SoundPlay, *-1
                    return
                }

                if ( lastZone != zone )
                {
                    totalDistance := distance

                    ; add stroke zone to the pseudo-array
                    ++m_StrokeCount
                    m_Stroke%m_StrokeCount% := zone

                    lastZone := zone
                    ; reset timeout counter
                    waitCounter := 0
                }
                else
                {
                    totalDistance += distance
                }

                if ( m_HighThreshold > 0 && totalDistance > m_HighThreshold )
                {
                    ;DEBUG
                    ;MsgBox, 64, DEBUG, Gesture stroke exceeded maximum stroke length: %totalDistance% / %m_HighThreshold%., 5
                    SoundPlay, *-1
                    Sleep, 150
                    SoundPlay, *-1
                    return
                }
            }
        }

        ; end loop when gesture key is released
        if ( !GetKeyState(m_GestureKey, "P") && !GetKeyState(m_GestureKey2, "P") )
            break
    }

    ; cancel gesture if the mouse was immobile for too long after the last gesture
    if ( m_Timeout > 0 && waitCounter > m_Timeout )
    {
        ;DEBUG
        SoundPlay, *-1
        ;MsgBox, 64, DEBUG, Gesture timeout- %waitCounter% / %m_Timeout%, 5
        if ( m_DefaultOnTimeout )
        {
            gesture = Gesture_Default
            if ( %gesture% )
                SendEvent % %gesture%
            else if ( IsLabel(gesture) )
                gosub %gesture%
        }
        return
    }

    gesture := c_GesturePrefix ? c_GesturePrefix : "Gesture"

    Loop %m_StrokeCount%
    {
        ; get the zone of this stroke
        zone := m_Stroke%A_Index%

        ; get descriptive label for zone, if possible
        if ( A_Index == 1 && m_InitialZoneCount >= 2 )
            zoneText := c_Zone%m_InitialZoneCount%_%zone%
        else
            zoneText := c_Zone%m_ZoneCount%_%zone%

        gesture .= "_"

        if ( zoneText )
            gesture .= zoneText
        else
            gesture .= zone
    }

    if ( m_StrokeCount == 0 )
        gesture = Gesture_Default

    ; if gesture points to a variable, send its contents as keystrokes
    wins := getWins(gesture . "_ID_", vars)
    if (wins <> "")
    {
        nWin := 0
        Loop, Parse, wins, ß
        {
            if ( SubStr(A_LoopField,1, StrLen(gesture)) == gesture)
            {
                ;gesture := substr(A_LoopField,1,instr(A_LoopField,"_ID_")-1)
                sid := substr(A_LoopField,instr(A_LoopField,"_ID_")+4,instr(A_LoopField,"[")-instr(A_LoopField,"_ID_")-4)
                win := substr(A_LoopField,InStr(A_LoopField,"<=>")+4,500)
                comando := substr(A_LoopField,instr(A_LoopField,"]: ")+3,instr(A_LoopField,"<win>")-instr(A_LoopField,"]: ")-3)
                nWin += 1
                SetTitleMatchMode, 2
    
                outputdebug, x%win%x

                if (WinActive(win))
                {
                    ToolTip %gesture% ID %sid%  inWin %win%
                    SetTimer, RemoveToolTip, 2500
                    send, %comando%
                    exit
                }
            }   
        }
        ToolTip, No %gesture%  in any of your %nWin% window definitions
        SetTimer, RemoveToolTip, 1500
        exit
    }
    ;Gesture_D_ID_a[42 of 63]: comandoScite<win>a<=>ahk_class SciTEWindow
    ;Gesture_D_ID_b[53 of 63]: comandoFirefox<win>b<=>ahk_class MozillaUIWindowClass
        
    else if ( %gesture% )
    {
        StringReplace, comando, %gesture%, <win>, Ì
        StringSplit, command, comando, Ì,  %A_Space%%A_Tab%
        if (command0 == 2)
        {
            SetTitleMatchMode, regex
            if WinActive( command2 )
                Send % %command1%
            Else
                gesture := gesture . "   no window " . command2
        }
        else
            Send % %gesture%
    }    ; else if gesture has a label (e.g Gesture_D_R = down, then right), go to it
    else if ( IsLabel(gesture) )
    {
        gosub %gesture%
    }
    else
        gesture := gesture . "   no existe"
;        MsgBox, , , Unknown gesture with %m_StrokeCount% strokes:`n %gesture%, 2
    if (gesture<>"Gesture_Default")
    {
        ToolTip %gesture%
        SetTimer, RemoveToolTip, 1500
    }

return


; get angle of {x,y} from positive x-axis, relative to {0,0}
; return value is in RADIANS
G_GetAngle( x, y )
{
    global c_PI, c_halfPI

    ; if {0,0}, angle can be any real value
    if ( x==0 && y==0 )
        return 0

    if ( x > 0 )
    {
        if ( y >= 0 )
            return ATan(y/x)
        ;y < 0
        return ATan(y/x) + 2*c_PI
    }
    else if ( x < 0 )
    {
        return ATan(y/x) + c_PI
    }
    else ; x == 0
    {
        if ( y > 0 )
            return c_halfPI
        ;y < 0
        return -c_halfPI
    }
}

; get distance of {x,y} from {0,0}
G_GetLength( x, y )
{
    return Sqrt(x*x + y*y)
}

; get the zone of an angle
;  angle:       specified in degrees
;  zoneCount:   number of zones (zone centers are at (360/zoneCount) degree intervals)
G_GetZone( angle, zoneCount )
{
    local degPerZone
    local zone
    local tolerance

    if ( zoneCount < 2 )
    { ; show debug error message
        MsgBox, 16, ERROR, Invalid zoneCount (%zoneCount%) passed to G_GetZone()., 5
        return 0
    }

    if ( angle < 0 ) {
        Loop { ; while ( angle < 0 )  would be nice...
            angle += 360
            if ( angle >= 0 )
                break
        }
    }

    ; calculate zone size
    degPerZone := 360 / zoneCount

    ; calculate zone - Round() finds the nearest zone (nearest integer)
    zone := Mod( Round(angle/degPerZone), zoneCount )

    ; calculate maximum tolerance
    tolerance := degPerZone/2.0
    ; calculate real tolerance from user-defined tolerance (percentage)
    if ( m_Tolerance < 100 )
        tolerance *= Abs(m_Tolerance)/100.0

    if ( zone == 0 && angle > 180 )
        angle -= 360

    ; return -1 if angle is not within tolerance of the nearest zone
    if ( Abs(angle-(zone*degPerZone)) >= tolerance )
        zone := -1

    return zone
}

G_LoadGestures( filename )
{
    ; declaring one or more local variables forces newly created variables to be global in scope
    local line, varname
    ; declare a local array (0=array length, 1=first item)
    local lineParts0
    ; these must be explicitly declared local
    ; (as of AHK 1.0.46.10, StringSplit creates them as local variables either way
    ;  because lineParts0 is local, but references to 'lineParts1' in script refer
    ;  to GLOBAL variables...)
    local lineParts1, lineParts2

    Loop, Read, %filename%
    {
        ; ignore comments
        StringLeft, line, A_LoopReadLine, 1
        if ( line = ";" )
            continue

        ; replace " = " with uncommon delimiter
        StringReplace, line, A_LoopReadLine, %A_Space%=%A_Space%, 
        ; split the string into (hopefully) two parts: variable, keys to send
        StringSplit, lineParts, line, , %A_Space%%A_Tab%

        if ( lineParts0 == 2 )
        {   ; first search for a window ( <win> )
            StringReplace, comando, lineParts2, <win>, Ì
            StringSplit, command, comando, Ì,  %A_Space%%A_Tab%
            if (command0 == 2)
            {   ; if there´s one, separate the id and the window
                StringReplace, comando, command2, <=>, Ì
                StringSplit, command, comando, Ì,  %A_Space%%A_Tab%
                
                %lineParts1%_ID_%command1% := lineParts2
                %lineParts1%%_ID_%WIN := command2
            }
            else
            {
                if ( StrLen(lineParts1) > 0 )
                {
                    ; allow underscores by removing them from comparison
                    StringReplace, varname, lineParts1, _, , All
                    ; check if variable name is alphanumeric
                    if varname is alnum
                        %lineParts1% := lineParts2
                }
                else
                {
                    MsgBox empty variable name on line %A_Index%
                }
            }
        }
    }
}


WM_COMMAND(wParam, lParam)
{
    static IsPaused, IsSuspended
    Critical
    id := wParam & 0xFFFF
    if id in 65305,65404,65306,65403
    {  ; "Suspend Hotkeys" or "Pause Script"
        if id in 65306,65403  ; pause
            IsPaused := ! IsPaused
        else  ; at this point, A_IsSuspended has not yet been toggled.
            IsSuspended := ! A_IsSuspended
        G_SetTrayIcon(!(IsPaused or IsSuspended))
    }
}

G_SetTrayIcon(is_enabled)
{
    icon := is_enabled ? "gestures.ico" : "nogestures.ico"
    icon = %A_ScriptDir%\%icon%

    ; avoid an error message if the icon doesn't exist
    IfExist, %icon%
        Menu, TRAY, Icon, %icon%,, 1
}


RemoveToolTip:
	SetTimer, RemoveToolTip, Off
	ToolTip
return


getVars() {
 
   DetectHiddenWindows, On
   IfEqual Section,, SetEnv Section, Key
   HidWin := WinExist(A_ScriptFullPath " - AutoHotkey v")
   OldPar := DllCall("GetParent", UInt,HidWin)
   GUI +LastFound
   DllCall("SetParent", UInt,HidWin, UInt,WinExist("ahk_class Shell_TrayWnd"))
   WinMenuSelectItem ahk_id %HidWin%,,View, Variables
   Sleep 0
   ControlGetText str, Edit1, ahk_id %HidWin%
   WinHide ahk_id %HidWin%
   DllCall("SetParent", UInt,HidWin, UInt,OldPar)
   Out1 := ""
   Loop, Parse, str, `n, `r
    {
        if SubStr(A_LoopField, 1, 8) == "Gesture_"
            Out1 .= A_LoopField  . "ß"
    }
   Return Out1
}

getWins(var, vars)
{   
   out := ""
   Loop, Parse, vars, ß
    {
        if ( SubStr(A_LoopField,1, StrLen(var)) == var)
            out := out . A_LoopField . "ß"
    }
return out
}

RunDebugView(){
    debuggerpath .= "c:\programme\autohotkey\DebugView\Dbgview.exe"
    debuggerTitel := "DebugView on \\" . A_ComputerName . " (local)"
    IfExist, %debuggerpath%
        ifwinNotexist, %debuggerTitel%
    Run, %debuggerpath%
}
; Syntax:
;   %c_GesturePrefix%_%zone1%_%zone2%_%zoneN% = keys (in Send-compatible format)

Gesture_WheelUp = ^+{Tab}
Gesture_WheelDown = ^{Tab}

Gesture_L_U = !{Up}

Gesture_D_U = {Browser_Home}
Gesture_DR_R = !{g}
Gesture_D_R = ^{F4}
Gesture_D_R_L = !{F4}
Gesture_L_DR = {Browser_Back}
Gesture_D_UR = ^{End}
Gesture_U_DR = ^{Home}
Gesture_U_UR = #c
Gesture_D = comandoScite<win>Scite<=>- SciTE
; with this text; ahk_class SciTEWindow, instead of the title doesnt work, y tried with regex too. Maibe you can see what is wrong
Gesture_D = comandoFirefox<win>Firefox<=>- Mozilla Firefox 

; Override Options specified in "Gestures Default.ahk"
m_GestureKey = RButton
m_GestureKey2 = 

Thanks a lot! :D

Juan

automaticman
  • Members
  • 658 posts
  • Last active: Nov 20 2012 06:10 PM
  • Joined: 27 Oct 2006
Thanks for sharing. I didn't "download" and try it out yet. As I prefer inputting mostly with the keys I'm not sure if I would use these gestures "often". Then I would have to leave the keyboard and move the mouse. But in contrary it might be useful if I already switched over to the mouse (because I had to) then those gestures might free me from jumping back to the keyboard just to fire some hotkeys.

I don't know how your solution works but interesting would be also having one "multiplication gesture", when triggered, opening a GUI with buttons in it like: DoThis, DoThat, AplicationXDoThis, ApplicationYDoThat which you can press and the GUI disappears again (similar to nDroid's GUI maybe). This would give us a multiplicative operation on gestures using
+ gesture + click on gui
as input mechanism. (Similar to this idea is enterpad here. Our enterpad would be the GUI.)
Maybe I should reactivate my Wacom tablet to try out these gestures.

+ The next step could be a ZUI enterpad, we wouldn't be limited to a given window size. We could zoom in/out into various regions and so have 1000's of hotkey/automation buttons in front of us. :)

jpsala
  • Members
  • 9 posts
  • Last active: Apr 30 2010 01:58 PM
  • Joined: 21 Jan 2008
yep, I like your ideas but don´t have the energy, nor the knowledge of autohotkey to do it, I hope someone can do it.
I don´t use the mouse either, but when surfing and researching the keyboard, with some minor exceptions, like gmail, is useless.
Thanks for the feedback

[ Moderator!: Please do not top-post., ie dont quote the entire previous post unneccessarily. This is not practiced in our forum. ]

automaticman
  • Members
  • 658 posts
  • Last active: Nov 20 2012 06:10 PM
  • Joined: 27 Oct 2006
This package seems not to include gestures for copy
paste
cut
delete
undo
and these are among the most general operations used all over applications.
+ Principle: Increasing it's power by increasing it's quantitative impact.

jpsala
  • Members
  • 9 posts
  • Last active: Apr 30 2010 01:58 PM
  • Joined: 21 Jan 2008
no, you can make your own gestures easily, just take a look at the files and the examples and youll be making yours in no time, if you come up with some idea tell me, good luck

automaticman
  • Members
  • 658 posts
  • Last active: Nov 20 2012 06:10 PM
  • Joined: 27 Oct 2006
I would apply gestures more to general operations as written above than "small space operations".