1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "backends/keymapper/keymap.h"
24
25 #include "common/system.h"
26 #include "common/tokenizer.h"
27
28 #include "backends/keymapper/action.h"
29 #include "backends/keymapper/hardware-input.h"
30 #include "backends/keymapper/keymapper-defaults.h"
31
32 #define KEYMAP_KEY_PREFIX "keymap_"
33
34 namespace Common {
35
Keymap(KeymapType type,const String & id,const U32String & description)36 Keymap::Keymap(KeymapType type, const String &id, const U32String &description) :
37 _type(type),
38 _id(id),
39 _description(description),
40 _enabled(true),
41 _configDomain(nullptr),
42 _hardwareInputSet(nullptr),
43 _backendDefaultBindings(nullptr) {
44
45 }
46
Keymap(KeymapType type,const String & id,const String & description)47 Keymap::Keymap(KeymapType type, const String &id, const String &description) :
48 _type(type),
49 _id(id),
50 _description(U32String(description)),
51 _enabled(true),
52 _configDomain(nullptr),
53 _hardwareInputSet(nullptr),
54 _backendDefaultBindings(nullptr) {
55
56 }
57
~Keymap()58 Keymap::~Keymap() {
59 for (ActionArray::iterator it = _actions.begin(); it != _actions.end(); ++it)
60 delete *it;
61 }
62
addAction(Action * action)63 void Keymap::addAction(Action *action) {
64 if (findAction(action->id))
65 error("Action with id %s already in KeyMap", action->id);
66
67 _actions.push_back(action);
68 }
69
registerMapping(Action * action,const HardwareInput & hwInput)70 void Keymap::registerMapping(Action *action, const HardwareInput &hwInput) {
71 ActionArray &actionArray = _hwActionMap.getOrCreateVal(hwInput);
72
73 // Don't allow an input to map to the same action multiple times
74 ActionArray::const_iterator found = find(actionArray.begin(), actionArray.end(), action);
75 if (found == actionArray.end()) {
76 actionArray.push_back(action);
77 }
78 }
79
unregisterMapping(Action * action)80 void Keymap::unregisterMapping(Action *action) {
81 // Remove the action from all the input mappings
82 for (HardwareActionMap::iterator itInput = _hwActionMap.begin(); itInput != _hwActionMap.end(); itInput++) {
83 for (ActionArray::iterator itAction = itInput->_value.begin(); itAction != itInput->_value.end(); itAction++) {
84 if (*itAction == action) {
85 itInput->_value.erase(itAction);
86 break;
87 }
88 }
89 if (itInput->_value.empty()) {
90 _hwActionMap.erase(itInput);
91 }
92 }
93 }
94
resetMapping(Action * action)95 void Keymap::resetMapping(Action *action) {
96 unregisterMapping(action);
97
98 StringArray hwInputIds = getActionDefaultMappings(action);
99 registerMappings(action, hwInputIds);
100 }
101
102 struct HardwareInputTypeIdComparator {
operator ()Common::HardwareInputTypeIdComparator103 bool operator()(const HardwareInput &x, const HardwareInput &y) const {
104 if (x.type != y.type) {
105 return x.type < y.type;
106 }
107 return x.id.compareTo(y.id);
108 }
109 };
110
getActionMapping(Action * action) const111 Array<HardwareInput> Keymap::getActionMapping(Action *action) const {
112 Array<HardwareInput> inputs;
113
114 for (HardwareActionMap::iterator itInput = _hwActionMap.begin(); itInput != _hwActionMap.end(); itInput++) {
115 for (ActionArray::iterator itAction = itInput->_value.begin(); itAction != itInput->_value.end(); itAction++) {
116 if (*itAction == action) {
117 inputs.push_back(itInput->_key);
118 break;
119 }
120 }
121 }
122
123 // Sort the inputs by type and then id for the remap dialog
124 Common::sort(inputs.begin(), inputs.end(), HardwareInputTypeIdComparator());
125
126 return inputs;
127 }
128
findAction(const char * id) const129 const Action *Keymap::findAction(const char *id) const {
130 for (ActionArray::const_iterator it = _actions.begin(); it != _actions.end(); ++it) {
131 if (strcmp((*it)->id, id) == 0)
132 return *it;
133 }
134
135 return nullptr;
136 }
137
getMappedActions(const Event & event,ActionArray & actions) const138 Keymap::KeymapMatch Keymap::getMappedActions(const Event &event, ActionArray &actions) const {
139 switch (event.type) {
140 case EVENT_KEYDOWN:
141 case EVENT_KEYUP: {
142 KeyState normalizedKeystate = KeyboardHardwareInputSet::normalizeKeyState(event.kbd);
143 HardwareInput hardwareInput = HardwareInput::createKeyboard("", normalizedKeystate, U32String());
144 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
145 if (!actions.empty()) {
146 return kKeymapMatchExact;
147 }
148
149 if (normalizedKeystate.flags & KBD_NON_STICKY) {
150 // If no matching actions and non-sticky keyboard modifiers are down,
151 // check again for matches without the exact keyboard modifiers
152 for (HardwareActionMap::const_iterator itInput = _hwActionMap.begin(); itInput != _hwActionMap.end(); ++itInput) {
153 if (itInput->_key.type == kHardwareInputTypeKeyboard && itInput->_key.key.keycode == normalizedKeystate.keycode) {
154 int flags = itInput->_key.key.flags;
155 if (flags & KBD_NON_STICKY && (flags & normalizedKeystate.flags) == flags) {
156 actions.push_back(itInput->_value);
157 return kKeymapMatchPartial;
158 }
159 }
160 }
161
162 // Lastly check again for matches no non-sticky keyboard modifiers
163 normalizedKeystate.flags &= ~KBD_NON_STICKY;
164 hardwareInput = HardwareInput::createKeyboard("", normalizedKeystate, U32String());
165 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
166 return actions.empty() ? kKeymapMatchNone : kKeymapMatchPartial;
167 }
168 break;
169 }
170 case EVENT_LBUTTONDOWN:
171 case EVENT_LBUTTONUP: {
172 HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_BUTTON_LEFT, U32String());
173 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
174 break;
175 }
176 case EVENT_RBUTTONDOWN:
177 case EVENT_RBUTTONUP: {
178 HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_BUTTON_RIGHT, U32String());
179 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
180 break;
181 }
182 case EVENT_MBUTTONDOWN:
183 case EVENT_MBUTTONUP: {
184 HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_BUTTON_MIDDLE, U32String());
185 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
186 break;
187 }
188 case Common::EVENT_WHEELUP: {
189 HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_WHEEL_UP, U32String());
190 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
191 break;
192 }
193 case Common::EVENT_WHEELDOWN: {
194 HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_WHEEL_DOWN, U32String());
195 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
196 break;
197 }
198 case EVENT_X1BUTTONDOWN:
199 case EVENT_X1BUTTONUP: {
200 HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_BUTTON_X1, U32String());
201 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
202 break;
203 }
204 case EVENT_X2BUTTONDOWN:
205 case EVENT_X2BUTTONUP: {
206 HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_BUTTON_X2, U32String());
207 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
208 break;
209 }
210 case EVENT_JOYBUTTON_DOWN:
211 case EVENT_JOYBUTTON_UP: {
212 HardwareInput hardwareInput = HardwareInput::createJoystickButton("", event.joystick.button, U32String());
213 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
214 break;
215 }
216 case EVENT_JOYAXIS_MOTION: {
217 if (event.joystick.position != 0) {
218 bool positiveHalf = event.joystick.position >= 0;
219 HardwareInput hardwareInput = HardwareInput::createJoystickHalfAxis("", event.joystick.axis, positiveHalf, U32String());
220 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
221 } else {
222 // Axis position zero is part of both half axes, and triggers actions bound to both
223 HardwareInput hardwareInputPos = HardwareInput::createJoystickHalfAxis("", event.joystick.axis, true, U32String());
224 HardwareInput hardwareInputNeg = HardwareInput::createJoystickHalfAxis("", event.joystick.axis, false, U32String());
225 actions.push_back(_hwActionMap.getValOrDefault(hardwareInputPos));
226 actions.push_back(_hwActionMap.getValOrDefault(hardwareInputNeg));
227 }
228 break;
229 }
230 case EVENT_CUSTOM_BACKEND_HARDWARE: {
231 HardwareInput hardwareInput = HardwareInput::createCustom("", event.customType, U32String());
232 actions.push_back(_hwActionMap.getValOrDefault(hardwareInput));
233 break;
234 }
235 default:
236 break;
237 }
238
239 return actions.empty() ? kKeymapMatchNone : kKeymapMatchExact;
240 }
241
setConfigDomain(ConfigManager::Domain * configDomain)242 void Keymap::setConfigDomain(ConfigManager::Domain *configDomain) {
243 _configDomain = configDomain;
244 }
245
setHardwareInputs(HardwareInputSet * hardwareInputSet)246 void Keymap::setHardwareInputs(HardwareInputSet *hardwareInputSet) {
247 _hardwareInputSet = hardwareInputSet;
248 }
249
setBackendDefaultBindings(const KeymapperDefaultBindings * backendDefaultBindings)250 void Keymap::setBackendDefaultBindings(const KeymapperDefaultBindings *backendDefaultBindings) {
251 _backendDefaultBindings = backendDefaultBindings;
252 }
253
getActionDefaultMappings(Action * action)254 StringArray Keymap::getActionDefaultMappings(Action *action) {
255 // Backend default mappings overrides keymap default mappings, so backends can resolve mapping conflicts.
256 // Empty mappings are valid and mean the action should not be mapped by default.
257 if (_backendDefaultBindings) {
258 KeymapperDefaultBindings::const_iterator it = _backendDefaultBindings->findDefaultBinding(_id, action->id);
259 if (it != _backendDefaultBindings->end()) {
260 if (it->_value.empty()) {
261 return StringArray();
262 }
263 return StringArray(1, it->_value);
264 }
265
266 // If no keymap-specific default mapping was found, look for a standard action binding
267 it = _backendDefaultBindings->findDefaultBinding(kStandardActionsKeymapName, action->id);
268 if (it != _backendDefaultBindings->end()) {
269 if (it->_value.empty()) {
270 return StringArray();
271 }
272 return StringArray(1, it->_value);
273 }
274 }
275
276 return action->getDefaultInputMapping();
277 }
278
loadMappings()279 void Keymap::loadMappings() {
280 assert(_configDomain);
281 assert(_hardwareInputSet);
282
283 if (_actions.empty()) {
284 return;
285 }
286
287 String prefix = KEYMAP_KEY_PREFIX + _id + "_";
288
289 _hwActionMap.clear();
290 for (ActionArray::const_iterator it = _actions.begin(); it != _actions.end(); ++it) {
291 Action *action = *it;
292 String confKey = prefix + action->id;
293
294 StringArray hwInputIds;
295 if (_configDomain->contains(confKey)) {
296 // The configuration value is a list of space separated hardware input ids
297 StringTokenizer hwInputTokenizer = _configDomain->getVal(confKey);
298
299 while (!hwInputTokenizer.empty()) {
300 hwInputIds.push_back(hwInputTokenizer.nextToken());
301 }
302 } else {
303 // If the configuration key was not found, use the default mapping
304 hwInputIds = getActionDefaultMappings(action);
305 }
306
307 registerMappings(action, hwInputIds);
308 }
309 }
310
registerMappings(Action * action,const StringArray & hwInputIds)311 void Keymap::registerMappings(Action *action, const StringArray &hwInputIds) {
312 assert(_hardwareInputSet);
313
314 for (uint i = 0; i < hwInputIds.size(); i++) {
315 HardwareInput hwInput = _hardwareInputSet->findHardwareInput(hwInputIds[i]);
316
317 if (hwInput.type == kHardwareInputTypeInvalid) {
318 // Silently ignore unknown hardware ids because the current device may not have inputs matching the defaults
319 debug(1, "HardwareInput with ID '%s' not known", hwInputIds[i].c_str());
320 continue;
321 }
322
323 // map the key
324 registerMapping(action, hwInput);
325 }
326 }
327
saveMappings()328 void Keymap::saveMappings() {
329 if (!_configDomain)
330 return;
331
332 String prefix = KEYMAP_KEY_PREFIX + _id + "_";
333
334 for (ActionArray::const_iterator it = _actions.begin(); it != _actions.end(); it++) {
335 Action *action = *it;
336 Array<HardwareInput> mappedInputs = getActionMapping(action);
337
338 if (areMappingsIdentical(mappedInputs, getActionDefaultMappings(action))) {
339 // If the current mapping is the default, don't write anything to the config manager
340 _configDomain->erase(prefix + action->id);
341 continue;
342 }
343
344 // The configuration value is a list of space separated hardware input ids
345 String confValue;
346 for (uint j = 0; j < mappedInputs.size(); j++) {
347 if (!confValue.empty()) {
348 confValue += " ";
349 }
350
351 confValue += mappedInputs[j].id;
352 }
353
354 _configDomain->setVal(prefix + action->id, confValue);
355 }
356 }
357
areMappingsIdentical(const Array<HardwareInput> & mappingsA,const StringArray & mappingsB)358 bool Keymap::areMappingsIdentical(const Array<HardwareInput> &mappingsA, const StringArray &mappingsB) {
359 // Assumes array values are not duplicated, but registerMapping and addDefaultInputMapping ensure that
360
361 uint foundCount = 0;
362 uint validDefaultMappings = 0;
363 for (uint i = 0; i < mappingsB.size(); i++) {
364 // We resolve the hardware input to make sure it is not a default for some hardware we don't have currently
365 HardwareInput mappingB = _hardwareInputSet->findHardwareInput(mappingsB[i]);
366 if (mappingB.type == kHardwareInputTypeInvalid) continue;
367 validDefaultMappings++;
368
369 for (uint j = 0; j < mappingsA.size(); j++) {
370 if (mappingsA[j].id == mappingB.id) {
371 foundCount++;
372 break;
373 }
374 }
375 }
376
377 return foundCount == mappingsA.size() && foundCount == validDefaultMappings;
378 }
379
380 } // End of namespace Common
381