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