Page 1 of 1

ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 05:12
by Maestr0
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/

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 05:24
by Maestr0
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
	}
}

Re: ClipCursor: Confining Mouse to a window

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

Cheers.

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 06:07
by Maestr0
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)
?

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 06:20
by Helgef
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.

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 07:21
by Maestr0
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)

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 09:39
by iseahound
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
}

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 09:40
by Helgef
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)
}

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 09:41
by Helgef

Code: Select all

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

Edit: And GlobalAlloc / GlobalLock return "ptr"

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 09:52
by iseahound
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!

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 09:54
by Helgef
I just meant dllcall(..., 'ptr').

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 09:58
by iseahound
: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

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 10:14
by Maestr0
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!

Re: ClipCursor: Confining Mouse to a window

Posted: 11 Aug 2019, 10:17
by Maestr0
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)