void CMSWindowsKeyState::fakeKey(const Keystroke& keystroke) { switch (keystroke.m_type) { case Keystroke::kButton: { LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up")); KeyButton button = keystroke.m_data.m_button.m_button; // windows doesn't send key ups for key repeats if (keystroke.m_data.m_button.m_repeat && !keystroke.m_data.m_button.m_press) { LOG((CLOG_DEBUG1 " discard key repeat release")); break; } // get the virtual key for the button UINT vk = keystroke.m_data.m_button.m_client; // special handling of VK_SNAPSHOT if (vk == VK_SNAPSHOT) { if ((getActiveModifiers() & KeyModifierAlt) != 0) { // snapshot active window button = 1; } else { // snapshot full screen button = 0; } } // synthesize event m_desks->fakeKeyEvent(button, vk, keystroke.m_data.m_button.m_press, keystroke.m_data.m_button.m_repeat); break; } case Keystroke::kGroup: // we don't restore the group. we'd like to but we can't be // sure the restoring group change will be processed after the // key events. if (!keystroke.m_data.m_group.m_restore) { if (keystroke.m_data.m_group.m_absolute) { LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group)); setWindowGroup(keystroke.m_data.m_group.m_group); } else { LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group)); setWindowGroup(getEffectiveGroup(pollActiveGroup(), keystroke.m_data.m_group.m_group)); } } break; } }
KeyID CMSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info, KeyModifierMask* maskOut) const { static const KeyModifierMask s_controlAlt = KeyModifierControl | KeyModifierAlt; // extract character, virtual key, and if we didn't use AltGr char c = (char)((charAndVirtKey & 0xff00u) >> 8); UINT vkCode = (charAndVirtKey & 0xffu); bool noAltGr = ((charAndVirtKey & 0xff0000u) != 0); // handle some keys via table lookup KeyID id = getKeyID(vkCode, (KeyButton)((info >> 16) & 0x1ffu)); // check if not in table; map character to key id if (id == kKeyNone && c != 0) { if ((c & 0x80u) == 0) { // ASCII id = static_cast<KeyID>(c) & 0xffu; } else { // character is not really ASCII. instead it's some // character in the current ANSI code page. try to // convert that to a Unicode character. if we fail // then use the single byte character as is. char src = c; wchar_t unicode; if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, &src, 1, &unicode, 1) > 0) { id = static_cast<KeyID>(unicode); } else { id = static_cast<KeyID>(c) & 0xffu; } } } // set modifier mask if (maskOut != NULL) { KeyModifierMask active = getActiveModifiers(); if (!noAltGr && (active & s_controlAlt) == s_controlAlt) { // if !noAltGr then we're only interested in matching the // key, not the AltGr. AltGr is down (i.e. control and alt // are down) but we don't want the client to have to match // that so we clear it. active &= ~s_controlAlt; } *maskOut = active; } return id; }
void CMSWindowsKeyState::useSavedModifiers(bool enable) { if (enable != m_useSavedModifiers) { m_useSavedModifiers = enable; if (!m_useSavedModifiers) { // transfer any modifier state changes to CKeyState's state KeyModifierMask mask = m_originalSavedModifiers ^ m_savedModifiers; getActiveModifiersRValue() = (getActiveModifiers() & ~mask) | (m_savedModifiers & mask); } } }
void COSXKeyState::fixStickyKeys() { KeyModifierMask synergyMask = getActiveModifiers(); KeyModifierMask hardwareMask = pollActiveModifiers(); if (synergyMask != hardwareMask) { // modifier key stuck // compute changed modifiers KeyModifierMask changed = (hardwareMask ^ synergyMask); if (changed) { KeyButton kb; CString keyFixed; // synthesize changed modifier keys if ((changed & KeyModifierShift) != 0) { kb = mapVirtualKeyToKeyButton(s_shiftVK); fakeKeyUp(kb); keyFixed.append("shift "); } if ((changed & KeyModifierControl) != 0) { kb = mapVirtualKeyToKeyButton(s_controlVK); fakeKeyUp(kb); keyFixed.append("ctrl "); } if ((changed & KeyModifierAlt) != 0) { kb = mapVirtualKeyToKeyButton(s_altVK); fakeKeyUp(kb); keyFixed.append("alt "); } if ((changed & KeyModifierSuper) != 0) { kb = mapVirtualKeyToKeyButton(s_superVK); fakeKeyUp(kb); keyFixed.append("cmd "); } LOG((CLOG_DEBUG "fixed stuck modifier key: %s", keyFixed.c_str())); } } }
bool COSXScreen::onKey(CGEventRef event) { CGEventType eventKind = CGEventGetType(event); // get the key and active modifiers UInt32 virtualKey = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode); CGEventFlags macMask = CGEventGetFlags(event); LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey)); // Special handling to track state of modifiers if (eventKind == kCGEventFlagsChanged) { // get old and new modifier state KeyModifierMask oldMask = getActiveModifiers(); KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask); m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask); // if the current set of modifiers exactly matches a modifiers-only // hot key then generate a hot key down event. if (m_activeModifierHotKey == 0) { if (m_modifierHotKeys.count(newMask) > 0) { m_activeModifierHotKey = m_modifierHotKeys[newMask]; m_activeModifierHotKeyMask = newMask; m_events->addEvent(CEvent(m_events->forIPrimaryScreen().hotKeyDown(), getEventTarget(), CHotKeyInfo::alloc(m_activeModifierHotKey))); } } // if a modifiers-only hot key is active and should no longer be // then generate a hot key up event. else if (m_activeModifierHotKey != 0) { KeyModifierMask mask = (newMask & m_activeModifierHotKeyMask); if (mask != m_activeModifierHotKeyMask) { m_events->addEvent(CEvent(m_events->forIPrimaryScreen().hotKeyUp(), getEventTarget(), CHotKeyInfo::alloc(m_activeModifierHotKey))); m_activeModifierHotKey = 0; m_activeModifierHotKeyMask = 0; } } return true; } // check for hot key. when we're on a secondary screen we disable // all hotkeys so we can capture the OS defined hot keys as regular // keystrokes but that means we don't get our own hot keys either. // so we check for a key/modifier match in our hot key map. if (!m_isOnScreen) { HotKeyToIDMap::const_iterator i = m_hotKeyToIDMap.find(CHotKeyItem(virtualKey, m_keyState->mapModifiersToCarbon(macMask) & 0xff00u)); if (i != m_hotKeyToIDMap.end()) { UInt32 id = i->second; // determine event type CEvent::Type type; //UInt32 eventKind = GetEventKind(event); if (eventKind == kCGEventKeyDown) { type = m_events->forIPrimaryScreen().hotKeyDown(); } else if (eventKind == kCGEventKeyUp) { type = m_events->forIPrimaryScreen().hotKeyUp(); } else { return false; } m_events->addEvent(CEvent(type, getEventTarget(), CHotKeyInfo::alloc(id))); return true; } } // decode event type bool down = (eventKind == kCGEventKeyDown); bool up = (eventKind == kCGEventKeyUp); bool isRepeat = (CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat) == 1); // map event to keys KeyModifierMask mask; COSXKeyState::CKeyIDs keys; KeyButton button = m_keyState->mapKeyFromEvent(keys, &mask, event); if (button == 0) { return false; } // check for AltGr in mask. if set we send neither the AltGr nor // the super modifiers to clients then remove AltGr before passing // the modifiers to onKey. KeyModifierMask sendMask = (mask & ~KeyModifierAltGr); if ((mask & KeyModifierAltGr) != 0) { sendMask &= ~KeyModifierSuper; } mask &= ~KeyModifierAltGr; // update button state if (down) { m_keyState->onKey(button, true, mask); } else if (up) { if (!m_keyState->isKeyDown(button)) { // up event for a dead key. throw it away. return false; } m_keyState->onKey(button, false, mask); } // send key events for (COSXKeyState::CKeyIDs::const_iterator i = keys.begin(); i != keys.end(); ++i) { m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, *i, sendMask, 1, button); } return true; }
void CMSWindowsKeyState::saveModifiers() { m_savedModifiers = getActiveModifiers(); m_originalSavedModifiers = m_savedModifiers; }
void CMSWindowsKeyState::fixKeys() { // fake key releases for the windows keys if we think they're // down but they're really up. we have to do this because if the // user presses and releases a windows key without pressing any // other key while it's down then the system will eat the key // release. if we don't detect that and synthesize the release // then the client won't take the usual windows key release action // (which on windows is to show the start menu). // // only check on the windows 95 family since the NT family reports // the key releases as usual. if (!m_is95Family) { return; } KeyButton leftButton = virtualKeyToButton(VK_LWIN); KeyButton rightButton = virtualKeyToButton(VK_RWIN); bool leftDown = isKeyDown(leftButton); bool rightDown = isKeyDown(rightButton); bool fix = (leftDown || rightDown); if (fix) { // check if either button is not really down bool leftAsyncDown = ((GetAsyncKeyState(VK_LWIN) & 0x8000) != 0); bool rightAsyncDown = ((GetAsyncKeyState(VK_RWIN) & 0x8000) != 0); if (leftAsyncDown != leftDown || rightAsyncDown != rightDown) { KeyModifierMask state = getActiveModifiers(); if (!leftAsyncDown && !rightAsyncDown) { // no win keys are down so remove super modifier state &= ~KeyModifierSuper; } // report up events if (leftDown && !leftAsyncDown) { LOG((CLOG_DEBUG1 "event: fake key release left windows key (0x%03x)", leftButton)); CKeyState::onKey(leftButton, false, state); CKeyState::sendKeyEvent(m_eventTarget, false, false, kKeySuper_L, state, 1, leftButton); } if (rightDown && !rightAsyncDown) { LOG((CLOG_DEBUG1 "event: fake key release right windows key (0x%03x)", rightButton)); CKeyState::onKey(rightButton, false, state); CKeyState::sendKeyEvent(m_eventTarget, false, false, kKeySuper_R, state, 1, rightButton); } } } if (fix && m_fixTimer == NULL) { // schedule check m_fixTimer = EVENTQUEUE->newTimer(0.1, NULL); EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer, new TMethodEventJob<CMSWindowsKeyState>( this, &CMSWindowsKeyState::handleFixKeys)); } else if (!fix && m_fixTimer != NULL) { // remove scheduled check EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer); EVENTQUEUE->deleteTimer(m_fixTimer); m_fixTimer = NULL; } }
KeyButton COSXKeyState::mapKeyFromEvent(CKeyIDs& ids, KeyModifierMask* maskOut, EventRef event) const { ids.clear(); // map modifier key if (maskOut != NULL) { KeyModifierMask activeMask = getActiveModifiers(); activeMask &= ~KeyModifierAltGr; *maskOut = activeMask; } // get virtual key UInt32 vkCode; GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(vkCode), NULL, &vkCode); // handle up events UInt32 eventKind = GetEventKind(event); if (eventKind == kEventRawKeyUp) { // the id isn't used. we just need the same button we used on // the key press. note that we don't use or reset the dead key // state; up events should not affect the dead key state. ids.push_back(kKeyNone); return mapVirtualKeyToKeyButton(vkCode); } // check for special keys CVirtualKeyMap::const_iterator i = m_virtualKeyMap.find(vkCode); if (i != m_virtualKeyMap.end()) { m_deadKeyState = 0; ids.push_back(i->second); return mapVirtualKeyToKeyButton(vkCode); } // get keyboard info KeyboardLayoutRef keyboardLayout; OSStatus status = KLGetCurrentKeyboardLayout(&keyboardLayout); if (status != noErr) { return kKeyNone; } // get the event modifiers and remove the command and control // keys. note if we used them though. UInt32 modifiers; GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers); static const UInt32 s_commandModifiers = cmdKey | controlKey | rightControlKey; bool isCommand = ((modifiers & s_commandModifiers) != 0); modifiers &= ~s_commandModifiers; // if we've used a command key then we want the glyph produced without // the option key (i.e. the base glyph). if (isCommand) { modifiers &= ~optionKey; } // translate via uchr resource const void* resource; if (KLGetKeyboardLayoutProperty(keyboardLayout, kKLuchrData, &resource) == noErr) { // choose action UInt16 action; switch (eventKind) { case kEventRawKeyDown: action = kUCKeyActionDown; break; case kEventRawKeyRepeat: action = kUCKeyActionAutoKey; break; default: return 0; } // translate key UniCharCount count; UniChar chars[2]; OSStatus status = UCKeyTranslate((const UCKeyboardLayout*)resource, vkCode & 0xffu, action, (modifiers >> 8) & 0xffu, LMGetKbdType(), 0, &m_deadKeyState, sizeof(chars) / sizeof(chars[0]), &count, chars); // get the characters if (status == 0) { if (count != 0 || m_deadKeyState == 0) { m_deadKeyState = 0; for (UniCharCount i = 0; i < count; ++i) { ids.push_back(CKeyResource::unicharToKeyID(chars[i])); } adjustAltGrModifier(ids, maskOut, isCommand); return mapVirtualKeyToKeyButton(vkCode); } return 0; } }
void ShellSystemInterface::HandleScrollWheel(int np){ int delta = (wheel_pos - np); // may need a - here for Apple "natural scrolling" wheel_pos = np; uiHandle->ProcessMouseWheel(delta, getActiveModifiers()); }
void ShellSystemInterface::HandleMouseToggle(int b, int s){ (uiHandle.get()->*mouseResponse[s])(b,getActiveModifiers()); }
void ShellSystemInterface::HandleMousePosition(int x, int y){ uiHandle->ProcessMouseMove(x, y, getActiveModifiers()); }
void ShellSystemInterface::HandleKeyToggle(int k, int s){ // This seems like a recipe for badness // How does Rocket handle KeyIdentifiers with potentially invalid values? (uiHandle.get()->*keyResponse[s])((Rocket::Core::Input::KeyIdentifier)(keymap[k]),getActiveModifiers()); }
KeyButton OSXKeyState::mapKeyFromEvent(KeyIDs& ids, KeyModifierMask* maskOut, CGEventRef event) const { ids.clear(); // map modifier key if (maskOut != NULL) { KeyModifierMask activeMask = getActiveModifiers(); activeMask &= ~KeyModifierAltGr; *maskOut = activeMask; } // get virtual key UInt32 vkCode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode); // handle up events UInt32 eventKind = CGEventGetType(event); if (eventKind == kCGEventKeyUp) { // the id isn't used. we just need the same button we used on // the key press. note that we don't use or reset the dead key // state; up events should not affect the dead key state. ids.push_back(kKeyNone); return mapVirtualKeyToKeyButton(vkCode); } // check for special keys VirtualKeyMap::const_iterator i = m_virtualKeyMap.find(vkCode); if (i != m_virtualKeyMap.end()) { m_deadKeyState = 0; ids.push_back(i->second); return mapVirtualKeyToKeyButton(vkCode); } // get keyboard info #if defined(MAC_OS_X_VERSION_10_5) TISInputSourceRef currentKeyboardLayout = TISCopyCurrentKeyboardLayoutInputSource(); #else KeyboardLayoutRef currentKeyboardLayout; OSStatus status = KLGetCurrentKeyboardLayout(¤tKeyboardLayout); #endif if (currentKeyboardLayout == NULL) { return kKeyNone; } // get the event modifiers and remove the command and control // keys. note if we used them though. // UCKeyTranslate expects old-style Carbon modifiers, so convert. UInt32 modifiers; modifiers = mapModifiersToCarbon(CGEventGetFlags(event)); static const UInt32 s_commandModifiers = cmdKey | controlKey | rightControlKey; bool isCommand = ((modifiers & s_commandModifiers) != 0); modifiers &= ~s_commandModifiers; // if we've used a command key then we want the glyph produced without // the option key (i.e. the base glyph). //if (isCommand) { modifiers &= ~optionKey; //} // choose action UInt16 action; if(eventKind==kCGEventKeyDown) { action = kUCKeyActionDown; } else if(CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat)==1) { action = kUCKeyActionAutoKey; } else { return 0; } // translate via uchr resource #if defined(MAC_OS_X_VERSION_10_5) CFDataRef ref = (CFDataRef) TISGetInputSourceProperty(currentKeyboardLayout, kTISPropertyUnicodeKeyLayoutData); const UCKeyboardLayout* layout = (const UCKeyboardLayout*) CFDataGetBytePtr(ref); const bool layoutValid = (layout != NULL); #else const void* resource; int err = KLGetKeyboardLayoutProperty(currentKeyboardLayout, kKLuchrData, &resource); const bool layoutValid = (err == noErr); const UCKeyboardLayout* layout = (const UCKeyboardLayout*)resource; #endif if (layoutValid) { // translate key UniCharCount count; UniChar chars[2]; LOG((CLOG_DEBUG2 "modifiers: %08x", modifiers & 0xffu)); OSStatus status = UCKeyTranslate(layout, vkCode & 0xffu, action, (modifiers >> 8) & 0xffu, LMGetKbdType(), 0, &m_deadKeyState, sizeof(chars) / sizeof(chars[0]), &count, chars); // get the characters if (status == 0) { if (count != 0 || m_deadKeyState == 0) { m_deadKeyState = 0; for (UniCharCount i = 0; i < count; ++i) { ids.push_back(KeyResource::unicharToKeyID(chars[i])); } adjustAltGrModifier(ids, maskOut, isCommand); return mapVirtualKeyToKeyButton(vkCode); } return 0; } } return 0; }
bool COSXScreen::onKey(EventRef event) { UInt32 eventKind = GetEventKind(event); // get the key and active modifiers UInt32 virtualKey, macMask; GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(virtualKey), NULL, &virtualKey); GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(macMask), NULL, &macMask); LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey)); // sadly, OS X doesn't report the virtualKey for modifier keys. // virtualKey will be zero for modifier keys. since that's not good // enough we'll have to figure out what the key was. if (virtualKey == 0 && eventKind == kEventRawKeyModifiersChanged) { // get old and new modifier state KeyModifierMask oldMask = getActiveModifiers(); KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask); m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask); // if the current set of modifiers exactly matches a modifiers-only // hot key then generate a hot key down event. if (m_activeModifierHotKey == 0) { if (m_modifierHotKeys.count(newMask) > 0) { m_activeModifierHotKey = m_modifierHotKeys[newMask]; m_activeModifierHotKeyMask = newMask; EVENTQUEUE->addEvent(CEvent(getHotKeyDownEvent(), getEventTarget(), CHotKeyInfo::alloc(m_activeModifierHotKey))); } } // if a modifiers-only hot key is active and should no longer be // then generate a hot key up event. else if (m_activeModifierHotKey != 0) { KeyModifierMask mask = (newMask & m_activeModifierHotKeyMask); if (mask != m_activeModifierHotKeyMask) { EVENTQUEUE->addEvent(CEvent(getHotKeyUpEvent(), getEventTarget(), CHotKeyInfo::alloc(m_activeModifierHotKey))); m_activeModifierHotKey = 0; m_activeModifierHotKeyMask = 0; } } return true; } // check for hot key. when we're on a secondary screen we disable // all hotkeys so we can capture the OS defined hot keys as regular // keystrokes but that means we don't get our own hot keys either. // so we check for a key/modifier match in our hot key map. if (!m_isOnScreen) { HotKeyToIDMap::const_iterator i = m_hotKeyToIDMap.find(CHotKeyItem(virtualKey, macMask & 0xff00u)); if (i != m_hotKeyToIDMap.end()) { UInt32 id = i->second; // determine event type CEvent::Type type; UInt32 eventKind = GetEventKind(event); if (eventKind == kEventRawKeyDown) { type = getHotKeyDownEvent(); } else if (eventKind == kEventRawKeyUp) { type = getHotKeyUpEvent(); } else { return false; } EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), CHotKeyInfo::alloc(id))); return true; } } // decode event type bool down = (eventKind == kEventRawKeyDown); bool up = (eventKind == kEventRawKeyUp); bool isRepeat = (eventKind == kEventRawKeyRepeat); // map event to keys KeyModifierMask mask; COSXKeyState::CKeyIDs keys; KeyButton button = m_keyState->mapKeyFromEvent(keys, &mask, event); if (button == 0) { return false; } // check for AltGr in mask. if set we send neither the AltGr nor // the super modifiers to clients then remove AltGr before passing // the modifiers to onKey. KeyModifierMask sendMask = (mask & ~KeyModifierAltGr); if ((mask & KeyModifierAltGr) != 0) { sendMask &= ~KeyModifierSuper; } mask &= ~KeyModifierAltGr; // update button state if (down) { m_keyState->onKey(button, true, mask); } else if (up) { if (!m_keyState->isKeyDown(button)) { // up event for a dead key. throw it away. return false; } m_keyState->onKey(button, false, mask); } // send key events for (COSXKeyState::CKeyIDs::const_iterator i = keys.begin(); i != keys.end(); ++i) { m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, *i, sendMask, 1, button); } return true; }