如何在Autohotkey中捕获和发送原始鼠标输入?

时间:2017-05-28 13:14:26

标签: autohotkey

我知道如何捕捉相对于屏幕的鼠标移动,但是在鼠标移动被独立于鼠标在屏幕上的位置使用的游戏中呢?

例如,在游戏中,您的光标是隐藏的,但您可以无限地向左移动鼠标并转入圆圈,远远超过鼠标在屏幕上移动的空间。游戏可能会将您的隐形鼠标锁定在中心,或让它移动直到它碰到窗口的边缘,但此时任何记录鼠标相对于屏幕的移动的尝试都是无用的。

那么如何捕获/发送原始鼠标输入。例如,如果我想告诉玩家通过鼠标输入向左转1000度并将玩家的鼠标输入记录到左边1000度,那么我该怎样做呢?

我的目标是最终记录玩家的各种控制,包括鼠标输入,以创建记录和回放用户输入的系统之一。我已经搜遍了整个ahk docs和Google,没有发现捕获和发送原始鼠标输入。

1 个答案:

答案 0 :(得分:1)

在一个名为evilC的AHK论坛上,一位出色的用户已经为您解决了这个问题。

它被称为mouse delta并跟踪原始鼠标输入的变化。

我发布了他的鼠标delta脚本的几个不同变体之一。请务必按照上面的链接查看他所做的所有不同的事情。

同样,这不是我的工作。

; 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)
    ;~ }

}