The success of UJR surprised me – it seemed to plug a gap in the market and seemed to have rapidly become the go-to tool to fix numerous joystick woes.
This inspired me to aim higher.
What if you could build something like UJR, but modular, and working with many kinds of inputs. Something where end-users could add new features themselves?
Towards the end of 2015, I started examining what would be required to achieve such a goal.
The limitations of what I could do in AHK presented the following hurdles:
I needed to be able to dynamically include a script which derived from a class in the main (running) code.
I needed to be able to spin up multiple copies of AHK (To detect input, and allow multiple profiles, and rapid profile switching)
I needed resizable, scrollable, dynamic GUIs.
Luckily, there is a fork of AutoHotkey called AutoHotkey_H which does the first two, and the author is a rather amenable chap, and we collaborated to implement dynamic scrollable GUIs in AHK_H’s C++ source code.
So with all the tools in place, I decided upon the following broad architecture:
From an end-user’s perspective, everything boils down ultimately to a plugin.
A plugin generally does one single unit of work – eg “Map X to Y”.
A number of basic plugins are provided (Map Button to Button, Axis To Axis, Button and Axis to each other etc) but as mentioned, it is intended to be as simple as possible to create a new plugin.
In order to do so, you create a plugin file, and define it’s inputs, outputs, and configuration options. This is done through a collection of custom GuiControls.
Input and Output GuiControls are custom GuiControls which allow the end-user, at run time, to select what inputs and outputs to use.
When you add an Input Control, you can pass a callback to be called when the input changes state.
When you add an Output Control, you are given an object which you can call methods on to set the state of the output which the end-user selected.
Regular Controls (EditBoxes etc) can also be added, and the values the user enters into these will automatically be remembered between runs, and their value is always easily accessible by the plugin code.
This makes it easy for the end-user to add the configuration options that they need, leaving them with only the logic that connects them to worry about.
Plugins can be organised into Profiles, and there are plugins which allow you to change profile using inputs, so you can make “Shift States”.