source: trunk/synergy/lib/platform/COSXKeyState.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: 28.9 KB
Line 
1/*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2004 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 "COSXKeyState.h"
16#include "CLog.h"
17#include "CArch.h"
18
19// Hardcoded virtual key table. Oddly, Apple doesn't document the
20// meaning of virtual key codes. The whole point of *virtual* key
21// codes is to make them hardware independent so these codes should
22// be constant across OS versions and hardware. Yet they don't
23// tell us what codes map to what keys so we have to figure it out
24// for ourselves.
25//
26// Note that some virtual keys codes appear more than once. The
27// first instance of a virtual key code maps to the KeyID that we
28// want to generate for that code. The others are for mapping
29// different KeyIDs to a single key code.
30static const UInt32 s_shiftVK = 56;
31static const UInt32 s_controlVK = 59;
32static const UInt32 s_altVK = 55;
33static const UInt32 s_superVK = 58;
34static const UInt32 s_capsLockVK = 57;
35struct CKeyEntry {
36public:
37 KeyID m_keyID;
38 UInt32 m_virtualKey;
39};
40static const CKeyEntry s_controlKeys[] = {
41 // cursor keys. if we don't do this we'll may still get these from
42 // the keyboard resource but they may not correspond to the arrow
43 // keys.
44 { kKeyLeft, 123 },
45 { kKeyRight, 124 },
46 { kKeyUp, 126 },
47 { kKeyDown, 125 },
48 { kKeyHome, 115 },
49 { kKeyEnd, 119 },
50 { kKeyPageUp, 116 },
51 { kKeyPageDown, 121 },
52
53 // function keys
54 { kKeyF1, 122 },
55 { kKeyF2, 120 },
56 { kKeyF3, 99 },
57 { kKeyF4, 118 },
58 { kKeyF5, 96 },
59 { kKeyF6, 97 },
60 { kKeyF7, 98 },
61 { kKeyF8, 100 },
62 { kKeyF9, 101 },
63 { kKeyF10, 109 },
64 { kKeyF11, 103 },
65 { kKeyF12, 111 },
66 { kKeyF13, 105 },
67 { kKeyF14, 107 },
68 { kKeyF15, 113 },
69 { kKeyF16, 106 },
70
71 // virtual key 110 is fn+enter and i have no idea what that's supposed
72 // to map to. also the enter key with numlock on is a modifier but i
73 // don't know which.
74
75 // modifier keys. OS X doesn't seem to support right handed versions
76 // of modifier keys so we map them to the left handed versions.
77 { kKeyShift_L, s_shiftVK },
78 { kKeyShift_R, s_shiftVK }, // 60
79 { kKeyControl_L, s_controlVK },
80 { kKeyControl_R, s_controlVK }, // 62
81 { kKeyAlt_L, s_altVK },
82 { kKeyAlt_R, s_altVK },
83 { kKeySuper_L, s_superVK },
84 { kKeySuper_R, s_superVK }, // 61
85 { kKeyMeta_L, s_superVK },
86 { kKeyMeta_R, s_superVK }, // 61
87
88 // toggle modifiers
89// { kKeyNumLock, 71 },
90 { kKeyCapsLock, s_capsLockVK }
91};
92
93
94//
95// COSXKeyState
96//
97
98COSXKeyState::COSXKeyState() :
99 m_deadKeyState(0)
100{
101 // build virtual key map
102 for (size_t i = 0; i < sizeof(s_controlKeys) /
103 sizeof(s_controlKeys[0]); ++i) {
104 m_virtualKeyMap[s_controlKeys[i].m_virtualKey] =
105 s_controlKeys[i].m_keyID;
106 }
107}
108
109COSXKeyState::~COSXKeyState()
110{
111 // do nothing
112}
113
114KeyModifierMask
115COSXKeyState::mapModifiersFromOSX(UInt32 mask) const
116{
117 // convert
118 KeyModifierMask outMask = 0;
119 if ((mask & shiftKey) != 0) {
120 outMask |= KeyModifierShift;
121 }
122 if ((mask & rightShiftKey) != 0) {
123 outMask |= KeyModifierShift;
124 }
125 if ((mask & controlKey) != 0) {
126 outMask |= KeyModifierControl;
127 }
128 if ((mask & rightControlKey) != 0) {
129 outMask |= KeyModifierControl;
130 }
131 if ((mask & cmdKey) != 0) {
132 outMask |= KeyModifierAlt;
133 }
134 if ((mask & optionKey) != 0) {
135 outMask |= KeyModifierSuper;
136 }
137 if ((mask & rightOptionKey) != 0) {
138 outMask |= KeyModifierSuper;
139 }
140 if ((mask & alphaLock) != 0) {
141 outMask |= KeyModifierCapsLock;
142 }
143
144 return outMask;
145}
146
147KeyButton
148COSXKeyState::mapKeyFromEvent(CKeyIDs& ids,
149 KeyModifierMask* maskOut, EventRef event) const
150{
151 ids.clear();
152
153 // map modifier key
154 if (maskOut != NULL) {
155 KeyModifierMask activeMask = getActiveModifiers();
156 activeMask &= ~KeyModifierAltGr;
157 *maskOut = activeMask;
158 }
159
160 // get virtual key
161 UInt32 vkCode;
162 GetEventParameter(event, kEventParamKeyCode, typeUInt32,
163 NULL, sizeof(vkCode), NULL, &vkCode);
164
165 // handle up events
166 UInt32 eventKind = GetEventKind(event);
167 if (eventKind == kEventRawKeyUp) {
168 // the id isn't used. we just need the same button we used on
169 // the key press. note that we don't use or reset the dead key
170 // state; up events should not affect the dead key state.
171 ids.push_back(kKeyNone);
172 return mapVirtualKeyToKeyButton(vkCode);
173 }
174
175 // check for special keys
176 CVirtualKeyMap::const_iterator i = m_virtualKeyMap.find(vkCode);
177 if (i != m_virtualKeyMap.end()) {
178 m_deadKeyState = 0;
179 ids.push_back(i->second);
180 return mapVirtualKeyToKeyButton(vkCode);
181 }
182
183 // get keyboard info
184 KeyboardLayoutRef keyboardLayout;
185 OSStatus status = KLGetCurrentKeyboardLayout(&keyboardLayout);
186 if (status != noErr) {
187 return kKeyNone;
188 }
189
190 // get the event modifiers and remove the command and control
191 // keys. note if we used them though.
192 UInt32 modifiers;
193 GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
194 NULL, sizeof(modifiers), NULL, &modifiers);
195 static const UInt32 s_commandModifiers =
196 cmdKey | controlKey | rightControlKey;
197 bool isCommand = ((modifiers & s_commandModifiers) != 0);
198 modifiers &= ~s_commandModifiers;
199
200 // if we've used a command key then we want the glyph produced without
201 // the option key (i.e. the base glyph).
202 if (isCommand) {
203 modifiers &= ~optionKey;
204 }
205
206 // translate via uchr resource
207 const void* resource;
208 if (KLGetKeyboardLayoutProperty(keyboardLayout,
209 kKLuchrData, &resource) == noErr) {
210 // choose action
211 UInt16 action;
212 switch (eventKind) {
213 case kEventRawKeyDown:
214 action = kUCKeyActionDown;
215 break;
216
217 case kEventRawKeyRepeat:
218 action = kUCKeyActionAutoKey;
219 break;
220
221 default:
222 return 0;
223 }
224
225 // translate key
226 UniCharCount count;
227 UniChar chars[2];
228 OSStatus status = UCKeyTranslate((const UCKeyboardLayout*)resource,
229 vkCode & 0xffu, action,
230 (modifiers >> 8) & 0xffu,
231 LMGetKbdType(), 0, &m_deadKeyState,
232 sizeof(chars) / sizeof(chars[0]), &count, chars);
233
234 // get the characters
235 if (status == 0) {
236 if (count != 0 || m_deadKeyState == 0) {
237 m_deadKeyState = 0;
238 for (UniCharCount i = 0; i < count; ++i) {
239 ids.push_back(CKeyResource::unicharToKeyID(chars[i]));
240 }
241 adjustAltGrModifier(ids, maskOut, isCommand);
242 return mapVirtualKeyToKeyButton(vkCode);
243 }
244 return 0;
245 }
246 }
247
248 // translate via KCHR resource
249 if (KLGetKeyboardLayoutProperty(keyboardLayout,
250 kKLKCHRData, &resource) == noErr) {
251 // build keycode
252 UInt16 keycode =
253 static_cast<UInt16>((modifiers & 0xff00u) | (vkCode & 0x00ffu));
254
255 // translate key
256 UInt32 result = KeyTranslate(resource, keycode, &m_deadKeyState);
257
258 // get the characters
259 UInt8 c1 = static_cast<UInt8>((result >> 16) & 0xffu);
260 UInt8 c2 = static_cast<UInt8>( result & 0xffu);
261 if (c2 != 0) {
262 m_deadKeyState = 0;
263 if (c1 != 0) {
264 ids.push_back(CKeyResource::getKeyID(c1));
265 }
266 ids.push_back(CKeyResource::getKeyID(c2));
267 adjustAltGrModifier(ids, maskOut, isCommand);
268 return mapVirtualKeyToKeyButton(vkCode);
269 }
270 }
271
272 return 0;
273}
274
275bool
276COSXKeyState::fakeCtrlAltDel()
277{
278 // pass keys through unchanged
279 return false;
280}
281
282KeyModifierMask
283COSXKeyState::pollActiveModifiers() const
284{
285 return mapModifiersFromOSX(GetCurrentKeyModifiers());
286}
287
288SInt32
289COSXKeyState::pollActiveGroup() const
290{
291 KeyboardLayoutRef keyboardLayout;
292 OSStatus status = KLGetCurrentKeyboardLayout(&keyboardLayout);
293 if (status == noErr) {
294 GroupMap::const_iterator i = m_groupMap.find(keyboardLayout);
295 if (i != m_groupMap.end()) {
296 return i->second;
297 }
298 }
299 return 0;
300}
301
302void
303COSXKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const
304{
305 KeyMap km;
306 GetKeys(km);
307 const UInt8* m = reinterpret_cast<const UInt8*>(km);
308 for (UInt32 i = 0; i < 16; ++i) {
309 for (UInt32 j = 0; j < 8; ++j) {
310 if ((m[i] & (1u << j)) != 0) {
311 pressedKeys.insert(mapVirtualKeyToKeyButton(8 * i + j));
312 }
313 }
314 }
315}
316
317void
318COSXKeyState::getKeyMap(CKeyMap& keyMap)
319{
320 // update keyboard groups
321 if (getGroups(m_groups)) {
322 m_groupMap.clear();
323 SInt32 numGroups = (SInt32)m_groups.size();
324 for (SInt32 g = 0; g < numGroups; ++g) {
325 m_groupMap[m_groups[g]] = g;
326 }
327 }
328
329 UInt32 keyboardType = LMGetKbdType();
330 for (SInt32 g = 0, n = (SInt32)m_groups.size(); g < n; ++g) {
331 // add special keys
332 getKeyMapForSpecialKeys(keyMap, g);
333
334 // add regular keys
335
336 // try uchr resource first
337 const void* resource;
338 if (KLGetKeyboardLayoutProperty(m_groups[g],
339 kKLuchrData, &resource) == noErr) {
340 CUCHRKeyResource uchr(resource, keyboardType);
341 if (uchr.isValid()) {
342 LOG((CLOG_DEBUG1 "using uchr resource for group %d", g));
343 getKeyMap(keyMap, g, uchr);
344 continue;
345 }
346 }
347
348 // try KCHR resource
349 if (KLGetKeyboardLayoutProperty(m_groups[g],
350 kKLKCHRData, &resource) == noErr) {
351 CKCHRKeyResource kchr(resource);
352 if (kchr.isValid()) {
353 LOG((CLOG_DEBUG1 "using KCHR resource for group %d", g));
354 getKeyMap(keyMap, g, kchr);
355 continue;
356 }
357 }
358
359 LOG((CLOG_DEBUG1 "no keyboard resource for group %d", g));
360 }
361}
362
363void
364COSXKeyState::fakeKey(const Keystroke& keystroke)
365{
366 switch (keystroke.m_type) {
367 case Keystroke::kButton:
368 LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, (UInt32)keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up"));
369
370 // let system figure out character for us
371 CGPostKeyboardEvent(0, mapKeyButtonToVirtualKey(
372 keystroke.m_data.m_button.m_button),
373 keystroke.m_data.m_button.m_press);
374
375 // add a delay if client data isn't zero
376 if (keystroke.m_data.m_button.m_client) {
377 ARCH->sleep(0.01);
378 }
379 break;
380
381 case Keystroke::kGroup:
382 if (keystroke.m_data.m_group.m_absolute) {
383 LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group));
384 setGroup(keystroke.m_data.m_group.m_group);
385 }
386 else {
387 LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group));
388 setGroup(getEffectiveGroup(pollActiveGroup(),
389 keystroke.m_data.m_group.m_group));
390 }
391 break;
392 }
393}
394
395void
396COSXKeyState::getKeyMapForSpecialKeys(CKeyMap& keyMap, SInt32 group) const
397{
398 // special keys are insensitive to modifers and none are dead keys
399 CKeyMap::KeyItem item;
400 for (size_t i = 0; i < sizeof(s_controlKeys) /
401 sizeof(s_controlKeys[0]); ++i) {
402 const CKeyEntry& entry = s_controlKeys[i];
403 item.m_id = entry.m_keyID;
404 item.m_group = group;
405 item.m_button = mapVirtualKeyToKeyButton(entry.m_virtualKey);
406 item.m_required = 0;
407 item.m_sensitive = 0;
408 item.m_dead = false;
409 item.m_client = 0;
410 CKeyMap::initModifierKey(item);
411 keyMap.addKeyEntry(item);
412
413 if (item.m_lock) {
414 // all locking keys are half duplex on OS X
415 keyMap.addHalfDuplexButton(item.m_button);
416 }
417 }
418
419 // note: we don't special case the number pad keys. querying the
420 // mac keyboard returns the non-keypad version of those keys but
421 // a CKeyState always provides a mapping from keypad keys to
422 // non-keypad keys so we'll be able to generate the characters
423 // anyway.
424}
425
426bool
427COSXKeyState::getKeyMap(CKeyMap& keyMap,
428 SInt32 group, const CKeyResource& r) const
429{
430 if (!r.isValid()) {
431 return false;
432 }
433
434 // space for all possible modifier combinations
435 std::vector<bool> modifiers(r.getNumModifierCombinations());
436
437 // make space for the keys that any single button can synthesize
438 std::vector<std::pair<KeyID, bool> > buttonKeys(r.getNumTables());
439
440 // iterate over each button
441 CKeyMap::KeyItem item;
442 for (UInt32 i = 0; i < r.getNumButtons(); ++i) {
443 item.m_button = mapVirtualKeyToKeyButton(i);
444
445 // the KeyIDs we've already handled
446 std::set<KeyID> keys;
447
448 // convert the entry in each table for this button to a KeyID
449 for (UInt32 j = 0; j < r.getNumTables(); ++j) {
450 buttonKeys[j].first = r.getKey(j, i);
451 buttonKeys[j].second = CKeyMap::isDeadKey(buttonKeys[j].first);
452 }
453
454 // iterate over each character table
455 for (UInt32 j = 0; j < r.getNumTables(); ++j) {
456 // get the KeyID for the button/table
457 KeyID id = buttonKeys[j].first;
458 if (id == kKeyNone) {
459 continue;
460 }
461
462 // if we've already handled the KeyID in the table then
463 // move on to the next table
464 if (keys.count(id) > 0) {
465 continue;
466 }
467 keys.insert(id);
468
469 // prepare item. the client state is 1 for dead keys.
470 item.m_id = id;
471 item.m_group = group;
472 item.m_dead = buttonKeys[j].second;
473 item.m_client = buttonKeys[j].second ? 1 : 0;
474 CKeyMap::initModifierKey(item);
475 if (item.m_lock) {
476 // all locking keys are half duplex on OS X
477 keyMap.addHalfDuplexButton(i);
478 }
479
480 // collect the tables that map to the same KeyID. we know it
481 // can't be any earlier tables because of the check above.
482 std::set<UInt8> tables;
483 tables.insert(static_cast<UInt8>(j));
484 for (UInt32 k = j + 1; k < r.getNumTables(); ++k) {
485 if (buttonKeys[k].first == id) {
486 tables.insert(static_cast<UInt8>(k));
487 }
488 }
489
490 // collect the modifier combinations that map to any of the
491 // tables we just collected
492 for (UInt32 k = 0; k < r.getNumModifierCombinations(); ++k) {
493 modifiers[k] = (tables.count(r.getTableForModifier(k)) > 0);
494 }
495
496 // figure out which modifiers the key is sensitive to. the
497 // key is insensitive to a modifier if for every modifier mask
498 // with the modifier bit unset in the modifiers we also find
499 // the same mask with the bit set.
500 //
501 // we ignore a few modifiers that we know aren't important
502 // for generating characters. in fact, we want to ignore any
503 // characters generated by the control key. we don't map
504 // those and instead expect the control modifier plus a key.
505 UInt32 sensitive = 0;
506 for (UInt32 k = 0; (1u << k) <
507 r.getNumModifierCombinations(); ++k) {
508 UInt32 bit = (1u << k);
509 if ((bit << 8) == cmdKey ||
510 (bit << 8) == controlKey ||
511 (bit << 8) == rightControlKey) {
512 continue;
513 }
514 for (UInt32 m = 0; m < r.getNumModifierCombinations(); ++m) {
515 if (modifiers[m] != modifiers[m ^ bit]) {
516 sensitive |= bit;
517 break;
518 }
519 }
520 }
521
522 // find each required modifier mask. the key can be synthesized
523 // using any of the masks.
524 std::set<UInt32> required;
525 for (UInt32 k = 0; k < r.getNumModifierCombinations(); ++k) {
526 if ((k & sensitive) == k && modifiers[k & sensitive]) {
527 required.insert(k);
528 }
529 }
530
531 // now add a key entry for each key/required modifier pair.
532 item.m_sensitive = mapModifiersFromOSX(sensitive << 8);
533 for (std::set<UInt32>::iterator k = required.begin();
534 k != required.end(); ++k) {
535 item.m_required = mapModifiersFromOSX(*k << 8);
536 keyMap.addKeyEntry(item);
537 }
538 }
539 }
540
541 return true;
542}
543
544bool
545COSXKeyState::mapSynergyHotKeyToMac(KeyID key, KeyModifierMask mask,
546 UInt32 &macVirtualKey, UInt32 &macModifierMask) const
547{
548 // look up button for key
549 KeyButton button = getButton(key, pollActiveGroup());
550 if (button == 0 && key != kKeyNone) {
551 return false;
552 }
553 macVirtualKey = mapKeyButtonToVirtualKey(button);
554
555 // calculate modifier mask
556 macModifierMask = 0;
557 if ((mask & KeyModifierShift) != 0) {
558 macModifierMask |= shiftKey;
559 }
560 if ((mask & KeyModifierControl) != 0) {
561 macModifierMask |= controlKey;
562 }
563 if ((mask & KeyModifierAlt) != 0) {
564 macModifierMask |= cmdKey;
565 }
566 if ((mask & KeyModifierSuper) != 0) {
567 macModifierMask |= optionKey;
568 }
569 if ((mask & KeyModifierCapsLock) != 0) {
570 macModifierMask |= alphaLock;
571 }
572
573 return true;
574}
575
576void
577COSXKeyState::handleModifierKeys(void* target,
578 KeyModifierMask oldMask, KeyModifierMask newMask)
579{
580 // compute changed modifiers
581 KeyModifierMask changed = (oldMask ^ newMask);
582
583 // synthesize changed modifier keys
584 if ((changed & KeyModifierShift) != 0) {
585 handleModifierKey(target, s_shiftVK, kKeyShift_L,
586 (newMask & KeyModifierShift) != 0, newMask);
587 }
588 if ((changed & KeyModifierControl) != 0) {
589 handleModifierKey(target, s_controlVK, kKeyControl_L,
590 (newMask & KeyModifierControl) != 0, newMask);
591 }
592 if ((changed & KeyModifierAlt) != 0) {
593 handleModifierKey(target, s_altVK, kKeyAlt_L,
594 (newMask & KeyModifierAlt) != 0, newMask);
595 }
596 if ((changed & KeyModifierSuper) != 0) {
597 handleModifierKey(target, s_superVK, kKeySuper_L,
598 (newMask & KeyModifierSuper) != 0, newMask);
599 }
600 if ((changed & KeyModifierCapsLock) != 0) {
601 handleModifierKey(target, s_capsLockVK, kKeyCapsLock,
602 (newMask & KeyModifierCapsLock) != 0, newMask);
603 }
604}
605
606void
607COSXKeyState::handleModifierKey(void* target,
608 UInt32 virtualKey, KeyID id,
609 bool down, KeyModifierMask newMask)
610{
611 KeyButton button = mapVirtualKeyToKeyButton(virtualKey);
612 onKey(button, down, newMask);
613 sendKeyEvent(target, down, false, id, newMask, 0, button);
614}
615
616bool
617COSXKeyState::getGroups(GroupList& groups) const
618{
619 // get number of layouts
620 CFIndex n;
621 OSStatus status = KLGetKeyboardLayoutCount(&n);
622 if (status != noErr) {
623 LOG((CLOG_DEBUG1 "can't get keyboard layouts"));
624 return false;
625 }
626
627 // get each layout
628 groups.clear();
629 for (CFIndex i = 0; i < n; ++i) {
630 KeyboardLayoutRef keyboardLayout;
631 status = KLGetKeyboardLayoutAtIndex(i, &keyboardLayout);
632 if (status == noErr) {
633 groups.push_back(keyboardLayout);
634 }
635 }
636 return true;
637}
638
639void
640COSXKeyState::setGroup(SInt32 group)
641{
642 KLSetCurrentKeyboardLayout(m_groups[group]);
643}
644
645void
646COSXKeyState::checkKeyboardLayout()
647{
648 // XXX -- should call this when notified that groups have changed.
649 // if no notification for that then we should poll.
650 GroupList groups;
651 if (getGroups(groups) && groups != m_groups) {
652 updateKeyMap();
653 updateKeyState();
654 }
655}
656
657void
658COSXKeyState::adjustAltGrModifier(const CKeyIDs& ids,
659 KeyModifierMask* mask, bool isCommand) const
660{
661 if (!isCommand) {
662 for (CKeyIDs::const_iterator i = ids.begin(); i != ids.end(); ++i) {
663 KeyID id = *i;
664 if (id != kKeyNone &&
665 ((id < 0xe000u || id > 0xefffu) ||
666 (id >= kKeyKP_Equal && id <= kKeyKP_9))) {
667 *mask |= KeyModifierAltGr;
668 return;
669 }
670 }
671 }
672}
673
674KeyButton
675COSXKeyState::mapVirtualKeyToKeyButton(UInt32 keyCode)
676{
677 // 'A' maps to 0 so shift every id
678 return static_cast<KeyButton>(keyCode + KeyButtonOffset);
679}
680
681UInt32
682COSXKeyState::mapKeyButtonToVirtualKey(KeyButton keyButton)
683{
684 return static_cast<UInt32>(keyButton - KeyButtonOffset);
685}
686
687
688//
689// COSXKeyState::CKeyResource
690//
691
692KeyID
693COSXKeyState::CKeyResource::getKeyID(UInt8 c)
694{
695 if (c == 0) {
696 return kKeyNone;
697 }
698 else if (c >= 32 && c < 127) {
699 // ASCII
700 return static_cast<KeyID>(c);
701 }
702 else {
703 // handle special keys
704 switch (c) {
705 case 0x01:
706 return kKeyHome;
707
708 case 0x02:
709 return kKeyKP_Enter;
710
711 case 0x03:
712 return kKeyKP_Enter;
713
714 case 0x04:
715 return kKeyEnd;
716
717 case 0x05:
718 return kKeyHelp;
719
720 case 0x08:
721 return kKeyBackSpace;
722
723 case 0x09:
724 return kKeyTab;
725
726 case 0x0b:
727 return kKeyPageUp;
728
729 case 0x0c:
730 return kKeyPageDown;
731
732 case 0x0d:
733 return kKeyReturn;
734
735 case 0x10:
736 // OS X maps all the function keys (F1, etc) to this one key.
737 // we can't determine the right key here so we have to do it
738 // some other way.
739 return kKeyNone;
740
741 case 0x1b:
742 return kKeyEscape;
743
744 case 0x1c:
745 return kKeyLeft;
746
747 case 0x1d:
748 return kKeyRight;
749
750 case 0x1e:
751 return kKeyUp;
752
753 case 0x1f:
754 return kKeyDown;
755
756 case 0x7f:
757 return kKeyDelete;
758
759 case 0x06:
760 case 0x07:
761 case 0x0a:
762 case 0x0e:
763 case 0x0f:
764 case 0x11:
765 case 0x12:
766 case 0x13:
767 case 0x14:
768 case 0x15:
769 case 0x16:
770 case 0x17:
771 case 0x18:
772 case 0x19:
773 case 0x1a:
774 // discard other control characters
775 return kKeyNone;
776
777 default:
778 // not special or unknown
779 break;
780 }
781
782 // create string with character
783 char str[2];
784 str[0] = static_cast<char>(c);
785 str[1] = 0;
786
787 // convert to unicode
788 CFStringRef cfString =
789 CFStringCreateWithCStringNoCopy(kCFAllocatorDefault,
790 str, GetScriptManagerVariable(smKeyScript),
791 kCFAllocatorNull);
792
793 // convert to precomposed
794 CFMutableStringRef mcfString =
795 CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString);
796 CFRelease(cfString);
797 CFStringNormalize(mcfString, kCFStringNormalizationFormC);
798
799 // check result
800 int unicodeLength = CFStringGetLength(mcfString);
801 if (unicodeLength == 0) {
802 CFRelease(mcfString);
803 return kKeyNone;
804 }
805 if (unicodeLength > 1) {
806 // FIXME -- more than one character, we should handle this
807 CFRelease(mcfString);
808 return kKeyNone;
809 }
810
811 // get unicode character
812 UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0);
813 CFRelease(mcfString);
814
815 // convert to KeyID
816 return static_cast<KeyID>(uc);
817 }
818}
819
820KeyID
821COSXKeyState::CKeyResource::unicharToKeyID(UniChar c)
822{
823 switch (c) {
824 case 3:
825 return kKeyKP_Enter;
826
827 case 8:
828 return kKeyBackSpace;
829
830 case 9:
831 return kKeyTab;
832
833 case 13:
834 return kKeyReturn;
835
836 case 27:
837 return kKeyEscape;
838
839 case 127:
840 return kKeyDelete;
841
842 default:
843 if (c < 32) {
844 return kKeyNone;
845 }
846 return static_cast<KeyID>(c);
847 }
848}
849
850
851//
852// COSXKeyState::CKCHRKeyResource
853//
854
855COSXKeyState::CKCHRKeyResource::CKCHRKeyResource(const void* resource)
856{
857 m_resource = reinterpret_cast<const KCHRResource*>(resource);
858}
859
860bool
861COSXKeyState::CKCHRKeyResource::isValid() const
862{
863 return (m_resource != NULL);
864}
865
866UInt32
867COSXKeyState::CKCHRKeyResource::getNumModifierCombinations() const
868{
869 // only 32 (not 256) because the righthanded modifier bits are ignored
870 return 32;
871}
872
873UInt32
874COSXKeyState::CKCHRKeyResource::getNumTables() const
875{
876 return m_resource->m_numTables;
877}
878
879UInt32
880COSXKeyState::CKCHRKeyResource::getNumButtons() const
881{
882 return 128;
883}
884
885UInt32
886COSXKeyState::CKCHRKeyResource::getTableForModifier(UInt32 mask) const
887{
888 assert(mask < getNumModifierCombinations());
889
890 return m_resource->m_tableSelectionIndex[mask];
891}
892
893KeyID
894COSXKeyState::CKCHRKeyResource::getKey(UInt32 table, UInt32 button) const
895{
896 assert(table < getNumTables());
897 assert(button < getNumButtons());
898
899 UInt8 c = m_resource->m_characterTables[table][button];
900 if (c == 0) {
901 // could be a dead key
902 const CKCHRDeadKeys* dkp =
903 reinterpret_cast<const CKCHRDeadKeys*>(
904 m_resource->m_characterTables[getNumTables()]);
905 const CKCHRDeadKeyRecord* dkr = dkp->m_records;
906 for (SInt16 i = 0; i < dkp->m_numRecords; ++i) {
907 if (dkr->m_tableIndex == table && dkr->m_virtualKey == button) {
908 // get the no completion entry
909 c = dkr->m_completion[dkr->m_numCompletions][1];
910 return CKeyMap::getDeadKey(getKeyID(c));
911 }
912
913 // next table. skip all the completions and the no match
914 // pair to get the next table.
915 dkr = reinterpret_cast<const CKCHRDeadKeyRecord*>(
916 dkr->m_completion[dkr->m_numCompletions + 1]);
917 }
918 }
919
920 return getKeyID(c);
921}
922
923
924//
925// COSXKeyState::CUCHRKeyResource
926//
927
928COSXKeyState::CUCHRKeyResource::CUCHRKeyResource(const void* resource,
929 UInt32 keyboardType) :
930 m_m(NULL),
931 m_cti(NULL),
932 m_sdi(NULL),
933 m_sri(NULL),
934 m_st(NULL)
935{
936 m_resource = reinterpret_cast<const UCKeyboardLayout*>(resource);
937 if (m_resource == NULL) {
938 return;
939 }
940
941 // find the keyboard info for the current keyboard type
942 const UCKeyboardTypeHeader* th = NULL;
943 const UCKeyboardLayout* r = m_resource;
944 for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) {
945 if (keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst &&
946 keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) {
947 th = r->keyboardTypeList + i;
948 break;
949 }
950 if (r->keyboardTypeList[i].keyboardTypeFirst == 0) {
951 // found the default. use it unless we find a match.
952 th = r->keyboardTypeList + i;
953 }
954 }
955 if (th == NULL) {
956 // cannot find a suitable keyboard type
957 return;
958 }
959
960 // get tables for keyboard type
961 const UInt8* base = reinterpret_cast<const UInt8*>(m_resource);
962 m_m = reinterpret_cast<const UCKeyModifiersToTableNum*>(base +
963 th->keyModifiersToTableNumOffset);
964 m_cti = reinterpret_cast<const UCKeyToCharTableIndex*>(base +
965 th->keyToCharTableIndexOffset);
966 m_sdi = reinterpret_cast<const UCKeySequenceDataIndex*>(base +
967 th->keySequenceDataIndexOffset);
968 if (th->keyStateRecordsIndexOffset != 0) {
969 m_sri = reinterpret_cast<const UCKeyStateRecordsIndex*>(base +
970 th->keyStateRecordsIndexOffset);
971 }
972 if (th->keyStateTerminatorsOffset != 0) {
973 m_st = reinterpret_cast<const UCKeyStateTerminators*>(base +
974 th->keyStateTerminatorsOffset);
975 }
976
977 // find the space key, but only if it can combine with dead keys.
978 // a dead key followed by a space yields the non-dead version of
979 // the dead key.
980 m_spaceOutput = 0xffffu;
981 UInt32 table = getTableForModifier(0);
982 for (UInt32 button = 0, n = getNumButtons(); button < n; ++button) {
983 KeyID id = getKey(table, button);
984 if (id == 0x20) {
985 UCKeyOutput c =
986 reinterpret_cast<const UCKeyOutput*>(base +
987 m_cti->keyToCharTableOffsets[table])[button];
988 if ((c & kUCKeyOutputTestForIndexMask) ==
989 kUCKeyOutputStateIndexMask) {
990 m_spaceOutput = (c & kUCKeyOutputGetIndexMask);
991 break;
992 }
993 }
994 }
995}
996
997bool
998COSXKeyState::CUCHRKeyResource::isValid() const
999{
1000 return (m_m != NULL);
1001}
1002
1003UInt32
1004COSXKeyState::CUCHRKeyResource::getNumModifierCombinations() const
1005{
1006 // only 32 (not 256) because the righthanded modifier bits are ignored
1007 return 32;
1008}
1009
1010UInt32
1011COSXKeyState::CUCHRKeyResource::getNumTables() const
1012{
1013 return m_cti->keyToCharTableCount;
1014}
1015
1016UInt32
1017COSXKeyState::CUCHRKeyResource::getNumButtons() const
1018{
1019 return m_cti->keyToCharTableSize;
1020}
1021
1022UInt32
1023COSXKeyState::CUCHRKeyResource::getTableForModifier(UInt32 mask) const
1024{
1025 if (mask >= m_m->modifiersCount) {
1026 return m_m->defaultTableNum;
1027 }
1028 else {
1029 return m_m->tableNum[mask];
1030 }
1031}
1032
1033KeyID
1034COSXKeyState::CUCHRKeyResource::getKey(UInt32 table, UInt32 button) const
1035{
1036 assert(table < getNumTables());
1037 assert(button < getNumButtons());
1038
1039 const UInt8* base = reinterpret_cast<const UInt8*>(m_resource);
1040 const UCKeyOutput c = reinterpret_cast<const UCKeyOutput*>(base +
1041 m_cti->keyToCharTableOffsets[table])[button];
1042
1043 KeySequence keys;
1044 switch (c & kUCKeyOutputTestForIndexMask) {
1045 case kUCKeyOutputStateIndexMask:
1046 if (!getDeadKey(keys, c & kUCKeyOutputGetIndexMask)) {
1047 return kKeyNone;
1048 }
1049 break;
1050
1051 case kUCKeyOutputSequenceIndexMask:
1052 default:
1053 if (!addSequence(keys, c)) {
1054 return kKeyNone;
1055 }
1056 break;
1057 }
1058
1059 // XXX -- no support for multiple characters
1060 if (keys.size() != 1) {
1061 return kKeyNone;
1062 }
1063
1064 return keys.front();
1065}
1066
1067bool
1068COSXKeyState::CUCHRKeyResource::getDeadKey(
1069 KeySequence& keys, UInt16 index) const
1070{
1071 if (m_sri == NULL || index >= m_sri->keyStateRecordCount) {
1072 // XXX -- should we be using some other fallback?
1073 return false;
1074 }
1075
1076 UInt16 state = 0;
1077 if (!getKeyRecord(keys, index, state)) {
1078 return false;
1079 }
1080 if (state == 0) {
1081 // not a dead key
1082 return true;
1083 }
1084
1085 // no dead keys if we couldn't find the space key
1086 if (m_spaceOutput == 0xffffu) {
1087 return false;
1088 }
1089
1090 // the dead key should not have put anything in the key list
1091 if (!keys.empty()) {
1092 return false;
1093 }
1094
1095 // get the character generated by pressing the space key after the
1096 // dead key. if we're still in a compose state afterwards then we're
1097 // confused so we bail.
1098 if (!getKeyRecord(keys, m_spaceOutput, state) || state != 0) {
1099 return false;
1100 }
1101
1102 // convert keys to their dead counterparts
1103 for (KeySequence::iterator i = keys.begin(); i != keys.end(); ++i) {
1104 *i = CKeyMap::getDeadKey(*i);
1105 }
1106
1107 return true;
1108}
1109
1110bool
1111COSXKeyState::CUCHRKeyResource::getKeyRecord(
1112 KeySequence& keys, UInt16 index, UInt16& state) const
1113{
1114 const UInt8* base = reinterpret_cast<const UInt8*>(m_resource);
1115 const UCKeyStateRecord* sr =
1116 reinterpret_cast<const UCKeyStateRecord*>(base +
1117 m_sri->keyStateRecordOffsets[index]);
1118 const UCKeyStateEntryTerminal* kset =
1119 reinterpret_cast<const UCKeyStateEntryTerminal*>(sr->stateEntryData);
1120
1121 UInt16 nextState = 0;
1122 bool found = false;
1123 if (state == 0) {
1124 found = true;
1125 nextState = sr->stateZeroNextState;
1126 if (!addSequence(keys, sr->stateZeroCharData)) {
1127 return false;
1128 }
1129 }
1130 else {
1131 // we have a next entry
1132 switch (sr->stateEntryFormat) {
1133 case kUCKeyStateEntryTerminalFormat:
1134 for (UInt16 j = 0; j < sr->stateEntryCount; ++j) {
1135 if (kset[j].curState == state) {
1136 if (!addSequence(keys, kset[j].charData)) {
1137 return false;
1138 }
1139 nextState = 0;
1140 found = true;
1141 break;
1142 }
1143 }
1144 break;
1145
1146 case kUCKeyStateEntryRangeFormat:
1147 // XXX -- not supported yet
1148 break;
1149
1150 default:
1151 // XXX -- unknown format
1152 return false;
1153 }
1154 }
1155 if (!found) {
1156 // use a terminator
1157 if (m_st != NULL && state < m_st->keyStateTerminatorCount) {
1158 if (!addSequence(keys, m_st->keyStateTerminators[state - 1])) {
1159 return false;
1160 }
1161 }
1162 nextState = sr->stateZeroNextState;
1163 if (!addSequence(keys, sr->stateZeroCharData)) {
1164 return false;
1165 }
1166 }
1167
1168 // next
1169 state = nextState;
1170
1171 return true;
1172}
1173
1174bool
1175COSXKeyState::CUCHRKeyResource::addSequence(
1176 KeySequence& keys, UCKeyCharSeq c) const
1177{
1178 if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) {
1179 UInt16 index = (c & kUCKeyOutputGetIndexMask);
1180 if (index < m_sdi->charSequenceCount &&
1181 m_sdi->charSequenceOffsets[index] !=
1182 m_sdi->charSequenceOffsets[index + 1]) {
1183 // XXX -- sequences not supported yet
1184 return false;
1185 }
1186 }
1187
1188 if (c != 0xfffe && c != 0xffff) {
1189 KeyID id = unicharToKeyID(c);
1190 if (id != kKeyNone) {
1191 keys.push_back(id);
1192 }
1193 }
1194
1195 return true;
1196}
Note: See TracBrowser for help on using the repository browser.