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.
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.
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.
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:
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.
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.
gremlin.base_classes.Functorwhich implements the execution logic of the action or container based on the contents of the data class.
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.
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
class. Parsing of action or container content is performed by the
corresponding data storage classes.