ClipCursor: Confining Mouse to a window

Share the finished AutoHotkey v2 Scripts and libraries you made here. Please put the current version of AutoHotkey v2 you used in Square Brackets at the start of the topic title.
User avatar
Maestr0
Posts: 81
Joined: 05 Dec 2013, 17:43

ClipCursor: Confining Mouse to a window

11 Aug 2019, 05:12

The following code has two hotkeys to confine the mouse cursor to a window, which can be handy when playing games on a dual monitor setup.
Tested with version 2.0-a103-56441b52

What is the alternative to VarSetCapacity? The docs state that VarSetCapacity may be removed later.

Comments/suggestions welcomed!
updated code:

Code: Select all

f1::
	global locked_ID
	toggle := !toggle
	if toggle
	{
		locked_ID := WinGetID("A")
		SetTimer("lock_engage", 250)
		tooltip("Mouse cursor locked!")
	}
	else
	{
		current_ID := ""
		SetTimer "lock_engage", "Delete"
		ClipCursor(False,0,0,0,0)
		tooltip("Mouse cursor unlocked!")
	}
	SetTimer () => ToolTip(), -3000
return

lock_engage() {
	global current_ID
	if !current_ID
		current_ID := WinGetID("A")
	if ( locked_ID == current_ID )
	{
		WinGetPos(VarX, VarY, Width, Height, "ahk_id " locked_ID)
		VarX2 := VarX + Width - 6
		VarY2 := VarY + Height - 6
		ClipCursor(True, VarX, VarY, VarX2, VarY2)
	}
	else
	{
		SetTimer , "Delete"
		ClipCursor(False,0,0,0,0)
		tooltip("Different window active: mouse cursor unlocked automatically!")
		SetTimer () => ToolTip(), -3000
	}
}

ClipCursor(Confine:=True, x1:=0, y1:=0, x2:=1, y2:=1) {
	local R := 0
	if confine {
		R := BufferAlloc(16)
		NumPut("int", x1, R)
		NumPut("int", y1, R, 4)
		NumPut("int", x2, R, 8)
		NumPut("int", y2, R, 12)
	}
	Return DllCall("ClipCursor", "Ptr", R)
}
old code:

Code: Select all

f1::     ;on
	WinGetPos VarX, VarY, Width, Height, "A"
	VarX2 := VarX + Width
	VarY2 := VarY + Height
	ClipCursor( True, VarX, VarY, VarX2, VarY2)
	tooltip("Mouse cursor locked!")
	SetTimer () => ToolTip(), -3000
Return

f2::	;off
	ClipCursor( False,0,0,0,0)
	tooltip("Mouse cursor unlocked!")
	SetTimer () => ToolTip(), -3000
return

ClipCursor( Confine:=True, x1:=0 , y1:=0, x2:=1, y2:=1 ) {
	VarSetCapacity(R,16,0)  
	NumPut("UPtr",x1,&R+0)
	NumPut("UPtr",y1,&R+4)
	NumPut("UPtr",x2,&R+8)
	NumPut("UPtr",y2,&R+12)
	Return Confine ? DllCall( "ClipCursor", "UInt",&R ) : DllCall( "ClipCursor" )
}
Based on Confining Mouse to a window https://autohotkey.com/board/topic/61753-confining-mouse-to-a-window/
Last edited by Maestr0 on 11 Aug 2019, 10:18, edited 1 time in total.
User avatar
Maestr0
Posts: 81
Joined: 05 Dec 2013, 17:43

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 05:24

Here's a variation (though the original script will already disable the lock when another window is active):

Code: Select all

f1:: ;on
	WinGetPos(VarX, VarY, Width, Height, "A")
	locked_ID := WinGetID("A")
	VarX2 := VarX + Width
	VarY2 := VarY + Height
	ClipCursor( True, VarX, VarY, VarX2, VarY2)
	tooltip("Mouse cursor locked!")
	SetTimer () => AutoDisable(locked_ID), 500
	SetTimer () => ToolTip(), -3000
Return

