#Persistent #NoEnv ; ----- XIX - xtravar's gamepad autohotkey script (XInput and WinMM) ; Version: 2011.3.31 ; Author: xtravar@yahoo.com ; License: GPLv2 (http://www.gnu.org/licenses/gpl-2.0.html) ; Requires: Autohotkey - perhaps autohotkey_l? http://www.autohotkey.net ; Notes: I'm new at autohotkey so this probably isn't perfect. ; This now allows WinMM in addition to XInput gamepads. ; It automatically attempts to map the buttons, or read a config INI file for this mode. ; ; See my Dragon Age 2 script for examples. ; Disclaimer: There is no warranty. ; ------------------------------------------------------------------------ ; ------------------------------------------------ ; EXPLANATION OF WHAT'S GOING ON ; ------------------------------------------------ ; This script allows you to listen for XInput events with callback functions. ; You may experience performance issues due to the architecture. If so, do a lower-level hook. ; The 'state' parameter is either 0 - up, 1 - hold, 2 - up ; All the button callbacks start with XIX_Button ; XIX_ButtonDPadUp(state) ; XIX_ButtonDPadDown(state) ; XIX_ButtonDPadLeft(state) ; XIX_ButtonDPadRight(state) ; XIX_ButtonStart(state) ; XIX_ButtonBack(state) ; XIX_ButtonLeftThumb(state) ; XIX_ButtonRightThumb(state) ; XIX_ButtonLeftShoulder(state) ; XIX_ButtonRightShoulder(state) ; XIX_ButtonA(state) ; XIX_ButtonX(state) ; XIX_ButtonY(state) ; XIX_ButtonB(state) ; And here are the axis callbacks (triggers are axis) ; XIX_LeftTrigger(state,value) ; XIX_RightTrigger(state,value) ; XIX_LeftThumb(state,x,y) ; XIX_RightThumb(state,x,y) ; ; To begin, call XIX_BeginWatching(poll interval) ; To end, call XIX_EndWatching() _XIX_Init() XIX_BeginWatching(interval) { global local label, statesize if(_xixAPI != "XINPUT" && _xixAPI != "WINMM") { return 0 } _xixState := 0 _xixButtons := 0 _xixLT := 0 _xixRT := 0 _xixLX := 0 _xixLY := 0 _xixRX := 0 _xixRY := 0 _xixLastButtons := 0 _xixLastLT := 0 _xixLastRT := 0 _xixLastLX := 0 _xixLastLY := 0 _xixLastRX := 0 _xixLastRY := 0 if(_xixAPI == "XINPUT") { label := "_XIX_WatchState" statesize := 16 } else { label := "_XIX_WatchWinMMState" statesize := 52 } VarSetCapacity(_xixState, statesize) SetTimer, %label%, %interval% return 1 } XIX_EndWatching() { global if(_xixAPI == "XINPUT") { SetTimer, _XIX_WatchState, Off } else { SetTimer, _XIX_WatchWinMMState, Off } } __XIXWatchFunction() { global local jpButtons, jpPov, button, name, val, func _XIX_WatchWinMMState: NumPut(52, _xixState, 0, "UInt") NumPut(JOY_RETURNALL, _xixState, 4, "UInt") _xixError := DllCall("winmm\joyGetPosEx", "ptr", 0, "ptr", &_xixState) if(_xixError) { return } _xixLX := _XILegacyAxis(NumGet(_xixState, 8 + _xixWM_LX * 4, "UInt")) _xixLY := -_XILegacyAxis(NumGet(_xixState, 8 + _xixWM_LY * 4, "UInt")) _xixRX := _XILegacyAxis(NumGet(_xixState, 8 + _xixWM_RX * 4, "UInt")) _xixRY := -_XILegacyAxis(NumGet(_xixState, 8 + _xixWM_RY * 4, "UInt")) jpButtons := NumGet(_xixState, 32, "UInt") if(_xixWM_TX < 0) { _xixLT := jpButtons & _xixWM_LT ? 255 : 0 _xixRT := jpButtons & _xixWM_RT ? 255 : 0 } else { val := _XILegacyAxis(NumGet(_xixState, 8 + _xixWM_TX * 4, "UInt")) / 128 if(val > 0) { _xixLT := val _xixRT := 0 } else { _xixLT := 0 _xixRT := -val } } button := 1 _xixButtons := 0 while(button <= XINPUT_GAMEPAD_Y) { if(_xixWM_%button% & jpButtons) { _xixButtons := _xixButtons | button } button <<= 1 } jpPOV := NumGet(_xixState, 40, "UInt") ; center = 65535 ; UP = 0, RIGHT = 9000, DOWN = 18000, LEFT = 27000 if(jpPOV == 65535 ) { } else if(jpPOV < 2250) { _xixButtons |= XINPUT_GAMEPAD_DPAD_UP } else if(jpPOV < 6750) { _xixButtons |= XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_DPAD_RIGHT } else if(jpPOV < 11250) { _xixButtons |= XINPUT_GAMEPAD_DPAD_RIGHT } else if(jpPOV < 15750) { _xixButtons |= XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_DPAD_DOWN } else if(jpPOV < 20250) { _xixButtons |= XINPUT_GAMEPAD_DPAD_DOWN } else if(jpPOV < 24750) { _xixButtons |= XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_DPAD_LEFT } else if(jpPOV < 29250) { _xixButtons |= XINPUT_GAMEPAD_DPAD_LEFT } else if(jpPOV < 33750) { _xixButtons |= XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_UP } else { _xixButtons |= XINPUT_GAMEPAD_DPAD_UP } goto _XIProcess _XIX_WatchState: _xixError := DllCall("xinput1_3\XInputGetState", "ptr", 0, "ptr", &_xixState) if(_xixError) { return } _xixButtons := NumGet(_xixState, 4, "UShort") _xixLT := NumGet(_xixState, 4 + 2, "UChar") _xixRT := NumGet(_xixState, 4 + 3, "UChar") _xixLX := NumGet(_xixState, 4 + 4, "Short") _xixLY := NumGet(_xixState, 4 + 6, "Short") _xixRX := NumGet(_xixState, 4 + 8, "Short") _xixRY := NumGet(_xixState, 4 + 10, "Short") _XIProcess: if(_xixLT < XINPUT_GAMEPAD_TRIGGER_THRESHOLD) { _xixLT := 0 } if(_xixRT < XINPUT_GAMEPAD_TRIGGER_THRESHOLD) { _xixRT := 0 } if(_xixLX < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE && _xixLX > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { _xixLX := 0 } if(_xixLY < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE && _xixLY > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { _xixLY := 0 } if(_xixRX < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE && _xixRX > -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { _xixRX := 0 } if(_xixRY < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE && _xixRY > -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { _xixRY := 0 } button := 1 while(button <= XINPUT_GAMEPAD_Y) { if(_xixButtons & button) { name := _xixName%button% func = XIX_Button%name% if(_xixLastButtons & button) { %func%(1) } else { %func%(0) } } else if(_xixLastButtons & button) { name := _xixName%button% func = XIX_Button%name% %func%(2) } button := button << 1 } if(_xixLT) { if(!_xixLastLT) { XIX_LeftTrigger(0,_xixLT) } else { XIX_LeftTrigger(1,_xixLT) } } else if(_xixLastLT) { XIX_LeftTrigger(2,_xixLT) } if(_xixRT) { if(!_xixLastRT) { XIX_RightTrigger(0,_xixRT) } else { XIX_RightTrigger(1,_xixRT) } } else if(_xixLastRT) { XIX_RightTrigger(2,_xixRT) } if(_xixLX + _xixLY != 0) { if(_xixLastLX + _xixLastLY == 0) { XIX_LeftThumb(0, _xixLX, _xixLY) } else { XIX_LeftThumb(1, _xixLX, _xixLY) } } else if(_xixLastLX + _xixLastLY != 0) { XIX_LeftThumb(2, _xixLX, _xixLY) } if(_xixRX + _xixRY != 0) { if(_xixLastRX + _xixLastRY == 0) { XIX_RightThumb(0, _xixRX, _xixRY) } else { XIX_RightThumb(1, _xixRX, _xixRY) } } else if(_xixLastRX + _xixLastRY != 0) { XIX_RightThumb(2, _xixRX, _xixRY) } _xixLastButtons := _xixButtons _xixLastLT := _xixLT _xixLastRT := _xixRT _xixLastLX := _xixLX _xixLastLY := _xixLY _xixLastRX := _xixRX _xixLastRY := _xixRY return } _XIX_Init() { global _xixAPI := "" _XIX_XINPUT_Init() _XIX_WINMM_Init() XIX_Detect() XIX_ReadINI() } XIX_DebugString() { global local retval, button, name retval := "" retval .= "Left Trigger: " . _xixLT . "`n" retval .= "Right Trigger: " . _xixRT . "`n" retval .= "Left X Axis: " . _xixLX . "`n" retval .= "Left Y Axis: " . _xixLY . "`n" retval .= "Right X Axis: " . _xixRX . "`n" retval .= "Right Y Axis: " . _xixRY . "`n" retval .= "Buttons:" button := 1 while(button <= XINPUT_GAMEPAD_Y) { if(_xixButtons & button) { name := _xixName%button% retval .= "`n " . name } button := button << 1 } return retval } _XIX_XINPUT_Init() { global XINPUT_GAMEPAD_DPAD_UP = 0x0001 XINPUT_GAMEPAD_DPAD_DOWN = 0x0002 XINPUT_GAMEPAD_DPAD_LEFT = 0x0004 XINPUT_GAMEPAD_DPAD_RIGHT = 0x0008 XINPUT_GAMEPAD_START = 0x0010 XINPUT_GAMEPAD_BACK = 0x0020 XINPUT_GAMEPAD_LEFT_THUMB = 0x0040 XINPUT_GAMEPAD_RIGHT_THUMB = 0x0080 XINPUT_GAMEPAD_LEFT_SHOULDER = 0x0100 XINPUT_GAMEPAD_RIGHT_SHOULDER = 0x0200 XINPUT_GAMEPAD_A = 0x1000 XINPUT_GAMEPAD_B = 0x2000 XINPUT_GAMEPAD_X = 0x4000 XINPUT_GAMEPAD_Y = 0x8000 _xixName1 := "DPadUp" _xixName2 := "DPadDown" _xixName4 := "DPadLeft" _xixName8 := "DPadRight" _xixName16 := "Start" _xixName32 := "Back" _xixName64 := "LeftThumb" _xixName128 := "RightThumb" _xixName256 := "LeftShoulder" _xixName512 := "RightShoulder" _xixName4096 := "A" _xixName8192 := "B" _xixName16384 := "X" _xixName32768 := "Y" XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE = 7849 XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE = 8689 XINPUT_GAMEPAD_TRIGGER_THRESHOLD = 30 } _XIX_WINMM_Init() { global JOY_BUTTON1 := 0x0001 JOY_BUTTON2 := 0x0002 JOY_BUTTON3 := 0x0004 JOY_BUTTON4 := 0x0008 JOY_BUTTON5 := 0x00000010 JOY_BUTTON6 := 0x00000020 JOY_BUTTON7 := 0x00000040 JOY_BUTTON8 := 0x00000080 JOY_BUTTON9 := 0x00000100 JOY_BUTTON10 := 0x00000200 JOY_BUTTON11 := 0x00000400 JOY_BUTTON12 := 0x00000800 JOY_BUTTON13 := 0x00001000 JOY_BUTTON14 := 0x00002000 JOY_BUTTON15 := 0x00004000 JOY_BUTTON16 := 0x00008000 JOY_BUTTON17 := 0x00010000 JOY_BUTTON18 := 0x00020000 JOY_BUTTON19 := 0x00040000 JOY_BUTTON20 := 0x00080000 JOY_BUTTON21 := 0x00100000 JOY_BUTTON22 := 0x00200000 JOY_BUTTON23 := 0x00400000 JOY_BUTTON24 := 0x00800000 JOY_BUTTON25 := 0x01000000 JOY_BUTTON26 := 0x02000000 JOY_BUTTON27 := 0x04000000 JOY_BUTTON28 := 0x08000000 JOY_BUTTON29 := 0x10000000 JOY_BUTTON30 := 0x20000000 JOY_BUTTON31 := 0x40000000 JOY_BUTTON32 := 0x80000000 JOY_RETURNX := 0x00000001 JOY_RETURNY := 0x00000002 JOY_RETURNZ := 0x00000004 JOY_RETURNR := 0x00000008 JOY_RETURNU := 0x00000010 JOY_RETURNV := 0x00000020 JOY_RETURNPOV := 0x00000040 JOY_RETURNBUTTONS := 0x00000080 JOY_RETURNALL := (JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNR | JOY_RETURNU | JOY_RETURNV | JOY_RETURNPOV | JOY_RETURNBUTTONS) } _XILegacyAxis(value) { if(value < 0) { return 0 } value := (value - 32767) return value } _RI(byref var, byref pos, sz, signed=0) { if(sz == 1) { type := "Char" } else if(sz == 2) { type := "Short" } else if(sz == 4) { type := "Int" } else if(sz == 8) { type := "Int64" } type := signed ? type : "U" . type retval := NumGet(var, pos, type) pos += sz return retval } XIX_Detect() { global _xixAPI if(_XINPUT_Detect()) { _xixAPI := "XINPUT" } else if(_WINMM_Detect(buttons, axes)) { if(axes >= 5 && buttons >= 10) { _xixAPI := "WINMM" } else if(axes == 4 && buttons >= 12) { _xixAPI := "WINMM" } else { msgbox, Your gamepad does not seem to match the requirements.`nYou need 4 axes and 12 buttons (or 3 axes and 14 buttons for non-analog triggers). exitapp } } else { MsgBox, No gamepad has been detected. Please connect one and restart. exitapp } if(_xixAPI == "WINMM") { ; MsgBox, 4, , You are running in compatibility mode. The buttons may be incorrect. ; Would you like to run the config utility? ;IfMsgBox, Yes ;{ ; XIX_ConfigUtility() ;} } } _WINMM_Detect(byref numButtons,byref numAxes) { VarSetCapacity(caps, 728) err := DllCall("winmm\joyGetDevCaps", "ptr", 0, "ptr", &caps, "uint", 728) if(err != 0) { return 0 } p := 0 mid := _RI(caps, p, 2) pid := _RI(caps, p, 2) p += 64 ; PName Xmin := _RI(caps, p, 4) Xmax := _RI(caps, p, 4) Ymin := _RI(caps, p, 4) Ymax := _RI(caps, p, 4) Zmin := _RI(caps, p, 4) Zmax := _RI(caps, p, 4) numButtons := _RI(caps, p, 4) periodMin := _RI(caps, p, 4) periodMax := _RI(caps, p, 4) Rmin := _RI(caps, p, 4) Rmax := _RI(caps, p, 4) Umin := _RI(caps, p, 4) Umax := _RI(caps, p, 4) Vmin := _RI(caps, p, 4) Vmax := _RI(caps, p, 4) wCaps := _RI(caps, p, 4) maxAxes := _RI(caps, p, 4) numAxes := _RI(caps, p, 4) maxButtons := _RI(caps, p, 4) p += 64 ; RegKey p += 520 ; OEMVxD return 1 } _XINPUT_Detect() { ; cap size 4 ; gp size 12 ; vibration size 4 VarSetCapacity(caps, 20) err := DllCall("xinput1_3\XInputGetCapabilities", "ptr", 0, "ptr", 0, "ptr", &caps) if(err != 0) { return 0 } ; someday i'll care type := _RI(caps, p, 1) subtype := _RI(caps, p, 1) flags := _RI(caps, p, 2) buttons := _RI(caps, p, 2) leftTrigger := _RI(caps, p, 1) rightTrigger := _RI(caps, p, 1) thumbLX := _RI(caps, p, 2, 1) thumbLY := _RI(caps, p, 2, 1) thumbRX := _RI(caps, p, 2, 1) thumbRY := _RI(caps, p, 2, 1) vibSpeedL := _RI(caps, p, 2, 1) vibSpeedR := _RI(caps, p, 2, 1) return 1 } _XIX_WR(file, name, def) { IniRead, var, %file%, XIX_WinMM, %name%, %def% return var } _WRA(file, name, def) { var := _XIX_WR(file, name, def) if(var > 0 && var <= 5) { var -= 1 } else { var := -1 } return var } _WRB(file, name, def) { var := _XIX_WR(file, name, def) if(var > 0) { var := JOY_BUTTON%var% } else { var := "" } return var } XIX_ReadINI(file = "") { global local mode, numButtons, numAxes if(file == "") { SplitPath, A_ScriptName, , , , file file .= ".ini" } _WINMM_Detect(numButtons, numAxes) mode := numAxes == 5 _xixWM_LX := _WRA(file, "LeftXAxis", mode ? 1 : 1) _xixWM_LY := _WRA(file, "LeftYAxis", mode ? 2 : 2) _xixWM_RX := _WRA(file, "RightXAxis", mode ? 5 : 3) _xixWM_RY := _WRA(file, "RightYAxis", mode ? 4 : 4) _xixWM_TX := _WRA(file, "TriggerAxis", mode ? 3 : 0) _xixWM_4096 := _WRB(file, "ButtonA", mode ? 1 : 2) _xixWM_8192 := _WRB(file, "ButtonB", mode ? 2 : 3) _xixWM_16384 := _WRB(file, "ButtonX", mode ? 3 : 1) _xixWM_32768 := _WRB(file, "ButtonY", mode ? 4 : 4) _xixWM_64 := _WRB(file, "ButtonLS", mode ? 9 : 11) _xixWM_128 := _WRB(file, "ButtonRS", mode ? 10 : 12) _xixWM_256 := _WRB(file, "ButtonLB", mode ? 5 : 5) _xixWM_512 := _WRB(file, "ButtonRB", mode ? 6 : 6) _xixWM_LT := _WRB(file, "ButtonLT", mode ? -1 : 7) _xixWM_RT := _WRB(file, "ButtonRT", mode ? -1 : 8) _xixWM_32 := _WRB(file, "ButtonBack", mode ? 7 : 9) _xixWM_16 := _WRB(file, "ButtonStart", mode ? 8 : 10) }