source: trunk/synergy/lib/platform/CXWindowsKeyState.cpp

Last change on this file was 2768, checked in by bird, 19 years ago

Expanded m_client to 64-bit. Finally managed to hack together a getKeyMap and fakeKey for PM.

File size: 23.1 KB
Line 
1/*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2003 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 "CXWindowsKeyState.h"
16#include "CXWindowsUtil.h"
17#include "CLog.h"
18#include "CStringUtil.h"
19#include "stdmap.h"
20#if X_DISPLAY_MISSING
21# error X11 is required to build synergy
22#else
23# include <X11/X.h>
24# include <X11/Xutil.h>
25# define XK_MISCELLANY
26# define XK_XKB_KEYS
27# include <X11/keysymdef.h>
28#if HAVE_XKB_EXTENSION
29# include <X11/XKBlib.h>
30#endif
31#endif
32
33CXWindowsKeyState::CXWindowsKeyState(Display* display, bool useXKB) :
34 m_display(display)
35{
36 XGetKeyboardControl(m_display, &m_keyboardState);
37#if HAVE_XKB_EXTENSION
38 if (useXKB) {
39 m_xkb = XkbGetMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask |
40 XkbAllClientInfoMask, XkbUseCoreKbd);
41 }
42 else {
43 m_xkb = NULL;
44 }
45#endif
46 setActiveGroup(kGroupPollAndSet);
47}
48
49CXWindowsKeyState::~CXWindowsKeyState()
50{
51#if HAVE_XKB_EXTENSION
52 if (m_xkb != NULL) {
53 XkbFreeKeyboard(m_xkb, 0, True);
54 }
55#endif
56}
57
58void
59CXWindowsKeyState::setActiveGroup(SInt32 group)
60{
61 if (group == kGroupPollAndSet) {
62 m_group = -1;
63 m_group = pollActiveGroup();
64 }
65 else if (group == kGroupPoll) {
66 m_group = -1;
67 }
68 else {
69 assert(group >= 0);
70 m_group = group;
71 }
72}
73
74void
75CXWindowsKeyState::setAutoRepeat(const XKeyboardState& state)
76{
77 m_keyboardState = state;
78}
79
80KeyModifierMask
81CXWindowsKeyState::mapModifiersFromX(unsigned int state) const
82{
83 UInt32 offset = 8 * getGroupFromState(state);
84 KeyModifierMask mask = 0;
85 for (int i = 0; i < 8; ++i) {
86 if ((state & (1u << i)) != 0) {
87 mask |= m_modifierFromX[offset + i];
88 }
89 }
90 return mask;
91}
92
93bool
94CXWindowsKeyState::mapModifiersToX(KeyModifierMask mask,
95 unsigned int& modifiers) const
96{
97 modifiers = 0;
98
99 for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) {
100 KeyModifierMask bit = (1u << i);
101 if ((mask & bit) != 0) {
102 KeyModifierToXMask::const_iterator j = m_modifierToX.find(bit);
103 if (j == m_modifierToX.end()) {
104 return false;
105 }
106 else {
107 modifiers |= j->second;
108 }
109 }
110 }
111
112 return true;
113}
114
115void
116CXWindowsKeyState::mapKeyToKeycodes(KeyID key, CKeycodeList& keycodes) const
117{
118 keycodes.clear();
119 std::pair<KeyToKeyCodeMap::const_iterator,
120 KeyToKeyCodeMap::const_iterator> range =
121 m_keyCodeFromKey.equal_range(key);
122 for (KeyToKeyCodeMap::const_iterator i = range.first;
123 i != range.second; ++i) {
124 keycodes.push_back(i->second);
125 }
126}
127
128bool
129CXWindowsKeyState::fakeCtrlAltDel()
130{
131 // pass keys through unchanged
132 return false;
133}
134
135KeyModifierMask
136CXWindowsKeyState::pollActiveModifiers() const
137{
138 Window root = DefaultRootWindow(m_display), window;
139 int xRoot, yRoot, xWindow, yWindow;
140 unsigned int state;
141 if (!XQueryPointer(m_display, root, &root, &window,
142 &xRoot, &yRoot, &xWindow, &yWindow, &state)) {
143 state = 0;
144 }
145 return mapModifiersFromX(state);
146}
147
148SInt32
149CXWindowsKeyState::pollActiveGroup() const
150{
151 if (m_group != -1) {
152 assert(m_group >= 0);
153 return m_group;
154 }
155
156#if HAVE_XKB_EXTENSION
157 if (m_xkb != NULL) {
158 XkbStateRec state;
159 if (XkbGetState(m_display, XkbUseCoreKbd, &state)) {
160 return state.group;
161 }
162 }
163#endif
164 return 0;
165}
166
167void
168CXWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const
169{
170 char keys[32];
171 XQueryKeymap(m_display, keys);
172 for (UInt32 i = 0; i < 32; ++i) {
173 for (UInt32 j = 0; j < 8; ++j) {
174 if ((keys[i] & (1u << j)) != 0) {
175 pressedKeys.insert(8 * i + j);
176 }
177 }
178 }
179}
180
181void
182CXWindowsKeyState::getKeyMap(CKeyMap& keyMap)
183{
184 // get autorepeat info. we must use the global_auto_repeat told to
185 // us because it may have modified by synergy.
186 int oldGlobalAutoRepeat = m_keyboardState.global_auto_repeat;
187 XGetKeyboardControl(m_display, &m_keyboardState);
188 m_keyboardState.global_auto_repeat = oldGlobalAutoRepeat;
189
190#if HAVE_XKB_EXTENSION
191 if (m_xkb != NULL) {
192 XkbGetUpdatedMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask |
193 XkbAllClientInfoMask, m_xkb);
194 updateKeysymMapXKB(keyMap);
195 }
196 else
197#endif
198 {
199 updateKeysymMap(keyMap);
200 }
201}
202
203void
204CXWindowsKeyState::fakeKey(const Keystroke& keystroke)
205{
206 switch (keystroke.m_type) {
207 case Keystroke::kButton:
208 LOG((CLOG_DEBUG1 " %03x (%08llx) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up"));
209 if (keystroke.m_data.m_button.m_repeat) {
210 int c = keystroke.m_data.m_button.m_button;
211 int i = (c >> 3);
212 int b = 1 << (c & 7);
213 if (m_keyboardState.global_auto_repeat == AutoRepeatModeOff ||
214 (m_keyboardState.auto_repeats[i] & b) == 0) {
215 LOG((CLOG_DEBUG1 " discard autorepeat"));
216 break;
217 }
218 }
219 XTestFakeKeyEvent(m_display, keystroke.m_data.m_button.m_button,
220 keystroke.m_data.m_button.m_press ? True : False,
221 CurrentTime);
222 break;
223
224 case Keystroke::kGroup:
225 if (keystroke.m_data.m_group.m_absolute) {
226 LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group));
227#if HAVE_XKB_EXTENSION
228 if (m_xkb != NULL) {
229 XkbLockGroup(m_display, XkbUseCoreKbd,
230 keystroke.m_data.m_group.m_group);
231 }
232 else
233#endif
234 {
235 LOG((CLOG_DEBUG1 " ignored"));
236 }
237 }
238 else {
239 LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group));
240#if HAVE_XKB_EXTENSION
241 if (m_xkb != NULL) {
242 XkbLockGroup(m_display, XkbUseCoreKbd,
243 getEffectiveGroup(pollActiveGroup(),
244 keystroke.m_data.m_group.m_group));
245 }
246 else
247#endif
248 {
249 LOG((CLOG_DEBUG1 " ignored"));
250 }
251 }
252 break;
253 }
254 XFlush(m_display);
255}
256
257void
258CXWindowsKeyState::updateKeysymMap(CKeyMap& keyMap)
259{
260 // there are up to 4 keysyms per keycode
261 static const int maxKeysyms = 4;
262
263 LOG((CLOG_DEBUG1 "non-XKB mapping"));
264
265 // prepare map from X modifier to KeyModifierMask. certain bits
266 // are predefined.
267 m_modifierFromX.clear();
268 m_modifierFromX.resize(8);
269 m_modifierFromX[ShiftMapIndex] = KeyModifierShift;
270 m_modifierFromX[LockMapIndex] = KeyModifierCapsLock;
271 m_modifierFromX[ControlMapIndex] = KeyModifierControl;
272 m_modifierToX.clear();
273 m_modifierToX[KeyModifierShift] = ShiftMask;
274 m_modifierToX[KeyModifierCapsLock] = LockMask;
275 m_modifierToX[KeyModifierControl] = ControlMask;
276
277 // prepare map from KeyID to KeyCode
278 m_keyCodeFromKey.clear();
279
280 // get the number of keycodes
281 int minKeycode, maxKeycode;
282 XDisplayKeycodes(m_display, &minKeycode, &maxKeycode);
283 int numKeycodes = maxKeycode - minKeycode + 1;
284
285 // get the keyboard mapping for all keys
286 int keysymsPerKeycode;
287 KeySym* allKeysyms = XGetKeyboardMapping(m_display,
288 minKeycode, numKeycodes,
289 &keysymsPerKeycode);
290
291 // it's more convenient to always have maxKeysyms KeySyms per key
292 {
293 KeySym* tmpKeysyms = new KeySym[maxKeysyms * numKeycodes];
294 for (int i = 0; i < numKeycodes; ++i) {
295 for (int j = 0; j < maxKeysyms; ++j) {
296 if (j < keysymsPerKeycode) {
297 tmpKeysyms[maxKeysyms * i + j] =
298 allKeysyms[keysymsPerKeycode * i + j];
299 }
300 else {
301 tmpKeysyms[maxKeysyms * i + j] = NoSymbol;
302 }
303 }
304 }
305 XFree(allKeysyms);
306 allKeysyms = tmpKeysyms;
307 }
308
309 // get the buttons assigned to modifiers. X11 does not predefine
310 // the meaning of any modifiers except shift, caps lock, and the
311 // control key. the meaning of a modifier bit (other than those)
312 // depends entirely on the KeySyms mapped to that bit. unfortunately
313 // you cannot map a bit back to the KeySym used to produce it.
314 // for example, let's say button 1 maps to Alt_L without shift and
315 // Meta_L with shift. now if mod1 is mapped to button 1 that could
316 // mean the user used Alt or Meta to turn on that modifier and there's
317 // no way to know which. it's also possible for one button to be
318 // mapped to multiple bits so both mod1 and mod2 could be generated
319 // by button 1.
320 //
321 // we're going to ignore any modifier for a button except the first.
322 // with the above example, that means we'll ignore the mod2 modifier
323 // bit unless it's also mapped to some other button. we're also
324 // going to ignore all KeySyms except the first modifier KeySym,
325 // which means button 1 above won't map to Meta, just Alt.
326 std::map<KeyCode, unsigned int> modifierButtons;
327 XModifierKeymap* modifiers = XGetModifierMapping(m_display);
328 for (unsigned int i = 0; i < 8; ++i) {
329 const KeyCode* buttons =
330 modifiers->modifiermap + i * modifiers->max_keypermod;
331 for (int j = 0; j < modifiers->max_keypermod; ++j) {
332 modifierButtons.insert(std::make_pair(buttons[j], i));
333 }
334 }
335 XFreeModifiermap(modifiers);
336 modifierButtons.erase(0);
337
338 // Hack to deal with VMware. When a VMware client grabs input the
339 // player clears out the X modifier map for whatever reason. We're
340 // notified of the change and arrive here to discover that there
341 // are no modifiers at all. Since this prevents the modifiers from
342 // working in the VMware client we'll use the last known good set
343 // of modifiers when there are no modifiers. If there are modifiers
344 // we update the last known good set.
345 if (!modifierButtons.empty()) {
346 m_lastGoodNonXKBModifiers = modifierButtons;
347 }
348 else {
349 modifierButtons = m_lastGoodNonXKBModifiers;
350 }
351
352 // add entries for each keycode
353 CKeyMap::KeyItem item;
354 for (int i = 0; i < numKeycodes; ++i) {
355 KeySym* keysyms = allKeysyms + maxKeysyms * i;
356 KeyCode keycode = static_cast<KeyCode>(i + minKeycode);
357 item.m_button = static_cast<KeyButton>(keycode);
358 item.m_client = 0;
359
360 // determine modifier sensitivity
361 item.m_sensitive = 0;
362
363 // if the keysyms in levels 2 or 3 exist and differ from levels
364 // 0 and 1 then the key is sensitive AltGr (Mode_switch)
365 if ((keysyms[2] != NoSymbol && keysyms[2] != keysyms[0]) ||
366 (keysyms[3] != NoSymbol && keysyms[2] != keysyms[1])) {
367 item.m_sensitive |= KeyModifierAltGr;
368 }
369
370 // check if the key is caps-lock sensitive. some systems only
371 // provide one keysym for keys sensitive to caps-lock. if we
372 // find that then fill in the missing keysym.
373 if (keysyms[0] != NoSymbol && keysyms[1] == NoSymbol &&
374 keysyms[2] == NoSymbol && keysyms[3] == NoSymbol) {
375 KeySym lKeysym, uKeysym;
376 XConvertCase(keysyms[0], &lKeysym, &uKeysym);
377 if (lKeysym != uKeysym) {
378 keysyms[0] = lKeysym;
379 keysyms[1] = uKeysym;
380 item.m_sensitive |= KeyModifierCapsLock;
381 }
382 }
383 else if (keysyms[0] != NoSymbol && keysyms[1] != NoSymbol) {
384 KeySym lKeysym, uKeysym;
385 XConvertCase(keysyms[0], &lKeysym, &uKeysym);
386 if (lKeysym != uKeysym &&
387 lKeysym == keysyms[0] &&
388 uKeysym == keysyms[1]) {
389 item.m_sensitive |= KeyModifierCapsLock;
390 }
391 else if (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol) {
392 XConvertCase(keysyms[2], &lKeysym, &uKeysym);
393 if (lKeysym != uKeysym &&
394 lKeysym == keysyms[2] &&
395 uKeysym == keysyms[3]) {
396 item.m_sensitive |= KeyModifierCapsLock;
397 }
398 }
399 }
400
401 // key is sensitive to shift if keysyms in levels 0 and 1 or
402 // levels 2 and 3 don't match. it's also sensitive to shift
403 // if it's sensitive to caps-lock.
404 if ((item.m_sensitive & KeyModifierCapsLock) != 0) {
405 item.m_sensitive |= KeyModifierShift;
406 }
407 else if ((keysyms[0] != NoSymbol && keysyms[1] != NoSymbol &&
408 keysyms[0] != keysyms[1]) ||
409 (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol &&
410 keysyms[2] != keysyms[3])) {
411 item.m_sensitive |= KeyModifierShift;
412 }
413
414 // key is sensitive to numlock if any keysym on it is
415 if (IsKeypadKey(keysyms[0]) || IsPrivateKeypadKey(keysyms[0]) ||
416 IsKeypadKey(keysyms[1]) || IsPrivateKeypadKey(keysyms[1]) ||
417 IsKeypadKey(keysyms[2]) || IsPrivateKeypadKey(keysyms[2]) ||
418 IsKeypadKey(keysyms[3]) || IsPrivateKeypadKey(keysyms[3])) {
419 item.m_sensitive |= KeyModifierNumLock;
420 }
421
422 // do each keysym (shift level)
423 for (int j = 0; j < maxKeysyms; ++j) {
424 item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[j]);
425 if (item.m_id == kKeyNone) {
426 if (j != 0 && modifierButtons.count(keycode) > 0) {
427 // pretend the modifier works in other shift levels
428 // because it probably does.
429 if (keysyms[1] == NoSymbol || j != 3) {
430 item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[0]);
431 }
432 else {
433 item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[1]);
434 }
435 }
436 if (item.m_id == kKeyNone) {
437 continue;
438 }
439 }
440
441 // group is 0 for levels 0 and 1 and 1 for levels 2 and 3
442 item.m_group = (j >= 2) ? 1 : 0;
443
444 // compute required modifiers
445 item.m_required = 0;
446 if ((j & 1) != 0) {
447 item.m_required |= KeyModifierShift;
448 }
449 if ((j & 2) != 0) {
450 item.m_required |= KeyModifierAltGr;
451 }
452
453 item.m_generates = 0;
454 item.m_lock = false;
455 if (modifierButtons.count(keycode) > 0) {
456 // get flags for modifier keys
457 CKeyMap::initModifierKey(item);
458
459 // add mapping from X (unless we already have)
460 if (item.m_generates != 0) {
461 unsigned int bit = modifierButtons[keycode];
462 if (m_modifierFromX[bit] == 0) {
463 m_modifierFromX[bit] = item.m_generates;
464 m_modifierToX[item.m_generates] = (1u << bit);
465 }
466 }
467 }
468
469 // add key
470 keyMap.addKeyEntry(item);
471 m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode));
472
473 // add other ways to synthesize the key
474 if ((j & 1) != 0) {
475 // add capslock version of key is sensitive to capslock
476 KeySym lKeysym, uKeysym;
477 XConvertCase(keysyms[j], &lKeysym, &uKeysym);
478 if (lKeysym != uKeysym &&
479 lKeysym == keysyms[j - 1] &&
480 uKeysym == keysyms[j]) {
481 item.m_required &= ~KeyModifierShift;
482 item.m_required |= KeyModifierCapsLock;
483 keyMap.addKeyEntry(item);
484 item.m_required |= KeyModifierShift;
485 item.m_required &= ~KeyModifierCapsLock;
486 }
487
488 // add numlock version of key if sensitive to numlock
489 if (IsKeypadKey(keysyms[j]) || IsPrivateKeypadKey(keysyms[j])) {
490 item.m_required &= ~KeyModifierShift;
491 item.m_required |= KeyModifierNumLock;
492 keyMap.addKeyEntry(item);
493 item.m_required |= KeyModifierShift;
494 item.m_required &= ~KeyModifierNumLock;
495 }
496 }
497 }
498 }
499
500 delete[] allKeysyms;
501}
502
503#if HAVE_XKB_EXTENSION
504void
505CXWindowsKeyState::updateKeysymMapXKB(CKeyMap& keyMap)
506{
507 static const XkbKTMapEntryRec defMapEntry = {
508 True, // active
509 0, // level
510 {
511 0, // mods.mask
512 0, // mods.real_mods
513 0 // mods.vmods
514 }
515 };
516
517 LOG((CLOG_DEBUG1 "XKB mapping"));
518
519 // find the number of groups
520 int maxNumGroups = 0;
521 for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
522 int numGroups = XkbKeyNumGroups(m_xkb, static_cast<KeyCode>(i));
523 if (numGroups > maxNumGroups) {
524 maxNumGroups = numGroups;
525 }
526 }
527
528 // prepare map from X modifier to KeyModifierMask
529 std::vector<int> modifierLevel(maxNumGroups * 8, 4);
530 m_modifierFromX.clear();
531 m_modifierFromX.resize(maxNumGroups * 8);
532 m_modifierToX.clear();
533
534 // prepare map from KeyID to KeyCode
535 m_keyCodeFromKey.clear();
536
537 // Hack to deal with VMware. When a VMware client grabs input the
538 // player clears out the X modifier map for whatever reason. We're
539 // notified of the change and arrive here to discover that there
540 // are no modifiers at all. Since this prevents the modifiers from
541 // working in the VMware client we'll use the last known good set
542 // of modifiers when there are no modifiers. If there are modifiers
543 // we update the last known good set.
544 bool useLastGoodModifiers = !hasModifiersXKB();
545 if (!useLastGoodModifiers) {
546 m_lastGoodXKBModifiers.clear();
547 }
548
549 // check every button. on this pass we save all modifiers as native
550 // X modifier masks.
551 CKeyMap::KeyItem item;
552 for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
553 KeyCode keycode = static_cast<KeyCode>(i);
554 item.m_button = static_cast<KeyButton>(keycode);
555 item.m_client = 0;
556
557 // skip keys with no groups (they generate no symbols)
558 if (XkbKeyNumGroups(m_xkb, keycode) == 0) {
559 continue;
560 }
561
562 // note half-duplex keys
563 const XkbBehavior& b = m_xkb->server->behaviors[keycode];
564 if ((b.type & XkbKB_OpMask) == XkbKB_Lock) {
565 keyMap.addHalfDuplexButton(item.m_button);
566 }
567
568 // iterate over all groups
569 for (int group = 0; group < maxNumGroups; ++group) {
570 item.m_group = group;
571 int eGroup = getEffectiveGroup(keycode, group);
572
573 // get key info
574 XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, eGroup);
575
576 // set modifiers the item is sensitive to
577 item.m_sensitive = type->mods.mask;
578
579 // iterate over all shift levels for the button (including none)
580 for (int j = -1; j < type->map_count; ++j) {
581 const XkbKTMapEntryRec* mapEntry =
582 ((j == -1) ? &defMapEntry : type->map + j);
583 if (!mapEntry->active) {
584 continue;
585 }
586 int level = mapEntry->level;
587
588 // set required modifiers for this item
589 item.m_required = mapEntry->mods.mask;
590 if ((item.m_required & LockMask) != 0 &&
591 j != -1 && type->preserve != NULL &&
592 (type->preserve[j].mask & LockMask) != 0) {
593 // sensitive caps lock and we preserve caps-lock.
594 // preserving caps-lock means we Xlib functions would
595 // yield the capitialized KeySym so we'll adjust the
596 // level accordingly.
597 if ((level ^ 1) < type->num_levels) {
598 level ^= 1;
599 }
600 }
601
602 // get the keysym for this item
603 KeySym keysym = XkbKeySymEntry(m_xkb, keycode, level, eGroup);
604
605 // check for group change actions, locking modifiers, and
606 // modifier masks.
607 item.m_lock = false;
608 bool isModifier = false;
609 UInt32 modifierMask = m_xkb->map->modmap[keycode];
610 if (XkbKeyHasActions(m_xkb, keycode)) {
611 XkbAction* action =
612 XkbKeyActionEntry(m_xkb, keycode, level, eGroup);
613 if (action->type == XkbSA_SetMods ||
614 action->type == XkbSA_LockMods) {
615 isModifier = true;
616
617 // note toggles
618 item.m_lock = (action->type == XkbSA_LockMods);
619
620 // maybe use action's mask
621 if ((action->mods.flags & XkbSA_UseModMapMods) == 0) {
622 modifierMask = action->mods.mask;
623 }
624 }
625 else if (action->type == XkbSA_SetGroup ||
626 action->type == XkbSA_LatchGroup ||
627 action->type == XkbSA_LockGroup) {
628 // ignore group change key
629 continue;
630 }
631 }
632 level = mapEntry->level;
633
634 // VMware modifier hack
635 if (useLastGoodModifiers) {
636 XKBModifierMap::const_iterator k =
637 m_lastGoodXKBModifiers.find(eGroup * 256 + keycode);
638 if (k != m_lastGoodXKBModifiers.end()) {
639 // Use last known good modifier
640 isModifier = true;
641 level = k->second.m_level;
642 modifierMask = k->second.m_mask;
643 item.m_lock = k->second.m_lock;
644 }
645 }
646 else if (isModifier) {
647 // Save known good modifier
648 XKBModifierInfo& info =
649 m_lastGoodXKBModifiers[eGroup * 256 + keycode];
650 info.m_level = level;
651 info.m_mask = modifierMask;
652 info.m_lock = item.m_lock;
653 }
654
655 // record the modifier mask for this key. don't bother
656 // for keys that change the group.
657 item.m_generates = 0;
658 UInt32 modifierBit =
659 CXWindowsUtil::getModifierBitForKeySym(keysym);
660 if (isModifier && modifierBit != kKeyModifierBitNone) {
661 item.m_generates = (1u << modifierBit);
662 for (SInt32 j = 0; j < 8; ++j) {
663 // skip modifiers this key doesn't generate
664 if ((modifierMask & (1u << j)) == 0) {
665 continue;
666 }
667
668 // skip keys that map to a modifier that we've
669 // already seen using fewer modifiers. that is
670 // if this key must combine with other modifiers
671 // and we know of a key that combines with fewer
672 // modifiers (or no modifiers) then prefer the
673 // other key.
674 if (level >= modifierLevel[8 * group + j]) {
675 continue;
676 }
677 modifierLevel[8 * group + j] = level;
678
679 // save modifier
680 m_modifierFromX[8 * group + j] |= (1u << modifierBit);
681 m_modifierToX.insert(std::make_pair(
682 1u << modifierBit, 1u << j));
683 }
684 }
685
686 // handle special cases of just one keysym for the keycode
687 if (type->num_levels == 1) {
688 // if there are upper- and lowercase versions of the
689 // keysym then add both.
690 KeySym lKeysym, uKeysym;
691 XConvertCase(keysym, &lKeysym, &uKeysym);
692 if (lKeysym != uKeysym) {
693 if (j != -1) {
694 continue;
695 }
696
697 item.m_sensitive |= ShiftMask | LockMask;
698
699 KeyID lKeyID = CXWindowsUtil::mapKeySymToKeyID(lKeysym);
700 KeyID uKeyID = CXWindowsUtil::mapKeySymToKeyID(uKeysym);
701 if (lKeyID == kKeyNone || uKeyID == kKeyNone) {
702 continue;
703 }
704
705 item.m_id = lKeyID;
706 item.m_required = 0;
707 keyMap.addKeyEntry(item);
708
709 item.m_id = uKeyID;
710 item.m_required = ShiftMask;
711 keyMap.addKeyEntry(item);
712 item.m_required = LockMask;
713 keyMap.addKeyEntry(item);
714
715 if (group == 0) {
716 m_keyCodeFromKey.insert(
717 std::make_pair(lKeyID, keycode));
718 m_keyCodeFromKey.insert(
719 std::make_pair(uKeyID, keycode));
720 }
721 continue;
722 }
723 }
724
725 // add entry
726 item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysym);
727 keyMap.addKeyEntry(item);
728 if (group == 0) {
729 m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode));
730 }
731 }
732 }
733 }
734
735 // change all modifier masks to synergy masks from X masks
736 keyMap.foreachKey(&CXWindowsKeyState::remapKeyModifiers, this);
737
738 // allow composition across groups
739 keyMap.allowGroupSwitchDuringCompose();
740}
741#endif
742
743void
744CXWindowsKeyState::remapKeyModifiers(KeyID id, SInt32 group,
745 CKeyMap::KeyItem& item, void* vself)
746{
747 CXWindowsKeyState* self = reinterpret_cast<CXWindowsKeyState*>(vself);
748 item.m_required =
749 self->mapModifiersFromX(XkbBuildCoreState(item.m_required, group));
750 item.m_sensitive =
751 self->mapModifiersFromX(XkbBuildCoreState(item.m_sensitive, group));
752}
753
754bool
755CXWindowsKeyState::hasModifiersXKB() const
756{
757#if HAVE_XKB_EXTENSION
758 // iterate over all keycodes
759 for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
760 KeyCode keycode = static_cast<KeyCode>(i);
761 if (XkbKeyHasActions(m_xkb, keycode)) {
762 // iterate over all groups
763 int numGroups = XkbKeyNumGroups(m_xkb, keycode);
764 for (int group = 0; group < numGroups; ++group) {
765 // iterate over all shift levels for the button (including none)
766 XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, group);
767 for (int j = -1; j < type->map_count; ++j) {
768 if (j != -1 && !type->map[j].active) {
769 continue;
770 }
771 int level = ((j == -1) ? 0 : type->map[j].level);
772 XkbAction* action =
773 XkbKeyActionEntry(m_xkb, keycode, level, group);
774 if (action->type == XkbSA_SetMods ||
775 action->type == XkbSA_LockMods) {
776 return true;
777 }
778 }
779 }
780 }
781 }
782#endif
783 return false;
784}
785
786int
787CXWindowsKeyState::getEffectiveGroup(KeyCode keycode, int group) const
788{
789 (void)keycode;
790#if HAVE_XKB_EXTENSION
791 // get effective group for key
792 int numGroups = XkbKeyNumGroups(m_xkb, keycode);
793 if (group >= numGroups) {
794 unsigned char groupInfo = XkbKeyGroupInfo(m_xkb, keycode);
795 switch (XkbOutOfRangeGroupAction(groupInfo)) {
796 case XkbClampIntoRange:
797 group = numGroups - 1;
798 break;
799
800 case XkbRedirectIntoRange:
801 group = XkbOutOfRangeGroupNumber(groupInfo);
802 if (group >= numGroups) {
803 group = 0;
804 }
805 break;
806
807 default:
808 // wrap
809 group %= numGroups;
810 break;
811 }
812 }
813#endif
814 return group;
815}
816
817UInt32
818CXWindowsKeyState::getGroupFromState(unsigned int state) const
819{
820#if HAVE_XKB_EXTENSION
821 if (m_xkb != NULL) {
822 return XkbGroupForCoreState(state);
823 }
824#endif
825 return 0;
826}
Note: See TracBrowser for help on using the repository browser.