5. Technical Design

This section contains information related to implementation details of Joystick Gremlin. This is mainly intended for those that want to get a deeper understanding of the internals or extend Joystick Gremlin in some way. The following provides an overview of the various components that are part of Joystick Gremlin. The components can roughly be split into two categories: functional and user interface.

5.1 Input Handling

User inputs are captured using SDL2 for joystick devices while keyboard and mouse inputs are obtained by hooking from Windows' event queue. Both types of events are processed by gremlin.event_handler.EventListener which converts the input events into Qt signals which other interested parts of Gremlin can be connected to. This makes use of the Signal & Slots mechanism from Qt.

Event
event_type
The type of the event this represents.
identifier
The identifier of the event source.
hardware_id
Hardware ID assigned to the deivce that created the event.
windows_id
Index assigned by Windows to the device that created the event.
is_pressed
If the event represents a button or key the value is True for pressed and False for released state.
value
If the event represents an axis or hat the value contains the state.

5.2 Macro System

Each macro consists of a sequence of actions which are executed one after the other. Each macro is run in a separate thread in order to prevent Joystick Gremlin to become unresponsive.

In order to be provide some control over the execution of macros there exists a macro manager which handles the queueing and dispatching of macros. This permits some macros to be run in an exclusive mode, i.e. only the particular macro can be active and no other macro can run during the life time of the exclusive macro.

The various modes in which a macro can be executed, i.e. repeat, hold, and toggle, are implemented inside the manager which takes care of repeating the macro and terminating it when required.

5.3 Action and Container System

Actions and containers work in a similar fashion under the hood and as such share quite a few design traits. For each action or container there are three different classes, each with their own purpose:

  • A data storage class derived from gremlin.base_classes.AbstractAction or gremlin.base_classes.AbstractCtonainer. This class defines the values stored by the action or container and loads them from an XML profile and stores them to it as well.
  • A UI widget class derived from gremlin.ui.input_item.AbstractActionWidget or gremlin.ui.input_item.AbstractContainerWidget. This class presents the data of the instance to the user and allows modifying the contents, storing changes back into the data storage class.
  • A functor class derived from gremlin.base_classes.Functor which implements the execution logic of the action or container based on the contents of the data class.

Interface Definitions

The following presents the interfaces that are inherited from when creating a new action or container in order to provide the functionality of the three classes outlined above.

AbstractAction
from_xml(node)
Populates the instance based on the XML node's content.
to_xml()
Returns an XML node representing the instance's content.
icon()
Returns the path to the icon indicating this action.
requires_virtual_button()
Returns True if this instance requires a virtual button to be used.
AbstractContainer
_parse_xml(node)
Populates the instance based on the XML node's content.
_generate_xml()
Returns an XML node representing the instance's content.
_is_container_valid()
Returns whether or not the contents of the container result in a valid setup.
AbstractActionWidget
_create_ui()
Creates all the necessary UI elements to show action elements.
_populate_ui()
Populates the UI elements Creates all the necessary UI elements for condition elements.
AbstractContainerWidget
_create_action_ui()
Creates all the necessary UI elements to show action elements.
_create_condition_ui()
Creates all the necessary UI elements for condition elements.
_get_window_title()
Returns the string to use for the window title.
AbstractFunctor
process_event(event, value)
Executes the action's or container's logic for the provided event and value inputs.

5.4 XML Profile Storage

Each profile is stored in a single XML file. The contents of the XML file are used to populate the UI as well as generate Python code representing the profile. The file is parsed by the gremlin.profile.Profile class. Parsing of action or container content is performed by the corresponding data storage classes.