Open main menu

Difference between revisions of "Keymapper"

90 bytes added ,  13:10, 6 March 2022
syntax fixes and update sample code
m (wording)
(syntax fixes and update sample code)
 
Line 2: Line 2:


== Basics ==
== Basics ==
* Backends define what input devices are available and with which characteristics (number of buttons, button names..).
* Backends define what input devices are available and with which characteristics (number of buttons, button names, etc.).
* Engines define what kind of events they expect for input handing (mouse button presses, keyboard shortcuts, custom actions..) through keymaps.
* Engines define what kind of events they expect for input handing (mouse button presses, keyboard shortcuts, custom actions..) through keymaps.
* The keymapper maps the events produced by the backend in use to those that are expected by the current engine based on default bindings and user preferences.
* The keymapper maps the events produced by the backend to those that are expected by the engine based on default bindings and user preferences.


The keymapper is not limited to mapping keyboard keys, it is meant to remap any kind of event. At the moment, mouse buttons, keyboard keys and joystick/gamepad buttons events are supported.
The keymapper is not limited to mapping keyboard keys, it is meant to remap any kind of event. At the moment, mouse buttons, keyboard keys and joystick/gamepad buttons events are supported.


== For engine developers ==
== For engine developers ==
* Keymaps are defined by implementing '''MetaEngine::initKeymaps'''. Keymaps can be seen as a contract for what the engine expects in terms of events for input handing. Key combinations,.. that are not defined by keymaps cannot be used on platforms without a mouse and keyboard. For engines that don't have a keymap, the default game keymap defined in ''metaengine.cpp'' is used. Events that don't match a keymap are sent to the engine unprocessed, as they are produced by the backend.
* Keymaps are defined by implementing '''MetaEngine::initKeymaps()'''. Keymaps can be seen as a contract for what the engine expects in terms of events for input handing. Key combinations that are not defined by keymaps cannot be used on platforms without a mouse and keyboard. For engines that don't have a keymap, the default game keymap defined in ''metaengine.cpp'' is used. Events that don't match a keymap are sent to the engine unprocessed, as they are produced by the backend.
* A keymap is made of actions. Using the remap dialog users can associate these actions to events generated by the backends. When defining actions engine developers decide what happens when the action is executed. There are two options (hybrids are possible):
* A keymap is made of actions. Using the remap dialog users can associate these actions to events generated by the backend. When defining actions, engine developers decide what happens when the action is executed. There are two options (hybrids are possible):
** Generate events that match what the existing event handling the engine expects. That's easy to do. Just declare the keymap and it's done. However there are issues with this approach. An user who changes the default binding for an action does not expect the original binding to still work. Depending on how the engine is structured this also may cause key handling to be defined twice. Once in the keymap, and once in the event loop.
** Generate events that match what the existing event handling the engine expects. That's easy to do. Just declare the keymap and it's done. However there are issues with this approach. A user who changes the default binding for an action does not expect the original binding to still work. Depending on how the engine is structured this also may cause key handling to be defined twice: Once in the keymap, and once in the event loop.
** Generate custom actions events with meaning private to the engine (''EVENT_CUSTOM_ENGINE_ACTION_{START,END}'').
** Generate custom actions events with meaning private to the engine (''EVENT_CUSTOM_ENGINE_ACTION_{START,END}'').


Line 18: Line 18:


<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
Common::Keymap *MyEngine::initKeymap(const char *target) {
Common::KeymapArray MyMetaEngine::initKeymaps(const char *target) const {
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "my-engine");
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "my-engine", "My game keymap");


Action *act;
Action *act;
Line 35: Line 35:
engineKeyMap->addAction(act);
engineKeyMap->addAction(act);


return engineKeyMap;
return Keymap::arrayOf(engineKeyMap);
}
}


Line 65: Line 65:
}
}


Common::Keymap *MyEngine::initKeymap(const char *target) {
Common::KeymapArray MyMetaEngine::initKeymaps(const char *target) const {
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "my-engine");
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "my-engine", "My game keymap");


Action *act;
Action *act;
Line 82: Line 82:
engineKeyMap->addAction(act);
engineKeyMap->addAction(act);


