[Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

Post gaming related scripts
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

[Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

05 Nov 2015, 16:22

For an AHK v2 version of this library, see here

MouseGetPos and other methods of retrieving mouse axis input are fundamentally flawed when it comes to games, because many games mess with the mouse cursor (Keep on moving it to middle of screen etc) or other limitations (eg when the cursor reaches the edge of the screen, you cannot generate any more input in that direction)

Games that do not operate by mouse cursor (eg FPS) generally read the mouse by "Delta" information - that is, the amount that it moved in each direction since the last update.

This library uses the RawInput API to read these Delta Move packets.

Note: If you wish to write scripts that send mouse movement at the kinds of rates that real mice operate at, see my LLMouse library.
================================== LIBRARY ========================================

Code: Select all

; 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)
	;~ }
 
}
================================== SAMPLE SCRIPT 1 ========================================
A diagnostic utility to show you the input that comes from a mouse, in real-time

Code: Select all

#include MouseDelta.ahk
#SingleInstance,Force
 
Gui, Add, ListBox, w300 h200 hwndhOutput
Gui, Add, Text, xm w300 center, Hit F12 to toggle on / off
Gui, Show,, Mouse Watcher
 
MacroOn := 0
md := new MouseDelta("MouseEvent")
 
return
 
GuiClose:
	md.Delete()
	md := ""
	ExitApp

F12::
	MacroOn := !MacroOn
	md.SetState(MacroOn)
	return
 
; Gets called when mouse moves
; x and y are DELTA moves (Amount moved since last message), NOT coordinates.
MouseEvent(MouseID, x := 0, y := 0){
	global hOutput
	static text := ""
	static LastTime := 0
 
	t := A_TickCount
	text := "x: " x ", y: " y (LastTime ? (", Delta Time: " t - LastTime " ms, MouseID: " MouseID) : "")
	GuiControl, , % hOutput, % text
	sendmessage, 0x115, 7, 0,, % "ahk_id " hOutput
	LastTime := t
}
================================== SAMPLE SCRIPT 2 ========================================
An example of usage that can map mouse axes to WSAD or Arrow keys is included.
If you wish to map mouse to Joystick, see my cVJoyinterface library on GitHub (Link in Sig).
There is also a complete Mouse to Joystick script in the Oneswitch-Utilities repo.

Code: Select all

#SingleInstance force
#include MouseDelta.ahk
 
MacroOn := 0
md := new MouseDelta("MouseEvent")
return
 
F12::
	MacroOn := !MacroOn
	md.SetState(MacroOn)
	return

; Keeps a track of a "Pool" for each direction of movement (u, d, l, r)
; The pools constantly empty every 10ms at the rate dictated by DecayRate
; Moving the mouse in a direction will add the delta value to the pool, to a max of PoolCap
; If one direction's pool crosses the threshold, that direction is considered "Held"
; In this case, the opposite direction's pool is emptied.
MouseEvent(MouseID, x := "", y := ""){
	; User configurables
	static DecayRate := 1		; The rate at which the pools decay
	static PoolCap := 20		; The highest value a pool can hold
	static Threshold := 3		; If a pool crosses this value, it is considered held
	; Output keys
	static KeyMap := {y: {-1: "Up", 1: "Down"}, x: {-1: "Left", 1: "Right"}}	; Arrow Keys
	; End of user configurables
	
	; StopFns are fired while the pool for an exis is not empty
	static StopFns := {x: Func("MouseEvent").Bind(-1,0), y: Func("MouseEvent").Bind(-1,"",0)}
	static VectorPools := {x: {-1: 0, 1: 0}, y: {-1: 0, 1: 0}}
	Static AxisStates := {x: 0, y: 0}
	static Vectors := {-1: "", 1: ""}	; just used for iteration
	static TimerStates := {x: 0, y: 0}
	
	input_data := {x: x, y: y}
	
	; Apply current input to the pools
	for axis, value in input_data {
		if (value == "")
			continue
		; Deplete vector pools
		for v, u in vectors {
			VectorPools[axis, v] -= DecayRate
			if (VectorPools[axis, v] < 0)
				VectorPools[axis, v] := 0
		}
		; Update pool states
		vector := GetVector(value)
		fn := StopFns[axis]
		if (vector){
			; Movement for this axis
			magnitude := abs(value)
			VectorPools[axis, vector] += magnitude
			if (VectorPools[axis, vector] > PoolCap){
				VectorPools[axis, vector] := PoolCap
			}
			is_over_thresh := (VectorPools[axis, vector] > Threshold)
			was_over_thresh := (VectorPools[axis, vector] > Threshold)
			if (!was_over_thresh && is_over_thresh){
				; Crossed threshold, cancel out opposite axis
				; This is used to allow switching of directions relatively quickly.
				; If we cross the threshold on a new vector, zero out the other vector
				VectorPools[axis, vector * -1] := 0
			}
			; If there was movement on this axis and the timer is not running, then start it running
			if (!TimerStates[axis]){
				SetTimer, % fn, 10
			}
		} else {
			; No movement for this axis
			if (VectorPools[axis, -1] + VectorPools[axis, 1] == 0){
				; Pools for this axis are empty, stop timer
				SetTimer, % fn, Off
			}
		}
	}
	
	; Change states of outputs according to pools
	for axis, value in input_data {
		new_vector := (VectorPools[axis, -1] > Threshold ? -1 : (VectorPools[axis, 1] > Threshold ? 1 : 0))
	
		if (new_vector != AxisStates[axis]){
			; This axis changed state
			if (AxisStates[axis] != 0){
				; One of the directions for this axis was previously held, release old key
				Send %  "{" KeyMap[axis, AxisStates[axis]] " up}"
				
			}
			if (new_vector){
				; The new state is non-zero, so press the new key
				Send % "{" KeyMap[axis, new_vector] " down}"
			}
		}
		AxisStates[axis] := new_vector
	}
}