f2::	;off
	ClipCursor(False,0,0,0,0)
	tooltip("Mouse cursor unlocked!")
	SetTimer () => AutoDisable(), "off"
	SetTimer () => ToolTip(), -3000
return

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

AutoDisable(locked_ID:="") {
	current_ID := WinGetID("A")
	if (locked_ID != current_ID)
	{
		ClipCursor(False,0,0,0,0)
		SetTimer( , "Off")
		tooltip("Mouse cursor automatically unlocked!")
		SetTimer () => ToolTip(), -3000
	}
}
Helgef
Posts: 4031
Joined: 17 Jul 2016, 01:02
Contact:

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 05:55

You should use bufferalloc instead of varsetcapacity.

Cheers.
User avatar
Maestr0
Posts: 81
Joined: 05 Dec 2013, 17:43

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 06:07

Helgef wrote:
11 Aug 2019, 05:55
You should use bufferalloc instead of varsetcapacity.

Cheers.
Thanks!
Do you mean I should do

Code: Select all

	R := BufferAlloc(16)
instead of

Code: Select all

	VarSetCapacity(R,16,0)
?
Helgef
Posts: 4031
Joined: 17 Jul 2016, 01:02
Contact:

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 06:20

Yes, but pass R, offset to numput instead of &R+offset. Dllcall also supports 'ptr', bufferobject.

Also, this is not correct,

Code: Select all

Return Confine ? DllCall("ClipCursor", "UInt", &R) : DllCall("ClipCursor")
You should use DllCall("ClipCursor", "Ptr", R) and let R := 0 if !confine.
Cheers.
User avatar
Maestr0
Posts: 81
Joined: 05 Dec 2013, 17:43

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 07:21

Helgef wrote:
11 Aug 2019, 06:20
Yes, but pass R, offset to numput instead of &R+offset. Dllcall also supports 'ptr', bufferobject.

Also, this is not correct,

Code: Select all

Return Confine ? DllCall("ClipCursor", "UInt", &R) : DllCall("ClipCursor")
You should use DllCall("ClipCursor", "Ptr", R) and let R := 0 if !confine.
Cheers.
Your help and comments are much appreciated, thank you!

Here's the code I have now, but F2 no longer unlocks the mouse cursor (though the tooltip is shown). What am I doing wrong?

Code: Select all

f1::     ;on
	WinGetPos VarX, VarY, Width, Height, "A"
	VarX2 := VarX + Width
	VarY2 := VarY + Height
	ClipCursor(True, VarX, VarY, VarX2, VarY2)
	tooltip("Mouse cursor locked!")
	SetTimer () => ToolTip(), -3000
Return

f2::	;off
	ClipCursor(False,0,0,0,0)
	tooltip("Mouse cursor unlocked!")
	SetTimer () => ToolTip(), -3000
return

ClipCursor(Confine:=True, x1:=0, y1:=0, x2:=1, y2:=1) {
	R := BufferAlloc(24)
	NumPut("UPtr", x1, R)
	NumPut("UPtr", y1, R, 4)
	NumPut("UPtr", x2, R, 8)
	NumPut("UPtr", y2, R, 12)
	Return Confine ? DllCall("ClipCursor", "Ptr", R) : (R := 0)
}
PS.
I increased the BufferAlloc, because if I kept it at 16, I'd get an error:
Error: Invalid parameter(s).
---> 026: NumPut("UPtr", y2, R, 12)
iseahound
Posts: 478
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 09:39

Here's a v1/v2 agnostic way of doing things:

Code: Select all

Esc::	ExitApp
f1::     ;on
	WinGetPos VarX, VarY, Width, Height, "A"
	VarX2 := VarX + Width
	VarY2 := VarY + Height
	ClipCursor( True, VarX, VarY, VarX2, VarY2)
	tooltip("Mouse cursor locked!")
	SetTimer () => ToolTip(), -3000
Return

f2::	;off
	ClipCursor( False,0,0,0,0)
	tooltip("Mouse cursor unlocked!")
	SetTimer () => ToolTip(), -3000
return