return engineKeyMap;
return Keymap::arrayOf(engineKeyMap);
}
}


Line 103: Line 103:
</syntaxhighlight>
</syntaxhighlight>


: Engines with engine-driven input handing should prefer using custom action events as this approach it is more powerful. Engines where input handling is script-driven probably have no choice but to use the first option. It's up to the engine developer to choose which is the best in each specific context.
: Engines with engine-driven input handing should prefer using custom action events as this approach is more powerful. Engines where input handling is script-driven probably have no choice but to use the first option. It's up to the engine developer to choose which is the best in each specific context.


* Keymaps can be situational, thus engines may define multiple keymaps. Keymaps can be enabled / disabled at any time to select which actions are relevant for the current situation. For example for a RPG game there may be one keymap for the overworld, one for combat and one for shops.  
* Keymaps can be situational, thus engines may define multiple keymaps. Keymaps can be enabled / disabled at any time to select which actions are relevant for the current situation. For example for a RPG game there may be one keymap for the overworld, one for combat and one for shops.  
Line 110: Line 110:


== For backend developers ==
== For backend developers ==
* To define the available input devices, a backend needs to implement '''OSystem::getHardwareInputSet'''. The declared input devices must match the events that are produced by the backend in reaction to user input. It's best not to have any hardcoded button mapping in the backend. For example in the case of a game console with solely a gamepad as input device, when the player presses a button, the backend should send ''EVENT_JOYBUTTON_{DOWN,UP}'' events, not ''EVENT_LBUTTON{DOWN,UP}''. The keymapper will do the transformation from joystick events to mouse events if necessary.
* To define the available input devices, a backend needs to implement '''OSystem::getHardwareInputSet()'''. The declared input devices must match the events that are produced by the backend in reaction to user input. It's best not to have any hardcoded button mapping in the backend. For example in the case of a game console with solely a gamepad as input device, when the player presses a button, the backend should send ''EVENT_JOYBUTTON_{DOWN,UP}'' events, not ''EVENT_LBUTTON{DOWN,UP}''. The keymapper will do the transformation from joystick events to mouse events if necessary.
* Backends can define (or replace) default bindings for any keymap. To do so, implement '''OSystem::getKeymapperDefaultBindings'''. This can be used to make room in the default keymaps for backend specific actions, or to provide better defaults for the platform than those defined in the keymap.
* Backends can define (or replace) default bindings for any keymap. To do so, implement '''OSystem::getKeymapperDefaultBindings()'''. This can be used to make room in the default keymaps for backend specific actions, or to provide better defaults for the platform than those defined in the keymap.
* Backends can define keymaps to allow players to remap the backend specific features. To do so, implement '''OSystem::getGlobalKeymaps'''. When implementing backend specific keymaps, it's best to request ''EVENT_CUSTOM_BACKEND_ACTION_{START,END}'' events in response to the user input. During event handling, ''Event::customType'' is used to recognize the action that needs to be executed.
* Backends can define keymaps to allow players to remap the backend specific features. To do so, implement '''OSystem::getGlobalKeymaps()'''. When implementing backend specific keymaps, it's best to request ''EVENT_CUSTOM_BACKEND_ACTION_{START,END}'' events in response to the user input. During event handling, ''Event::customType'' is used to recognize the action that needs to be executed.


== TODO ==
== TODO ==
* Consider adding support for joystick button combos
* Consider adding support for joystick button combos
* Rework the way keyboard input is mapped to actions. Match only on the keycode and then filter on the modifiers to handle partial matches. Besides keep a list of currently started actions. When receiving a key up event, check if an end event needs to be emitted for one of the started actions ignoring the modifiers (to handle the case where the user stops pressing the modifier keys first). Additionally filter the emitted actions based on the list of currently started actions to prevent an action from being started twice at the same time by two different inputs.
* Rework the way keyboard input is mapped to actions. Match only on the keycode and then filter on the modifiers to handle partial matches. Besides keep a list of currently started actions. When receiving a key up event, check if an end event needs to be emitted for one of the started actions ignoring the modifiers (to handle the case where the user stops pressing the modifier keys first). Additionally filter the emitted actions based on the list of currently started actions to prevent an action from being started twice at the same time by two different inputs.
1,316

edits