; Returns -1 for negative input, +1 for positive, else 0
GetVector(val){
	if (val == 0)
		return 0
	else if (val < 0)
		return -1
	else
		return 1
}
================================== SAMPLE SCRIPT 3 ========================================
How to implement a "Sniper Mode" with any mouse.
When sniper mode is OFF (Default), it multiplies mouse movement by the value you set in ScaleFactor.
When you turn it off, it stops amplifying movement.
You will likely need to turn the mouse sensitivity down in the game to compensate.

Code: Select all

#SingleInstance force
#include MouseDelta.ahk

; ============= START USER-CONFIGURABLE SECTION =============
ShiftKey := "F12"	; The key used to shift DPI. Can be any key name from the AHK Key list: https://autohotkey.com/docs/KeyList.htm
ShiftMode := 1		; 0 for shift-while-held, 1 for toggle
ScaleFactor := 4	; The amount to multiply movement by when not in Sniper Mode
; ============= END USER-CONFIGURABLE SECTION =============

; Adjust ScaleFactor. If the user wants 2x sensitivity, we only need to send 1x input...
; ... because the user already moved the mouse once, so we only need to send that input 1x more...
; ... to achieve 2x sensitivity
ScaleFactor -= 1
SniperMode := 0
md := new MouseDelta("MouseEvent").Start()

hotkey, % ShiftKey, ShiftPressed
if (!ShiftMode){
	hotkey, % ShiftKey " up", ShiftReleased
}
return
 
ShiftPressed:
	if (ShiftMode){
		SniperMode := !SniperMode
	} else {
		SniperMode := 1
	}
	md.SetState(!SniperMode)
	return

ShiftReleased:
	if (!ShiftMode){
		SniperMode := 0
	}
	md.SetState(!SniperMode)
	return

; Gets called when mouse moves or stops
; x and y are DELTA moves (Amount moved since last message), NOT coordinates.
MouseEvent(MouseID, x := 0, y := 0){
	global ScaleFactor
 
	if (MouseID){
		x *= ScaleFactor, y *= ScaleFactor
		DllCall("mouse_event",uint,1,int, x ,int, y,uint,0,int,0)
	}
}
Last edited by evilC on 26 Aug 2023, 12:40, edited 6 times in total.
Too
Posts: 3
Joined: 10 Feb 2016, 07:23

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

10 Feb 2016, 12:14

I've been looking a script like this for very long time and the game i need it for is CS:S for BunnyHopping, if u want u can go to youtube and search for "css bhop" to see and understand what excatly what im looking for. So the problem is atm that when i jump and start strafing (moving mouse to the right and left constantly nonstop) i dont get the same speed as i get when im doing this manually. It feels like when i move my mouse to one direction it presses A or D only once or max few times but not holding it down constantly because when i do it manually for example run straight W down, then jump and turn 180 degrees right with D manually it turn around instantly and very sharply without loosing any speed and with the Script if i do the same the turn around doesnt go as sharp as it was manually and that because it feels that it doesnt hold the D down all the way trough the 180 turn. I was thinking that 1 solution would be if it was possible to make it like this: when the script detects the mouse moving right direction it holds down "D" EVEN if the movement is NOT constant BUT if u move the mouse even 1 pixel to the LEFT direction it will unpress the D and hold down A instead. And the same goes here. Hope u understand what i mean.

Not sure if that's even possible to do with script but thank you anyways, much appreciate it ^^
HelloThere

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

20 Feb 2016, 06:54

Hello,

I am not PRO in these things so please excuse my describtion.
I have very similar problem. I am playing game where all classic mouse clickers are blocked. It works only in Windows 7 but when I ALT+TAB to the game it stops immediately.

Could you please help me? I would be ok if the script would just nonstop click left mouse button. But no

Thank you very much.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

20 Feb 2016, 10:08

This code is nothing to do with mouse clicks I am afraid, it is only to do with mouse movement.

