While common configuration tasks can be performed directly via the UI, more advanced and specialised configurations may either only be possible or much simpler by using a user plugin. Each plugin is a simple Python script which defines the variables a user can configure via the UI and the functions (callbacks) triggered in reaction to (configured) inputs being used. Since the functions are written in Python there is no limit as to what can be expressed. The following section assumes some basic familiarity with Python.
We start with a general overview of the layout of a user plugin in Section 4.1 which is followed by an quick overview of the API in Section 4.2 followed by the description of the decorator based callback system in Section 4.3 with periodic function callbacks described in Section 4.4. Some words on how to debug user plugins is provided in Section 4.5. Finally, Section 4.6 provides a few practical examples.
Joystick Gremlin uses callbacks, i.e. functions that are executed in reaction to user inputs such as key presses or axis motion. These callbacks have access to some convenience functions which allow accessing and controlling commonly used parts of the system, such as setting the value of vJoy devices or retrieving keyboard and joystick states. Combining these readily available functions with custom code allows the implementation of varied functionality.
In order to make user plugins reusable and convenient to use a set of classes exist which allow setting them via the UI, thus allowing a user to customize the plugins directly from the UI. These variable classes allow configuration of commonly used types such as modes, inputs, as well as numerical values.
The general structure of a callback is as follows:
The event
parameter contains information about the
event that triggered the execution of the function. Each event is of
type gremlin.event_handler.Event
and contains the
following data:
event_type
identifier
device_guid
is_pressed
value
raw_value
From this list the only values that are typically of interest are
the is_pressed
and value
entries depending
on the input type.
The following describes the API of the optional variables exposed via the decorator plugin framework. The plugins provide access to commonly used information by simply adding a properly named parameter to the callback function.
These parameters must be listed after the event parameter in the case of user input callbacks.
Any decorated function that has a parameter named vjoy
in its parameter list will have access to all vJoy devices.
Accessing a specific VJoy
instance is done by indexing
the vjoy
object. This object then allows setting the
state of inputs by indexing the member variables axis
,
button
, and hat
. Indices of buttons and
hats start at 1. For axes the indices correspond to the axis index
as defined by the device. The following demonstrates typical usage:
Any decorated function that has a parameter named joy
in its parameter list will have access to all joystick devices via
that variable.
Accessing a specific joystick In order to access a specific joystick its system id needs to be known. Using the device's system id as index the joystick can be accessed by:
Reading axis value To read the current value of a joystick axis both the index of the axis as well as the system id of the joystick, starting with 1, are needed, with these the axis value is obtained as:
Reading button state To read the current state of a button both the joystick's system id as well as index of the button, starting at 1, are needed. The following then reads the button state:
Reading hat position To read the current position of a hat both the joystick's system id and hat index, starting at 1, are needed. The position of the hat is reported as a \((x, y)\) tuple \(x, y \in \left\{-1, 0, 1\right\}\). A \(x\) value of 1 is right and -1 left while a value of 1 for \(y\) means up and -1 down. A value of \(0\) represents a centred position. The value is read as follows:
Any decorated function that has a parameter named
keyboard
in its parameter list will have access to the
state of all keyboard keys.
Reading key state
To read the key state the string representation of the key or the
gremlin.macro.Key
instance corresponding to the key is
needed. Both can be found in the gremlin.macro
module.
Reading the state is then done as follows:
Callbacks reacting to user inputs are created by decorating functions using
specific decorators. Here are two useful links if you're not familiar with
decorators, official
PEP and an exhaustive
StackOverflow answer There are two types of decorators,
one for joysticks and one for the keyboard. Joystick decorators are created
for specific devices using the
gremlin.input_devices.JoystickDecorator
class as follows:
The value of device_guid
is the unique dentifier of the
device. An object created in this way has three decorators
customised for the given joystick and mode, which can be used as
follows:
The keyboard decorator can be used directly as follows:
Where key name
can be either a string representation
of the key's name as or an instance of gremlin.marco.Key
which
are both defined in the gremlin.macro
module.
The event
parameter of the decorated function is always
required and contains the state of the input that triggered the callback,
the contents of the variable are described in Section
4.1.
In some situations a function needs to be executed at regular intervals. This is facilitated by a decorator that ensures that the function is run at a specified interval while Joystick Gremlin is active.
The decorator takes a single argument that indicates the interval, i.e. the
duration, between executions of the function in seconds. The callback
function can use the same plugin system as the user input callbacks to gain
access to device information, e.g. vjoy
, joy
, and
keyboard
. A generic example of periodic function callback is
shown below.
In order to allow user plugins to be configured by users via the UI several types of variable classes exist, that are automatically extracted from the plugin and presented to the user for customization.
The following variable types exist and will be explained in more detail below.
IntegerVariable
FloatVariable
BoolVariable
StringVariable
ModeVariable
VirtualInputVariable
PhysicalInputVariable
This variable can hold any single integer value and presents the user with a field which allows entering of values. The variable also allows the specification of limits for valid values.
label
description
initial_value
min_value
max_value
This variable can hold any single floating point value and presents the user with a field which allows entering of values. The variable also allows the specification of limits for valid values.
label
description
initial_value
min_value
max_value
This variable can hold any single boolean value and presents the user with a checkbox. This can be used to turn features of a plugin on and off.
label
description
initial_value
This variable can hold any string and presents the user with a text input field.
label
description
initial_value
This variable holds the name of one mode present in this profile. The user is presented with a dropdown list containing all modes that exist.
label
description
This variable holds one specific vJoy input selection. The user is presented with the typical vJoy dropdown boxes.
label
description
valid_types
gremlin.common.InputType
valuesThis variable holds one specific physical input device selection. The user can press a button at which point Gremlin will record the physical input being activated next.
label
description
valid_types
gremlin.common.InputType
valuesThis example plugin lets a user specify four physical joystick buttons and map them to a single virtual hat output.
To facilitate the debugging of custom modules without setting up the
source code of Joystick Gremlin in an IDE the logging function
gremlin.util.log()
can be used. This stores the
provided text to the user log file which can be viewed directly in
Joystick Gremlin via the Tools -> Log display option.
For more detailed debugging Joystick Gremlin needs to be run from within an IDE by getting the development environment setup. While this provides the best debugging experience it also involves the most work, thus for simple tasks the logging approach may be preferable.
In the following a few examples of custom modules are shown. They provide an illustration of some of the things that can be achieved thanks to the combination of Joystick Gremlin provided functions and custom Python code.
This script allows the user to control an analogue throttle in 1/3rd increments using the 1, 2, 3, and 4 number keys.
This script configures a response curve which provides more control around the centre position and uses it for the X and Y axis of the joystick.
This script presents a few different ways of using mode switching functionalities. The first callback switches to the Radio mode while the button is being held down and switches back to the previous mode once the button is released. The next callback cycles through the Global, Radio, and Landing modes with each button press. The last callback switches directly to the Global mode when the button is pressed.
This script switches to a lower sensitivity curve when any of the weapon groups are being fired and switches back to the default profile once no weapon is being fired any more. This is similar to the "sniper mode" that some gaming mice have, which drops the DPI setting at the press of a button. In this instance pressing the trigger automatically enables and disables this by switching the used response curve to one which halves the maximum response provided by the joystick at maximum deflection.