Limit mouse movement to window, control or custom rectangle

Post your working scripts, libraries and tools for AHK v1.1 and older
sobuj53
Posts: 55
Joined: 19 Mar 2015, 16:08

Limit mouse movement to window, control or custom rectangle

13 May 2017, 05:16

This scirpt is a modified version of https://autohotkey.com/board/topic/9215 ... rectangle/,
I've only modified few things which ultimately resolves following limitation,

The original script stops working whenever a new window is created. This modified script doesn’t have this limitation.

This script is particularly suitable for duel monitor usages. For my case I run a virtual machine which is on the second monitor and my 1st monitor I run the Host OS. Now to stop my mouse from interrupting the guest user I use this script to limit its movement within a defined area. I know there are other tools to stop mouse going from 2nd monitor but
Ctrl+F4 ; Trap to rectangle
function is very useful as I can exclude show desktop area in taskbar, so that host can't minimize application of the 2nd monitor. I sincerely want to thank RUNIE from #ahkscript IRC channel for helping me out on this.

You can select the context to which trap the mouse
- from the tray menu
- with hotkeys. The default ones are:

Code: Select all

Ctrl+F2	 Trap to win
Ctrl+F3	 Trap to control
Ctrl+F4	 Trap to rectangle
Ctrl+Esc   Free mouse
Ctrl+Alt+Q	Exit

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
/*
- Traps the mouse in a rectangular area defined by a window, control or user-drawn rectangle.

- Originally, the script used different tray icons to show the mouse state (trapped to control, free, etc), which were taken from this set: http://b-boy1968.deviantart.com/art/MB-Design-Letter-Icons-2-99406904
  Relevant lines are commented out
*/

; Modified by Sobuj53 for multi-monitor use

global using := False
global curpos := [0,  0, A_ScreenWidth, A_ScreenHeight]

help =
(
Ctrl+F2	 Trap to win
Ctrl+F3	 Trap to control
Ctrl+F4	 Trap to rectangle
Ctrl+Esc   Free mouse
Ctrl+Alt+Q	Exit
)

 ; Icon_free := A_ScriptDir "\F.ico"
 ; Icon_win  := A_ScriptDir "\W.ico"
 ; Icon_ctrl := A_ScriptDir "\C.ico"
 ; Icon_rect := A_ScriptDir "\R.ico"

;---

OnExit, Fin
CoordMode, Mouse, Screen
GroupAdd, all_windows  ;  include all windows except Program Manager

; Menu, Tray, Icon, % Icon_free
Menu, Tray, Add
Menu, Tray, Add, Trap to Window (Ctrl + F2), Menu_win
Menu, Tray, Add, Trap to Control (Ctrl + F3), Menu_ctrl
Menu, Tray, Add, Trap to Rectangle (Ctrl + F4), Menu_rect

TrayTip, ClipCursor, %help%,,1
SetTimer, KillTrayTip, -6000
; return

;---

Gui +LastFound 
hWnd := WinExist()
Ptr := A_PtrSize ? "Ptr" : "UInt"
DllCall("RegisterShellHookWindow", Ptr, A_ScriptHwnd)
OnMessage(DllCall("RegisterWindowMessage", Str, "SHELLHOOK"), "WinActiveChange")

DllCall( "RegisterShellHookWindow", UInt,Hwnd )
MsgNum := DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" )
OnMessage( MsgNum, "ShellMessage" )
if using = True
{
 ClipCursor(True, curpos*)
}
; Return
 
 ;---
 

^F2::ClipCursor_wrapper("win")
^F3::ClipCursor_wrapper("ctrl")
^F4::ClipCursor_wrapper("rect")

^!q::
Fin:
	using := False
	ClipCursor(False)
ExitApp


Menu_win:
Menu_ctrl:
Menu_rect:
	SetTimer, KillTrayTip, off
	StringTrimLeft, context, A_ThisLabel, 5
	if (context = "rect")
	{
		TrayTip, ClipCursor, Activate the target window
		Hotkey, ^Esc, Free_mouse, on
		WinWaitActive, ahk_group all_windows
		ClipCursor_wrapper(context)
	}
	else
	{
		TrayTip, ClipCursor, % "Click on the " (context = "win" ? "window" : "control" ) " you wanna`ntrap the mouse into."
		Hotkey, LButton, Done_positioning, on, UseErrorLevel
		Hotkey, ^Esc, Free_mouse, on
	}
return


Done_positioning:
	Hotkey, LButton, off
	TrayTip
	ClipCursor_wrapper(context)
return


Free_mouse:
	using := False
	ClipCursor(False)
	Hotkey, ^Esc, off
	; Menu, Tray, Icon, % Icon_free
	TrayTip
return


