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/keymapper.h"
24
25 #include "backends/keymapper/action.h"
26 #include "backends/keymapper/hardware-input.h"
27 #include "backends/keymapper/keymapper-defaults.h"
28
29 #include "common/system.h"
30
31 namespace Common {
32
33 // These magic numbers are provided by fuzzie and WebOS
34 static const uint32 kDelayKeyboardEventMillis = 250;
35 static const uint32 kDelayMouseEventMillis = 50;
36
Keymapper(EventManager * eventMan)37 Keymapper::Keymapper(EventManager *eventMan) :
38 _eventMan(eventMan),
39 _hardwareInputs(nullptr),
40 _backendDefaultBindings(nullptr),
41 _delayedEventSource(new DelayedEventSource()),
42 _enabled(true),
43 _enabledKeymapType(Keymap::kKeymapTypeGame) {
44 _eventMan->getEventDispatcher()->registerSource(_delayedEventSource, true);
45 resetInputState();
46 }
47
~Keymapper()48 Keymapper::~Keymapper() {
49 clear();
50 }
51
clear()52 void Keymapper::clear() {
53 for (KeymapArray::iterator it = _keymaps.begin(); it != _keymaps.end(); it++) {
54 delete *it;
55 }
56 _keymaps.clear();
57
58 delete _backendDefaultBindings;
59 _backendDefaultBindings = nullptr;
60
61 delete _hardwareInputs;
62 _hardwareInputs = nullptr;
63 }
64
registerHardwareInputSet(HardwareInputSet * inputs,KeymapperDefaultBindings * backendDefaultBindings)65 void Keymapper::registerHardwareInputSet(HardwareInputSet *inputs, KeymapperDefaultBindings *backendDefaultBindings) {
66 bool reloadMappings = false;
67 if (_hardwareInputs) {
68 reloadMappings = true;
69 delete _hardwareInputs;
70 }
71 if (_backendDefaultBindings) {
72 reloadMappings = true;
73 delete _backendDefaultBindings;
74 }
75
76 if (!inputs) {
77 warning("No hardware input were defined, using defaults");
78 CompositeHardwareInputSet *compositeInputs = new CompositeHardwareInputSet();
79 compositeInputs->addHardwareInputSet(new MouseHardwareInputSet(defaultMouseButtons));
80 compositeInputs->addHardwareInputSet(new KeyboardHardwareInputSet(defaultKeys, defaultModifiers));
81 inputs = compositeInputs;
82 }
83
84 _hardwareInputs = inputs;
85 _backendDefaultBindings = backendDefaultBindings;
86
87 if (reloadMappings) {
88 reloadAllMappings();
89 }
90 }
91
addGlobalKeymap(Keymap * keymap)92 void Keymapper::addGlobalKeymap(Keymap *keymap) {
93 assert(keymap->getType() == Keymap::kKeymapTypeGlobal
94 || keymap->getType() == Keymap::kKeymapTypeGui);
95
96 ConfigManager::Domain *keymapperDomain = ConfMan.getDomain(ConfigManager::kKeymapperDomain);
97 initKeymap(keymap, keymapperDomain);
98 _keymaps.push_back(keymap);
99 }
100
addGameKeymap(Keymap * keymap)101 void Keymapper::addGameKeymap(Keymap *keymap) {
102 assert(keymap->getType() == Keymap::kKeymapTypeGame);
103
104 ConfigManager::Domain *gameDomain = ConfMan.getActiveDomain();
105
106 if (!gameDomain) {
107 error("Call to Keymapper::addGameKeymap when no game loaded");
108 }
109
110 initKeymap(keymap, gameDomain);
111 _keymaps.push_back(keymap);
112 }
113
initKeymap(Keymap * keymap,ConfigManager::Domain * domain)114 void Keymapper::initKeymap(Keymap *keymap, ConfigManager::Domain *domain) {
115 if (!_hardwareInputs) {
116 warning("No hardware inputs were registered yet (%s)", keymap->getId().c_str());
117 return;
118 }
119
120 keymap->setConfigDomain(domain);
121 reloadKeymapMappings(keymap);
122 }
123
reloadKeymapMappings(Keymap * keymap)124 void Keymapper::reloadKeymapMappings(Keymap *keymap) {
125 keymap->setHardwareInputs(_hardwareInputs);
126 keymap->setBackendDefaultBindings(_backendDefaultBindings);
127 keymap->loadMappings();
128 }
129
cleanupGameKeymaps()130 void Keymapper::cleanupGameKeymaps() {
131 // Flush all game specific keymaps
132 KeymapArray::iterator it = _keymaps.begin();
133 while (it != _keymaps.end()) {
134 if ((*it)->getType() == Keymap::kKeymapTypeGame) {
135 delete *it;
136 it = _keymaps.erase(it);
137 } else {
138 it++;
139 }
140 }
141 }
142
getKeymap(const String & id) const143 Keymap *Keymapper::getKeymap(const String &id) const {
144 for (KeymapArray::const_iterator it = _keymaps.begin(); it != _keymaps.end(); it++) {
145 if ((*it)->getId() == id) {
146 return *it;
147 }
148 }
149
150 return nullptr;
151 }
152
reloadAllMappings()153 void Keymapper::reloadAllMappings() {
154 for (uint i = 0; i < _keymaps.size(); i++) {
155 reloadKeymapMappings(_keymaps[i]);
156 }
157 }
158
setEnabledKeymapType(Keymap::KeymapType type)159 void Keymapper::setEnabledKeymapType(Keymap::KeymapType type) {
160 assert(type == Keymap::kKeymapTypeGui || type == Keymap::kKeymapTypeGame);
161 _enabledKeymapType = type;
162 }
163
mapEvent(const Event & ev)164 List<Event> Keymapper::mapEvent(const Event &ev) {
165 if (!_enabled) {
166 List<Event> originalEvent;
167 originalEvent.push_back(ev);
168 return originalEvent;
169 }
170
171 hardcodedEventMapping(ev);
172
173 Keymap::ActionArray actions;
174 Keymap::KeymapMatch match = getMappedActions(ev, actions, _enabledKeymapType);
175 if (match != Keymap::kKeymapMatchExact) {
176 // If we found exact matching actions this input in the game / gui keymaps,
177 // no need to look at the global keymaps. An input resulting in actions
178 // from system and game keymaps would lead to unexpected user experience.
179 Keymap::ActionArray globalActions;
180 match = getMappedActions(ev, globalActions, Keymap::kKeymapTypeGlobal);
181 if (match == Keymap::kKeymapMatchExact || actions.empty()) {
182 actions = globalActions;
183 }
184 }
185
186 bool matchedAction = !actions.empty();
187 List<Event> mappedEvents;
188 for (Keymap::ActionArray::const_iterator it = actions.begin(); it != actions.end(); it++) {
189 Event mappedEvent = executeAction(*it, ev);
190 if (mappedEvent.type == EVENT_INVALID) {
191 continue;
192 }
193
194 // In case we mapped a mouse event to something else, we need to generate an artificial
195 // mouse move event so event observers can keep track of the mouse position.
196 // Makes it possible to reliably use the mouse position from EventManager when consuming
197 // custom action events.
198 if (isMouseEvent(ev) && !isMouseEvent(mappedEvent)) {
199 Event fakeMouseEvent;
200 fakeMouseEvent.type = EVENT_MOUSEMOVE;
201 fakeMouseEvent.mouse = ev.mouse;
202
203 mappedEvents.push_back(fakeMouseEvent);
204 }
205
206 mappedEvents.push_back(mappedEvent);
207 }
208
209 if (ev.type == EVENT_JOYAXIS_MOTION && ev.joystick.axis < ARRAYSIZE(_joystickAxisPreviouslyPressed)) {
210 if (ABS<int32>(ev.joystick.position) >= kJoyAxisPressedTreshold) {
211 _joystickAxisPreviouslyPressed[ev.joystick.axis] = true;
212 } else if (ABS<int32>(ev.joystick.position) < kJoyAxisUnpressedTreshold) {
213 _joystickAxisPreviouslyPressed[ev.joystick.axis] = false;
214 }
215 }
216
217 if (!matchedAction) {
218 // if it didn't get mapped, just pass it through
219 mappedEvents.push_back(ev);
220 }
221
222 return mappedEvents;
223 }
224
getMappedActions(const Event & event,Keymap::ActionArray & actions,Keymap::KeymapType keymapType) const225 Keymap::KeymapMatch Keymapper::getMappedActions(const Event &event, Keymap::ActionArray &actions, Keymap::KeymapType keymapType) const {
226 Keymap::KeymapMatch match = Keymap::kKeymapMatchNone;
227
228 for (uint i = 0; i < _keymaps.size(); i++) {
229 if (!_keymaps[i]->isEnabled() || _keymaps[i]->getType() != keymapType) {
230 continue;
231 }
232
233 Keymap::ActionArray array;
234 Keymap::KeymapMatch match2 = _keymaps[i]->getMappedActions(event, array);
235 if (match2 == match) {
236 actions.push_back(array);
237 } else if (match2 > match) {
238 match = match2;
239 actions.clear();
240 actions.push_back(array);
241 }
242 }
243 return match;
244 }
245
convertToIncomingEventType(const Event & ev) const246 Keymapper::IncomingEventType Keymapper::convertToIncomingEventType(const Event &ev) const {
247 if (ev.type == EVENT_CUSTOM_BACKEND_HARDWARE
248 || ev.type == EVENT_WHEELDOWN
249 || ev.type == EVENT_WHEELUP) {
250 return kIncomingEventInstant;
251 } else if (ev.type == EVENT_JOYAXIS_MOTION) {
252 if (ev.joystick.axis >= ARRAYSIZE(_joystickAxisPreviouslyPressed)) {
253 return kIncomingEventIgnored;
254 }
255
256 if (!_joystickAxisPreviouslyPressed[ev.joystick.axis] && ABS<int32>(ev.joystick.position) >= kJoyAxisPressedTreshold) {
257 return kIncomingEventStart;
258 } else if (_joystickAxisPreviouslyPressed[ev.joystick.axis] && ABS<int32>(ev.joystick.position) < kJoyAxisUnpressedTreshold) {
259 return kIncomingEventEnd;
260 } else {
261 return kIncomingEventIgnored;
262 }
263 } else if (ev.type == EVENT_KEYDOWN
264 || ev.type == EVENT_LBUTTONDOWN
265 || ev.type == EVENT_RBUTTONDOWN
266 || ev.type == EVENT_MBUTTONDOWN
267 || ev.type == EVENT_X1BUTTONDOWN
268 || ev.type == EVENT_X2BUTTONDOWN
269 || ev.type == EVENT_JOYBUTTON_DOWN) {
270 return kIncomingEventStart;
271 } else {
272 return kIncomingEventEnd;
273 }
274 }
275
executeAction(const Action * action,const Event & incomingEvent)276 Event Keymapper::executeAction(const Action *action, const Event &incomingEvent) {
277 Event outgoingEvent = Event(action->event);
278
279 IncomingEventType incomingType = convertToIncomingEventType(incomingEvent);
280
281 if (outgoingEvent.type == EVENT_JOYAXIS_MOTION
282 || outgoingEvent.type == EVENT_CUSTOM_BACKEND_ACTION_AXIS) {
283 if (incomingEvent.type == EVENT_JOYAXIS_MOTION) {
284 // At the moment only half-axes can be bound to actions, hence taking
285 // the absolute value. If full axes were to be mappable, the action
286 // could carry the information allowing to distinguish cases here.
287 outgoingEvent.joystick.position = ABS(incomingEvent.joystick.position);
288 } else if (incomingType == kIncomingEventStart) {
289 outgoingEvent.joystick.position = JOYAXIS_MAX;
290 } else if (incomingType == kIncomingEventEnd) {
291 outgoingEvent.joystick.position = 0;
292 }
293
294 return outgoingEvent;
295 }
296
297 if (incomingType == kIncomingEventIgnored) {
298 outgoingEvent.type = EVENT_INVALID;
299 return outgoingEvent;
300 }
301
302 if (incomingEvent.type == EVENT_KEYDOWN && incomingEvent.kbdRepeat && !action->shouldTriggerOnKbdRepeats()) {
303 outgoingEvent.type = EVENT_INVALID;
304 return outgoingEvent;
305 }
306
307 EventType convertedType = convertStartToEnd(outgoingEvent.type);
308
309 // hardware keys need to send up instead when they are up
310 if (incomingType == kIncomingEventEnd) {
311 outgoingEvent.type = convertedType;
312 }
313
314 if (outgoingEvent.type == EVENT_KEYDOWN && incomingEvent.type == EVENT_KEYDOWN) {
315 outgoingEvent.kbdRepeat = incomingEvent.kbdRepeat;
316 }
317
318 if (isMouseEvent(outgoingEvent)) {
319 if (isMouseEvent(incomingEvent)) {
320 outgoingEvent.mouse = incomingEvent.mouse;
321 } else {
322 outgoingEvent.mouse = _eventMan->getMousePos();
323 }
324 }
325
326 // Check if the event is coming from a non-key hardware event
327 // that is mapped to a key event
328 if (incomingType == kIncomingEventInstant && convertedType != EVENT_INVALID) {
329 // WORKAROUND: Delay the down events coming from non-key hardware events
330 // with a zero delay. This is to prevent DOWN1 DOWN2 UP1 UP2.
331 _delayedEventSource->scheduleEvent(outgoingEvent, 0);
332
333 // non-keys need to send up as well
334 // WORKAROUND: Delay the up events coming from non-key hardware events
335 // This is for engines that run scripts that check on key being down
336 outgoingEvent.type = convertedType;
337 const uint32 delay = (convertedType == EVENT_KEYUP ? kDelayKeyboardEventMillis : kDelayMouseEventMillis);
338 _delayedEventSource->scheduleEvent(outgoingEvent, delay);
339 }
340
341 return outgoingEvent;
342 }
343
convertStartToEnd(EventType type)344 EventType Keymapper::convertStartToEnd(EventType type) {
345 EventType result = EVENT_INVALID;
346 switch (type) {
347 case EVENT_KEYDOWN:
348 result = EVENT_KEYUP;
349 break;
350 case EVENT_LBUTTONDOWN:
351 result = EVENT_LBUTTONUP;
352 break;
353 case EVENT_RBUTTONDOWN:
354 result = EVENT_RBUTTONUP;
355 break;
356 case EVENT_MBUTTONDOWN:
357 result = EVENT_MBUTTONUP;
358 break;
359 case EVENT_X1BUTTONDOWN:
360 result = EVENT_X1BUTTONUP;
361 break;
362 case EVENT_X2BUTTONDOWN:
363 result = EVENT_X2BUTTONUP;
364 break;
365 case EVENT_JOYBUTTON_DOWN:
366 result = EVENT_JOYBUTTON_UP;
367 break;
368 case EVENT_CUSTOM_BACKEND_ACTION_START:
369 result = EVENT_CUSTOM_BACKEND_ACTION_END;
370 break;
371 case EVENT_CUSTOM_ENGINE_ACTION_START:
372 result = EVENT_CUSTOM_ENGINE_ACTION_END;
373 break;
374 default:
375 break;
376 }
377 return result;
378 }
379
findHardwareInput(const Event & event)380 HardwareInput Keymapper::findHardwareInput(const Event &event) {
381 return _hardwareInputs->findHardwareInput(event);
382 }
383
hardcodedEventMapping(Event ev)384 void Keymapper::hardcodedEventMapping(Event ev) {
385 // TODO: Either add support for long presses to the keymapper
386 // or move this elsewhere as an event observer + source
387 #ifdef ENABLE_VKEYBD
388 // Trigger virtual keyboard on long press of more than 1 second
389 // of middle mouse button.
390 const uint32 vkeybdTime = 1000;
391
392 static uint32 vkeybdThen = 0;
393
394 if (ev.type == EVENT_MBUTTONDOWN) {
395 vkeybdThen = g_system->getMillis();
396 }
397
398 if (ev.type == EVENT_MBUTTONUP) {
399 if ((g_system->getMillis() - vkeybdThen) >= vkeybdTime) {
400 Event vkeybdEvent;
401 vkeybdEvent.type = EVENT_VIRTUAL_KEYBOARD;
402
403 // Avoid blocking event from engine.
404 _delayedEventSource->scheduleEvent(vkeybdEvent, 100);
405 }
406 }
407 #endif
408 }
409
resetInputState()410 void Keymapper::resetInputState() {
411 for (uint i = 0; i < ARRAYSIZE(_joystickAxisPreviouslyPressed); i++) {
412 _joystickAxisPreviouslyPressed[i] = false;
413 }
414 }
415
scheduleEvent(const Event & ev,uint32 delayMillis)416 void DelayedEventSource::scheduleEvent(const Event &ev, uint32 delayMillis) {
417 if (_delayedEvents.empty()) {
418 _delayedEffectiveTime = g_system->getMillis() + delayMillis;
419 delayMillis = 0;
420 }
421 DelayedEventsEntry entry = DelayedEventsEntry(delayMillis, ev);
422 _delayedEvents.push(entry);
423 }
424
pollEvent(Event & event)425 bool DelayedEventSource::pollEvent(Event &event) {
426 if (_delayedEvents.empty()) {
427 return false;
428 }
429
430 uint32 now = g_system->getMillis(true);
431
432 if (now >= _delayedEffectiveTime) {
433 event = _delayedEvents.pop().event;
434
435 if (!_delayedEvents.empty()) {
436 _delayedEffectiveTime += _delayedEvents.front().timerOffset;
437 }
438
439 return true;
440 }
441
442 return false;
443 }
444
allowMapping() const445 bool DelayedEventSource::allowMapping() const {
446 return false; // Events from this source have already been mapped, and should not be mapped again
447 }
448
449 } // End of namespace Common
450