Hi, jamz.
I just made a script for multiple cursors, it is very simple to add inverted axis to that, as long as you don't move both mice simultaneously, the cursor acts pretty normal. This is is a modified version if you like to try. You invert the axis by setting a negative sensitivity. (Left edit is for x, right for y). You also need to edit the the confinment rectangle under the coordinates tab. More instructions:
.
Code: Select all
; TODO,
; remove debug hotkey - and ea() func
; reconsider choise of default cursor when A_Cursor is Unknown.
; Manage click up when more than mouse is moving and click is down....
class Cursor extends _UCR.Classes.Plugin
{
type:="Cursor"
description:="Mulitple cursor controller"
static AllCursors:=[] ; Tracks all instances of this plugin
static MouseIdCounter:=[] ; For tracking multiple active cursor from the same mouse id.
static doNothingFunc:=ObjBindMethod(Cursor, "doNothing") ; For disabling mouse buttons.
init()
{
this.LoadCursor() ; Loads cursor handles.
; Gui
; Some control(s) are placed at rowX, colY, defined here:
row0:=" y5 ", row0_:=" y2 "
row1:=" y35 ", row1_:=" y32 "
row2:=" y65 ", row2_:=" y62 "
row3:=" y95 ", row3_:=" y92 "
row4:=" y125 ", row4_:=" y123 "
row5:=" y155 ", row5_:=" y153 "
row6:=" y185 ", row6_:=" y183 "
row7:=" y215 ", row7_:=" y213 "
; Columns for gui placement
col1:=" x25 ", col2:=" x145 ", col3:=" x260 ", col4:=" x375 ", col5:=" x490 "
Gui, Add, Tab2, w650 h65, % "Main|Cycle|Coordinates|Help"
Gui, Tab, 1
; Mouse selection
Gui, Add, Button, % " w100 h25 hwndselectMouseButton", % "Select mouse"
this.selectMouseButton:=selectMouseButton
gFunc := this.SelectMouseInit.Bind(this)
GuiControl, +g, % this.selectMouseButton, % gFunc
this.AddControl("Edit", "MouseIdEdit", this.MouseIdSet.Bind(this),"x+10 yp+3 w75 center")
this.AddControl("InputButton", "toggle", 0, this.toggle.Bind(this),"x+10 h25")
; Toggle light
this.AddControl("ButtonPreview", "bp1", 0,0, "x+10")
; Sensitivity
Gui,Add,Text,x+10 yp+3,Sensitivity:
this.AddControl("Edit", "sensEditX", this.sensChanged.Bind(this,1),"x+10 yp-3 w35 center",1)
this.AddControl("Edit", "sensEditY", this.sensChanged.Bind(this,2),"x+5 w35 center",1)
; Hide/show option
this.AddControl("Checkbox", "hideWhenInactiveCB", this.hideWhenInactiveCBChanged.Bind(this),"x+10 yp+3 checked", "Hide when inactive")
Gui, Tab, 2
Gui,Add,Text,,Cycle cursors:
this.AddControl("InputButton", "cycleCursor", 0, this.cycleCursor.Bind(this),"x+10 h25")
this.AddControl("Checkbox", "cycleCursorCB", 0,"x+10 yp+3 checked", "Cycle by id")
; Coordinates tab
Gui, Tab, 3
Gui,Add,Text, x+5,Confine to: (x1,y1)
this.AddControl("Edit", "confineX1", this.confineEditChanged.Bind(this,1),"x+5 yp-3 w35 number center",0)
this.AddControl("Edit", "confineY1", this.confineEditChanged.Bind(this,2),"x+5 w35 number center",0)
Gui,Add,Text, x+5 yp+3,(x2,y2)
this.AddControl("Edit", "confineX2", this.confineEditChanged.Bind(this,3),"x+5 yp-3 w35 number center",A_ScreenWidth)
this.AddControl("Edit", "confineY2", this.confineEditChanged.Bind(this,4),"x+5 w35 number center",A_ScreenHeight)
Gui,Add,Text, x+5 yp+3,Start at: (x,y)
this.AddControl("Edit", "startX1", this.startEditChanged.Bind(this,1),"x+5 yp-3 w35 number center",A_ScreenWidth//2)
this.AddControl("Edit", "startY1", this.startEditChanged.Bind(this,2),"x+5 w35 number center",A_ScreenHeight//2)
Gui, Add, Button, % "x+5 hwndresetButton", % "Reset"
this.resetButton:=resetButton
gFunc := this.resetCoords.Bind(this)
GuiControl, +g, % this.resetButton, % gFunc
; Help tab
Gui, Tab, 4
Gui, Font, s8 w400, Courier New
Gui, add, Link, % row1 col1, % "Author: Helgef (2017-03-03). "
. "Instructions are available at: <a href=""https://autohotkey.com/boards/viewtopic.php?f=19&t=24538"">AutoHotkey.com forum.</a>`n"
; Misc.
this.createCursor() ; Make the cursor's gui.
Cursor.AllCursors.Push(this) ; The plugin is self aware.
this.hasBeenToggled:=0
this.hideWhenInactive && !this.toggleState ? this.hideCursor() : this.showCursor()
; DEBUG - REMOVE
; ff:=ObjBindMethod(Cursor,"ea")
; try
; Hotkey, Esc, % ff
}
; Clean up for UCR.
onClose(){
GuiControl, -g, % this.selectMouseButton
GuiControl, -g, % this.resetButton
for k, cur in Cursor.AllCursors ; Find position in AllCursors list.
if (cur=this)
break
Cursor.AllCursors.RemoveAt(k) ; Remove from AllCursors list
Cursor.MouseIdCounter.HasKey(this.SelectedMouse) ? --Cursor.MouseIdCounter[this.SelectedMouse] : "" ; Decerment mouse id counter
if !Cursor.AllCursors.length()
Cursor.md.Delete(),Cursor.md:="",Cursor.toggleHotkeys("Off") ; Remove the mouse delta if no more cursor plugins exists.
this.destroyCursor() ; Destroy the gui cursor.
base.onClose() ;?
}
; Gui functions
; Coords tab
confineEditChanged(num,val){ ; Confinement rectangle.
if (num=1)
this.minX:=val
else if (num=2)
this.minY:=val
else if (num=3)
this.maxX:=val
else if (num=4)
this.maxY:=val
return
}
startEditChanged(num,val){ ; start position
if (num=1)
this.oX:=val
else if (num=2)
this.oY:=val
return
}
resetCoords(){ ; Reset coords button
this.GuiControls.confineX1.Set(0)
this.GuiControls.confineY1.Set(0)
this.GuiControls.confineX2.Set(A_ScreenWidth)
this.GuiControls.confineY2.Set(A_ScreenHeight)
this.GuiControls.startX1.Set(A_ScreenWidth//2)
this.GuiControls.startY1.Set(A_ScreenHeight//2)
}
sensChanged(num,val){
if val is number
{
if (num=1)
this.sensX:=val ; Set sensitivity x-axis
else if (num=2)
this.sensY:=val ; Set sensitivity y-axis
}
return
}
hideWhenInactiveCBChanged(state){
state && !this.toggleState ? this.hideCursor() : this.showCursor() ; Show/hide cursor to match setting.
return this.hideWhenInactive:=state ; Indicates wether cursor is hidden/shown when inactive
}
createCursor(){
if this.hWin
return ;?
this.x:=this.oX
this.y:=this.oY
Gui, new, +hwndhWin -caption +toolwindow +alwaysontop +E0x20
this.hWin:=hWin
Gui, % hWin ": Margin",0,0
Gui, % hWin ": Color", abcdef
Gui, % hWin ": Add", Picture, % "x0 y0 hwndhPic", % "hIcon:*" this.hIcons["Arrow"]
this.hPic:=hPic
this.setCursor()
this.hs:=Cursor.GetCursorHotspot()
GuiControl,, % this.hPic, % "hIcon:*" this.hIcons[this.IDC] ; Consider to remove this.
;Gui, % hWin ": Show", % "Hide NA x" this.x " y" this.y
this.hidden:=1
WinSet, TransColor, abcdef 255, % "ahk_id " hWin
return
}
; Handle mouse selection
SelectMouseInit(){
; Starts the mouse selection procedure.
; How to:
; - Press the button
; - Move the mouse within 5 seconds.
; - Done!
p:=this.SelectedMouse ; Previous mouse
selectMouseMD:= new Cursor.MouseDelta(ObjBindMethod(this,"SelectMouse"))
selectMouseMD.Start()
timeout:=A_TickCount ; For timeout
while (this.SelectedMouse=p && A_TickCount-timeout<5000) ; 5 second timeout.
Sleep,-1
if (this.SelectedMouse!=p){
Cursor.MouseIdCounter.HasKey(p) ? --Cursor.MouseIdCounter[p] : "" ; A new mouse chosen
Cursor.MouseIdCounter.HasKey(this.SelectedMouse) ? ++Cursor.MouseIdCounter[this.SelectedMouse] : (Cursor.MouseIdCounter[this.SelectedMouse]:=1)
}
selectMouseMD.Delete() ; Delete the old mouse delta
return
}
MouseIdSet(id){
return this.SelectedMouse:=id
}
SelectMouse(MouseID,dx,dy) {
; Callback function for temporary mouse delta instance.
if (this.selectedMouse=MouseID)
return
this.GuiControls.MouseIdEdit.Set(MouseID)
return
}
; destroy/hide/show/moveCursor() - these functions apply to the "gui cursor" not the real windows cursor.
destroyCursor(){
if !this.hWin
return
Gui, % this.hWin ": Destroy"
return this.hWin:=""
}
hideCursor(){
if !this.hWin
return
if this.hidden
return
Gui, % this.hWin ": Show", % "NA hide"
this.hidden:=1
return
}
showCursor(move:=0){
; Consider toggle aot to avoid getting under other aot win.
if !this.hWin
return
if (this.hidden && move)
return this.showCursor()
else if (!this.hidden && !move)
return
this.hidden:=0
if (this.IDC!=this.pIDC) ; Only update if changed
GuiControl,, % this.hPic, % "hIcon:*" this.hIcons[this.IDC]
this.pIDC:=this.IDC
if !move
Gui, % this.hWin ": Show", % "NA x" this.x-this.hs[1] " y" this.y-this.hs[2] ; Adjustment for hotspot.
else
WinMove, % "ahk_id " this.hWin,, this.x-this.hs[1], this.y-this.hs[2]
return
}
moveCursor(){
return this.showCursor(true)
}
ea(){ ; remove
ExitApp
}
; For suppressing native mouse button functionality
doNothing(){
return
}
; Main togle
toggle(state) {
if !state
return
if !this.hasBeenToggled { ; Add to MouseIdCounter on first time toggle on.
Cursor.MouseIdCounter.HasKey(this.SelectedMouse) ? ++Cursor.MouseIdCounter[this.SelectedMouse] : (this.SelectedMouse ? (Cursor.MouseIdCounter[this.SelectedMouse]:=1) : "")
this.hasBeenToggled:=true
}
this.toggleState:=!this.toggleState
if (!Cursor.md && this.toggleState) {
Cursor.md:=new Cursor.MouseDelta(ObjBindMethod(Cursor,"MoveCursors")) ; Create a new mousedelta for the Cursor class, if non exists.
}
Cursor.globalToggle(this.toggleState)
if !this.toggleState {
if (this.hideWhenInactive) ; Hide/show according to settings
this.hideCursor()
else
this.showCursor()
} else {
this.showCursor()
}
this.GuiControls.bp1.SetState(this.toggleState) ; Green light when on (in UCR gui)
return
}
globalToggle(state){
; Sets the correct state of the MouseDelta: Cursor.md, depending on all toggleStates of all cursors in Cursor.AllCursors
mdState:=Cursor.md.State
if (state=mdState) ; State is matched, do nothing
return
else if (state && !mdState) { ; State is on, and md is off, turn on md.
Cursor.toggleHotkeys("On")
Cursor.md.Start()
return
}else if (!state && mdState) { ; State is off, but md is on, check if any other cursor is on, then do nothing, if no other is on, restore mouse functionality and turn off md.
for k, cur in Cursor.AllCursors
if cur.toggleState
return
Cursor.toggleHotkeys("Off")
Cursor.md.Stop()
}
return
}
toggleHotkeys(state){
; Normal mouse button functionality is suppressed while a cursor is active.
hkfn:=Cursor.doNothingFunc
Hotkey, *LButton, % hkfn, % state
Hotkey, *RButton, % hkfn, % state
Hotkey, *MButton, % hkfn, % state
Hotkey, *WheelUp, % hkfn, % state
Hotkey, *WheelDown, % hkfn, % state
Hotkey, *XButton1, % hkfn, % state
Hotkey, *XButton2, % hkfn, % state
if (state="On") ; Disables all mouse movement when "On"
BlockInput, MouseMove
else
BlockInput, MouseMoveOff
return
}
; Cycle cursors.
cycleCursor(state){
if !state ; Manage hotkey up event - do nothing.
return
if this.GuiControls.cycleCursorCB.Get() ; Only cycle same id
this.cycleSame()
else
this.cycleAll()
}
cycleSame(){
for k, cur in Cursor.AllCursors
if (cur.selectedMouse!=this.selectedMouse) { ; Wrong id, continue
continue
} else if (cur.toggleState) { ; Turn off the first one that is on
cur.toggle(1), oneOff:=1
} else if (oneOff) { ; Now we can turn on the next
cur.toggle(1), oneOn:=1
break
}
if !oneOn ; No cursor was turned on, turn on the first with correct id.
for k, cur in Cursor.AllCursors
if (cur.selectedMouse=this.selectedMouse) {
cur.toggle(1)
break
}
return
}
cycleAll(){
; Find the next cursor, turn it on
for k, cur in Cursor.AllCursors
if (cur.toggleState) { ; Turn off the first one that is on
cur.toggle(1), oneOff:=1
} else if (oneOff) {
cur.toggle(1), oneOn:=1 ; Turn on the next one
break
}
if !oneOn
Cursor.AllCursors[1].toggle(1) ; No cursor turned on, turn on the first one.
return
}
MoveCursors(MouseID, dx, dy, usButtonFlags, usButtonData){
; Move all cursors for the MouseId that generated the movement, dx,dy.
; Confine to (x,y) ∈ [cur.MinW,cur.MaxW]x[cur.MinH,cur.MaxH]
; Note: hide/show/move Cursor functions refer to the cursor GUI representation, not the actual (windows) cursor.
Cursor.md.SetState(0)
SetWinDelay,-1
SetMouseDelay,-1
CoordMode, Mouse, Screen
idCtr:=Cursor.MouseIdCounter[MouseID]
for k, cur in Cursor.AllCursors ; Consider all cursors. each "cur" is one instances of the plugin.
if (cur.toggleState && cur.SelectedMouse=MouseID) {
cur.x+=dx?dx*cur.sensX:0 ; Update position
cur.y+=dy?dy*cur.sensY:0
cur.x:= cur.x>cur.MaxX?cur.MaxX:(cur.x<cur.MinX?cur.MinX:cur.x) ; Confine
cur.y:= cur.y>cur.MaxY?cur.MaxY:(cur.y<cur.MinY?cur.MinY:cur.y)
MouseMove, cur.x, cur.y, 0
cs:=A_Cursor
cur.hs:=Cursor.GetCursorHotspot(cs) ; Adjustment for gui, w.r.t cursor hotspot
cur.setCursor(cs)
--idCtr
if !(idCtr) {
cur.hideCursor() ; Hide the gui, the real mouse cursor is showing
} else {
cur.moveCursor() ; Move the gui, the real mouse is not here
}
if usButtonFlags
Cursor.MouseButtons(usButtonFlags,usButtonData) ; Handle mouse buttons
} else if (cur.toggleState && cur.hidden) { ; Show the gui, the real cursor has moved elsewhere.
cur.showCursor()
}
Cursor.md.SetState(1)
return
}
setCursor(CursorStyle:=""){
; Decides which cursor image to show on the cursor gui.
if !CursorStyle
CursorStyle:=A_Cursor
if (this.IDC=CursorStyle)
return
if this.hIcons.HasKey(CursorStyle)
this.IDC:=CursorStyle
else
this.IDC:="Arrow" ; reconsider this choise. This is when A_Cursor is "Unknown" <--- NOTE
return
}
MouseButtons(usButtonFlags,usButtonData){
/*
RAWMOUSE structure
URL:https://msdn.microsoft.com/en-us/library/windows/desktop/ms645578(v=vs.85).aspx
usButtonFlags:
The transition state of the mouse buttons. This member can be one or more of the following values:
RI_MOUSE_LEFT_BUTTON_DOWN:=0x0001
RI_MOUSE_LEFT_BUTTON_UP:=0x0002
RI_MOUSE_RIGHT_BUTTON_DOWN:=0x0004
RI_MOUSE_RIGHT_BUTTON_UP:=0x0008
RI_MOUSE_MIDDLE_BUTTON_DOWN:=0x0010
RI_MOUSE_MIDDLE_BUTTON_UP:=0x0020
RI_MOUSE_BUTTON_4_DOWN:=0x0040
RI_MOUSE_BUTTON_4_UP:=0x0080
RI_MOUSE_BUTTON_5_DOWN:=0x100
RI_MOUSE_BUTTON_5_UP:=0x0200
RI_MOUSE_WHEEL:=0x0400
usButtonData:
If usButtonFlags is RI_MOUSE_WHEEL, this member is a signed value that specifies the wheel delta.
*/
static ClickParams:= { 1:"Down", 2:"Up", 4:"Down Right", 8:"Up Right"
, 16:"Down Middle", 32:"Up Middle", 64:"Down X1", 128:"Up X1"
, 256:"Down X2", 512:"Up X2", 1024:"Wheel" } ; 1024:Wheel isn't used.
static WheelDelta:=120
(RI_MOUSE_WHEEL:= usButtonFlags & 1024) ? usButtonFlags^=1024 : ""
while usButtonFlags {
if ((paramNum:=2**(A_Index-1)) & usButtonFlags) {
SendInput, % "{Click " . ClickParams[paramNum] . "}"
usButtonFlags^=paramNum
}
}
if RI_MOUSE_WHEEL ; Mouse wheel
Loop, % Round(abs(usButtonData)/WheelDelta)
SendInput % "{Click " . (usButtonData>0 ? "WheelUp" : "WheelDown") . "}"
return
}
LoadCursor(){
; Url https://msdn.microsoft.com/en-us/library/windows/desktop/ms648391%28v=vs.85%29.aspx
static cursorIds:={ IDC_ARROW :32512
, IDC_IBEAM :32513
, IDC_WAIT :32514
, IDC_CROSS :32515
, IDC_UPARROW :32516
, IDC_SIZE :32640
, IDC_ICON :32641
, IDC_SIZENWSE :32642
, IDC_SIZENESW :32643
, IDC_SIZEWE :32644
, IDC_SIZENS :32645
, IDC_SIZEALL :32646
, IDC_NO :32648
, IDC_HAND :32649
, IDC_APPSTARTING :32650
, IDC_HELP :32651 }
this.hIcons:={}
for IDC, id in cursorIds
this.hIcons[SubStr(IDC,5)]:=DllCall("LoadCursor", "Uint", 0, "Uint", id)
return
}
GetCursorHotspot(CursorStyle:=""){
; Url :
; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648389%28v=vs.85%29.aspx (GetCursorInfo)
; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648381(v=vs.85).aspx (Cursorinfo struct)
; struct cursorinfo {
; DWORD cbSize;
; DWORD flags;
; HCURSOR hCursor;
; POINT ptScreenPos;
; }
; Url:
; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648070(v=vs.85).aspx (GetIconInfo)
; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648052(v=vs.85).aspx (Iconinfo struct)
;
; struct _ICONINFO {
; BOOL fIcon; fIcon := NumGet(&PICONINFO, 0,"Int")
; DWORD xHotspot;
; DWORD yHotspot;
; HBITMAP hbmMask; hBMMask := NumGet(&PICONINFO,12,"UPtr")
; HBITMAP hbmColor; BMColor := NumGet(&PICONINFO,12+A_PtrSize,"UPtr")
; }
;
; Get Cursor handle
static hotspots:={}
if !CursorStyle
CursorStyle:=A_Cursor
if hotspots.Haskey(CursorStyle)
return hotspots[CursorStyle]
VarSetCapacity(pci, (cbSize:=16+A_PtrSize), 0)
NumPut(cbSize,&pci,"Uint")
DllCall("GetCursorInfo", "UPtr", &pci)
hCursor := NumGet(pci, 8, "UPtr")
; Get icon info - cursor hotspot
VarSetCapacity(PICONINFO, 12+2*A_PtrSize, 0)
DllCall("GetIconInfo", "Ptr", hCursor, "Ptr", &PICONINFO)
xHotspot := NumGet(&PICONINFO, 4,"UInt")
yHotspot := NumGet(&PICONINFO, 8,"UInt")
hs:=[xHotspot?xHotspot:0, yHotspot?yHotspot:0]
hotspots[CursorStyle]:=hs
return hs
}
; Credits, Class MouseDelta: https://autohotkey.com/boards/viewtopic.php?f=19&t=10159
; This version is slightly modified.
Class MouseDelta {
State := 0
__New(callback){
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.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 := {usButtonFlags:12+A_PtrSize*2, usButtonData:14+A_PtrSize*2, x: (20+A_PtrSize*2), y: (24+A_PtrSize*2)}, uRawInput
; 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
if !(ThisMouse := NumGet(header, 8, "UPtr"))
return
; 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))
; Ensure we always report a number for an axis. Needed? - No it is overwritten by the numget.
dx := NumGet(&uRawInput, offsets.x, "Int")
dy := NumGet(&uRawInput, offsets.y, "Int")
usButtonFlags:=NumGet(&uRawInput, offsets.usButtonFlags, "UShort") ; Transition state of the mouse buttons.
usButtonData:=NumGet(&uRawInput, offsets.usButtonData, "Short") ; Contains mouse wheel delta, if usButtonFlags=RI_MOUSE_WHEEL=0x0400
this.Callback.(ThisMouse, dx, dy, usButtonFlags, usButtonData)
}
}
}
@evilC. Cool stuff (the band plugin and c#). And, the mouse hook can't tell which mouse generated the movement, as far as I know.