ClipCursor( Confine:=True, x1:=0 , y1:=0, x2:=1, y2:=1 ) {

	hData := DllCall("GlobalAlloc", "uint",0x2, "ptr",16)
	pData := DllCall("GlobalLock", "ptr",hData)
	NumPut("UPtr",x1,pData+0)
	NumPut("UPtr",y1,pData+4)
	NumPut("UPtr",x2,pData+8)
	NumPut("UPtr",y2,pData+12)

	value :=  Confine ? DllCall( "ClipCursor", "Ptr",pData ) : DllCall( "ClipCursor" )
	DllCall("GlobalUnlock", "ptr",hData)
	DllCall("GlobalFree", "ptr",hData)

	return value
}
Helgef
Posts: 4031
Joined: 17 Jul 2016, 01:02
Contact:

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 09:40

RECT members are long, so use int. Eg,

Code: Select all

ClipCursor(Confine:=True, x1:=0, y1:=0, x2:=1, y2:=1) {
	local R := 0
	if confine {
		R := BufferAlloc(16)
		NumPut("int", x1, R)
		NumPut("int", y1, R, 4)
		NumPut("int", x2, R, 8)
		NumPut("int", y2, R, 12)
	}
	Return DllCall("ClipCursor", "Ptr", R)
}
Helgef
Posts: 4031
Joined: 17 Jul 2016, 01:02
Contact:

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 09:41

Code: Select all

DllCall( "ClipCursor" )
No, you must pass all (one) parameters.

Edit: And GlobalAlloc / GlobalLock return "ptr"
iseahound
Posts: 478
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 09:52

Helgef wrote:
11 Aug 2019, 09:41
Edit: And GlobalAlloc / GlobalLock return "ptr"
Windows API:
Memory allocations are limited only by the available physical memory, including storage in the paging file on disk. When you allocate fixed memory, GlobalAlloc and LocalAlloc return a pointer that the calling process can immediately use to access the memory. When you allocate moveable memory, the return value is a handle. To get a pointer to a movable memory object, use the GlobalLock and LocalLock functions.
So I should call GlobalAlloc without the 0x2 flag to get a pointer directly, without requiring Global Lock?

@Maestr0 your code is really fun to play around with. Thanks!
Last edited by iseahound on 11 Aug 2019, 09:54, edited 1 time in total.
Helgef
Posts: 4031
Joined: 17 Jul 2016, 01:02
Contact:

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 09:54

I just meant dllcall(..., 'ptr').
iseahound
Posts: 478
Joined: 13 Aug 2016, 21:04
GitHub: iseahound

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 09:58

:facepalm: You have better coding habits than me

Code: Select all

	pData := DllCall("GlobalAlloc", "uint",0, "uptr",16, "ptr")
	NumPut("UPtr",x1,pData+0)
	NumPut("UPtr",y1,pData+4)
	NumPut("UPtr",x2,pData+8)
	NumPut("UPtr",y2,pData+12)

	value :=  Confine ? DllCall( "ClipCursor", "Ptr",pData ) : DllCall( "ClipCursor" )
	DllCall("GlobalFree", "ptr",pData)
	return value
User avatar
Maestr0
Posts: 81
Joined: 05 Dec 2013, 17:43

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 10:14

iseahound wrote:
11 Aug 2019, 09:52
@Maestr0 your code is really fun to play around with. Thanks!
You're welcome!

Thanks to all contributing here, this is (for me) that which is makes this community awesome!
User avatar
Maestr0
Posts: 81
Joined: 05 Dec 2013, 17:43

Re: ClipCursor: Confining Mouse to a window

11 Aug 2019, 10:17

Helgef wrote:
11 Aug 2019, 09:41

Code: Select all

DllCall( "ClipCursor" )
No, you must pass all (one) parameters.

Edit: And GlobalAlloc / GlobalLock return "ptr"
Again, much appreciated, Seems to have fixed all the issues! I'll update the top post (while keeping the old code so our thread makes sense for people reading this later)

Return to “AutoHotkey v2 Scripts and Functions”

Who is online

Users browsing this forum: No registered users and 9 guests