2 mouses, 1 cursor, axis locking

Ask gaming related questions (AHK v1.1 and older)
Zonex2004
Posts: 1
Joined: 16 Feb 2018, 18:20

2 mouses, 1 cursor, axis locking

16 Feb 2018, 18:26

Hi fellas,

I've looked around quite a bit, and have found different scripts that do different aspects of my specific use case. Please help me get started and/or find a solution =D

I have 2 mouses, 1 for each hand, I would like to use each mouse to move the single cursor on the screen in different axis. The right mouse would move only the X axis, and the left mouse would move only the Y axis. I need to lock the axis of each mouse that doesn't move.

I'm going to be using this for a game called Osu!, it's a fairly fast paced game that requires quite a high polling rate from my mouse; I'm guessing I should also look into Raw Mouse Input to retain the high polling rates from each mouse?

Any help would be greatly appreciated. Thank you!
Brulok

Re: 2 mouses, 1 cursor, axis locking

05 Jun 2018, 13:21

This is something that is probably best done with a mouse that has onboard memory. Set the DPI on each mouse to a super low amount on the necessary axis. That'll cause that axis movement to do nothing for that mouse.
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: 2 mouses, 1 cursor, axis locking

08 Jun 2018, 10:59

This is normally not possible, as windows does not have a built-in way to block input from a specific device.
You can do it, however, with AutoHotInterception

However, the technique you need to use is VERY RISKY, because you need to block ALL movement from BOTH devices, and then fake the input that you want to keep.
ie block X and Y for both mice, and then when the input comes in, synthesize appropriate movement

I would HEAVILY recommend having a 3rd mouse handy, or at the very least knowing the keyboard shortcuts you would need to use to kill the script

Anyway, here is roughly what you would need to do what you want (Untested code)

Code: Select all

AHI := new AutoHotInterception()
mouse1 := AHI.GetMouseId(1234, ABCD)	; Fill in VID/PID for mouse #1 here
mouse2 := AHI.GetMouseId(2345, BCDE)	; Fill in VID/PID for mouse #2 here
AHI.SubscribeMouseMove(mouse1, true, Func("MouseEvent").Bind(1))
AHI.SubscribeMouseMove(mouse1, true, Func("MouseEvent").Bind(2))

MouseEvent(mouseId, x, y){
	if (mouseId == 1){
		; Simulate mouse movement on the X axis
		DllCall("mouse_event", uint, 1, int, x, int, 0, uint, 0, int, 0)
	} else {
		; Simulate mouse movement on the Y axis
		DllCall("mouse_event", uint, 1, int, 0, int, y, uint, 0, int, 0)
	}
}
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: 2 mouses, 1 cursor, axis locking

08 Jun 2018, 12:55

I've done things like this with MouseDelta, it should be easy.
Simple example, untested, because I'm on laptop with no real mouse,

Code: Select all


; f1 to toggle, esc to exit.
setmousedelay -1
md:= new MouseDelta("f")

f1::
	md.SetState(toggle:=!toggle)
	BlockInput % toggle ? "MouseMove" : "MouseMoveOff"
return


f(id,dx,dy){
	static id1 := 0, id2 := 0
	if !id
		return
	if !id1
		id1 := id
	else if (!id2 && id != id1)
		id2 := id
	if (id == id1)
		MouseMove dx, 0, 0, R
	else if (id == id2)
		MouseMove 0, dy, 0, R
	return
}

esc::
	BlockInput MouseMoveOff
	exitapp
