void CKeyState::fakeKeyEvents(const Keystrokes& keys, UInt32 count) { // do nothing if no keys or no repeats if (count == 0 || keys.empty()) { return; } // generate key events LOG((CLOG_DEBUG2 "keystrokes:")); for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { if (k->m_repeat) { // repeat from here up to but not including the next key // with m_repeat == false count times. Keystrokes::const_iterator start = k; while (count-- > 0) { // send repeating events for (k = start; k != keys.end() && k->m_repeat; ++k) { fakeKeyEvent(k->m_key, k->m_press, true); } } // note -- k is now on the first non-repeat key after the // repeat keys, exactly where we'd like to continue from. } else { // send event fakeKeyEvent(k->m_key, k->m_press, false); // next key ++k; } } }
void CKeyState::fakeToggle(KeyModifierMask modifier) { const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(modifier)]; if (buttons.empty() || !isToggle(modifier)) { return; } KeyButton button = buttons[0]; // get the sequence of keys to simulate key toggle Keystrokes keys; Keystroke keystroke; keystroke.m_key = button; keystroke.m_press = true; keystroke.m_repeat = false; keys.push_back(keystroke); keystroke.m_press = false; keys.push_back(keystroke); // generate key events fakeKeyEvents(keys, 1); // note the toggle m_keys[button] ^= kToggled; m_mask ^= modifier; }
void CKeyState::fakeKeyRepeat( KeyID id, KeyModifierMask mask, SInt32 count, KeyButton serverID) { serverID &= kButtonMask; // if we haven't seen this button go down then ignore it KeyButton oldLocalID = m_serverKeys[serverID]; if (oldLocalID == 0) { return; } // get keys for key repeat Keystrokes keys; ModifierToKeys oldActiveModifiers = m_activeModifiers; const CKeyMap::KeyItem* keyItem = m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers, m_mask, mask, true); if (keyItem == NULL) { return; } KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask); if (localID == 0) { return; } // if the KeyButton for the auto-repeat is not the same as for the // initial press then mark the initial key as released and the new // key as pressed. this can happen when we auto-repeat after a // dead key. for example, a dead accent followed by 'a' will // generate an 'a with accent' followed by a repeating 'a'. the // KeyButtons for the two KeyIDs might be different. if (localID != oldLocalID) { // replace key up with previous KeyButton but leave key down // alone so it uses the new KeyButton. for (Keystrokes::iterator index = keys.begin(); index != keys.end(); ++index) { if (index->m_type == Keystroke::kButton && index->m_data.m_button.m_button == localID) { index->m_data.m_button.m_button = oldLocalID; break; } } // note that old key is now up --m_keys[oldLocalID]; --m_syntheticKeys[oldLocalID]; // note keys down updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers); ++m_keys[localID]; ++m_syntheticKeys[localID]; m_keyClientData[localID] = keyItem->m_client; m_serverKeys[serverID] = localID; } // generate key events fakeKeys(keys, count); }
void CKeyState::fakeKeyRepeat( KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button) { button &= kButtonMask; // if we haven't seen this button go down then ignore it KeyButton oldLocalID = m_serverKeyMap[button]; if (oldLocalID == 0) { return; } // get the sequence of keys to simulate key repeat and the final // modifier state. Keystrokes keys; KeyButton localID = (KeyButton)(mapKey(keys, id, mask, true) & kButtonMask); if (localID == 0) { LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); return; } if (keys.empty()) { // do nothing if there are no associated keys return; } // if the keycode for the auto-repeat is not the same as for the // initial press then mark the initial key as released and the new // key as pressed. this can happen when we auto-repeat after a // dead key. for example, a dead accent followed by 'a' will // generate an 'a with accent' followed by a repeating 'a'. the // keycodes for the two keysyms might be different. if (localID != oldLocalID) { // replace key up with previous key id but leave key down // alone so it uses the new keycode. for (Keystrokes::iterator index = keys.begin(); index != keys.end(); ++index) { if (index->m_key == localID) { index->m_key = oldLocalID; break; } } // note that old key is now up m_keys[oldLocalID] &= ~kDown; // map server key to new key m_serverKeyMap[button] = localID; // note that new key is now down m_keys[localID] |= kDown; } // generate key events fakeKeyEvents(keys, count); }
void CKeyState::fakeAllKeysUp() { Keystrokes keys; for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) { if (m_syntheticKeys[i] > 0) { keys.push_back(Keystroke(i, false, false, m_keyClientData[i])); m_keys[i] = 0; m_syntheticKeys[i] = 0; } } fakeKeys(keys, 1); }
void CKeyState::fakeAllKeysUp() { Keystrokes keys; for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) { if (m_syntheticKeys[i] > 0) { keys.push_back(Keystroke(i, false, false, m_keyClientData[i])); m_keys[i] = 0; m_syntheticKeys[i] = 0; } } fakeKeys(keys, 1); memset(&m_serverKeys, 0, sizeof(m_serverKeys)); m_activeModifiers.clear(); m_mask = pollActiveModifiers(); }
bool KeyState::fakeKeyUp(KeyButton serverID) { // if we haven't seen this button go down then ignore it KeyButton localID = m_serverKeys[serverID & kButtonMask]; if (localID == 0) { return false; } // get the sequence of keys to simulate key release Keystrokes keys; keys.push_back(Keystroke(localID, false, false, m_keyClientData[localID])); // note keys down --m_keys[localID]; --m_syntheticKeys[localID]; m_serverKeys[serverID] = 0; // check if this is a modifier ModifierToKeys::iterator i = m_activeModifiers.begin(); while (i != m_activeModifiers.end()) { if (i->second.m_button == localID && !i->second.m_lock) { // modifier is no longer down KeyModifierMask mask = i->first; ModifierToKeys::iterator tmp = i; ++i; m_activeModifiers.erase(tmp); if (m_activeModifiers.count(mask) == 0) { // no key for modifier is down so deactivate modifier m_mask &= ~mask; LOG((CLOG_DEBUG1 "new state %04x", m_mask)); } } else { ++i; } } // generate key events fakeKeys(keys, 1); return true; }
void CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button) { // get the sequence of keys to simulate key press and the final // modifier state. Keystrokes keys; KeyButton localID = (KeyButton)(mapKey(keys, id, mask, false) & kButtonMask); if (keys.empty()) { // do nothing if there are no associated keys LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id)); return; } // generate key events fakeKeyEvents(keys, 1); // note that key is down updateKeyState((KeyButton)(button & kButtonMask), localID, true, true); }
void CKeyState::fakeKeyUp(KeyButton button) { // if we haven't seen this button go down then ignore it KeyButton localID = m_serverKeyMap[button & kButtonMask]; if (localID == 0) { return; } // get the sequence of keys to simulate key release Keystrokes keys; Keystroke keystroke; keystroke.m_key = localID; keystroke.m_press = false; keystroke.m_repeat = false; keys.push_back(keystroke); // generate key events fakeKeyEvents(keys, 1); // note that key is now up updateKeyState(button, localID, false, true); }
bool CKeyState::mapModifier(Keystrokes& keys, Keystrokes& undo, KeyModifierMask mask, bool desireActive, bool force) const { // look up modifier const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)]; if (buttons.empty()) { return false; } // ignore if already in desired state if (!force && isModifierActive(mask) == desireActive) { return true; } // initialize keystroke Keystroke keystroke; keystroke.m_repeat = false; // handle toggles if (isToggle(mask)) { keystroke.m_key = buttons[0]; keystroke.m_press = true; keys.push_back(keystroke); keystroke.m_press = false; keys.push_back(keystroke); keystroke.m_press = false; undo.push_back(keystroke); keystroke.m_press = true; undo.push_back(keystroke); } else if (desireActive) { // press keystroke.m_key = buttons[0]; keystroke.m_press = true; keys.push_back(keystroke); keystroke.m_press = false; undo.push_back(keystroke); } else { // releasing a modifier is quite different from pressing one. // when we release a modifier we have to release every keycode that // is assigned to the modifier since the modifier is active if any // one of them is down. when we press a modifier we just have to // press one of those keycodes. for (KeyButtons::const_iterator j = buttons.begin(); j != buttons.end(); ++j) { if (isKeyDown(*j)) { keystroke.m_key = *j; keystroke.m_press = false; keys.push_back(keystroke); keystroke.m_press = true; undo.push_back(keystroke); } } } return true; }
void CKeyMap::addKeystrokes(EKeystroke type, const KeyItem& keyItem, ModifierToKeys& activeModifiers, KeyModifierMask& currentState, Keystrokes& keystrokes) const { KeyButton button = keyItem.m_button; UInt32 data = keyItem.m_client; switch (type) { case kKeystrokePress: keystrokes.push_back(Keystroke(button, true, false, data)); if (keyItem.m_generates != 0) { if (!keyItem.m_lock || (currentState & keyItem.m_generates) == 0) { // add modifier key and activate modifier activeModifiers.insert(std::make_pair( keyItem.m_generates, keyItem)); currentState |= keyItem.m_generates; } else { // deactivate locking modifier activeModifiers.erase(keyItem.m_generates); currentState &= ~keyItem.m_generates; } } break; case kKeystrokeRelease: keystrokes.push_back(Keystroke(button, false, false, data)); if (keyItem.m_generates != 0 && !keyItem.m_lock) { // remove key from active modifiers std::pair<ModifierToKeys::iterator, ModifierToKeys::iterator> range = activeModifiers.equal_range(keyItem.m_generates); for (ModifierToKeys::iterator i = range.first; i != range.second; ++i) { if (i->second.m_button == button) { activeModifiers.erase(i); break; } } // if no more keys for this modifier then deactivate modifier if (activeModifiers.count(keyItem.m_generates) == 0) { currentState &= ~keyItem.m_generates; } } break; case kKeystrokeRepeat: keystrokes.push_back(Keystroke(button, false, true, data)); keystrokes.push_back(Keystroke(button, true, true, data)); // no modifier changes on key repeat break; case kKeystrokeClick: keystrokes.push_back(Keystroke(button, true, false, data)); keystrokes.push_back(Keystroke(button, false, false, data)); // no modifier changes on key click break; case kKeystrokeModify: case kKeystrokeUnmodify: if (keyItem.m_lock) { // we assume there's just one button for this modifier if (m_halfDuplex.count(button) > 0) { if (type == kKeystrokeModify) { // turn half-duplex toggle on (press) keystrokes.push_back(Keystroke(button, true, false, data)); } else { // turn half-duplex toggle off (release) keystrokes.push_back(Keystroke(button, false, false, data)); } } else { // toggle (click) keystrokes.push_back(Keystroke(button, true, false, data)); keystrokes.push_back(Keystroke(button, false, false, data)); } } else if (type == kKeystrokeModify) { // press modifier keystrokes.push_back(Keystroke(button, true, false, data)); } else { // release all the keys that generate the modifier that are // currently down std::pair<ModifierToKeys::const_iterator, ModifierToKeys::const_iterator> range = activeModifiers.equal_range(keyItem.m_generates); for (ModifierToKeys::const_iterator i = range.first; i != range.second; ++i) { keystrokes.push_back(Keystroke(i->second.m_button, false, false, i->second.m_client)); } } if (type == kKeystrokeModify) { activeModifiers.insert(std::make_pair( keyItem.m_generates, keyItem)); currentState |= keyItem.m_generates; } else { activeModifiers.erase(keyItem.m_generates); currentState &= ~keyItem.m_generates; } break; } }
bool CKeyMap::keysForKeyItem(const KeyItem& keyItem, SInt32& group, ModifierToKeys& activeModifiers, KeyModifierMask& currentState, KeyModifierMask desiredState, KeyModifierMask overrideModifiers, bool isAutoRepeat, Keystrokes& keystrokes) const { static const KeyModifierMask s_notRequiredMask = KeyModifierAltGr | KeyModifierNumLock | KeyModifierScrollLock; // add keystrokes to adjust the group if (group != keyItem.m_group) { group = keyItem.m_group; keystrokes.push_back(Keystroke(group, true, false)); } EKeystroke type; if (keyItem.m_dead) { // adjust modifiers for dead key if (!keysForModifierState(keyItem.m_button, group, activeModifiers, currentState, keyItem.m_required, keyItem.m_sensitive, 0, keystrokes)) { LOG((CLOG_DEBUG1 "unable to match modifier state for dead key %d", keyItem.m_button)); return false; } // press and release the dead key type = kKeystrokeClick; } else { // if this a command key then we don't have to match some of the // key's required modifiers. KeyModifierMask sensitive = keyItem.m_sensitive & ~overrideModifiers; // XXX -- must handle pressing a modifier. in particular, if we want // to synthesize a KeyID on level 1 of a KeyButton that has Shift_L // mapped to level 0 then we must release that button if it's down // (in order to satisfy a shift modifier) then press a different // button (any other button) mapped to the shift modifier and then // the Shift_L button. // match key's required state LOG((CLOG_DEBUG1 "state: %04x,%04x,%04x", currentState, keyItem.m_required, sensitive)); if (!keysForModifierState(keyItem.m_button, group, activeModifiers, currentState, keyItem.m_required, sensitive, 0, keystrokes)) { LOG((CLOG_DEBUG1 "unable to match modifier state (%04x,%04x) for key %d", keyItem.m_required, keyItem.m_sensitive, keyItem.m_button)); return false; } // match desiredState as closely as possible. we must not // change any modifiers in keyItem.m_sensitive. and if the key // is a modifier, we don't want to change that modifier. LOG((CLOG_DEBUG1 "desired state: %04x %04x,%04x,%04x", desiredState, currentState, keyItem.m_required, keyItem.m_sensitive)); if (!keysForModifierState(keyItem.m_button, group, activeModifiers, currentState, desiredState, ~(sensitive | keyItem.m_generates), s_notRequiredMask, keystrokes)) { LOG((CLOG_DEBUG1 "unable to match desired modifier state (%04x,%04x) for key %d", desiredState, ~keyItem.m_sensitive & 0xffffu, keyItem.m_button)); return false; } // repeat or press of key type = isAutoRepeat ? kKeystrokeRepeat : kKeystrokePress; } addKeystrokes(type, keyItem, activeModifiers, currentState, keystrokes); return true; }