AHK Forum Thread (Code contributions, design input etc all very much welcome)
A C# DLL wrapper using SharpDX, to replace AHK's joystick functionality
Recordings of an AutoHotkey script loading the C# DLL and subscribing to inputs via DirectInput and XInput
I love AutoHotkey, but frankly it's joystick support sucks, for the following reasons:
- Up events for Buttons fire immediately after the down event.
In order to detect held buttons, you need aGetKeyState()
loop - Axes are not event-based.
You need to use a
GetKeyState()
loop - POVs are not event-based.
You guessed it, yet another
GetKeyState()
loop - The axis scale is 0..100.
At first this seems sensible and useful. Until you realize that centered sticks do not report as 50
Because the underlying range is 0..65535, with the mid-point being 32767, the conversion causes the mid-point to end up as 49.997
So deciding if you are at the mid-point (or, say, within a deadzone) becomes a case of floating-point comparisons and all their ambiguities. - AHK joystick support is implemented using the WinMM API.
So you only have access to 6 axes, 32 buttons, 1 POV
DirectX supports 8 axes, 128 buttons, 4 POVs.
So, for example, some of the axes on my Thrustmaster T.16000M / TWCS combo are unreadable using AHK.
Also, this means that all sticks must be referred to by ID number, and these IDs have a habit of changing. - Lexikos, the author of AutoHotkey_L, has expressed no interest in updating AHK's joystick support to something better.
Lexikos has released some really great stuff recently, one of which being his CLR Library which allows AHK to interface with C# code.
At the time of writing, I am starting a career in automated test using C#, so I thought it would be a good idea to work with it in my spare time.
There is also a DirectX wrapper for C# called SharpDX which allows easy reading of sticks in C#, so over the course of a weeked I sat down and knocked up a POC for reading sticks in AHK via DirectX.
- Copy
JoystickWrapper.ahk
and all DLL files from theJoystickWrapper
folder
Place them in the same folder as your script - Include the JoystickWrapper library
#include JoystickWrapper.ahk
- Instantiate AHK
JoystickWrapper
class, passing it the path to the DLL
jw := new JoystickWrapper("JoystickWrapper.dll")
- You may now subscribe to inputs, eg using one of the API commands, eg:
jw.SubscribeAxis(<some stick guid>, 1, Func("SomeFunc"))
The subscription commands share some common parameters:
guid - A GUID for a stick.
This uniquely identifies a stick on your system, and should be constant across runs.
You can find GUIDs using one of the Query Commands below.
If you wish to have multiple subscriptions on the same input, you can call subscribe commands multiple times...
... But each time, pass a different Subscriber ID. It can be any string.
If omitted, "0" is used.
jw.SubscribeAxis(<stick guid>, <axis id>, <callback>[ ,<subscriber id>])
jw.UnSubscribeAxis(<stick guid>, <axis id>, [ ,<subscriber id>])
axis id = 1 - 8
jw.SubscribeButton(<stick guid>, <button id>, <callback>[ ,<subscriber id>])
jw.UnSubscribeButton(<stick guid>, <button id>, [ ,<subscriber id>])
button id = 1 - 128
jw.SubscribePov(<stick guid>, <pov id>, <callback>[ ,<subscriber id>])
jw.UnSubscribePov(<stick guid>, <pov id>, [ ,<subscriber id>])
pov id = 1 - 4
jw.SubscribePovDirection(<stick guid>, <pov id>, <pov direction>,<callback>[ ,<subscriber id>])
jw.UnSubscribePovDirection(<stick guid>, <pov id>, <pov direction> ,[ ,<subscriber id>])
pov id = 1 - 4
pov direction =
1 : Up / North
2: Right / East
3: Down / South
4: Left / West
Each direction has a tolerance of 90 degrees, so eg up + right will trigger both up and right.
jw.GetDevices()
Returns a list of devices, their names, guids, and capabilities.
guid := jw.GetAnyDeviceGuid()
jw.SuscribeAxis(guid, 1)
Gets any guid that it can find. Useful to make demo scripts simple
striked items are done
100% Event-driven reporting of stick states (From the perspective of the AHK code)
ie you pass the wrapper a callback and a stick GUID, and it fires the callback only when it changesFull 8 axis, 128 button, 4 POV supportEither native 0..65535 reporting, or a rescaled range that does not sit at an odd valueAbility to get names of sticks
Ability to subscribe to individual axesAbility to subscribe to individual buttonsAbility to subscribe to individual POVsAbility to subscribe to individual POV directions like UCR currently does
- Integration with Nefarius' upcoming HidGuardian / HidCerberus system
This would allow hiding of the phyical stick from any other application than JoystickWrapper
In this manner, true "remapping" of sticks could be achieved, in combination with Shaul's vJoy or Nefarius' ViGEm