ClipCursor_wrapper(context) {
	if (context = "win")
	{
		MouseGetPos,,, wid
		WinActivate, ahk_id %wid%
		WinGetPos, wx,wy,ww,wh, ahk_id %wid%
		if not (wx and wy and ww and wh)
			return
		x1:=wx, y1:=wy, x2:=wx+ww, y2:=wy+wh
	}
	else if (context = "ctrl")  ; FIXME: probably could be done with coordmouse window
	{
		MouseGetPos,,, wid, cid, 2
		WinActivate, ahk_id %wid%
		WinGetPos, wx,wy,ww,wh, ahk_id %wid%
		ControlGetPos, cx,cy,cw,ch,, ahk_id %cid%
		if not (cx and cy and cw and ch)
			return
		x1:=wx+cx, y1:=wy+cy, x2:=wx+cx+cw, y2:=wy+cy+ch
	}
	else if (context = "rect")
	{
		ClipCursor(False)
		TrayTip, ClipCursor, Draw the rectangle you wanna`ntrap the mouse into
		LetUserSelectRect(x1, y1, x2, y2)
		TrayTip
	}
	else
		return
	
	ClipCursor(True, x1, y1, x2, y2)
	using := True
	Hotkey, ^Esc, Free_mouse, on
	; Menu, Tray, Icon, % Icon_%context%
}

; Lexikos', http://www.autohotkey.com/forum/topic49784.html
LetUserSelectRect(ByRef X1, ByRef Y1, ByRef X2, ByRef Y2)
{
    static r := 1
    ; Create the "selection rectangle" GUIs (one for each edge).
    Loop 4 {
        Gui, %A_Index%: -Caption +ToolWindow +AlwaysOnTop
        Gui, %A_Index%: Color, Red
    }
    ; Disable LButton.
    Hotkey, *LButton, lusr_return, On
    ; Wait for user to press LButton.
    KeyWait, LButton, D
    ; Get initial coordinates.
    MouseGetPos, xorigin, yorigin
    ; Set timer for updating the selection rectangle.
    SetTimer, lusr_update, 10
    ; Wait for user to release LButton.
    KeyWait, LButton
    ; Re-enable LButton.
    Hotkey, *LButton, Off
    ; Disable timer.
    SetTimer, lusr_update, Off
    ; Destroy "selection rectangle" GUIs.
    Loop 4
        Gui, %A_Index%: Destroy
    return
 
    lusr_update:
        MouseGetPos, x, y
        if (x = xlast && y = ylast)
            ; Mouse hasn't moved so there's nothing to do.
            return
        if (x < xorigin)
             x1 := x, x2 := xorigin
        else x2 := x, x1 := xorigin
        if (y < yorigin)
             y1 := y, y2 := yorigin
        else y2 := y, y1 := yorigin
        ; Update the "selection rectangle".
        Gui, 1:Show, % "NA X" x1 " Y" y1 " W" x2-x1 " H" r
        Gui, 2:Show, % "NA X" x1 " Y" y2-r " W" x2-x1 " H" r
        Gui, 3:Show, % "NA X" x1 " Y" y1 " W" r " H" y2-y1
        Gui, 4:Show, % "NA X" x2-r " Y" y1 " W" r " H" y2-y1
    lusr_return:
    return
}

; SKAN's, http://www.autohotkey.com/forum/post-141037.html#141037
ClipCursor(Confine=True, x1=0 , y1=0, x2=1, y2=1)

 {
	curpos := [x1, y1, x2, y2]
	VarSetCapacity(R,16,0),  NumPut(x1,&R+0),NumPut(y1,&R+4),NumPut(x2,&R+8),NumPut(y2,&R+12)
	Return Confine ? DllCall( "ClipCursor", UInt,&R ) : DllCall( "ClipCursor" )
}

; https://autohotkey.com/board/topic/66726-method-to-detect-active-window-change/
WinActiveChange(wParam, hwnd) {
	if (wParam = 32772 && using = True) {

		ClipCursor(True, curpos*)
	
	}
}
Return

; Windows 7 64 bit compitability
ShellMessage( wParam,lParam )
{
 WinGetTitle, title, ahk_id %lParam%
 If (wParam = 4 && using = True) { ;HSHELL_WINDOWACTIVATED
  ClipCursor(True, curpos*)
 }
}
Return

;---

KillTrayTip:
	TrayTip
return
I'm well aware that my script isn't perfect, but if you can make it please feel to do that. Also you can post your changes so other user can get help from it.
Last edited by sobuj53 on 29 May 2017, 01:07, edited 1 time in total.
User avatar
runie
Posts: 304
Joined: 03 May 2014, 14:50
Contact:

Re: Limit mouse movement to window, control or custom rectangle

13 May 2017, 05:54

Can you explain what you mean by "Windows 7 64 bit compitability" when you register the shellhook twice?
sobuj53
Posts: 55
Joined: 19 Mar 2015, 16:08

Re: Limit mouse movement to window, control or custom rectangle

16 May 2017, 01:34

Well, The above scipt works just fine in win10 without ShellMessage( wParam,lParam ) function, but in win7 it fails whenever a new window is created, with the Windows 7 64 bit compitability function, it works both in win7 and win10. Weird I know :crazy:
User avatar
runie
Posts: 304
Joined: 03 May 2014, 14:50
Contact:

Re: Limit mouse movement to window, control or custom rectangle

20 May 2017, 14:13

Did some testing with a windows 7 user.

You can replace

Code: Select all

Gui +LastFound 
hWnd := WinExist()
Ptr := A_PtrSize ? "Ptr" : "UInt"
DllCall("RegisterShellHookWindow", Ptr, A_ScriptHwnd)
OnMessage(DllCall("RegisterWindowMessage", Str, "SHELLHOOK"), "WinActiveChange")

DllCall( "RegisterShellHookWindow", UInt,Hwnd )
MsgNum := DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" )
OnMessage( MsgNum, "ShellMessage" )
with

Code: Select all

Gui +LastFound
DllCall("RegisterShellHookWindow", "ptr", WinExist())
OnMessage(DllCall("RegisterWindowMessage", "Str", "SHELLHOOK"), "WinActiveChange")
and..

Code: Select all

WinActiveChange(wParam, hwnd) {
	if (wParam = 32772 && using = True) {

		ClipCursor(True, curpos*)
	
	}
}
with

Code: Select all

WinActiveChange(wParam, hwnd) {
	if (wParam ~= "^(4|32772)$") && using
		ClipCursor(True, curpos*)
}
And you can remove the ShellMessage function. Your current implementation means both WinActiveChange and ShellMessage will run for Windows 10 users.
sobuj53
Posts: 55
Joined: 19 Mar 2015, 16:08

Re: Limit mouse movement to window, control or custom rectangle

20 May 2017, 23:04

Thank you for your reply, just tested your suggestions. Seems like it doesn't work as expected for "Trap to rectangle" function. When ever I click on the show hidden icons button in taskbar, the mouse pointer loses it's confinement. AHK version 1.1.25.02, Windows 7 64bit, Service pack 1.
sobuj53
Posts: 55
Joined: 19 Mar 2015, 16:08

Re: Limit mouse movement to window, control or custom rectangle

21 May 2017, 03:02

Update: The problem seems like occuring from this function.

Code: Select all

Gui +LastFound
DllCall("RegisterShellHookWindow", "ptr", WinExist())
OnMessage(DllCall("RegisterWindowMessage", "Str", "SHELLHOOK"), "WinActiveChange")
. I've already replaced

Code: Select all

WinActiveChange(wParam, hwnd) {
	if (wParam = 32772 && using = True) {

		ClipCursor(True, curpos*)
	
	}
}
with your new code and removed the ShellMessage function, it works flawlessly in my win7 64bit.
User avatar
runie
Posts: 304
Joined: 03 May 2014, 14:50
Contact:

Re: Limit mouse movement to window, control or custom rectangle

21 May 2017, 03:25

If ClipCursor() runs on win7 for you when you activate a window, then the shellhook is fixed.
The reason I'm checking for both HSHELL_WINDOWACTIVATED (4) and HSHELL_RUDEAPPACTIVATED (32772) is because on win7, only 4 is received and on win10, only 32772. They both indicate literally the exact same thing but for some reason the differ between windows versions..

If this change is introducing bugs to your code, I'd investigate if there's any differences to when (and how often) 4 is sent on win7 compared to 32772 on win10, and how that might affect your code on the different windows versions.
sobuj53
Posts: 55
Joined: 19 Mar 2015, 16:08

Re: Limit mouse movement to window, control or custom rectangle

21 May 2017, 04:19

So I think, the following function needs to be used for windows compitability purpose.

Code: Select all

Gui +LastFound 
hWnd := WinExist()
Ptr := A_PtrSize ? "Ptr" : "UInt"
DllCall("RegisterShellHookWindow", Ptr, A_ScriptHwnd)
OnMessage(DllCall("RegisterWindowMessage", Str, "SHELLHOOK"), "WinActiveChange")

DllCall( "RegisterShellHookWindow", UInt,Hwnd )
MsgNum := DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" )
OnMessage( MsgNum, "ShellMessage" )
Got it. One question though, How can a preserve my Current settings to a *.ini file and then load from it?
User avatar
runie
Posts: 304
Joined: 03 May 2014, 14:50
Contact:

Re: Limit mouse movement to window, control or custom rectangle

21 May 2017, 19:42

Read the documentation to learn how to read/write to ini files.
THX1138
Posts: 63
Joined: 25 Jul 2017, 00:36

Re: Limit mouse movement to window, control or custom rectangle

08 Dec 2017, 01:21

Is there a way to make this restrict the cursor to specified monitors on a multi-monitor system (but two or three monitors and not a single monitor at a time)?

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 134 guests