1 | /*
|
---|
2 | * synergy -- mouse and keyboard sharing utility
|
---|
3 | * Copyright (C) 2005 Chris Schoeneman
|
---|
4 | *
|
---|
5 | * This package is free software; you can redistribute it and/or
|
---|
6 | * modify it under the terms of the GNU General Public License
|
---|
7 | * found in the file COPYING that should have accompanied this file.
|
---|
8 | *
|
---|
9 | * This package is distributed in the hope that it will be useful,
|
---|
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
12 | * GNU General Public License for more details.
|
---|
13 | */
|
---|
14 |
|
---|
15 | #include "CKeyMap.h"
|
---|
16 | #include "KeyTypes.h"
|
---|
17 | #include "CLog.h"
|
---|
18 | #include <assert.h>
|
---|
19 | #include <ctype.h>
|
---|
20 | #include <stdlib.h>
|
---|
21 |
|
---|
22 | CKeyMap::CNameToKeyMap* CKeyMap::s_nameToKeyMap = NULL;
|
---|
23 | CKeyMap::CNameToModifierMap* CKeyMap::s_nameToModifierMap = NULL;
|
---|
24 | CKeyMap::CKeyToNameMap* CKeyMap::s_keyToNameMap = NULL;
|
---|
25 | CKeyMap::CModifierToNameMap* CKeyMap::s_modifierToNameMap = NULL;
|
---|
26 |
|
---|
27 | CKeyMap::CKeyMap() :
|
---|
28 | m_numGroups(0),
|
---|
29 | m_composeAcrossGroups(false)
|
---|
30 | {
|
---|
31 | m_modifierKeyItem.m_id = kKeyNone;
|
---|
32 | m_modifierKeyItem.m_group = 0;
|
---|
33 | m_modifierKeyItem.m_button = 0;
|
---|
34 | m_modifierKeyItem.m_required = 0;
|
---|
35 | m_modifierKeyItem.m_sensitive = 0;
|
---|
36 | m_modifierKeyItem.m_generates = 0;
|
---|
37 | m_modifierKeyItem.m_dead = false;
|
---|
38 | m_modifierKeyItem.m_lock = false;
|
---|
39 | m_modifierKeyItem.m_client = 0;
|
---|
40 | }
|
---|
41 |
|
---|
42 | CKeyMap::~CKeyMap()
|
---|
43 | {
|
---|
44 | // do nothing
|
---|
45 | }
|
---|
46 |
|
---|
47 | void
|
---|
48 | CKeyMap::swap(CKeyMap& x)
|
---|
49 | {
|
---|
50 | m_keyIDMap.swap(x.m_keyIDMap);
|
---|
51 | m_modifierKeys.swap(x.m_modifierKeys);
|
---|
52 | m_halfDuplex.swap(x.m_halfDuplex);
|
---|
53 | m_halfDuplexMods.swap(x.m_halfDuplexMods);
|
---|
54 | SInt32 tmp1 = m_numGroups;
|
---|
55 | m_numGroups = x.m_numGroups;
|
---|
56 | x.m_numGroups = tmp1;
|
---|
57 | bool tmp2 = m_composeAcrossGroups;
|
---|
58 | m_composeAcrossGroups = x.m_composeAcrossGroups;
|
---|
59 | x.m_composeAcrossGroups = tmp2;
|
---|
60 | }
|
---|
61 |
|
---|
62 | void
|
---|
63 | CKeyMap::addKeyEntry(const KeyItem& item)
|
---|
64 | {
|
---|
65 | // ignore kKeyNone
|
---|
66 | if (item.m_id == kKeyNone) {
|
---|
67 | return;
|
---|
68 | }
|
---|
69 |
|
---|
70 | // resize number of groups for key
|
---|
71 | SInt32 numGroups = item.m_group + 1;
|
---|
72 | if (getNumGroups() > numGroups) {
|
---|
73 | numGroups = getNumGroups();
|
---|
74 | }
|
---|
75 | KeyGroupTable& groupTable = m_keyIDMap[item.m_id];
|
---|
76 | if (groupTable.size() < static_cast<size_t>(numGroups)) {
|
---|
77 | groupTable.resize(numGroups);
|
---|
78 | }
|
---|
79 |
|
---|
80 | // make a list from the item
|
---|
81 | KeyItemList items;
|
---|
82 | items.push_back(item);
|
---|
83 |
|
---|
84 | // set group and dead key flag on the item
|
---|
85 | KeyItem& newItem = items.back();
|
---|
86 | newItem.m_dead = isDeadKey(item.m_id);
|
---|
87 |
|
---|
88 | // mask the required bits with the sensitive bits
|
---|
89 | newItem.m_required &= newItem.m_sensitive;
|
---|
90 |
|
---|
91 | // see if we already have this item; just return if so
|
---|
92 | KeyEntryList& entries = groupTable[item.m_group];
|
---|
93 | for (size_t i = 0, n = entries.size(); i < n; ++i) {
|
---|
94 | if (entries[i].size() == 1 && newItem == entries[i][0]) {
|
---|
95 | return;
|
---|
96 | }
|
---|
97 | }
|
---|
98 |
|
---|
99 | // add item list
|
---|
100 | entries.push_back(items);
|
---|
101 | LOG((CLOG_DEBUG1 "add key: %04x %d %03x %04x (%04x %04x %04x)%s", newItem.m_id, newItem.m_group, newItem.m_button, newItem.m_client, newItem.m_required, newItem.m_sensitive, newItem.m_generates, newItem.m_dead ? " dead" : ""));
|
---|
102 | }
|
---|
103 |
|
---|
104 | void
|
---|
105 | CKeyMap::addKeyAliasEntry(KeyID targetID, SInt32 group,
|
---|
106 | KeyModifierMask targetRequired,
|
---|
107 | KeyModifierMask targetSensitive,
|
---|
108 | KeyID sourceID,
|
---|
109 | KeyModifierMask sourceRequired,
|
---|
110 | KeyModifierMask sourceSensitive)
|
---|
111 | {
|
---|
112 | // if we can already generate the target as desired then we're done.
|
---|
113 | if (findCompatibleKey(targetID, group, targetRequired,
|
---|
114 | targetSensitive) != NULL) {
|
---|
115 | return;
|
---|
116 | }
|
---|
117 |
|
---|
118 | // find a compatible source, preferably in the same group
|
---|
119 | for (SInt32 gd = 0, n = getNumGroups(); gd < n; ++gd) {
|
---|
120 | SInt32 eg = getEffectiveGroup(group, gd);
|
---|
121 | const KeyItemList* sourceEntry =
|
---|
122 | findCompatibleKey(sourceID, eg,
|
---|
123 | sourceRequired, sourceSensitive);
|
---|
124 | if (sourceEntry != NULL && sourceEntry->size() == 1) {
|
---|
125 | CKeyMap::KeyItem targetItem = sourceEntry->back();
|
---|
126 | targetItem.m_id = targetID;
|
---|
127 | targetItem.m_group = eg;
|
---|
128 | addKeyEntry(targetItem);
|
---|
129 | break;
|
---|
130 | }
|
---|
131 | }
|
---|
132 | }
|
---|
133 |
|
---|
134 | bool
|
---|
135 | CKeyMap::addKeyCombinationEntry(KeyID id, SInt32 group,
|
---|
136 | const KeyID* keys, UInt32 numKeys)
|
---|
137 | {
|
---|
138 | // disallow kKeyNone
|
---|
139 | if (id == kKeyNone) {
|
---|
140 | return false;
|
---|
141 | }
|
---|
142 |
|
---|
143 | SInt32 numGroups = group + 1;
|
---|
144 | if (getNumGroups() > numGroups) {
|
---|
145 | numGroups = getNumGroups();
|
---|
146 | }
|
---|
147 | KeyGroupTable& groupTable = m_keyIDMap[id];
|
---|
148 | if (groupTable.size() < static_cast<size_t>(numGroups)) {
|
---|
149 | groupTable.resize(numGroups);
|
---|
150 | }
|
---|
151 | if (!groupTable[group].empty()) {
|
---|
152 | // key is already in the table
|
---|
153 | return false;
|
---|
154 | }
|
---|
155 |
|
---|
156 | // convert to buttons
|
---|
157 | KeyItemList items;
|
---|
158 | for (UInt32 i = 0; i < numKeys; ++i) {
|
---|
159 | KeyIDMap::const_iterator gtIndex = m_keyIDMap.find(keys[i]);
|
---|
160 | if (gtIndex == m_keyIDMap.end()) {
|
---|
161 | return false;
|
---|
162 | }
|
---|
163 | const KeyGroupTable& groupTable = gtIndex->second;
|
---|
164 |
|
---|
165 | // if we allow group switching during composition then search all
|
---|
166 | // groups for keys, otherwise search just the given group.
|
---|
167 | SInt32 n = 1;
|
---|
168 | if (m_composeAcrossGroups) {
|
---|
169 | n = (SInt32)groupTable.size();
|
---|
170 | }
|
---|
171 |
|
---|
172 | bool found = false;
|
---|
173 | for (SInt32 gd = 0; gd < n && !found; ++gd) {
|
---|
174 | SInt32 eg = (group + gd) % getNumGroups();
|
---|
175 | const KeyEntryList& entries = groupTable[eg];
|
---|
176 | for (size_t j = 0; j < entries.size(); ++j) {
|
---|
177 | if (entries[j].size() == 1) {
|
---|
178 | found = true;
|
---|
179 | items.push_back(entries[j][0]);
|
---|
180 | break;
|
---|
181 | }
|
---|
182 | }
|
---|
183 | }
|
---|
184 | if (!found) {
|
---|
185 | // required key is not in keyboard group
|
---|
186 | return false;
|
---|
187 | }
|
---|
188 | }
|
---|
189 |
|
---|
190 | // add key
|
---|
191 | groupTable[group].push_back(items);
|
---|
192 | return true;
|
---|
193 | }
|
---|
194 |
|
---|
195 | void
|
---|
196 | CKeyMap::allowGroupSwitchDuringCompose()
|
---|
197 | {
|
---|
198 | m_composeAcrossGroups = true;
|
---|
199 | }
|
---|
200 |
|
---|
201 | void
|
---|
202 | CKeyMap::addHalfDuplexButton(KeyButton button)
|
---|
203 | {
|
---|
204 | m_halfDuplex.insert(button);
|
---|
205 | }
|
---|
206 |
|
---|
207 | void
|
---|
208 | CKeyMap::clearHalfDuplexModifiers()
|
---|
209 | {
|
---|
210 | m_halfDuplexMods.clear();
|
---|
211 | }
|
---|
212 |
|
---|
213 | void
|
---|
214 | CKeyMap::addHalfDuplexModifier(KeyID key)
|
---|
215 | {
|
---|
216 | m_halfDuplexMods.insert(key);
|
---|
217 | }
|
---|
218 |
|
---|
219 | void
|
---|
220 | CKeyMap::finish()
|
---|
221 | {
|
---|
222 | m_numGroups = findNumGroups();
|
---|
223 |
|
---|
224 | // make sure every key has the same number of groups
|
---|
225 | for (KeyIDMap::iterator i = m_keyIDMap.begin();
|
---|
226 | i != m_keyIDMap.end(); ++i) {
|
---|
227 | i->second.resize(m_numGroups);
|
---|
228 | }
|
---|
229 |
|
---|
230 | // compute keys that generate each modifier
|
---|
231 | setModifierKeys();
|
---|
232 | }
|
---|
233 |
|
---|
234 | void
|
---|
235 | CKeyMap::foreachKey(ForeachKeyCallback cb, void* userData)
|
---|
236 | {
|
---|
237 | for (KeyIDMap::iterator i = m_keyIDMap.begin();
|
---|
238 | i != m_keyIDMap.end(); ++i) {
|
---|
239 | KeyGroupTable& groupTable = i->second;
|
---|
240 | for (size_t group = 0; group < groupTable.size(); ++group) {
|
---|
241 | KeyEntryList& entryList = groupTable[group];
|
---|
242 | for (size_t j = 0; j < entryList.size(); ++j) {
|
---|
243 | KeyItemList& itemList = entryList[j];
|
---|
244 | for (size_t k = 0; k < itemList.size(); ++k) {
|
---|
245 | (*cb)(i->first, static_cast<SInt32>(group),
|
---|
246 | itemList[k], userData);
|
---|
247 | }
|
---|
248 | }
|
---|
249 | }
|
---|
250 | }
|
---|
251 | }
|
---|
252 |
|
---|
253 | const CKeyMap::KeyItem*
|
---|
254 | CKeyMap::mapKey(Keystrokes& keys, KeyID id, SInt32 group,
|
---|
255 | ModifierToKeys& activeModifiers,
|
---|
256 | KeyModifierMask& currentState,
|
---|
257 | KeyModifierMask desiredMask,
|
---|
258 | bool isAutoRepeat) const
|
---|
259 | {
|
---|
260 | LOG((CLOG_DEBUG1 "mapKey %04x (%d) with mask %04x, start state: %04x", id, id, desiredMask, currentState));
|
---|
261 |
|
---|
262 | // handle group change
|
---|
263 | if (id == kKeyNextGroup) {
|
---|
264 | keys.push_back(Keystroke(1, false, false));
|
---|
265 | return NULL;
|
---|
266 | }
|
---|
267 | else if (id == kKeyPrevGroup) {
|
---|
268 | keys.push_back(Keystroke(-1, false, false));
|
---|
269 | return NULL;
|
---|
270 | }
|
---|
271 |
|
---|
272 | const KeyItem* item;
|
---|
273 | switch (id) {
|
---|
274 | case kKeyShift_L:
|
---|
275 | case kKeyShift_R:
|
---|
276 | case kKeyControl_L:
|
---|
277 | case kKeyControl_R:
|
---|
278 | case kKeyAlt_L:
|
---|
279 | case kKeyAlt_R:
|
---|
280 | case kKeyMeta_L:
|
---|
281 | case kKeyMeta_R:
|
---|
282 | case kKeySuper_L:
|
---|
283 | case kKeySuper_R:
|
---|
284 | case kKeyAltGr:
|
---|
285 | case kKeyCapsLock:
|
---|
286 | case kKeyNumLock:
|
---|
287 | case kKeyScrollLock:
|
---|
288 | item = mapModifierKey(keys, id, group, activeModifiers,
|
---|
289 | currentState, desiredMask, isAutoRepeat);
|
---|
290 | break;
|
---|
291 |
|
---|
292 | case kKeySetModifiers:
|
---|
293 | if (!keysForModifierState(0, group, activeModifiers, currentState,
|
---|
294 | desiredMask, desiredMask, 0, keys)) {
|
---|
295 | LOG((CLOG_DEBUG1 "unable to set modifiers %04x", desiredMask));
|
---|
296 | return NULL;
|
---|
297 | }
|
---|
298 | return &m_modifierKeyItem;
|
---|
299 |
|
---|
300 | case kKeyClearModifiers:
|
---|
301 | if (!keysForModifierState(0, group, activeModifiers, currentState,
|
---|
302 | currentState & ~desiredMask,
|
---|
303 | desiredMask, 0, keys)) {
|
---|
304 | LOG((CLOG_DEBUG1 "unable to clear modifiers %04x", desiredMask));
|
---|
305 | return NULL;
|
---|
306 | }
|
---|
307 | return &m_modifierKeyItem;
|
---|
308 |
|
---|
309 | default:
|
---|
310 | if (isCommand(desiredMask)) {
|
---|
311 | item = mapCommandKey(keys, id, group, activeModifiers,
|
---|
312 | currentState, desiredMask, isAutoRepeat);
|
---|
313 | }
|
---|
314 | else {
|
---|
315 | item = mapCharacterKey(keys, id, group, activeModifiers,
|
---|
316 | currentState, desiredMask, isAutoRepeat);
|
---|
317 | }
|
---|
318 | break;
|
---|
319 | }
|
---|
320 |
|
---|
321 | if (item != NULL) {
|
---|
322 | LOG((CLOG_DEBUG1 "mapped to %03x, new state %04x", item->m_button, currentState));
|
---|
323 | }
|
---|
324 | return item;
|
---|
325 | }
|
---|
326 |
|
---|
327 | SInt32
|
---|
328 | CKeyMap::getNumGroups() const
|
---|
329 | {
|
---|
330 | return m_numGroups;
|
---|
331 | }
|
---|
332 |
|
---|
333 | SInt32
|
---|
334 | CKeyMap::getEffectiveGroup(SInt32 group, SInt32 offset) const
|
---|
335 | {
|
---|
336 | return (group + offset + getNumGroups()) % getNumGroups();
|
---|
337 | }
|
---|
338 |
|
---|
339 | const CKeyMap::KeyItemList*
|
---|
340 | CKeyMap::findCompatibleKey(KeyID id, SInt32 group,
|
---|
341 | KeyModifierMask required, KeyModifierMask sensitive) const
|
---|
342 | {
|
---|
343 | assert(group >= 0 && group < getNumGroups());
|
---|
344 |
|
---|
345 | KeyIDMap::const_iterator i = m_keyIDMap.find(id);
|
---|
346 | if (i == m_keyIDMap.end()) {
|
---|
347 | return NULL;
|
---|
348 | }
|
---|
349 |
|
---|
350 | const KeyEntryList& entries = i->second[group];
|
---|
351 | for (size_t j = 0; j < entries.size(); ++j) {
|
---|
352 | if ((entries[j].back().m_sensitive & sensitive) == 0 ||
|
---|
353 | (entries[j].back().m_required & sensitive) ==
|
---|
354 | (required & sensitive)) {
|
---|
355 | return &entries[j];
|
---|
356 | }
|
---|
357 | }
|
---|
358 |
|
---|
359 | return NULL;
|
---|
360 | }
|
---|
361 |
|
---|
362 | bool
|
---|
363 | CKeyMap::isHalfDuplex(KeyID key, KeyButton button) const
|
---|
364 | {
|
---|
365 | return (m_halfDuplex.count(button) + m_halfDuplexMods.count(key) > 0);
|
---|
366 | }
|
---|
367 |
|
---|
368 | bool
|
---|
369 | CKeyMap::isCommand(KeyModifierMask mask) const
|
---|
370 | {
|
---|
371 | return ((mask & getCommandModifiers()) != 0);
|
---|
372 | }
|
---|
373 |
|
---|
374 | KeyModifierMask
|
---|
375 | CKeyMap::getCommandModifiers() const
|
---|
376 | {
|
---|
377 | // we currently treat ctrl, alt, meta and super as command modifiers.
|
---|
378 | // some platforms may have a more limited set (OS X only needs Alt)
|
---|
379 | // but this works anyway.
|
---|
380 | return KeyModifierControl |
|
---|
381 | KeyModifierAlt |
|
---|
382 | KeyModifierMeta |
|
---|
383 | KeyModifierSuper;
|
---|
384 | }
|
---|
385 |
|
---|
386 | void
|
---|
387 | CKeyMap::collectButtons(const ModifierToKeys& mods, ButtonToKeyMap& keys)
|
---|
388 | {
|
---|
389 | keys.clear();
|
---|
390 | for (ModifierToKeys::const_iterator i = mods.begin();
|
---|
391 | i != mods.end(); ++i) {
|
---|
392 | keys.insert(std::make_pair(i->second.m_button, &i->second));
|
---|
393 | }
|
---|
394 | }
|
---|
395 |
|
---|
396 | void
|
---|
397 | CKeyMap::initModifierKey(KeyItem& item)
|
---|
398 | {
|
---|
399 | item.m_generates = 0;
|
---|
400 | item.m_lock = false;
|
---|
401 | switch (item.m_id) {
|
---|
402 | case kKeyShift_L:
|
---|
403 | case kKeyShift_R:
|
---|
404 | item.m_generates = KeyModifierShift;
|
---|
405 | break;
|
---|
406 |
|
---|
407 | case kKeyControl_L:
|
---|
408 | case kKeyControl_R:
|
---|
409 | item.m_generates = KeyModifierControl;
|
---|
410 | break;
|
---|
411 |
|
---|
412 | case kKeyAlt_L:
|
---|
413 | case kKeyAlt_R:
|
---|
414 | item.m_generates = KeyModifierAlt;
|
---|
415 | break;
|
---|
416 |
|
---|
417 | case kKeyMeta_L:
|
---|
418 | case kKeyMeta_R:
|
---|
419 | item.m_generates = KeyModifierMeta;
|
---|
420 | break;
|
---|
421 |
|
---|
422 | case kKeySuper_L:
|
---|
423 | case kKeySuper_R:
|
---|
424 | item.m_generates = KeyModifierSuper;
|
---|
425 | break;
|
---|
426 |
|
---|
427 | case kKeyAltGr:
|
---|
428 | item.m_generates = KeyModifierAltGr;
|
---|
429 | break;
|
---|
430 |
|
---|
431 | case kKeyCapsLock:
|
---|
432 | item.m_generates = KeyModifierCapsLock;
|
---|
433 | item.m_lock = true;
|
---|
434 | break;
|
---|
435 |
|
---|
436 | case kKeyNumLock:
|
---|
437 | item.m_generates = KeyModifierNumLock;
|
---|
438 | item.m_lock = true;
|
---|
439 | break;
|
---|
440 |
|
---|
441 | case kKeyScrollLock:
|
---|
442 | item.m_generates = KeyModifierScrollLock;
|
---|
443 | item.m_lock = true;
|
---|
444 | break;
|
---|
445 |
|
---|
446 | default:
|
---|
447 | // not a modifier
|
---|
448 | break;
|
---|
449 | }
|
---|
450 | }
|
---|
451 |
|
---|
452 | SInt32
|
---|
453 | CKeyMap::findNumGroups() const
|
---|
454 | {
|
---|
455 | size_t max = 0;
|
---|
456 | for (KeyIDMap::const_iterator i = m_keyIDMap.begin();
|
---|
457 | i != m_keyIDMap.end(); ++i) {
|
---|
458 | if (i->second.size() > max) {
|
---|
459 | max = i->second.size();
|
---|
460 | }
|
---|
461 | }
|
---|
462 | return static_cast<SInt32>(max);
|
---|
463 | }
|
---|
464 |
|
---|
465 | void
|
---|
466 | CKeyMap::setModifierKeys()
|
---|
467 | {
|
---|
468 | m_modifierKeys.clear();
|
---|
469 | m_modifierKeys.resize(kKeyModifierNumBits * getNumGroups());
|
---|
470 | for (KeyIDMap::const_iterator i = m_keyIDMap.begin();
|
---|
471 | i != m_keyIDMap.end(); ++i) {
|
---|
472 | const KeyGroupTable& groupTable = i->second;
|
---|
473 | for (size_t g = 0; g < groupTable.size(); ++g) {
|
---|
474 | const KeyEntryList& entries = groupTable[g];
|
---|
475 | for (size_t j = 0; j < entries.size(); ++j) {
|
---|
476 | // skip multi-key sequences
|
---|
477 | if (entries[j].size() != 1) {
|
---|
478 | continue;
|
---|
479 | }
|
---|
480 |
|
---|
481 | // skip keys that don't generate a modifier
|
---|
482 | const KeyItem& item = entries[j].back();
|
---|
483 | if (item.m_generates == 0) {
|
---|
484 | continue;
|
---|
485 | }
|
---|
486 |
|
---|
487 | // add key to each indicated modifier in this group
|
---|
488 | for (SInt32 b = 0; b < kKeyModifierNumBits; ++b) {
|
---|
489 | // skip if item doesn't generate bit b
|
---|
490 | if (((1u << b) & item.m_generates) != 0) {
|
---|
491 | SInt32 mIndex = g * kKeyModifierNumBits + b;
|
---|
492 | m_modifierKeys[mIndex].push_back(&item);
|
---|
493 | }
|
---|
494 | }
|
---|
495 | }
|
---|
496 | }
|
---|
497 | }
|
---|
498 | }
|
---|
499 |
|
---|
500 | const CKeyMap::KeyItem*
|
---|
501 | CKeyMap::mapCommandKey(Keystrokes& keys, KeyID id, SInt32 group,
|
---|
502 | ModifierToKeys& activeModifiers,
|
---|
503 | KeyModifierMask& currentState,
|
---|
504 | KeyModifierMask desiredMask,
|
---|
505 | bool isAutoRepeat) const
|
---|
506 | {
|
---|
507 | static const KeyModifierMask s_overrideModifiers = 0xffffu;
|
---|
508 |
|
---|
509 | // find KeySym in table
|
---|
510 | KeyIDMap::const_iterator i = m_keyIDMap.find(id);
|
---|
511 | if (i == m_keyIDMap.end()) {
|
---|
512 | // unknown key
|
---|
513 | LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id));
|
---|
514 | return NULL;
|
---|
515 | }
|
---|
516 | const KeyGroupTable& keyGroupTable = i->second;
|
---|
517 |
|
---|
518 | // find the first key that generates this KeyID
|
---|
519 | const KeyItem* keyItem = NULL;
|
---|
520 | SInt32 numGroups = getNumGroups();
|
---|
521 | for (SInt32 groupOffset = 0; groupOffset < numGroups; ++groupOffset) {
|
---|
522 | SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset);
|
---|
523 | const KeyEntryList& entryList = keyGroupTable[effectiveGroup];
|
---|
524 | for (size_t i = 0; i < entryList.size(); ++i) {
|
---|
525 | if (entryList[i].size() != 1) {
|
---|
526 | // ignore multikey entries
|
---|
527 | continue;
|
---|
528 | }
|
---|
529 |
|
---|
530 | // only match based on shift; we're after the right button
|
---|
531 | // not the right character. we'll use desiredMask as-is,
|
---|
532 | // overriding the key's required modifiers, when synthesizing
|
---|
533 | // this button.
|
---|
534 | const KeyItem& item = entryList[i].back();
|
---|
535 | if ((item.m_required & KeyModifierShift & desiredMask) ==
|
---|
536 | (item.m_sensitive & KeyModifierShift & desiredMask)) {
|
---|
537 | LOG((CLOG_DEBUG1 "found key in group %d", effectiveGroup));
|
---|
538 | keyItem = &item;
|
---|
539 | break;
|
---|
540 | }
|
---|
541 | }
|
---|
542 | if (keyItem != NULL) {
|
---|
543 | break;
|
---|
544 | }
|
---|
545 | }
|
---|
546 | if (keyItem == NULL) {
|
---|
547 | // no mapping for this keysym
|
---|
548 | LOG((CLOG_DEBUG1 "no mapping for key %04x", id));
|
---|
549 | return NULL;
|
---|
550 | }
|
---|
551 |
|
---|
552 | // make working copy of modifiers
|
---|
553 | ModifierToKeys newModifiers = activeModifiers;
|
---|
554 | KeyModifierMask newState = currentState;
|
---|
555 | SInt32 newGroup = group;
|
---|
556 |
|
---|
557 | // don't try to change CapsLock
|
---|
558 | desiredMask = (desiredMask & ~KeyModifierCapsLock) |
|
---|
559 | (currentState & KeyModifierCapsLock);
|
---|
560 |
|
---|
561 | // add the key
|
---|
562 | if (!keysForKeyItem(*keyItem, newGroup, newModifiers,
|
---|
563 | newState, desiredMask,
|
---|
564 | s_overrideModifiers, isAutoRepeat, keys)) {
|
---|
565 | LOG((CLOG_DEBUG1 "can't map key"));
|
---|
566 | keys.clear();
|
---|
567 | return NULL;
|
---|
568 | }
|
---|
569 |
|
---|
570 | // add keystrokes to restore modifier keys
|
---|
571 | if (!keysToRestoreModifiers(*keyItem, group, newModifiers, newState,
|
---|
572 | activeModifiers, keys)) {
|
---|
573 | LOG((CLOG_DEBUG1 "failed to restore modifiers"));
|
---|
574 | keys.clear();
|
---|
575 | return NULL;
|
---|
576 | }
|
---|
577 |
|
---|
578 | // add keystrokes to restore group
|
---|
579 | if (newGroup != group) {
|
---|
580 | keys.push_back(Keystroke(group, true, true));
|
---|
581 | }
|
---|
582 |
|
---|
583 | // save new modifiers
|
---|
584 | activeModifiers = newModifiers;
|
---|
585 | currentState = newState;
|
---|
586 |
|
---|
587 | return keyItem;
|
---|
588 | }
|
---|
589 |
|
---|
590 | const CKeyMap::KeyItem*
|
---|
591 | CKeyMap::mapCharacterKey(Keystrokes& keys, KeyID id, SInt32 group,
|
---|
592 | ModifierToKeys& activeModifiers,
|
---|
593 | KeyModifierMask& currentState,
|
---|
594 | KeyModifierMask desiredMask,
|
---|
595 | bool isAutoRepeat) const
|
---|
596 | {
|
---|
597 | // find KeySym in table
|
---|
598 | KeyIDMap::const_iterator i = m_keyIDMap.find(id);
|
---|
599 | if (i == m_keyIDMap.end()) {
|
---|
600 | // unknown key
|
---|
601 | LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id));
|
---|
602 | return NULL;
|
---|
603 | }
|
---|
604 | const KeyGroupTable& keyGroupTable = i->second;
|
---|
605 |
|
---|
606 | // find best key in any group, starting with the active group
|
---|
607 | SInt32 keyIndex = -1;
|
---|
608 | SInt32 numGroups = getNumGroups();
|
---|
609 | SInt32 groupOffset;
|
---|
610 | LOG((CLOG_DEBUG1 "find best: %04x %04x", currentState, desiredMask));
|
---|
611 | for (groupOffset = 0; groupOffset < numGroups; ++groupOffset) {
|
---|
612 | SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset);
|
---|
613 | keyIndex = findBestKey(keyGroupTable[effectiveGroup],
|
---|
614 | currentState, desiredMask);
|
---|
615 | if (keyIndex != -1) {
|
---|
616 | LOG((CLOG_DEBUG1 "found key in group %d", effectiveGroup));
|
---|
617 | break;
|
---|
618 | }
|
---|
619 | }
|
---|
620 | if (keyIndex == -1) {
|
---|
621 | // no mapping for this keysym
|
---|
622 | LOG((CLOG_DEBUG1 "no mapping for key %04x", id));
|
---|
623 | return NULL;
|
---|
624 | }
|
---|
625 |
|
---|
626 | // get keys to press for key
|
---|
627 | SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset);
|
---|
628 | const KeyItemList& itemList = keyGroupTable[effectiveGroup][keyIndex];
|
---|
629 | if (itemList.empty()) {
|
---|
630 | return NULL;
|
---|
631 | }
|
---|
632 | const KeyItem& keyItem = itemList.back();
|
---|
633 |
|
---|
634 | // make working copy of modifiers
|
---|
635 | ModifierToKeys newModifiers = activeModifiers;
|
---|
636 | KeyModifierMask newState = currentState;
|
---|
637 | SInt32 newGroup = group;
|
---|
638 |
|
---|
639 | // add each key
|
---|
640 | for (size_t j = 0; j < itemList.size(); ++j) {
|
---|
641 | if (!keysForKeyItem(itemList[j], newGroup, newModifiers,
|
---|
642 | newState, desiredMask,
|
---|
643 | 0, isAutoRepeat, keys)) {
|
---|
644 | LOG((CLOG_DEBUG1 "can't map key"));
|
---|
645 | keys.clear();
|
---|
646 | return NULL;
|
---|
647 | }
|
---|
648 | }
|
---|
649 |
|
---|
650 | // add keystrokes to restore modifier keys
|
---|
651 | if (!keysToRestoreModifiers(keyItem, group, newModifiers, newState,
|
---|
652 | activeModifiers, keys)) {
|
---|
653 | LOG((CLOG_DEBUG1 "failed to restore modifiers"));
|
---|
654 | keys.clear();
|
---|
655 | return NULL;
|
---|
656 | }
|
---|
657 |
|
---|
658 | // add keystrokes to restore group
|
---|
659 | if (newGroup != group) {
|
---|
660 | keys.push_back(Keystroke(group, true, true));
|
---|
661 | }
|
---|
662 |
|
---|
663 | // save new modifiers
|
---|
664 | activeModifiers = newModifiers;
|
---|
665 | currentState = newState;
|
---|
666 |
|
---|
667 | return &keyItem;
|
---|
668 | }
|
---|
669 |
|
---|
670 | const CKeyMap::KeyItem*
|
---|
671 | CKeyMap::mapModifierKey(Keystrokes& keys, KeyID id, SInt32 group,
|
---|
672 | ModifierToKeys& activeModifiers,
|
---|
673 | KeyModifierMask& currentState,
|
---|
674 | KeyModifierMask desiredMask,
|
---|
675 | bool isAutoRepeat) const
|
---|
676 | {
|
---|
677 | return mapCharacterKey(keys, id, group, activeModifiers,
|
---|
678 | currentState, desiredMask, isAutoRepeat);
|
---|
679 | }
|
---|
680 |
|
---|
681 | SInt32
|
---|
682 | CKeyMap::findBestKey(const KeyEntryList& entryList,
|
---|
683 | KeyModifierMask /*currentState*/,
|
---|
684 | KeyModifierMask desiredState) const
|
---|
685 | {
|
---|
686 | // check for an item that can accommodate the desiredState exactly
|
---|
687 | for (size_t i = 0; i < entryList.size(); ++i) {
|
---|
688 | const KeyItem& item = entryList[i].back();
|
---|
689 | if ((item.m_required & desiredState) ==
|
---|
690 | (item.m_sensitive & desiredState)) {
|
---|
691 | LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i, entryList.size()));
|
---|
692 | return i;
|
---|
693 | }
|
---|
694 | }
|
---|
695 |
|
---|
696 | // choose the item that requires the fewest modifier changes
|
---|
697 | SInt32 bestCount = 32;
|
---|
698 | SInt32 bestIndex = -1;
|
---|
699 | for (size_t i = 0; i < entryList.size(); ++i) {
|
---|
700 | const KeyItem& item = entryList[i].back();
|
---|
701 | KeyModifierMask change =
|
---|
702 | ((item.m_required ^ desiredState) & item.m_sensitive);
|
---|
703 | SInt32 n = getNumModifiers(change);
|
---|
704 | if (n < bestCount) {
|
---|
705 | bestCount = n;
|
---|
706 | bestIndex = i;
|
---|
707 | }
|
---|
708 | }
|
---|
709 | if (bestIndex != -1) {
|
---|
710 | LOG((CLOG_DEBUG1 "best key index %d of %d (%d modifiers)", bestIndex, entryList.size(), bestCount));
|
---|
711 | }
|
---|
712 |
|
---|
713 | return bestIndex;
|
---|
714 | }
|
---|
715 |
|
---|
716 |
|
---|
717 | const CKeyMap::KeyItem*
|
---|
718 | CKeyMap::keyForModifier(KeyButton button, SInt32 group,
|
---|
719 | SInt32 modifierBit) const
|
---|
720 | {
|
---|
721 | assert(modifierBit >= 0 && modifierBit < kKeyModifierNumBits);
|
---|
722 | assert(group >= 0 && group < getNumGroups());
|
---|
723 |
|
---|
724 | // find a key that generates the given modifier in the given group
|
---|
725 | // but doesn't use the given button, presumably because we're trying
|
---|
726 | // to generate a KeyID that's only bound the the given button.
|
---|
727 | // this is important when a shift button is modified by shift; we
|
---|
728 | // must use the other shift button to do the shifting.
|
---|
729 | const ModifierKeyItemList& items =
|
---|
730 | m_modifierKeys[group * kKeyModifierNumBits + modifierBit];
|
---|
731 | for (ModifierKeyItemList::const_iterator i = items.begin();
|
---|
732 | i != items.end(); ++i) {
|
---|
733 | if ((*i)->m_button != button) {
|
---|
734 | return (*i);
|
---|
735 | }
|
---|
736 | }
|
---|
737 | return NULL;
|
---|
738 | }
|
---|
739 |
|
---|
740 | bool
|
---|
741 | CKeyMap::keysForKeyItem(const KeyItem& keyItem, SInt32& group,
|
---|
742 | ModifierToKeys& activeModifiers,
|
---|
743 | KeyModifierMask& currentState, KeyModifierMask desiredState,
|
---|
744 | KeyModifierMask overrideModifiers,
|
---|
745 | bool isAutoRepeat,
|
---|
746 | Keystrokes& keystrokes) const
|
---|
747 | {
|
---|
748 | static const KeyModifierMask s_notRequiredMask =
|
---|
749 | KeyModifierAltGr | KeyModifierNumLock | KeyModifierScrollLock;
|
---|
750 |
|
---|
751 | // add keystrokes to adjust the group
|
---|
752 | if (group != keyItem.m_group) {
|
---|
753 | group = keyItem.m_group;
|
---|
754 | keystrokes.push_back(Keystroke(group, true, false));
|
---|
755 | }
|
---|
756 |
|
---|
757 | EKeystroke type;
|
---|
758 | if (keyItem.m_dead) {
|
---|
759 | // adjust modifiers for dead key
|
---|
760 | if (!keysForModifierState(keyItem.m_button, group,
|
---|
761 | activeModifiers, currentState,
|
---|
762 | keyItem.m_required, keyItem.m_sensitive,
|
---|
763 | 0, keystrokes)) {
|
---|
764 | LOG((CLOG_DEBUG1 "unable to match modifier state for dead key %d", keyItem.m_button));
|
---|
765 | return false;
|
---|
766 | }
|
---|
767 |
|
---|
768 | // press and release the dead key
|
---|
769 | type = kKeystrokeClick;
|
---|
770 | }
|
---|
771 | else {
|
---|
772 | // if this a command key then we don't have to match some of the
|
---|
773 | // key's required modifiers.
|
---|
774 | KeyModifierMask sensitive = keyItem.m_sensitive & ~overrideModifiers;
|
---|
775 |
|
---|
776 | // XXX -- must handle pressing a modifier. in particular, if we want
|
---|
777 | // to synthesize a KeyID on level 1 of a KeyButton that has Shift_L
|
---|
778 | // mapped to level 0 then we must release that button if it's down
|
---|
779 | // (in order to satisfy a shift modifier) then press a different
|
---|
780 | // button (any other button) mapped to the shift modifier and then
|
---|
781 | // the Shift_L button.
|
---|
782 | // match key's required state
|
---|
783 | LOG((CLOG_DEBUG1 "state: %04x,%04x,%04x", currentState, keyItem.m_required, sensitive));
|
---|
784 | if (!keysForModifierState(keyItem.m_button, group,
|
---|
785 | activeModifiers, currentState,
|
---|
786 | keyItem.m_required, sensitive,
|
---|
787 | 0, keystrokes)) {
|
---|
788 | LOG((CLOG_DEBUG1 "unable to match modifier state (%04x,%04x) for key %d", keyItem.m_required, keyItem.m_sensitive, keyItem.m_button));
|
---|
789 | return false;
|
---|
790 | }
|
---|
791 |
|
---|
792 | // match desiredState as closely as possible. we must not
|
---|
793 | // change any modifiers in keyItem.m_sensitive. and if the key
|
---|
794 | // is a modifier, we don't want to change that modifier.
|
---|
795 | LOG((CLOG_DEBUG1 "desired state: %04x %04x,%04x,%04x", desiredState, currentState, keyItem.m_required, keyItem.m_sensitive));
|
---|
796 | if (!keysForModifierState(keyItem.m_button, group,
|
---|
797 | activeModifiers, currentState,
|
---|
798 | desiredState,
|
---|
799 | ~(sensitive | keyItem.m_generates),
|
---|
800 | s_notRequiredMask, keystrokes)) {
|
---|
801 | LOG((CLOG_DEBUG1 "unable to match desired modifier state (%04x,%04x) for key %d", desiredState, ~keyItem.m_sensitive & 0xffffu, keyItem.m_button));
|
---|
802 | return false;
|
---|
803 | }
|
---|
804 |
|
---|
805 | // repeat or press of key
|
---|
806 | type = isAutoRepeat ? kKeystrokeRepeat : kKeystrokePress;
|
---|
807 | }
|
---|
808 | addKeystrokes(type, keyItem, activeModifiers, currentState, keystrokes);
|
---|
809 |
|
---|
810 | return true;
|
---|
811 | }
|
---|
812 |
|
---|
813 | bool
|
---|
814 | CKeyMap::keysToRestoreModifiers(const KeyItem& keyItem, SInt32,
|
---|
815 | ModifierToKeys& activeModifiers,
|
---|
816 | KeyModifierMask& currentState,
|
---|
817 | const ModifierToKeys& desiredModifiers,
|
---|
818 | Keystrokes& keystrokes) const
|
---|
819 | {
|
---|
820 | // XXX -- we're not considering modified modifiers here
|
---|
821 |
|
---|
822 | ModifierToKeys oldModifiers = activeModifiers;
|
---|
823 |
|
---|
824 | // get the pressed modifier buttons before and after
|
---|
825 | ButtonToKeyMap oldKeys, newKeys;
|
---|
826 | collectButtons(oldModifiers, oldKeys);
|
---|
827 | collectButtons(desiredModifiers, newKeys);
|
---|
828 |
|
---|
829 | // release unwanted keys
|
---|
830 | for (ModifierToKeys::const_iterator i = oldModifiers.begin();
|
---|
831 | i != oldModifiers.end(); ++i) {
|
---|
832 | KeyButton button = i->second.m_button;
|
---|
833 | if (button != keyItem.m_button && newKeys.count(button) == 0) {
|
---|
834 | EKeystroke type = kKeystrokeRelease;
|
---|
835 | if (i->second.m_lock) {
|
---|
836 | type = kKeystrokeUnmodify;
|
---|
837 | }
|
---|
838 | addKeystrokes(type, i->second,
|
---|
839 | activeModifiers, currentState, keystrokes);
|
---|
840 | }
|
---|
841 | }
|
---|
842 |
|
---|
843 | // press wanted keys
|
---|
844 | for (ModifierToKeys::const_iterator i = desiredModifiers.begin();
|
---|
845 | i != desiredModifiers.end(); ++i) {
|
---|
846 | KeyButton button = i->second.m_button;
|
---|
847 | if (button != keyItem.m_button && oldKeys.count(button) == 0) {
|
---|
848 | EKeystroke type = kKeystrokePress;
|
---|
849 | if (i->second.m_lock) {
|
---|
850 | type = kKeystrokeModify;
|
---|
851 | }
|
---|
852 | addKeystrokes(type, i->second,
|
---|
853 | activeModifiers, currentState, keystrokes);
|
---|
854 | }
|
---|
855 | }
|
---|
856 |
|
---|
857 | return true;
|
---|
858 | }
|
---|
859 |
|
---|
860 | bool
|
---|
861 | CKeyMap::keysForModifierState(KeyButton button, SInt32 group,
|
---|
862 | ModifierToKeys& activeModifiers,
|
---|
863 | KeyModifierMask& currentState,
|
---|
864 | KeyModifierMask requiredState, KeyModifierMask sensitiveMask,
|
---|
865 | KeyModifierMask notRequiredMask,
|
---|
866 | Keystrokes& keystrokes) const
|
---|
867 | {
|
---|
868 | // compute which modifiers need changing
|
---|
869 | KeyModifierMask flipMask = ((currentState ^ requiredState) & sensitiveMask);
|
---|
870 | // if a modifier is not required then don't even try to match it. if
|
---|
871 | // we don't mask out notRequiredMask then we'll try to match those
|
---|
872 | // modifiers but succeed if we can't. however, this is known not
|
---|
873 | // to work if the key itself is a modifier (the numlock toggle can
|
---|
874 | // interfere) so we don't try to match at all.
|
---|
875 | flipMask &= ~notRequiredMask;
|
---|
876 | LOG((CLOG_DEBUG1 "flip: %04x (%04x vs %04x in %04x - %04x)", flipMask, currentState, requiredState, sensitiveMask & 0xffffu, notRequiredMask & 0xffffu));
|
---|
877 | if (flipMask == 0) {
|
---|
878 | return true;
|
---|
879 | }
|
---|
880 |
|
---|
881 | // fix modifiers. this is complicated by the fact that a modifier may
|
---|
882 | // be sensitive to other modifiers! (who thought that up?)
|
---|
883 | //
|
---|
884 | // we'll assume that modifiers with higher bits are affected by modifiers
|
---|
885 | // with lower bits. there's not much basis for that assumption except
|
---|
886 | // that we're pretty sure shift isn't changed by other modifiers.
|
---|
887 | for (SInt32 bit = kKeyModifierNumBits; bit-- > 0; ) {
|
---|
888 | KeyModifierMask mask = (1u << bit);
|
---|
889 | if ((flipMask & mask) == 0) {
|
---|
890 | // modifier is already correct
|
---|
891 | continue;
|
---|
892 | }
|
---|
893 |
|
---|
894 | // do we want the modifier active or inactive?
|
---|
895 | bool active = ((requiredState & mask) != 0);
|
---|
896 |
|
---|
897 | // get the KeyItem for the modifier in the group
|
---|
898 | const KeyItem* keyItem = keyForModifier(button, group, bit);
|
---|
899 | if (keyItem == NULL) {
|
---|
900 | if ((mask & notRequiredMask) == 0) {
|
---|
901 | LOG((CLOG_DEBUG1 "no key for modifier %04x", mask));
|
---|
902 | return false;
|
---|
903 | }
|
---|
904 | else {
|
---|
905 | continue;
|
---|
906 | }
|
---|
907 | }
|
---|
908 |
|
---|
909 | // if this modifier is sensitive to modifiers then adjust those
|
---|
910 | // modifiers. also check if our assumption was correct. note
|
---|
911 | // that we only need to adjust the modifiers on key down.
|
---|
912 | KeyModifierMask sensitive = keyItem->m_sensitive;
|
---|
913 | if ((sensitive & mask) != 0) {
|
---|
914 | // modifier is sensitive to itself. that makes no sense
|
---|
915 | // so ignore it.
|
---|
916 | LOG((CLOG_DEBUG1 "modifier %04x modified by itself", mask));
|
---|
917 | sensitive &= ~mask;
|
---|
918 | }
|
---|
919 | if (sensitive != 0) {
|
---|
920 | if (sensitive > mask) {
|
---|
921 | // our assumption is incorrect
|
---|
922 | LOG((CLOG_DEBUG1 "modifier %04x modified by %04x", mask, sensitive));
|
---|
923 | return false;
|
---|
924 | }
|
---|
925 | if (active && !keysForModifierState(button, group,
|
---|
926 | activeModifiers, currentState,
|
---|
927 | keyItem->m_required, sensitive,
|
---|
928 | notRequiredMask, keystrokes)) {
|
---|
929 | return false;
|
---|
930 | }
|
---|
931 | else if (!active) {
|
---|
932 | // release the modifier
|
---|
933 | // XXX -- this doesn't work! if Alt and Meta are mapped
|
---|
934 | // to one key and we want to release Meta we can't do
|
---|
935 | // that without also releasing Alt.
|
---|
936 | // need to think about support for modified modifiers.
|
---|
937 | }
|
---|
938 | }
|
---|
939 |
|
---|
940 | // current state should match required state
|
---|
941 | if ((currentState & sensitive) != (keyItem->m_required & sensitive)) {
|
---|
942 | LOG((CLOG_DEBUG1 "unable to match modifier state for modifier %04x (%04x vs %04x in %04x)", mask, currentState, keyItem->m_required, sensitive));
|
---|
943 | return false;
|
---|
944 | }
|
---|
945 |
|
---|
946 | // add keystrokes
|
---|
947 | EKeystroke type = active ? kKeystrokeModify : kKeystrokeUnmodify;
|
---|
948 | addKeystrokes(type, *keyItem, activeModifiers, currentState,
|
---|
949 | keystrokes);
|
---|
950 | }
|
---|
951 |
|
---|
952 | return true;
|
---|
953 | }
|
---|
954 |
|
---|
955 | void
|
---|
956 | CKeyMap::addKeystrokes(EKeystroke type, const KeyItem& keyItem,
|
---|
957 | ModifierToKeys& activeModifiers,
|
---|
958 | KeyModifierMask& currentState,
|
---|
959 | Keystrokes& keystrokes) const
|
---|
960 | {
|
---|
961 | KeyButton button = keyItem.m_button;
|
---|
962 | UInt32 data = keyItem.m_client;
|
---|
963 | switch (type) {
|
---|
964 | case kKeystrokePress:
|
---|
965 | keystrokes.push_back(Keystroke(button, true, false, data));
|
---|
966 | if (keyItem.m_generates != 0) {
|
---|
967 | if (!keyItem.m_lock || (currentState & keyItem.m_generates) == 0) {
|
---|
968 | // add modifier key and activate modifier
|
---|
969 | activeModifiers.insert(std::make_pair(
|
---|
970 | keyItem.m_generates, keyItem));
|
---|
971 | currentState |= keyItem.m_generates;
|
---|
972 | }
|
---|
973 | else {
|
---|
974 | // deactivate locking modifier
|
---|
975 | activeModifiers.erase(keyItem.m_generates);
|
---|
976 | currentState &= ~keyItem.m_generates;
|
---|
977 | }
|
---|
978 | }
|
---|
979 | break;
|
---|
980 |
|
---|
981 | case kKeystrokeRelease:
|
---|
982 | keystrokes.push_back(Keystroke(button, false, false, data));
|
---|
983 | if (keyItem.m_generates != 0 && !keyItem.m_lock) {
|
---|
984 | // remove key from active modifiers
|
---|
985 | std::pair<ModifierToKeys::iterator,
|
---|
986 | ModifierToKeys::iterator> range =
|
---|
987 | activeModifiers.equal_range(keyItem.m_generates);
|
---|
988 | for (ModifierToKeys::iterator i = range.first;
|
---|
989 | i != range.second; ++i) {
|
---|
990 | if (i->second.m_button == button) {
|
---|
991 | activeModifiers.erase(i);
|
---|
992 | break;
|
---|
993 | }
|
---|
994 | }
|
---|
995 |
|
---|
996 | // if no more keys for this modifier then deactivate modifier
|
---|
997 | if (activeModifiers.count(keyItem.m_generates) == 0) {
|
---|
998 | currentState &= ~keyItem.m_generates;
|
---|
999 | }
|
---|
1000 | }
|
---|
1001 | break;
|
---|
1002 |
|
---|
1003 | case kKeystrokeRepeat:
|
---|
1004 | keystrokes.push_back(Keystroke(button, false, true, data));
|
---|
1005 | keystrokes.push_back(Keystroke(button, true, true, data));
|
---|
1006 | // no modifier changes on key repeat
|
---|
1007 | break;
|
---|
1008 |
|
---|
1009 | case kKeystrokeClick:
|
---|
1010 | keystrokes.push_back(Keystroke(button, true, false, data));
|
---|
1011 | keystrokes.push_back(Keystroke(button, false, false, data));
|
---|
1012 | // no modifier changes on key click
|
---|
1013 | break;
|
---|
1014 |
|
---|
1015 | case kKeystrokeModify:
|
---|
1016 | case kKeystrokeUnmodify:
|
---|
1017 | if (keyItem.m_lock) {
|
---|
1018 | // we assume there's just one button for this modifier
|
---|
1019 | if (m_halfDuplex.count(button) > 0) {
|
---|
1020 | if (type == kKeystrokeModify) {
|
---|
1021 | // turn half-duplex toggle on (press)
|
---|
1022 | keystrokes.push_back(Keystroke(button, true, false, data));
|
---|
1023 | }
|
---|
1024 | else {
|
---|
1025 | // turn half-duplex toggle off (release)
|
---|
1026 | keystrokes.push_back(Keystroke(button, false, false, data));
|
---|
1027 | }
|
---|
1028 | }
|
---|
1029 | else {
|
---|
1030 | // toggle (click)
|
---|
1031 | keystrokes.push_back(Keystroke(button, true, false, data));
|
---|
1032 | keystrokes.push_back(Keystroke(button, false, false, data));
|
---|
1033 | }
|
---|
1034 | }
|
---|
1035 | else if (type == kKeystrokeModify) {
|
---|
1036 | // press modifier
|
---|
1037 | keystrokes.push_back(Keystroke(button, true, false, data));
|
---|
1038 | }
|
---|
1039 | else {
|
---|
1040 | // release all the keys that generate the modifier that are
|
---|
1041 | // currently down
|
---|
1042 | std::pair<ModifierToKeys::const_iterator,
|
---|
1043 | ModifierToKeys::const_iterator> range =
|
---|
1044 | activeModifiers.equal_range(keyItem.m_generates);
|
---|
1045 | for (ModifierToKeys::const_iterator i = range.first;
|
---|
1046 | i != range.second; ++i) {
|
---|
1047 | keystrokes.push_back(Keystroke(i->second.m_button,
|
---|
1048 | false, false, i->second.m_client));
|
---|
1049 | }
|
---|
1050 | }
|
---|
1051 |
|
---|
1052 | if (type == kKeystrokeModify) {
|
---|
1053 | activeModifiers.insert(std::make_pair(
|
---|
1054 | keyItem.m_generates, keyItem));
|
---|
1055 | currentState |= keyItem.m_generates;
|
---|
1056 | }
|
---|
1057 | else {
|
---|
1058 | activeModifiers.erase(keyItem.m_generates);
|
---|
1059 | currentState &= ~keyItem.m_generates;
|
---|
1060 | }
|
---|
1061 | break;
|
---|
1062 | }
|
---|
1063 | }
|
---|
1064 |
|
---|
1065 | SInt32
|
---|
1066 | CKeyMap::getNumModifiers(KeyModifierMask state)
|
---|
1067 | {
|
---|
1068 | SInt32 n = 0;
|
---|
1069 | for (; state != 0; state >>= 1) {
|
---|
1070 | if ((state & 1) != 0) {
|
---|
1071 | ++n;
|
---|
1072 | }
|
---|
1073 | }
|
---|
1074 | return n;
|
---|
1075 | }
|
---|
1076 |
|
---|
1077 | bool
|
---|
1078 | CKeyMap::isDeadKey(KeyID key)
|
---|
1079 | {
|
---|
1080 | return (key == kKeyCompose || (key >= 0x0300 && key <= 0x036f));
|
---|
1081 | }
|
---|
1082 |
|
---|
1083 | KeyID
|
---|
1084 | CKeyMap::getDeadKey(KeyID key)
|
---|
1085 | {
|
---|
1086 | if (isDeadKey(key)) {
|
---|
1087 | // already dead
|
---|
1088 | return key;
|
---|
1089 | }
|
---|
1090 |
|
---|
1091 | switch (key) {
|
---|
1092 | case '`':
|
---|
1093 | return kKeyDeadGrave;
|
---|
1094 |
|
---|
1095 | case 0xb4u:
|
---|
1096 | return kKeyDeadAcute;
|
---|
1097 |
|
---|
1098 | case '^':
|
---|
1099 | case 0x2c6:
|
---|
1100 | return kKeyDeadCircumflex;
|
---|
1101 |
|
---|
1102 | case '~':
|
---|
1103 | case 0x2dcu:
|
---|
1104 | return kKeyDeadTilde;
|
---|
1105 |
|
---|
1106 | case 0xafu:
|
---|
1107 | return kKeyDeadMacron;
|
---|
1108 |
|
---|
1109 | case 0x2d8u:
|
---|
1110 | return kKeyDeadBreve;
|
---|
1111 |
|
---|
1112 | case 0x2d9u:
|
---|
1113 | return kKeyDeadAbovedot;
|
---|
1114 |
|
---|
1115 | case 0xa8u:
|
---|
1116 | return kKeyDeadDiaeresis;
|
---|
1117 |
|
---|
1118 | case 0xb0u:
|
---|
1119 | case 0x2dau:
|
---|
1120 | return kKeyDeadAbovering;
|
---|
1121 |
|
---|
1122 | case '\"':
|
---|
1123 | case 0x2ddu:
|
---|
1124 | return kKeyDeadDoubleacute;
|
---|
1125 |
|
---|
1126 | case 0x2c7u:
|
---|
1127 | return kKeyDeadCaron;
|
---|
1128 |
|
---|
1129 | case 0xb8u:
|
---|
1130 | return kKeyDeadCedilla;
|
---|
1131 |
|
---|
1132 | case 0x2dbu:
|
---|
1133 | return kKeyDeadOgonek;
|
---|
1134 |
|
---|
1135 | default:
|
---|
1136 | // unknown
|
---|
1137 | return kKeyNone;
|
---|
1138 | }
|
---|
1139 | }
|
---|
1140 |
|
---|
1141 | CString
|
---|
1142 | CKeyMap::formatKey(KeyID key, KeyModifierMask mask)
|
---|
1143 | {
|
---|
1144 | // initialize tables
|
---|
1145 | initKeyNameMaps();
|
---|
1146 |
|
---|
1147 | CString x;
|
---|
1148 | for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) {
|
---|
1149 | KeyModifierMask mod = (1u << i);
|
---|
1150 | if ((mask & mod) != 0 && s_modifierToNameMap->count(mod) > 0) {
|
---|
1151 | x += s_modifierToNameMap->find(mod)->second;
|
---|
1152 | x += "+";
|
---|
1153 | }
|
---|
1154 | }
|
---|
1155 | if (key != kKeyNone) {
|
---|
1156 | if (s_keyToNameMap->count(key) > 0) {
|
---|
1157 | x += s_keyToNameMap->find(key)->second;
|
---|
1158 | }
|
---|
1159 | // XXX -- we're assuming ASCII here
|
---|
1160 | else if (key >= 33 && key < 127) {
|
---|
1161 | x += (char)key;
|
---|
1162 | }
|
---|
1163 | else {
|
---|
1164 | x += CStringUtil::print("\\u%04x", key);
|
---|
1165 | }
|
---|
1166 | }
|
---|
1167 | else if (!x.empty()) {
|
---|
1168 | // remove trailing '+'
|
---|
1169 | x.erase(x.size() - 1);
|
---|
1170 | }
|
---|
1171 | return x;
|
---|
1172 | }
|
---|
1173 |
|
---|
1174 | bool
|
---|
1175 | CKeyMap::parseKey(const CString& x, KeyID& key)
|
---|
1176 | {
|
---|
1177 | // initialize tables
|
---|
1178 | initKeyNameMaps();
|
---|
1179 |
|
---|
1180 | // parse the key
|
---|
1181 | key = kKeyNone;
|
---|
1182 | if (s_nameToKeyMap->count(x) > 0) {
|
---|
1183 | key = s_nameToKeyMap->find(x)->second;
|
---|
1184 | }
|
---|
1185 | // XXX -- we're assuming ASCII encoding here
|
---|
1186 | else if (x.size() == 1) {
|
---|
1187 | if (!isgraph(x[0])) {
|
---|
1188 | // unknown key
|
---|
1189 | return false;
|
---|
1190 | }
|
---|
1191 | key = (KeyID)x[0];
|
---|
1192 | }
|
---|
1193 | else if (x.size() == 6 && x[0] == '\\' && x[1] == 'u') {
|
---|
1194 | // escaped unicode (\uXXXX where XXXX is a hex number)
|
---|
1195 | char* end;
|
---|
1196 | key = (KeyID)strtol(x.c_str() + 2, &end, 16);
|
---|
1197 | if (*end != '\0') {
|
---|
1198 | return false;
|
---|
1199 | }
|
---|
1200 | }
|
---|
1201 | else if (!x.empty()) {
|
---|
1202 | // unknown key
|
---|
1203 | return false;
|
---|
1204 | }
|
---|
1205 |
|
---|
1206 | return true;
|
---|
1207 | }
|
---|
1208 |
|
---|
1209 | bool
|
---|
1210 | CKeyMap::parseModifiers(CString& x, KeyModifierMask& mask)
|
---|
1211 | {
|
---|
1212 | // initialize tables
|
---|
1213 | initKeyNameMaps();
|
---|
1214 |
|
---|
1215 | mask = 0;
|
---|
1216 | CString::size_type tb = x.find_first_not_of(" \t", 0);
|
---|
1217 | while (tb != CString::npos) {
|
---|
1218 | // get next component
|
---|
1219 | CString::size_type te = x.find_first_of(" \t+)", tb);
|
---|
1220 | if (te == CString::npos) {
|
---|
1221 | te = x.size();
|
---|
1222 | }
|
---|
1223 | CString c = x.substr(tb, te - tb);
|
---|
1224 | if (c.empty()) {
|
---|
1225 | // missing component
|
---|
1226 | return false;
|
---|
1227 | }
|
---|
1228 |
|
---|
1229 | if (s_nameToModifierMap->count(c) > 0) {
|
---|
1230 | KeyModifierMask mod = s_nameToModifierMap->find(c)->second;
|
---|
1231 | if ((mask & mod) != 0) {
|
---|
1232 | // modifier appears twice
|
---|
1233 | return false;
|
---|
1234 | }
|
---|
1235 | mask |= mod;
|
---|
1236 | }
|
---|
1237 | else {
|
---|
1238 | // unknown string
|
---|
1239 | x.erase(0, tb);
|
---|
1240 | CString::size_type tb = x.find_first_not_of(" \t");
|
---|
1241 | CString::size_type te = x.find_last_not_of(" \t");
|
---|
1242 | if (tb == CString::npos) {
|
---|
1243 | x = "";
|
---|
1244 | }
|
---|
1245 | else {
|
---|
1246 | x = x.substr(tb, te - tb + 1);
|
---|
1247 | }
|
---|
1248 | return true;
|
---|
1249 | }
|
---|
1250 |
|
---|
1251 | // check for '+' or end of string
|
---|
1252 | tb = x.find_first_not_of(" \t", te);
|
---|
1253 | if (tb != CString::npos) {
|
---|
1254 | if (x[tb] != '+') {
|
---|
1255 | // expected '+'
|
---|
1256 | return false;
|
---|
1257 | }
|
---|
1258 | tb = x.find_first_not_of(" \t", tb + 1);
|
---|
1259 | }
|
---|
1260 | }
|
---|
1261 |
|
---|
1262 | // parsed the whole thing
|
---|
1263 | x = "";
|
---|
1264 | return true;
|
---|
1265 | }
|
---|
1266 |
|
---|
1267 | void
|
---|
1268 | CKeyMap::initKeyNameMaps()
|
---|
1269 | {
|
---|
1270 | // initialize tables
|
---|
1271 | if (s_nameToKeyMap == NULL) {
|
---|
1272 | s_nameToKeyMap = new CNameToKeyMap;
|
---|
1273 | s_keyToNameMap = new CKeyToNameMap;
|
---|
1274 | for (const KeyNameMapEntry* i = kKeyNameMap; i->m_name != NULL; ++i) {
|
---|
1275 | (*s_nameToKeyMap)[i->m_name] = i->m_id;
|
---|
1276 | (*s_keyToNameMap)[i->m_id] = i->m_name;
|
---|
1277 | }
|
---|
1278 | }
|
---|
1279 | if (s_nameToModifierMap == NULL) {
|
---|
1280 | s_nameToModifierMap = new CNameToModifierMap;
|
---|
1281 | s_modifierToNameMap = new CModifierToNameMap;
|
---|
1282 | for (const KeyModifierNameMapEntry* i = kModifierNameMap;
|
---|
1283 | i->m_name != NULL; ++i) {
|
---|
1284 | (*s_nameToModifierMap)[i->m_name] = i->m_mask;
|
---|
1285 | (*s_modifierToNameMap)[i->m_mask] = i->m_name;
|
---|
1286 | }
|
---|
1287 | }
|
---|
1288 | }
|
---|
1289 |
|
---|
1290 |
|
---|
1291 | //
|
---|
1292 | // CKeyMap::KeyItem
|
---|
1293 | //
|
---|
1294 |
|
---|
1295 | bool
|
---|
1296 | CKeyMap::KeyItem::operator==(const KeyItem& x) const
|
---|
1297 | {
|
---|
1298 | return (m_id == x.m_id &&
|
---|
1299 | m_group == x.m_group &&
|
---|
1300 | m_button == x.m_button &&
|
---|
1301 | m_required == x.m_required &&
|
---|
1302 | m_sensitive == x.m_sensitive &&
|
---|
1303 | m_generates == x.m_generates &&
|
---|
1304 | m_dead == x.m_dead &&
|
---|
1305 | m_lock == x.m_lock &&
|
---|
1306 | m_client == x.m_client);
|
---|
1307 | }
|
---|
1308 |
|
---|
1309 |
|
---|
1310 | //
|
---|
1311 | // CKeyMap::Keystroke
|
---|
1312 | //
|
---|
1313 |
|
---|
1314 | CKeyMap::Keystroke::Keystroke(KeyButton button,
|
---|
1315 | bool press, bool repeat, UInt32 data) :
|
---|
1316 | m_type(kButton)
|
---|
1317 | {
|
---|
1318 | m_data.m_button.m_button = button;
|
---|
1319 | m_data.m_button.m_press = press;
|
---|
1320 | m_data.m_button.m_repeat = repeat;
|
---|
1321 | m_data.m_button.m_client = data;
|
---|
1322 | }
|
---|
1323 |
|
---|
1324 | CKeyMap::Keystroke::Keystroke(SInt32 group, bool absolute, bool restore) :
|
---|
1325 | m_type(kGroup)
|
---|
1326 | {
|
---|
1327 | m_data.m_group.m_group = group;
|
---|
1328 | m_data.m_group.m_absolute = absolute;
|
---|
1329 | m_data.m_group.m_restore = restore;
|
---|
1330 | }
|
---|