return
; Instantiate this class and pass it a func name or a Function Object
; The specified function will be called with the delta move for the X and Y axes
; Normally, there is no windows message "mouse stopped", so one is simulated.
; After 10ms of no mouse movement, the callback is called with 0 for X and Y
Class MouseDelta {
	State := 0
	__New(callback){
		;~ this.TimeoutFn := this.TimeoutFunc.Bind(this)
		this.MouseMovedFn := this.MouseMoved.Bind(this)

		this.Callback := callback
	}

	Start(){
		static DevSize := 8 + A_PtrSize, RIDEV_INPUTSINK := 0x00000100
		; Register mouse for WM_INPUT messages.
		VarSetCapacity(RAWINPUTDEVICE, DevSize)
		NumPut(1, RAWINPUTDEVICE, 0, "UShort")
		NumPut(2, RAWINPUTDEVICE, 2, "UShort")
		NumPut(RIDEV_INPUTSINK, RAWINPUTDEVICE, 4, "Uint")
		; WM_INPUT needs a hwnd to route to, so get the hwnd of the AHK Gui.
		; It doesn't matter if the GUI is showing, it still exists
		Gui +hwndhwnd
		NumPut(hwnd, RAWINPUTDEVICE, 8, "Uint")
 
		this.RAWINPUTDEVICE := RAWINPUTDEVICE
		DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
		OnMessage(0x00FF, this.MouseMovedFn)
		this.State := 1
		return this	; allow chaining
	}
	
	Stop(){
		static RIDEV_REMOVE := 0x00000001
		static DevSize := 8 + A_PtrSize
		OnMessage(0x00FF, this.MouseMovedFn, 0)
		RAWINPUTDEVICE := this.RAWINPUTDEVICE
		NumPut(RIDEV_REMOVE, RAWINPUTDEVICE, 4, "Uint")
		DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
		this.State := 0
		return this	; allow chaining
	}
	
	SetState(state){
		if (state && !this.State)
			this.Start()
		else if (!state && this.State)
			this.Stop()
		return this	; allow chaining
	}

	Delete(){
		this.Stop()
		;~ this.TimeoutFn := ""
		this.MouseMovedFn := ""
	}
	
	; Called when the mouse moved.
	; Messages tend to contain small (+/- 1) movements, and happen frequently (~20ms)
	MouseMoved(wParam, lParam){
		Critical
		; RawInput statics
		static DeviceSize := 2 * A_PtrSize, iSize := 0, sz := 0, pcbSize:=8+2*A_PtrSize, offsets := {x: (20+A_PtrSize*2), y: (24+A_PtrSize*2)}, uRawInput
 
		static axes := {x: 1, y: 2}
 
		; Get hDevice from RAWINPUTHEADER to identify which mouse this data came from
		VarSetCapacity(header, pcbSize, 0)
		If (!DllCall("GetRawInputData", "UPtr", lParam, "uint", 0x10000005, "UPtr", &header, "Uint*", pcbSize, "Uint", pcbSize) or ErrorLevel)
			Return 0
		ThisMouse := NumGet(header, 8, "UPtr")

		; Find size of rawinput data - only needs to be run the first time.
		if (!iSize){
			r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", iSize, "UInt", 8 + (A_PtrSize * 2))
			VarSetCapacity(uRawInput, iSize)
		}
		sz := iSize	; param gets overwritten with # of bytes output, so preserve iSize
		; Get RawInput data
		r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", &uRawInput, "UInt*", sz, "UInt", 8 + (A_PtrSize * 2))
 
		x := 0, y := 0	; Ensure we always report a number for an axis. Needed?
		x := NumGet(&uRawInput, offsets.x, "Int")
		y := NumGet(&uRawInput, offsets.y, "Int")
 
		this.Callback.(ThisMouse, x, y)
 
		;~ ; There is no message for "Stopped", so simulate one
		;~ fn := this.TimeoutFn
		;~ SetTimer, % fn, -50
	}
 
	;~ TimeoutFunc(){
		;~ this.Callback.("", 0, 0)
	;~ }
 
}
On first toggle, move the x-axis mouse first, then the y-axis mouse.

If it is in a game, the mouse block might not be effective.
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: 2 mouses, 1 cursor, axis locking

09 Jun 2018, 11:46

@Helgef - I am not sure how nice that will feel, as RawInput reports values in Mickeys, but you are sending MouseMoves, which uses Pixels. Why not replace the MouseMoves with mouse_event DLLCalls?
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: 2 mouses, 1 cursor, axis locking

09 Jun 2018, 13:04

Hello evilC. I have no idea, still didn't test it :wave: .

Return to “Gaming Help (v1)”

Who is online

Users browsing this forum: No registered users and 49 guests