I would be surprised if the game you are playing cannot see AHK's clicks. Try running the script as admin (Right click AHK file, Run As Admin) and try making AHK hold the mouse button down for longer (50ms is normally enough) as if AHK is holding the mouse button down for too short a time, many games do not recognize it.
HelloThere

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

20 Feb 2016, 11:40

Thanks for reply.

I am really big noob in programming. Could you show me how the script should be please?

Thank you very much!
HelloThere

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

20 Feb 2016, 12:00

I tried very simple:

Code: Select all

F11::Click
It works in Windows but it doesn't work in game.
HelloThere

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

20 Feb 2016, 12:37

Code: Select all

F10::Click
Click Down    ; Mouse button down
Sleep 50    ; xx ms delay
Click Up    ; Mouse button up
It doesn't work. I tried sleep 30,50,100,1000
Ruevil2
Posts: 173
Joined: 14 Jul 2014, 10:39

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

23 May 2016, 10:44

HelloThere wrote:I tried very simple:

Code: Select all

F11::Click
It works in Windows but it doesn't work in game.

Look here. https://autohotkey.com/boards/viewtopic.php?f=7&t=11084
zeno

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

07 Jul 2016, 04:42

Hello, I'm very interested in this type of script and I tried this script in Fallout NV and Csgo but it didn't work :(
zeno

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

08 Jul 2016, 10:34

My bad, I read that it could map WSAD or Arrow keys to mouse axes while it does the opposite. I'm sorry. :crazy:
By the way have you ever made a keys to mouse axes script?
Tactical Shrimp
Posts: 31
Joined: 11 Nov 2015, 22:29

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

09 Jul 2016, 13:44

alright thank you for this however i am stumped i have a script with a gui and a dropdown and you select a line and a different mousemove script activates so my question is the mouse move script that i had worked in menus of the game and windows desktop however in the game itself it didn't work i have a new mousemove but this one cant go under 1 and 1 is way to fast for my DPI and sensitivity here it is

{code}
; v Left or Right (Negative or Positive)
DllCall("mouse_event", uint, 1, int, -1, int, 1, uint, 0, int, 0)
; ^ Up or Down (Negative or Positive)
{/code}
i labeled the values i know what do but i have no idea what the other ones do is there one in there that can change the speed if so please let me know well the thing about your code is that i cant make heads or tales of it im new to ahk but i do know c++ and this is wildly different for the most part
Tactical Shrimp
Posts: 31
Joined: 11 Nov 2015, 22:29

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

10 Jul 2016, 12:59

Code: Select all

Gui, Add, DropDownList, w370 vDDL_Text gDDL_Changed, none|Solstice|Pulsar C|Orion
Gui, Show, x356 y116 h452 w373, New GUI Window
Return
#SingleInstance, Force
 
;BlockInput Off
SetDefaultMouseSpeed, 4
 
GuiClose:
ExitApp
 
 
DDL_Changed:
Gui,Submit,Nohide
{
    if (DDL_Text = "Solstice")
    {
        Hotkey,~LButton,Solstice,On
        
    }
    else if((DDL_Text = "Pulsar C"))
    {
        
    
    
    }
    else if((DDL_Text = "Orion"))
    {
        
        
        
    }
    else
    Hotkey,~LButton,Solstice,Off
    return
    
    Solstice:
    while(GetKeyState("LButton", "P"))
    ;                                    
    SendMode Input("mouse_event",uint,1,int,-1,int,1,uint,0,int,0)
    ;                                            
    
    return
    
    
    
}
there is my script i dont know why you need the whole thing but there it is
Tactical Shrimp
Posts: 31
Joined: 11 Nov 2015, 22:29

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

10 Jul 2016, 22:48

evilC wrote:SendMode Input("mouse_event",uint,1,int,-1,int,1,uint,0,int,0)?
Shouldn't that be DllCall("mouse_event",uint,1,int,-1,int,1,uint,0,int,0)?
yea it was that i dont know why that changed but that isnt the problem is there a way to make the speed of the mouse slower
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

11 Jul 2016, 08:35

No, a mouse_event of 1 unit is literally the smallest movement you can make with a mouse.
With moves of 1 unit, normally a single mouse_event would do next to nothing, you would have to execute lots (ie 100 or so) within a short timeframe (eg 1 every ms) to simulate normal mouse movement.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

11 Jul 2016, 09:21

Yeah... It seems like he omitted a critical line from the code he posted...

This is what he was quoting, but he omitted the Loop statement.

Code: Select all

$F2::
Loop, 3
	;                                    ↓ Left or Right (Negative or Positive)
	DllCall("mouse_event", uint, 1, int, 2, int, 2, uint, 0, int, 0)
	;                                            ↑ Up or Down (Negative or Positive)
return
So I am guessing that 1 mouse_event is not too much, he just issued 3 with no delay between, which is a VERY quick movement.

Return to “Gaming Scripts (v1)”

Who is online

Users browsing this forum: No registered users and 39 guests