barrier/lib/synergy/CKeyState.cpp
crs 8d99fd2511 Checkpoint. Converted X11 to new keyboard state tracking design.
This new design is simpler.  For keyboard support, clients need only
implement 4 virtual methods on a class derived from CKeyState and
one trivial method in the class derived from CPlatformScreen, which
is now the superclass of platform screens instead of IPlatformScreen.
Keyboard methods have been removed from IPlatformScreen, IPrimaryScreen
and ISecondaryScreen.  Also, all keyboard state tracking is now in
exactly one place (the CKeyState subclass) rather than in CScreen,
the platform screen, and the key mapper.  Still need to convert Win32.
2004-03-21 20:01:41 +00:00

533 lines
12 KiB
C++

/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CKeyState.h"
#include "IEventQueue.h"
#include "CLog.h"
#include <string.h>
//
// CKeyState
//
CKeyState::CKeyState() :
m_halfDuplex(0),
m_mask(0)
{
memset(&m_keys, 0, sizeof(m_keys));
memset(&m_serverKeyMap, 0, sizeof(m_serverKeyMap));
memset(&m_keyToMask, 0, sizeof(m_keyToMask));
}
CKeyState::~CKeyState()
{
// do nothing
}
void
CKeyState::setKeyDown(KeyButton button, bool down)
{
button &= kButtonMask;
if (button != 0) {
if (down) {
m_keys[button] |= kDown;
}
else {
m_keys[button] &= ~kDown;
}
}
}
void
CKeyState::setToggled(KeyModifierMask modifier)
{
if (isToggle(modifier)) {
const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(modifier)];
for (KeyButtons::const_iterator j = buttons.begin();
j != buttons.end(); ++j) {
m_keys[(*j) & kButtonMask] |= kToggled;
}
}
}
void
CKeyState::sendKeyEvent(
void* target, bool press, bool isAutoRepeat,
KeyID key, KeyModifierMask mask,
SInt32 count, KeyButton button)
{
if (isHalfDuplex(m_keyToMask[button])) {
if (isAutoRepeat) {
// ignore auto-repeat on half-duplex keys
}
else {
EVENTQUEUE->addEvent(CEvent(getKeyDownEvent(), target,
CKeyInfo::alloc(key, mask, button, 1)));
EVENTQUEUE->addEvent(CEvent(getKeyUpEvent(), target,
CKeyInfo::alloc(key, mask, button, 1)));
}
}
else {
if (isAutoRepeat) {
EVENTQUEUE->addEvent(CEvent(getKeyRepeatEvent(), target,
CKeyInfo::alloc(key, mask, button, count)));
}
else if (press) {
EVENTQUEUE->addEvent(CEvent(getKeyDownEvent(), target,
CKeyInfo::alloc(key, mask, button, 1)));
}
else {
EVENTQUEUE->addEvent(CEvent(getKeyUpEvent(), target,
CKeyInfo::alloc(key, mask, button, 1)));
}
}
}
void
CKeyState::updateKeys()
{
static const KeyModifierMask s_masks[] = {
KeyModifierShift,
KeyModifierControl,
KeyModifierAlt,
KeyModifierMeta,
KeyModifierSuper,
KeyModifierModeSwitch,
KeyModifierCapsLock,
KeyModifierNumLock,
KeyModifierScrollLock
};
// reset our state
memset(&m_keys, 0, sizeof(m_keys));
memset(&m_serverKeyMap, 0, sizeof(m_serverKeyMap));
memset(&m_keyToMask, 0, sizeof(m_keyToMask));
for (UInt32 i = 0; i < sizeof(m_maskToKeys)/sizeof(m_maskToKeys[0]); ++i) {
m_maskToKeys[i].clear();
}
// let subclass set the state
doUpdateKeys();
// figure out the active modifiers
m_mask = 0;
for (UInt32 i = 0; i < sizeof(s_masks) / sizeof(s_masks[0]); ++i) {
if (isModifierActive(s_masks[i])) {
m_mask |= s_masks[i];
}
}
LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask));
}
void
CKeyState::setHalfDuplexMask(KeyModifierMask mask)
{
m_halfDuplex = mask & (KeyModifierCapsLock |
KeyModifierNumLock |
KeyModifierScrollLock);
}
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 = (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(button & kButtonMask, localID, true);
}
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 = (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::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);
}
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;
}
bool
CKeyState::isKeyDown(KeyButton button) const
{
return ((m_keys[button & kButtonMask] & kDown) != 0);
}
KeyModifierMask
CKeyState::getActiveModifiers() const
{
return m_mask;
}
void
CKeyState::addModifier(KeyModifierMask modifier, const KeyButtons& buttons)
{
// the mask must not be zero
assert(modifier != 0);
// the mask must have exactly one high bit
assert((modifier & (modifier - 1)) == 0);
for (KeyButtons::const_iterator j = buttons.begin();
j != buttons.end(); ++j) {
KeyButton button = static_cast<KeyButton>(((*j) & kButtonMask));
if (button != 0) {
m_keyToMask[button] = modifier;
}
}
// index keys by mask
m_maskToKeys[getIndexForModifier(modifier)] = buttons;
}
bool
CKeyState::mapModifier(Keystrokes& keys, Keystrokes& undo,
KeyModifierMask mask, bool desireActive) const
{
// look up modifier
const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)];
if (buttons.empty()) {
return false;
}
// ignore if already in desired state
if (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;
}
bool
CKeyState::isToggle(KeyModifierMask mask) const
{
return (mask == KeyModifierCapsLock ||
mask == KeyModifierNumLock ||
mask == KeyModifierScrollLock);
}
bool
CKeyState::isHalfDuplex(KeyModifierMask mask) const
{
return ((mask & m_halfDuplex) != 0);
}
bool
CKeyState::isModifierActive(KeyModifierMask mask) const
{
const KeyButtons& buttons = m_maskToKeys[getIndexForModifier(mask)];
KeyButtons::const_iterator j = buttons.begin();
if (isToggle(mask)) {
// modifier is a toggle
if ((m_keys[*j] & kToggled) != 0) {
return true;
}
}
else {
// modifier is not a toggle
for (; j != buttons.end(); ++j) {
if ((m_keys[*j] & kDown) != 0) {
return true;
}
}
}
return false;
}
UInt32
CKeyState::getIndexForModifier(KeyModifierMask mask) const
{
switch (mask) {
case KeyModifierShift:
return 0;
case KeyModifierControl:
return 1;
case KeyModifierAlt:
return 2;
case KeyModifierMeta:
return 3;
case KeyModifierSuper:
return 4;
case KeyModifierModeSwitch:
return 5;
case KeyModifierCapsLock:
return 6;
case KeyModifierNumLock:
return 7;
case KeyModifierScrollLock:
return 8;
default:
assert(0 && "invalid modifier mask");
return 0;
}
}
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::fakeKeyEvent(KeyButton button, bool press, bool isAutoRepeat)
{
// half-duplex keys are special. we ignore releases and convert
// a press when the toggle is active to a release.
KeyModifierMask mask = m_keyToMask[button];
if (isHalfDuplex(mask)) {
if (isAutoRepeat || !press) {
return;
}
if (isModifierActive(mask)) {
press = false;
}
}
// send key event
LOG((CLOG_DEBUG2 " %d %s%s", button, press ? "down" : "up", isAutoRepeat ? " repeat" : ""));
doFakeKeyEvent(button, press, isAutoRepeat);
}
void
CKeyState::updateKeyState(KeyButton serverID, KeyButton localID, bool press)
{
// ignore bogus keys
if (serverID == 0 || localID == 0) {
return;
}
// update key state. state doesn't change when auto-repeating.
if (press) {
m_serverKeyMap[serverID] = localID;
m_keys[localID] |= kDown;
}
else {
m_serverKeyMap[serverID] = 0;
m_keys[localID] &= ~kDown;
}
// update modifier state
KeyModifierMask mask = m_keyToMask[localID];
if (mask != 0) {
if (isToggle(mask)) {
m_keys[localID] ^= kToggled;
m_mask ^= mask;
// never report half-duplex keys as down
if (isHalfDuplex(mask)) {
m_keys[localID] &= ~kDown;
}
}
else {
if (press) {
m_mask |= mask;
}
else if (!isModifierActive(mask)) {
m_mask &= ~mask;
}
}
LOG((CLOG_DEBUG2 "new mask: 0x%04x", m_mask));
}
}