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.
This commit is contained in:
crs 2004-03-21 20:01:41 +00:00
parent 19559d4b4e
commit 8d99fd2511
20 changed files with 1306 additions and 1027 deletions

View File

@ -181,8 +181,7 @@ CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool)
{
m_active = true;
m_screen->mouseMove(xAbs, yAbs);
m_screen->enter();
m_screen->setToggleState(mask);
m_screen->enter(mask);
}
bool

View File

@ -12,9 +12,10 @@
* GNU General Public License for more details.
*/
#include "CXWindowsKeyMapper.h"
#include "CXWindowsKeyState.h"
#include "CXWindowsUtil.h"
#include "CLog.h"
#include "CStringUtil.h"
#if defined(X_DISPLAY_MISSING)
# error X11 is required to build synergy
#else
@ -137,73 +138,119 @@ static const KeySym g_mapE000[] =
};
#endif
CXWindowsKeyMapper::CXWindowsKeyMapper()
CXWindowsKeyState::CXWindowsKeyState(Display* display) :
m_display(display)
{
// do nothing
}
CXWindowsKeyMapper::~CXWindowsKeyMapper()
CXWindowsKeyState::~CXWindowsKeyState()
{
// do nothing
}
KeyModifierMask
CXWindowsKeyState::mapModifiersFromX(unsigned int state) const
{
KeyModifierMask mask = 0;
if (state & ShiftMask)
mask |= KeyModifierShift;
if (state & LockMask)
mask |= KeyModifierCapsLock;
if (state & ControlMask)
mask |= KeyModifierControl;
if (state & m_altMask)
mask |= KeyModifierAlt;
if (state & m_metaMask)
mask |= KeyModifierMeta;
if (state & m_superMask)
mask |= KeyModifierSuper;
if (state & m_modeSwitchMask)
mask |= KeyModifierModeSwitch;
if (state & m_numLockMask)
mask |= KeyModifierNumLock;
if (state & m_scrollLockMask)
mask |= KeyModifierScrollLock;
return mask;
}
const char*
CXWindowsKeyState::getKeyName(KeyButton keycode) const
{
KeySym keysym = XKeycodeToKeysym(m_display, keycode, 0);
char* name = XKeysymToString(keysym);
if (name != NULL) {
return name;
}
else {
static char buffer[20];
return strcpy(buffer,
CStringUtil::print("keycode %d", keycode).c_str());
}
}
void
CXWindowsKeyMapper::update(Display* display, IKeyState* keyState)
CXWindowsKeyState::doUpdateKeys()
{
// query which keys are pressed
char keys[32];
XQueryKeymap(display, keys);
XQueryKeymap(m_display, keys);
// save the auto-repeat mask
XGetKeyboardControl(display, &m_keyControl);
XGetKeyboardControl(m_display, &m_keyControl);
// query the pointer to get the keyboard state
Window root = DefaultRootWindow(display), window;
Window root = DefaultRootWindow(m_display), window;
int xRoot, yRoot, xWindow, yWindow;
unsigned int state;
if (!XQueryPointer(display, root, &root, &window,
if (!XQueryPointer(m_display, root, &root, &window,
&xRoot, &yRoot, &xWindow, &yWindow, &state)) {
state = 0;
}
// update mappings
updateKeysymMap(display, keyState);
updateKeysymMap();
updateModifiers();
// transfer to our state
for (UInt32 i = 0, j = 0; i < 32; j += 8, ++i) {
if ((keys[i] & 0x01) != 0)
keyState->setKeyDown(j + 0, true);
setKeyDown(j + 0, true);
if ((keys[i] & 0x02) != 0)
keyState->setKeyDown(j + 1, true);
setKeyDown(j + 1, true);
if ((keys[i] & 0x04) != 0)
keyState->setKeyDown(j + 2, true);
setKeyDown(j + 2, true);
if ((keys[i] & 0x08) != 0)
keyState->setKeyDown(j + 3, true);
setKeyDown(j + 3, true);
if ((keys[i] & 0x10) != 0)
keyState->setKeyDown(j + 4, true);
setKeyDown(j + 4, true);
if ((keys[i] & 0x20) != 0)
keyState->setKeyDown(j + 5, true);
setKeyDown(j + 5, true);
if ((keys[i] & 0x40) != 0)
keyState->setKeyDown(j + 6, true);
setKeyDown(j + 6, true);
if ((keys[i] & 0x80) != 0)
keyState->setKeyDown(j + 7, true);
setKeyDown(j + 7, true);
}
// set toggle modifier states
if ((state & LockMask) != 0)
keyState->setToggled(KeyModifierCapsLock);
setToggled(KeyModifierCapsLock);
if ((state & m_numLockMask) != 0)
keyState->setToggled(KeyModifierNumLock);
setToggled(KeyModifierNumLock);
if ((state & m_scrollLockMask) != 0)
keyState->setToggled(KeyModifierScrollLock);
setToggled(KeyModifierScrollLock);
}
void
CXWindowsKeyState::doFakeKeyEvent(KeyButton keycode, bool press, bool)
{
XTestFakeKeyEvent(m_display, keycode, press ? True : False, CurrentTime);
XFlush(m_display);
}
KeyButton
CXWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys,
const IKeyState& keyState, KeyID id,
KeyModifierMask desiredMask,
bool isAutoRepeat) const
CXWindowsKeyState::mapKey(Keystrokes& keys, KeyID id,
KeyModifierMask desiredMask, bool isAutoRepeat) const
{
// the system translates key events into characters depending
// on the modifier key state at the time of the event. to
@ -243,7 +290,7 @@ CXWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys,
if (keyIndex != m_keysymMap.end()) {
// the keysym is mapped to some keycode. create the keystrokes
// for this keysym.
return mapToKeystrokes(keys, keyState, keyIndex, isAutoRepeat);
return mapToKeystrokes(keys, keyIndex, isAutoRepeat);
}
// we can't find the keysym mapped to any keycode. this doesn't
@ -272,7 +319,7 @@ CXWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys,
}
// the keysym is mapped to some keycode
keycode = mapToKeystrokes(keys, keyState, keyIndex, isAutoRepeat);
keycode = mapToKeystrokes(keys, keyIndex, isAutoRepeat);
if (keycode == 0) {
return 0;
}
@ -281,45 +328,20 @@ CXWindowsKeyMapper::mapKey(IKeyState::Keystrokes& keys,
return keycode;
}
KeyModifierMask
CXWindowsKeyMapper::mapModifier(unsigned int state) const
{
KeyModifierMask mask = 0;
if (state & ShiftMask)
mask |= KeyModifierShift;
if (state & LockMask)
mask |= KeyModifierCapsLock;
if (state & ControlMask)
mask |= KeyModifierControl;
if (state & m_altMask)
mask |= KeyModifierAlt;
if (state & m_metaMask)
mask |= KeyModifierMeta;
if (state & m_superMask)
mask |= KeyModifierSuper;
if (state & m_modeSwitchMask)
mask |= KeyModifierModeSwitch;
if (state & m_numLockMask)
mask |= KeyModifierNumLock;
if (state & m_scrollLockMask)
mask |= KeyModifierScrollLock;
return mask;
}
void
CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState)
CXWindowsKeyState::updateKeysymMap()
{
// there are up to 4 keysyms per keycode
static const unsigned int maxKeysyms = 4;
// get the number of keycodes
int minKeycode, maxKeycode;
XDisplayKeycodes(display, &minKeycode, &maxKeycode);
XDisplayKeycodes(m_display, &minKeycode, &maxKeycode);
const int numKeycodes = maxKeycode - minKeycode + 1;
// get the keyboard mapping for all keys
int keysymsPerKeycode;
KeySym* keysyms = XGetKeyboardMapping(display,
KeySym* keysyms = XGetKeyboardMapping(m_display,
minKeycode, numKeycodes,
&keysymsPerKeycode);
@ -364,7 +386,7 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState)
}
// get modifier map from server
XModifierKeymap* modifiers = XGetModifierMapping(display);
XModifierKeymap* modifiers = XGetModifierMapping(m_display);
unsigned int keysPerModifier = modifiers->max_keypermod;
// clear state
@ -387,7 +409,7 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState)
for (unsigned int i = 0; i < 8; ++i) {
// no keycodes for this modifier yet
KeyModifierMask mask = 0;
IKeyState::KeyButtons modifierKeys;
KeyButtons modifierKeys;
// add each keycode for modifier
for (unsigned int j = 0; j < keysPerModifier; ++j) {
@ -429,9 +451,9 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState)
mapping.m_numLockSensitive = false;
}
// tell keyState about this modifier
if (mask != 0 && keyState != NULL) {
keyState->addModifier(mask, modifierKeys);
// note this modifier
if (mask != 0) {
addModifier(mask, modifierKeys);
}
}
@ -485,7 +507,7 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState)
}
KeyModifierMask
CXWindowsKeyMapper::mapToModifierMask(unsigned int i, KeySym keysym)
CXWindowsKeyState::mapToModifierMask(unsigned int i, KeySym keysym)
{
// some modifier indices (0,1,2) are dedicated to particular uses,
// the rest depend on the keysyms bound.
@ -546,16 +568,16 @@ CXWindowsKeyMapper::mapToModifierMask(unsigned int i, KeySym keysym)
}
void
CXWindowsKeyMapper::updateModifiers()
CXWindowsKeyState::updateModifiers()
{
struct CModifierBitInfo {
public:
KeySym CXWindowsKeyMapper::*m_keysym;
KeySym CXWindowsKeyState::*m_keysym;
KeySym m_left;
KeySym m_right;
};
static const CModifierBitInfo s_modifierBitTable[] = {
{ &CXWindowsKeyMapper::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol },
{ &CXWindowsKeyState::m_modeSwitchKeysym, XK_Mode_switch, NoSymbol },
};
// choose the keysym to use for some modifiers. if a modifier has
@ -603,7 +625,7 @@ CXWindowsKeyMapper::updateModifiers()
}
KeySym
CXWindowsKeyMapper::keyIDToKeySym(KeyID id, KeyModifierMask mask) const
CXWindowsKeyState::keyIDToKeySym(KeyID id, KeyModifierMask mask) const
{
// convert id to keysym
KeySym keysym = NoSymbol;
@ -718,15 +740,13 @@ CXWindowsKeyMapper::keyIDToKeySym(KeyID id, KeyModifierMask mask) const
}
KeyButton
CXWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys,
const IKeyState& keyState,
KeySymIndex keyIndex,
bool isAutoRepeat) const
CXWindowsKeyState::mapToKeystrokes(Keystrokes& keys,
KeySymIndex keyIndex, bool isAutoRepeat) const
{
// keyIndex must be valid
assert(keyIndex != m_keysymMap.end());
KeyModifierMask currentMask = keyState.getActiveModifiers();
KeyModifierMask currentMask = getActiveModifiers();
// get the keysym we're trying to generate and possible keycodes
const KeySym keysym = keyIndex->first;
@ -798,14 +818,14 @@ CXWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys,
}
// adjust the modifiers to match the desired modifiers
IKeyState::Keystrokes undo;
if (!adjustModifiers(keys, undo, keyState, desiredMask)) {
Keystrokes undo;
if (!adjustModifiers(keys, undo, desiredMask)) {
LOG((CLOG_DEBUG2 "failed to adjust modifiers"));
return 0;
}
// add the key event
IKeyState::Keystroke keystroke;
Keystroke keystroke;
keystroke.m_key = keycode;
if (!isAutoRepeat) {
keystroke.m_press = true;
@ -830,7 +850,7 @@ CXWindowsKeyMapper::mapToKeystrokes(IKeyState::Keystrokes& keys,
}
unsigned int
CXWindowsKeyMapper::findBestKeyIndex(KeySymIndex keyIndex,
CXWindowsKeyState::findBestKeyIndex(KeySymIndex keyIndex,
KeyModifierMask /*currentMask*/) const
{
// there are up to 4 keycodes per keysym to choose from. the
@ -856,7 +876,7 @@ CXWindowsKeyMapper::findBestKeyIndex(KeySymIndex keyIndex,
}
bool
CXWindowsKeyMapper::isShiftInverted(KeySymIndex keyIndex,
CXWindowsKeyState::isShiftInverted(KeySymIndex keyIndex,
KeyModifierMask currentMask) const
{
// each keycode has up to 4 keysym associated with it, one each for:
@ -885,12 +905,11 @@ CXWindowsKeyMapper::isShiftInverted(KeySymIndex keyIndex,
}
bool
CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys,
IKeyState::Keystrokes& undo,
const IKeyState& keyState,
CXWindowsKeyState::adjustModifiers(Keystrokes& keys,
Keystrokes& undo,
KeyModifierMask desiredMask) const
{
KeyModifierMask currentMask = keyState.getActiveModifiers();
KeyModifierMask currentMask = getActiveModifiers();
// get mode switch set correctly. do this before shift because
// mode switch may be sensitive to the shift modifier and will
@ -909,8 +928,7 @@ CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys,
if (wantShift != haveShift) {
// add shift keystrokes
LOG((CLOG_DEBUG2 "fix shift for mode switch"));
if (!keyState.mapModifier(keys, undo,
KeyModifierShift, wantShift)) {
if (!mapModifier(keys, undo, KeyModifierShift, wantShift)) {
return false;
}
currentMask ^= KeyModifierShift;
@ -918,8 +936,7 @@ CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys,
}
// add mode switch keystrokes
if (!keyState.mapModifier(keys, undo,
KeyModifierModeSwitch, wantModeSwitch)) {
if (!mapModifier(keys, undo, KeyModifierModeSwitch, wantModeSwitch)) {
return false;
}
currentMask ^= KeyModifierModeSwitch;
@ -931,7 +948,7 @@ CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys,
if (wantShift != haveShift) {
// add shift keystrokes
LOG((CLOG_DEBUG2 "fix shift"));
if (!keyState.mapModifier(keys, undo, KeyModifierShift, wantShift)) {
if (!mapModifier(keys, undo, KeyModifierShift, wantShift)) {
return false;
}
currentMask ^= KeyModifierShift;
@ -941,13 +958,13 @@ CXWindowsKeyMapper::adjustModifiers(IKeyState::Keystrokes& keys,
}
bool
CXWindowsKeyMapper::isNumLockSensitive(KeySym keysym) const
CXWindowsKeyState::isNumLockSensitive(KeySym keysym) const
{
return (IsKeypadKey(keysym) || IsPrivateKeypadKey(keysym));
}
bool
CXWindowsKeyMapper::isCapsLockSensitive(KeySym keysym) const
CXWindowsKeyState::isCapsLockSensitive(KeySym keysym) const
{
KeySym lKey, uKey;
XConvertCase(keysym, &lKey, &uKey);
@ -956,10 +973,10 @@ CXWindowsKeyMapper::isCapsLockSensitive(KeySym keysym) const
//
// CXWindowsKeyMapper::KeyMapping
// CXWindowsKeyState::KeyMapping
//
CXWindowsKeyMapper::KeyMapping::KeyMapping()
CXWindowsKeyState::KeyMapping::KeyMapping()
{
m_keycode[0] = 0;
m_keycode[1] = 0;

View File

@ -12,62 +12,55 @@
* GNU General Public License for more details.
*/
#ifndef CXWINDOWSKEYMAPPER_H
#define CXWINDOWSKEYMAPPER_H
#ifndef CXWINDOWSKEYSTATE_H
#define CXWINDOWSKEYSTATE_H
#include "IKeyState.h"
#include "CKeyState.h"
#include "stdmap.h"
#if defined(X_DISPLAY_MISSING)
# error X11 is required to build synergy
#else
# include <X11/Xlib.h>
# if defined(HAVE_X11_EXTENSIONS_XTEST_H)
# include <X11/extensions/XTest.h>
# else
# error The XTest extension is required to build synergy
# endif
#endif
//! X Windows key mapper
//! X Windows key state
/*!
This class maps KeyIDs to keystrokes.
A key state for X Windows.
*/
class CXWindowsKeyMapper {
class CXWindowsKeyState : public CKeyState {
public:
CXWindowsKeyMapper();
~CXWindowsKeyMapper();
CXWindowsKeyState(Display*);
~CXWindowsKeyState();
//! @name manipulators
//@{
//! Update key mapper
/*!
Updates the key mapper's internal tables according to the display's
current keyboard mapping and updates \c keyState.
*/
void update(Display*, IKeyState* keyState);
//@}
//! @name accessors
//@{
//! Map key press/repeat to keystrokes
/*!
Converts a press/repeat of key \c id with the modifiers as given
in \c desiredMask into the keystrokes necessary to synthesize
that key event. Returns the platform specific code of the key
being pressed, or 0 if the key cannot be mapped or \c isAutoRepeat
is true and the key does not auto-repeat.
*/
KeyButton mapKey(IKeyState::Keystrokes&,
const IKeyState& keyState, KeyID id,
KeyModifierMask desiredMask,
bool isAutoRepeat) const;
//! Convert X modifier mask to synergy mask
/*!
Returns the synergy modifier mask corresponding to the given X
modifier mask.
Returns the synergy modifier mask corresponding to the X modifier
mask in \p state.
*/
KeyModifierMask mapModifier(unsigned int state) const;
KeyModifierMask mapModifiersFromX(unsigned int state) const;
//@}
// IKeyState overrides
virtual const char* getKeyName(KeyButton) const;
protected:
// IKeyState overrides
virtual void doUpdateKeys();
virtual void doFakeKeyEvent(KeyButton button,
bool press, bool isAutoRepeat);
virtual KeyButton mapKey(Keystrokes& keys, KeyID id,
KeyModifierMask desiredMask,
bool isAutoRepeat) const;
private:
class KeyMapping {
public:
@ -90,9 +83,8 @@ private:
typedef std::map<KeySym, KeyMapping> KeySymMap;
typedef KeySymMap::const_iterator KeySymIndex;
// save the current keyboard mapping and note the currently
// pressed keys in \c keyState.
void updateKeysymMap(Display* display, IKeyState* keyState);
// save the current keyboard mapping and note the modifiers
void updateKeysymMap();
// note interesting modifier KeySyms
void updateModifiers();
@ -105,8 +97,7 @@ private:
KeySym keyIDToKeySym(KeyID id, KeyModifierMask mask) const;
// map a KeySym into the keystrokes to produce it
KeyButton mapToKeystrokes(IKeyState::Keystrokes& keys,
const IKeyState& keyState,
KeyButton mapToKeystrokes(Keystrokes& keys,
KeySymIndex keyIndex,
bool isAutoRepeat) const;
@ -120,9 +111,8 @@ private:
// returns the keystrokes to adjust the modifiers into the desired
// state the keystrokes to get back to the current state.
bool adjustModifiers(IKeyState::Keystrokes& keys,
IKeyState::Keystrokes& undo,
const IKeyState& keyState,
bool adjustModifiers(Keystrokes& keys,
Keystrokes& undo,
KeyModifierMask desiredMask) const;
// returns true if keysym is sensitive to the NumLock state
@ -132,6 +122,8 @@ private:
bool isCapsLockSensitive(KeySym keysym) const;
private:
Display* m_display;
// keysym to keycode mapping
KeySymMap m_keysymMap;

View File

@ -15,6 +15,7 @@
#include "CXWindowsScreen.h"
#include "CXWindowsClipboard.h"
#include "CXWindowsEventQueueBuffer.h"
#include "CXWindowsKeyState.h"
#include "CXWindowsScreenSaver.h"
#include "CXWindowsUtil.h"
#include "CClipboard.h"
@ -131,7 +132,6 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) :
m_xCenter(0), m_yCenter(0),
m_xCursor(0), m_yCursor(0),
m_keyState(NULL),
m_keyMapper(),
m_im(NULL),
m_ic(NULL),
m_lastKeycode(0),
@ -154,6 +154,7 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) :
m_window = openWindow();
m_screensaver = new CXWindowsScreenSaver(m_display,
m_window, getEventTarget());
m_keyState = new CXWindowsKeyState(m_display);
LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_xinerama ? "(xinerama)" : ""));
LOG((CLOG_DEBUG "window is 0x%08x", m_window));
}
@ -201,7 +202,9 @@ CXWindowsScreen::~CXWindowsScreen()
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
delete m_clipboard[id];
}
delete m_keyState;
delete m_screensaver;
m_keyState = NULL;
m_screensaver = NULL;
if (m_display != NULL) {
// FIXME -- is it safe to clean up the IC and IM without a display?
@ -219,12 +222,6 @@ CXWindowsScreen::~CXWindowsScreen()
s_screen = NULL;
}
void
CXWindowsScreen::setKeyState(IKeyState* keyState)
{
m_keyState = keyState;
}
void
CXWindowsScreen::enable()
{
@ -319,7 +316,6 @@ CXWindowsScreen::leave()
}
// raise and show the window
// FIXME -- take focus?
XMapRaised(m_display, m_window);
// grab the mouse and keyboard, if primary and possible
@ -328,6 +324,9 @@ CXWindowsScreen::leave()
return false;
}
// take focus
XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime);
// now warp the mouse. we warp after showing the window so we're
// guaranteed to get the mouse leave event and to prevent the
// keyboard focus from changing under point-to-focus policies.
@ -428,14 +427,6 @@ CXWindowsScreen::setOptions(const COptionsList& options)
}
}
void
CXWindowsScreen::updateKeys()
{
// update keyboard and mouse button mappings
m_keyMapper.update(m_display, m_keyState);
updateButtons();
}
void
CXWindowsScreen::setSequenceNumber(UInt32 seqNum)
{
@ -547,21 +538,6 @@ CXWindowsScreen::isAnyMouseButtonDown() const
return false;
}
KeyModifierMask
CXWindowsScreen::getActiveModifiers() const
{
// query the pointer to get the modifier state
Window root, window;
int xRoot, yRoot, xWindow, yWindow;
unsigned int state;
if (XQueryPointer(m_display, m_root, &root, &window,
&xRoot, &yRoot, &xWindow, &yWindow, &state)) {
return m_keyMapper.mapModifier(state);
}
return 0;
}
void
CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
{
@ -569,28 +545,6 @@ CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
y = m_yCenter;
}
const char*
CXWindowsScreen::getKeyName(KeyButton keycode) const
{
KeySym keysym = XKeycodeToKeysym(m_display, keycode, 0);
char* name = XKeysymToString(keysym);
if (name != NULL) {
return name;
}
else {
static char buffer[20];
return strcpy(buffer,
CStringUtil::print("keycode %d", keycode).c_str());
}
}
void
CXWindowsScreen::fakeKeyEvent(KeyButton keycode, bool press) const
{
XTestFakeKeyEvent(m_display, keycode, press ? True : False, CurrentTime);
XFlush(m_display);
}
bool
CXWindowsScreen::fakeCtrlAltDel() const
{
@ -645,15 +599,6 @@ CXWindowsScreen::fakeMouseWheel(SInt32 delta) const
XFlush(m_display);
}
KeyButton
CXWindowsScreen::mapKey(IKeyState::Keystrokes& keys,
const IKeyState& keyState, KeyID id,
KeyModifierMask desiredMask,
bool isAutoRepeat) const
{
return m_keyMapper.mapKey(keys, keyState, id, desiredMask, isAutoRepeat);
}
Display*
CXWindowsScreen::openDisplay() const
{
@ -788,6 +733,7 @@ CXWindowsScreen::openIM()
// open the input methods
XIM im = XOpenIM(m_display, NULL, NULL, NULL);
if (im == NULL) {
LOG((CLOG_INFO "no support for IM"));
return;
}
@ -811,7 +757,7 @@ CXWindowsScreen::openIM()
}
XFree(styles);
if (style == 0) {
LOG((CLOG_WARN "no supported IM styles"));
LOG((CLOG_INFO "no supported IM styles"));
XCloseIM(im);
return;
}
@ -859,6 +805,12 @@ CXWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id)
sendEvent(type, info);
}
IKeyState*
CXWindowsScreen::getKeyState() const
{
return m_keyState;
}
Bool
CXWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg)
{
@ -1063,7 +1015,7 @@ void
CXWindowsScreen::onKeyPress(XKeyEvent& xkey)
{
LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xkey.keycode, xkey.state));
const KeyModifierMask mask = m_keyMapper.mapModifier(xkey.state);
const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state);
KeyID key = mapKeyFromX(&xkey);
if (key != kKeyNone) {
// check for ctrl+alt+del emulation
@ -1083,19 +1035,15 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey)
}
// handle key
sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask, keycode, 1));
KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode);
if (m_keyState->isHalfDuplex(keyMask)) {
sendEvent(getKeyUpEvent(),
CKeyInfo::alloc(key, mask | keyMask, keycode, 1));
}
m_keyState->sendKeyEvent(getEventTarget(),
true, false, key, mask, 1, keycode);
}
}
void
CXWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat)
{
const KeyModifierMask mask = m_keyMapper.mapModifier(xkey.state);
const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state);
KeyID key = mapKeyFromX(&xkey);
if (key != kKeyNone) {
// check for ctrl+alt+del emulation
@ -1112,12 +1060,8 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat)
if (!isRepeat) {
// no press event follows so it's a plain release
LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", keycode, xkey.state));
KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode);
if (m_keyState->isHalfDuplex(keyMask)) {
sendEvent(getKeyDownEvent(),
CKeyInfo::alloc(key, mask, keycode, 1));
}
sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, keycode, 1));
m_keyState->sendKeyEvent(getEventTarget(),
false, false, key, mask, 1, keycode);
}
else {
// found a press event following so it's a repeat.
@ -1125,8 +1069,8 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat)
// repeats but we'll just send a repeat of 1.
// note that we discard the press event.
LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state));
sendEvent(getKeyRepeatEvent(),
CKeyInfo::alloc(key, mask, keycode, 1));
m_keyState->sendKeyEvent(getEventTarget(),
false, true, key, mask, 1, keycode);
}
}
}

View File

@ -15,8 +15,7 @@
#ifndef CXWINDOWSSCREEN_H
#define CXWINDOWSSCREEN_H
#include "IPlatformScreen.h"
#include "CXWindowsKeyMapper.h"
#include "CPlatformScreen.h"
#include "stdvector.h"
#if defined(X_DISPLAY_MISSING)
# error X11 is required to build synergy
@ -25,10 +24,11 @@
#endif
class CXWindowsClipboard;
class CXWindowsKeyState;
class CXWindowsScreenSaver;
//! Implementation of IPlatformScreen for X11
class CXWindowsScreen : public IPlatformScreen {
class CXWindowsScreen : public CPlatformScreen {
public:
CXWindowsScreen(bool isPrimary);
virtual ~CXWindowsScreen();
@ -38,23 +38,6 @@ public:
//@}
// IPlatformScreen overrides
virtual void setKeyState(IKeyState*);
virtual void enable();
virtual void disable();
virtual void enter();
virtual bool leave();
virtual bool setClipboard(ClipboardID, const IClipboard*);
virtual void checkClipboards();
virtual void openScreensaver(bool notify);
virtual void closeScreensaver();
virtual void screensaver(bool activate);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual void updateKeys();
virtual void setSequenceNumber(UInt32);
virtual bool isPrimary() const;
// IScreen overrides
virtual void* getEventTarget() const;
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
@ -67,29 +50,40 @@ public:
virtual void warpCursor(SInt32 x, SInt32 y);
virtual SInt32 getJumpZoneSize() const;
virtual bool isAnyMouseButtonDown() const;
virtual KeyModifierMask getActiveModifiers() const;
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
virtual const char* getKeyName(KeyButton) const;
// ISecondaryScreen overrides
virtual void fakeKeyEvent(KeyButton id, bool press) const;
virtual bool fakeCtrlAltDel() const;
virtual void fakeMouseButton(ButtonID id, bool press) const;
virtual void fakeMouseMove(SInt32 x, SInt32 y) const;
virtual void fakeMouseWheel(SInt32 delta) const;
virtual KeyButton mapKey(IKeyState::Keystrokes&,
const IKeyState& keyState, KeyID id,
KeyModifierMask desiredMask,
bool isAutoRepeat) const;
// IPlatformScreen overrides
virtual void enable();
virtual void disable();
virtual void enter();
virtual bool leave();
virtual bool setClipboard(ClipboardID, const IClipboard*);
virtual void checkClipboards();
virtual void openScreensaver(bool notify);
virtual void closeScreensaver();
virtual void screensaver(bool activate);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual void setSequenceNumber(UInt32);
virtual bool isPrimary() const;
protected:
// IPlatformScreen overrides
virtual void handleSystemEvent(const CEvent&, void*);
virtual void updateButtons();
virtual IKeyState* getKeyState() const;
private:
// event sending
void sendEvent(CEvent::Type, void* = NULL);
void sendClipboardEvent(CEvent::Type, ClipboardID);
// event handling
void handleSystemEvent(const CEvent&, void*);
// create the transparent cursor
Cursor createBlankCursor() const;
@ -138,8 +132,6 @@ private:
void warpCursorNoFlush(SInt32 x, SInt32 y);
void updateButtons();
static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg);
private:
@ -162,8 +154,7 @@ private:
SInt32 m_xCursor, m_yCursor;
// keyboard stuff
IKeyState* m_keyState;
CXWindowsKeyMapper m_keyMapper;
CXWindowsKeyState* m_keyState;
// input method stuff
XIM m_im;

View File

@ -54,7 +54,7 @@ libplatform_a_SOURCES = \
CXWindowsClipboardUCS2Converter.cpp \
CXWindowsClipboardUTF8Converter.cpp \
CXWindowsEventQueueBuffer.cpp \
CXWindowsKeyMapper.cpp \
CXWindowsKeyState.cpp \
CXWindowsScreen.cpp \
CXWindowsScreenSaver.cpp \
CXWindowsUtil.cpp \
@ -63,7 +63,7 @@ libplatform_a_SOURCES = \
CXWindowsClipboardUCS2Converter.h \
CXWindowsClipboardUTF8Converter.h \
CXWindowsEventQueueBuffer.h \
CXWindowsKeyMapper.h \
CXWindowsKeyState.h \
CXWindowsScreen.h \
CXWindowsScreenSaver.h \
CXWindowsUtil.h \

View File

@ -105,13 +105,13 @@ CPrimaryClient::disable()
void
CPrimaryClient::enter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask, bool screensaver)
UInt32 seqNum, KeyModifierMask mask, bool screensaver)
{
m_screen->setSequenceNumber(seqNum);
if (!screensaver) {
m_screen->warpCursor(xAbs, yAbs);
}
m_screen->enter();
m_screen->enter(mask);
}
bool

532
lib/synergy/CKeyState.cpp Normal file
View File

@ -0,0 +1,532 @@
/*
* 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));
}
}

185
lib/synergy/CKeyState.h Normal file
View File

@ -0,0 +1,185 @@
/*
* 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.
*/
#ifndef CKEYSTATE_H
#define CKEYSTATE_H
#include "IKeyState.h"
#include "stdvector.h"
//! Core key state
/*!
This class provides key state services. Subclasses must implement a few
platform specific methods.
*/
class CKeyState : public IKeyState {
public:
CKeyState();
virtual ~CKeyState();
//! @name manipulators
//@{
//! Mark key as being down
/*!
Sets the state of \p button to down or up.
*/
void setKeyDown(KeyButton button, bool down);
//! Mark modifier as being toggled on
/*!
Sets the state of the keys for the given (single) \p modifier to be
toggled on.
*/
void setToggled(KeyModifierMask modifier);
//! Post a key event
/*!
Posts a key event. This may adjust the event or post additional
events in some circumstances.
*/
void sendKeyEvent(void* target,
bool press, bool isAutoRepeat,
KeyID key, KeyModifierMask mask,
SInt32 count, KeyButton button);
//@}
//! @name accessors
//@{
//@}
// IKeyState overrides
virtual void updateKeys();
virtual void setHalfDuplexMask(KeyModifierMask);
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button);
virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button);
virtual void fakeKeyUp(KeyButton button);
virtual void fakeToggle(KeyModifierMask modifier);
virtual bool isKeyDown(KeyButton) const;
virtual KeyModifierMask
getActiveModifiers() const;
virtual const char* getKeyName(KeyButton) const = 0;
protected:
class Keystroke {
public:
KeyButton m_key;
bool m_press;
bool m_repeat;
};
typedef std::vector<Keystroke> Keystrokes;
typedef std::vector<KeyButton> KeyButtons;
//! @name protocted manipulators
//@{
//! Add keys for modifier
/*!
Sets the buttons that are mapped to the given (single) \p modifier. For
example, if buttons 5 and 23 were mapped to KeyModifierShift (perhaps
as left and right shift keys) then the mask would be KeyModifierShift
and \c buttons would contain 5 and 23. A modifier with no keys is
ignored. Buttons that are zero are ignored.
*/
void addModifier(KeyModifierMask modifier,
const KeyButtons& buttons);
//! Get key events to change modifier state
/*!
Retrieves the key events necessary to activate (\c desireActive is true)
or deactivate (\c desireActive is false) the modifier given by \c mask
by pushing them onto the back of \c keys. \c mask must specify exactly
one modifier. \c undo receives the key events necessary to restore the
modifier's previous state. They're pushed onto \c undo in the reverse
order they should be executed. Returns true if the modifier can be
adjusted, false otherwise.
*/
bool mapModifier(Keystrokes& keys, Keystrokes& undo,
KeyModifierMask mask, bool desireActive) const;
//! Update the key state
/*!
Update the key state to reflect the physical keyboard state and
current keyboard mapping. This must call \c setKeyDown, \c setToggled,
and \c addModifier to set the current state.
*/
virtual void doUpdateKeys() = 0;
//! Fake a key event
/*!
Synthesize a key event for \p button. If \p press is true then
synthesize a key press and, if false, a key release. If
\p isAutoRepeat is true then the event is an auto-repeat.
*/
virtual void doFakeKeyEvent(KeyButton button,
bool press, bool isAutoRepeat) = 0;
//! Map key press/repeat to keystrokes
/*!
Converts a press/repeat of key \p id with the modifiers as given
in \p desiredMask into the keystrokes necessary to synthesize
that key event. Returns the platform specific code of the key
being pressed, or 0 if the key cannot be mapped or \p isAutoRepeat
is true and the key does not auto-repeat.
*/
virtual KeyButton mapKey(Keystrokes& keys, KeyID id,
KeyModifierMask desiredMask,
bool isAutoRepeat) const = 0;
//@}
private:
bool isHalfDuplex(KeyModifierMask) const;
bool isToggle(KeyModifierMask) const;
bool isModifierActive(KeyModifierMask) const;
UInt32 getIndexForModifier(KeyModifierMask) const;
void fakeKeyEvents(const Keystrokes&, UInt32 count);
void fakeKeyEvent(KeyButton, bool press, bool isAutoRepeat);
void updateKeyState(KeyButton serverID,
KeyButton localID, bool press);
private:
enum {
kNumModifiers = 9,
kButtonMask = kNumButtons - 1
};
typedef UInt8 KeyState;
enum EKeyState {
kDown = 0x01, //!< Key is down
kToggled = 0x02 //!< Key is toggled on
};
// modifiers that are half-duplex
KeyModifierMask m_halfDuplex;
// current modifier state
KeyModifierMask m_mask;
// current keyboard state
KeyState m_keys[kNumButtons];
// map from server button ID to local button ID for pressed keys
KeyButton m_serverKeyMap[kNumButtons];
// map button to the modifier mask it represents
KeyModifierMask m_keyToMask[kNumButtons];
// map modifier to buttons with that modifier
KeyButtons m_maskToKeys[kNumModifiers];
};
#endif

View File

@ -0,0 +1,82 @@
/*
* 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 "CPlatformScreen.h"
CPlatformScreen::CPlatformScreen()
{
// do nothing
}
CPlatformScreen::~CPlatformScreen()
{
// do nothing
}
void
CPlatformScreen::updateKeys()
{
getKeyState()->updateKeys();
updateButtons();
}
void
CPlatformScreen::setHalfDuplexMask(KeyModifierMask mask)
{
getKeyState()->setHalfDuplexMask(mask);
}
void
CPlatformScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button)
{
getKeyState()->fakeKeyDown(id, mask, button);
}
void
CPlatformScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button)
{
getKeyState()->fakeKeyRepeat(id, mask, count, button);
}
void
CPlatformScreen::fakeKeyUp(KeyButton button)
{
getKeyState()->fakeKeyUp(button);
}
void
CPlatformScreen::fakeToggle(KeyModifierMask modifier)
{
getKeyState()->fakeToggle(modifier);
}
bool
CPlatformScreen::isKeyDown(KeyButton button) const
{
return getKeyState()->isKeyDown(button);
}
KeyModifierMask
CPlatformScreen::getActiveModifiers() const
{
return getKeyState()->getActiveModifiers();
}
const char*
CPlatformScreen::getKeyName(KeyButton button) const
{
return getKeyState()->getKeyName(button);
}

View File

@ -0,0 +1,99 @@
/*
* 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.
*/
#ifndef CPLATFORMSCREEN_H
#define CPLATFORMSCREEN_H
#include "IPlatformScreen.h"
//! Base screen implementation
/*!
This screen implementation is the superclass of all other screen
implementations. It implements a handful of methods and requires
subclasses to implement the rest.
*/
class CPlatformScreen : public IPlatformScreen {
public:
CPlatformScreen();
virtual ~CPlatformScreen();
// IScreen overrides
virtual void* getEventTarget() const = 0;
virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const = 0;
virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
// IPrimaryScreen overrides
virtual void reconfigure(UInt32 activeSides) = 0;
virtual void warpCursor(SInt32 x, SInt32 y) = 0;
virtual SInt32 getJumpZoneSize() const = 0;
virtual bool isAnyMouseButtonDown() const = 0;
virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
// ISecondaryScreen overrides
virtual bool fakeCtrlAltDel() const = 0;
virtual void fakeMouseButton(ButtonID id, bool press) const = 0;
virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0;
virtual void fakeMouseWheel(SInt32 delta) const = 0;
// IKeyState overrides
virtual void updateKeys();
virtual void setHalfDuplexMask(KeyModifierMask);
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button);
virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button);
virtual void fakeKeyUp(KeyButton button);
virtual void fakeToggle(KeyModifierMask modifier);
virtual bool isKeyDown(KeyButton) const;
virtual KeyModifierMask
getActiveModifiers() const;
virtual const char* getKeyName(KeyButton) const;
// IPlatformScreen overrides
virtual void enable() = 0;
virtual void disable() = 0;
virtual void enter() = 0;
virtual bool leave() = 0;
virtual bool setClipboard(ClipboardID, const IClipboard*) = 0;
virtual void checkClipboards() = 0;
virtual void openScreensaver(bool notify) = 0;
virtual void closeScreensaver() = 0;
virtual void screensaver(bool activate) = 0;
virtual void resetOptions() = 0;
virtual void setOptions(const COptionsList& options) = 0;
virtual void setSequenceNumber(UInt32) = 0;
virtual bool isPrimary() const = 0;
protected:
//! Update mouse buttons
/*!
Subclasses must implement this method to update their internal mouse
button mapping and, if desired, state tracking.
*/
virtual void updateButtons() = 0;
//! Get the key state
/*!
Subclasses must implement this method to return the platform specific
key state object that each subclass must have.
*/
virtual IKeyState* getKeyState() const = 0;
// IPlatformScreen overrides
virtual void handleSystemEvent(const CEvent& event, void*) = 0;
};
#endif

View File

@ -32,9 +32,6 @@ CScreen::CScreen(IPlatformScreen* platformScreen) :
{
assert(m_screen != NULL);
// open screen
m_screen->setKeyState(this);
// reset options
resetOptions();
@ -78,7 +75,7 @@ CScreen::disable()
leave();
}
else if (m_isPrimary && !m_entered) {
enter();
enter(0);
}
m_screen->disable();
if (m_isPrimary) {
@ -93,7 +90,7 @@ CScreen::disable()
}
void
CScreen::enter()
CScreen::enter(KeyModifierMask toggleMask)
{
assert(m_entered == false);
LOG((CLOG_INFO "entering screen"));
@ -105,7 +102,7 @@ CScreen::enter()
enterPrimary();
}
else {
enterSecondary();
enterSecondary(toggleMask);
}
m_screen->enter();
}
@ -186,22 +183,7 @@ CScreen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
return;
}
}
// get the sequence of keys to simulate key press and the final
// modifier state.
Keystrokes keys;
KeyButton key = m_screen->mapKey(keys, *this, id, mask, false);
if (keys.empty()) {
// do nothing if there are no associated keys
LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id));
return;
}
// generate key events
doKeystrokes(keys, 1);
// note that key is down
updateKeyState(button, key, true);
m_screen->fakeKeyDown(id, mask, button);
}
void
@ -209,86 +191,14 @@ CScreen::keyRepeat(KeyID id,
KeyModifierMask mask, SInt32 count, KeyButton button)
{
assert(!m_isPrimary);
// if we haven't seen this button go down then ignore it
ServerKeyMap::iterator index = m_serverKeyMap.find(button);
if (index == m_serverKeyMap.end()) {
return;
}
// get the sequence of keys to simulate key repeat and the final
// modifier state.
Keystrokes keys;
KeyButton key = m_screen->mapKey(keys, *this, id, mask, true);
if (key == 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.
key &= 0x1ffu;
if (key != index->second) {
// replace key up with previous key id but leave key down
// alone so it uses the new keycode and store that keycode
// in the server key map.
for (Keystrokes::iterator index2 = keys.begin();
index2 != keys.end(); ++index2) {
if ((index2->m_key & 0x1ffu) == key) {
index2->m_key = index->second;
break;
}
}
// note that old key is now up
m_keys[index->second] &= ~kDown;
m_fakeKeys[index->second] &= ~kDown;
// map server key to new key
index->second = key;
// note that new key is now down
m_keys[index->second] |= kDown;
m_fakeKeys[index->second] |= kDown;
}
// generate key events
doKeystrokes(keys, count);
m_screen->fakeKeyRepeat(id, mask, count, button);
}
void
CScreen::keyUp(KeyID, KeyModifierMask, KeyButton button)
{
assert(!m_isPrimary);
// if we haven't seen this button go down then ignore it
ServerKeyMap::iterator index = m_serverKeyMap.find(button);
if (index == m_serverKeyMap.end()) {
return;
}
KeyButton key = index->second;
// get the sequence of keys to simulate key release
Keystrokes keys;
Keystroke keystroke;
keystroke.m_key = key;
keystroke.m_press = false;
keystroke.m_repeat = false;
keys.push_back(keystroke);
// generate key events
doKeystrokes(keys, 1);
// note that key is now up
updateKeyState(button, key, false);
m_screen->fakeKeyUp(button);
}
void
@ -323,8 +233,7 @@ void
CScreen::resetOptions()
{
// reset options
m_numLockHalfDuplex = false;
m_capsLockHalfDuplex = false;
m_halfDuplex = 0;
// if screen saver synchronization was off then turn it on since
// that's the default option state.
@ -350,12 +259,24 @@ CScreen::setOptions(const COptionsList& options)
LOG((CLOG_DEBUG1 "screen saver synchronization %s", m_screenSaverSync ? "on" : "off"));
}
else if (options[i] == kOptionHalfDuplexCapsLock) {
m_capsLockHalfDuplex = (options[i + 1] != 0);
LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", m_capsLockHalfDuplex ? "on" : "off"));
if (options[i + 1] != 0) {
m_halfDuplex |= KeyModifierCapsLock;
}
else {
m_halfDuplex &= ~KeyModifierCapsLock;
}
m_screen->setHalfDuplexMask(m_halfDuplex);
LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", ((m_halfDuplex & KeyModifierCapsLock) != 0) ? "on" : "off"));
}
else if (options[i] == kOptionHalfDuplexNumLock) {
m_numLockHalfDuplex = (options[i + 1] != 0);
LOG((CLOG_DEBUG1 "half-duplex num-lock %s", m_numLockHalfDuplex ? "on" : "off"));
if (options[i + 1] != 0) {
m_halfDuplex |= KeyModifierNumLock;
}
else {
m_halfDuplex &= ~KeyModifierNumLock;
}
m_screen->setHalfDuplexMask(m_halfDuplex);
LOG((CLOG_DEBUG1 "half-duplex num-lock %s", ((m_halfDuplex & KeyModifierNumLock) != 0) ? "on" : "off"));
}
}
@ -401,7 +322,7 @@ CScreen::isLockedToScreen() const
// be necessary but we don't seem to get some key release
// events sometimes. this is an emergency backup so the
// client doesn't get stuck on the screen.
const_cast<CScreen*>(this)->updateKeys();
m_screen->updateKeys();
KeyButton key2 = isAnyKeyDown();
if (key2 != 0) {
LOG((CLOG_DEBUG "locked by %s", m_screen->getKeyName(key2)));
@ -433,6 +354,12 @@ CScreen::getCursorCenter(SInt32& x, SInt32& y) const
m_screen->getCursorCenter(x, y);
}
KeyModifierMask
CScreen::getActiveModifiers() const
{
return m_screen->getActiveModifiers();
}
void*
CScreen::getEventTarget() const
{
@ -457,253 +384,6 @@ CScreen::getCursorPos(SInt32& x, SInt32& y) const
m_screen->getCursorPos(x, y);
}
void
CScreen::updateKeys()
{
// clear key state
memset(m_keys, 0, sizeof(m_keys));
memset(m_fakeKeys, 0, sizeof(m_fakeKeys));
m_maskToKeys.clear();
m_keyToMask.clear();
// let subclass set m_keys
m_screen->updateKeys();
// figure out active modifier mask
m_mask = getModifierMask();
LOG((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask));
}
void
CScreen::releaseKeys()
{
// release keys that we've synthesized a press for and only those
// keys. we don't want to synthesize a release on a key the user
// is still physically pressing.
for (KeyButton i = 1; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) {
if ((m_fakeKeys[i] & kDown) != 0) {
fakeKeyEvent(i, false, false);
m_keys[i] &= ~kDown;
m_fakeKeys[i] &= ~kDown;
}
}
}
void
CScreen::setKeyDown(KeyButton key, bool down)
{
if (!isHalfDuplex(getMaskForKey(key))) {
if (down) {
m_keys[key & 0x1ffu] |= kDown;
}
else {
m_keys[key & 0x1ffu] &= ~kDown;
}
}
}
void
CScreen::setToggled(KeyModifierMask mask)
{
if (!isToggle(mask)) {
return;
}
MaskToKeys::const_iterator i = m_maskToKeys.find(mask);
if (i == m_maskToKeys.end()) {
return;
}
for (KeyButtons::const_iterator j = i->second.begin();
j != i->second.end(); ++j) {
m_keys[(*j) & 0x1ffu] |= kToggled;
}
}
void
CScreen::addModifier(KeyModifierMask mask, KeyButtons& keys)
{
// the modifier must have associated keys
if (keys.empty()) {
return;
}
// the mask must not be zero
assert(mask != 0);
// the mask must have exactly one high bit
assert((mask & (mask - 1)) == 0);
// index mask by keycodes
for (KeyButtons::iterator j = keys.begin(); j != keys.end(); ++j) {
// key must be valid
if (((*j) & 0x1ffu) != 0) {
m_keyToMask[static_cast<KeyButton>((*j) & 0x1ffu)] = mask;
}
}
// index keys by mask
m_maskToKeys[mask].swap(keys);
}
void
CScreen::setToggleState(KeyModifierMask mask)
{
// toggle modifiers that don't match the desired state
KeyModifierMask different = (m_mask ^ mask);
if ((different & KeyModifierCapsLock) != 0) {
toggleKey(KeyModifierCapsLock);
}
if ((different & KeyModifierNumLock) != 0) {
toggleKey(KeyModifierNumLock);
}
if ((different & KeyModifierScrollLock) != 0) {
toggleKey(KeyModifierScrollLock);
}
}
KeyButton
CScreen::isAnyKeyDown() const
{
for (UInt32 i = 1; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) {
if ((m_keys[i] & kDown) != 0) {
return static_cast<KeyButton>(i);
}
}
return 0;
}
bool
CScreen::isKeyDown(KeyButton key) const
{
key &= 0x1ffu;
return (key != 0 && ((m_keys[key] & kDown) != 0));
}
bool
CScreen::isToggle(KeyModifierMask mask) const
{
static const KeyModifierMask s_toggleMask =
KeyModifierCapsLock | KeyModifierNumLock | KeyModifierScrollLock;
return ((mask & s_toggleMask) != 0);
}
bool
CScreen::isHalfDuplex(KeyModifierMask mask) const
{
return ((mask == KeyModifierCapsLock && m_capsLockHalfDuplex) ||
(mask == KeyModifierNumLock && m_numLockHalfDuplex));
}
bool
CScreen::isModifierActive(KeyModifierMask mask) const
{
MaskToKeys::const_iterator i = m_maskToKeys.find(mask);
if (i == m_maskToKeys.end()) {
return false;
}
KeyButtons::const_iterator j = i->second.begin();
if (isToggle(mask)) {
// modifier is a toggle
if (isKeyToggled(*j)) {
return true;
}
}
else {
// modifier is not a toggle
for (; j != i->second.end(); ++j) {
if (isKeyDown(*j)) {
return true;
}
}
}
return false;
}
KeyModifierMask
CScreen::getActiveModifiers() const
{
if (m_isPrimary) {
return m_screen->getActiveModifiers();
}
else {
return m_mask;
}
}
bool
CScreen::mapModifier(Keystrokes& keys, Keystrokes& undo,
KeyModifierMask mask, bool desireActive) const
{
// look up modifier
MaskToKeys::const_iterator i = m_maskToKeys.find(mask);
if (i == m_maskToKeys.end()) {
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 = i->second.front();
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 = i->second.front();
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 = i->second.begin();
j != i->second.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;
}
KeyModifierMask
CScreen::getMaskForKey(KeyButton key) const
{
KeyToMask::const_iterator i = m_keyToMask.find(key);
if (i == m_keyToMask.end()) {
return 0;
}
else {
return i->second;
}
}
void
CScreen::enablePrimary()
{
@ -749,13 +429,16 @@ CScreen::enterPrimary()
}
void
CScreen::enterSecondary()
CScreen::enterSecondary(KeyModifierMask toggleMask)
{
// update our keyboard state to reflect the local state
updateKeys();
m_screen->updateKeys();
// remember toggle key state. we'll restore this when we leave.
m_toggleKeys = m_mask;
m_toggleKeys = getActiveModifiers();
// restore toggle key state
setToggleState(toggleMask);
}
void
@ -774,170 +457,42 @@ CScreen::leaveSecondary()
setToggleState(m_toggleKeys);
}
KeyModifierMask
CScreen::getModifierMask() const
{
KeyModifierMask mask = 0;
if (isModifierActive(KeyModifierShift)) {
mask |= KeyModifierShift;
}
if (isModifierActive(KeyModifierControl)) {
mask |= KeyModifierControl;
}
if (isModifierActive(KeyModifierAlt)) {
mask |= KeyModifierAlt;
}
if (isModifierActive(KeyModifierMeta)) {
mask |= KeyModifierMeta;
}
if (isModifierActive(KeyModifierSuper)) {
mask |= KeyModifierSuper;
}
if (isModifierActive(KeyModifierModeSwitch)) {
mask |= KeyModifierModeSwitch;
}
if (isModifierActive(KeyModifierNumLock)) {
mask |= KeyModifierNumLock;
}
if (isModifierActive(KeyModifierCapsLock)) {
mask |= KeyModifierCapsLock;
}
if (isModifierActive(KeyModifierScrollLock)) {
mask |= KeyModifierScrollLock;
}
return mask;
}
void
CScreen::doKeystrokes(const Keystrokes& keys, SInt32 count)
CScreen::releaseKeys()
{
// do nothing if no keys or no repeats
if (count < 1 || 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;
for (; count > 0; --count) {
// 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;
// release keys that we've synthesized a press for and only those
// keys. we don't want to synthesize a release on a key the user
// is still physically pressing.
for (KeyButton i = 1; i < IKeyState::kNumButtons; ++i) {
if (m_screen->isKeyDown(i)) {
m_screen->fakeKeyUp(i);
}
}
}
void
CScreen::fakeKeyEvent(KeyButton key, bool press, bool repeat) const
CScreen::setToggleState(KeyModifierMask mask)
{
// half-duplex keys are special. we ignore releases and convert
// a press when the toggle is active to a release.
KeyModifierMask mask = getMaskForKey(key);
if (isHalfDuplex(mask)) {
if (repeat || !press) {
return;
// toggle modifiers that don't match the desired state
KeyModifierMask different = (m_screen->getActiveModifiers() ^ mask);
if ((different & KeyModifierCapsLock) != 0) {
m_screen->fakeToggle(KeyModifierCapsLock);
}
if (isModifierActive(mask)) {
press = false;
if ((different & KeyModifierNumLock) != 0) {
m_screen->fakeToggle(KeyModifierNumLock);
}
if ((different & KeyModifierScrollLock) != 0) {
m_screen->fakeToggle(KeyModifierScrollLock);
}
}
// send key event
LOG((CLOG_DEBUG2 " %d %s%s", key, press ? "down" : "up", repeat ? " repeat" : ""));
m_screen->fakeKeyEvent(key, press);
}
void
CScreen::updateKeyState(KeyButton button, KeyButton key, bool press)
KeyButton
CScreen::isAnyKeyDown() const
{
// ignore bogus keys
key &= 0x1ffu;
if (button == 0 || key == 0) {
return;
}
// update shadow state. shadow state doesn't change on auto-repeat.
if (press) {
// key is now down
m_serverKeyMap[button] = key;
m_keys[key] |= kDown;
m_fakeKeys[key] |= kDown;
}
else {
// key is now up
m_serverKeyMap.erase(button);
m_keys[key] &= ~kDown;
m_fakeKeys[key] &= ~kDown;
}
KeyModifierMask mask = getMaskForKey(key);
if (mask != 0) {
// key is a modifier
if (isToggle(mask)) {
// key is a toggle modifier
if (press) {
m_keys[key] ^= kToggled;
m_mask ^= mask;
// if key is half duplex then don't report it as down
if (isHalfDuplex(mask)) {
m_keys[key] &= ~kDown;
m_fakeKeys[key] &= ~kDown;
for (KeyButton i = 1; i < IKeyState::kNumButtons; ++i) {
if (m_screen->isKeyDown(i)) {
return i;
}
}
}
else {
// key is a normal modifier
if (press) {
m_mask |= mask;
}
else if (!isModifierActive(mask)) {
// no key for modifier is down anymore
m_mask &= ~mask;
}
}
LOG((CLOG_DEBUG2 "new mask: 0x%04x", m_mask));
}
}
void
CScreen::toggleKey(KeyModifierMask mask)
{
// get the system key ID for this toggle key ID
MaskToKeys::const_iterator i = m_maskToKeys.find(mask);
if (i == m_maskToKeys.end()) {
return;
}
KeyButton key = i->second.front();
// toggle the key
fakeKeyEvent(key, true, false);
fakeKeyEvent(key, false, false);
// toggle shadow state
m_mask ^= mask;
key &= 0x1ffu;
m_keys[key] ^= kToggled;
}
bool
CScreen::isKeyToggled(KeyButton key) const
{
key &= 0x1ffu;
return (key != 0 && ((m_keys[key] & kToggled) != 0));
return 0;
}

View File

@ -15,12 +15,11 @@
#ifndef CSCREEN_H
#define CSCREEN_H
#include "IKeyState.h"
#include "IScreen.h"
#include "ClipboardTypes.h"
#include "KeyTypes.h"
#include "MouseTypes.h"
#include "OptionTypes.h"
#include "stdmap.h"
class IClipboard;
class IPlatformScreen;
@ -30,7 +29,7 @@ class IPlatformScreen;
This is a platform independent screen. It can work as either a
primary or secondary screen.
*/
class CScreen : public IScreen, public IKeyState {
class CScreen : public IScreen {
public:
CScreen(IPlatformScreen* platformScreen);
virtual ~CScreen();
@ -55,9 +54,10 @@ public:
//! Enter screen
/*!
Called when the user navigates to this screen.
Called when the user navigates to this screen. \p toggleMask has the
toggle keys that should be turned on on the secondary screen.
*/
void enter();
void enter(KeyModifierMask toggleMask);
//! Leave screen
/*!
@ -208,6 +208,12 @@ public:
*/
void getCursorCenter(SInt32& x, SInt32& y) const;
//! Get the active modifiers
/*!
Returns the modifiers that are currently active.
*/
KeyModifierMask getActiveModifiers() const;
//@}
// IScreen overrides
@ -217,25 +223,6 @@ public:
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
// IKeyState overrides
virtual void updateKeys();
virtual void releaseKeys();
virtual void setKeyDown(KeyButton key, bool);
virtual void setToggled(KeyModifierMask);
virtual void addModifier(KeyModifierMask, KeyButtons&);
virtual void setToggleState(KeyModifierMask);
virtual KeyButton isAnyKeyDown() const;
virtual bool isKeyDown(KeyButton) const;
virtual bool isToggle(KeyModifierMask) const;
virtual bool isHalfDuplex(KeyModifierMask) const;
virtual bool isModifierActive(KeyModifierMask) const;
virtual KeyModifierMask
getActiveModifiers() const;
virtual bool mapModifier(Keystrokes& keys, Keystrokes& undo,
KeyModifierMask mask, bool desireActive) const;
virtual KeyModifierMask
getMaskForKey(KeyButton) const;
protected:
void enablePrimary();
void enableSecondary();
@ -243,35 +230,16 @@ protected:
void disableSecondary();
void enterPrimary();
void enterSecondary();
void enterSecondary(KeyModifierMask toggleMask);
void leavePrimary();
void leaveSecondary();
private:
// Get the modifier mask for the current key state
KeyModifierMask getModifierMask() const;
// Send fake keystrokes
void doKeystrokes(const Keystrokes&, SInt32 count);
// Send a fake key event
void fakeKeyEvent(KeyButton, bool press, bool repeat) const;
// Update the shadow state for a key
void updateKeyState(KeyButton button,
KeyButton key, bool press);
// Toggle a modifier
void toggleKey(KeyModifierMask);
// Test if a modifier is toggled
bool isKeyToggled(KeyButton) const;
void releaseKeys();
void setToggleState(KeyModifierMask);
KeyButton isAnyKeyDown() const;
private:
typedef std::map<KeyButton, KeyButton> ServerKeyMap;
typedef std::map<KeyModifierMask, KeyButtons> MaskToKeys;
typedef std::map<KeyButton, KeyModifierMask> KeyToMask;
// our platform dependent screen
IPlatformScreen* m_screen;
@ -289,26 +257,7 @@ private:
// note toggle keys that toggles on up/down (false) or on
// transition (true)
bool m_numLockHalfDuplex;
bool m_capsLockHalfDuplex;
// keyboard state
// map server key buttons to local system keys
ServerKeyMap m_serverKeyMap;
// system key states as set by us or the user
KeyState m_keys[512];
// system key states as set by us
KeyState m_fakeKeys[512];
// modifier info
MaskToKeys m_maskToKeys;
KeyToMask m_keyToMask;
// current active modifiers
KeyModifierMask m_mask;
KeyModifierMask m_halfDuplex;
// the toggle key state when this screen was last entered
KeyModifierMask m_toggleKeys;

61
lib/synergy/IKeyState.cpp Normal file
View File

@ -0,0 +1,61 @@
/*
* 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 "IKeyState.h"
//
// IKeyState
//
CEvent::Type IKeyState::s_keyDownEvent = CEvent::kUnknown;
CEvent::Type IKeyState::s_keyUpEvent = CEvent::kUnknown;
CEvent::Type IKeyState::s_keyRepeatEvent = CEvent::kUnknown;
CEvent::Type
IKeyState::getKeyDownEvent()
{
return CEvent::registerTypeOnce(s_keyDownEvent,
"IKeyState::keyDown");
}
CEvent::Type
IKeyState::getKeyUpEvent()
{
return CEvent::registerTypeOnce(s_keyUpEvent,
"IKeyState::keyUp");
}
CEvent::Type
IKeyState::getKeyRepeatEvent()
{
return CEvent::registerTypeOnce(s_keyRepeatEvent,
"IKeyState::keyRepeat");
}
//
// IKeyState::CKeyInfo
//
IKeyState::CKeyInfo*
IKeyState::CKeyInfo::alloc(KeyID id,
KeyModifierMask mask, KeyButton button, SInt32 count)
{
CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo));
info->m_key = id;
info->m_mask = mask;
info->m_button = button;
info->m_count = count;
return info;
}

View File

@ -17,20 +17,30 @@
#include "IInterface.h"
#include "KeyTypes.h"
#include "CString.h"
#include "stdvector.h"
#include "CEvent.h"
//! Key state interface
/*!
This interface provides access to set and query the keyboard state and
to synthesize key events.
*/
class IKeyState : public IInterface {
public:
class Keystroke {
public:
KeyButton m_key;
bool m_press;
bool m_repeat;
enum {
kNumButtons = 0x200
};
typedef std::vector<Keystroke> Keystrokes;
typedef std::vector<KeyButton> KeyButtons;
//! Key event data
class CKeyInfo {
public:
static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count);
public:
KeyID m_key;
KeyModifierMask m_mask;
KeyButton m_button;
SInt32 m_count;
};
//! @name manipulators
//@{
@ -42,79 +52,52 @@ public:
*/
virtual void updateKeys() = 0;
//! Release fake pressed keys
//! Set half-duplex mask
/*!
Send fake key events to release keys that aren't physically pressed
but are logically pressed.
Sets which modifier toggle keys are half-duplex. A half-duplex
toggle key doesn't report a key release when toggled on and
doesn't report a key press when toggled off.
*/
virtual void releaseKeys() = 0;
virtual void setHalfDuplexMask(KeyModifierMask) = 0;
//! Mark key as being down
//! Fake a key press
/*!
Sets the state of \c key to down or up.
Synthesizes a key press event and updates the key state.
*/
virtual void setKeyDown(KeyButton key, bool down) = 0;
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button) = 0;
//! Mark modifier as being toggled on
//! Fake a key repeat
/*!
Sets the state of the keys for the given (single) modifier to be
toggled on.
Synthesizes a key repeat event and updates the key state.
*/
virtual void setToggled(KeyModifierMask) = 0;
virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button) = 0;
//! Add keys for modifier
//! Fake a key release
/*!
Sets the keys that are mapped to the given (single) modifier. For
example, if buttons 5 and 23 were mapped to KeyModifierShift (perhaps
as left and right shift keys) then the mask would be KeyModifierShift
and \c keys would contain 5 and 23. A modifier with no keys is
ignored. Keys that are zero are ignored. \c keys may be modified
by the call.
Synthesizes a key release event and updates the key state.
*/
virtual void addModifier(KeyModifierMask, KeyButtons& keys) = 0;
virtual void fakeKeyUp(KeyButton button) = 0;
//! Set toggle key state
//! Fake a modifier toggle
/*!
Update the local toggle key state to match the given state.
Synthesizes key press/release events to toggle the given \p modifier
and updates the key state.
*/
virtual void setToggleState(KeyModifierMask) = 0;
virtual void fakeToggle(KeyModifierMask modifier) = 0;
//@}
//! @name accessors
//@{
//! Test if any key is down
/*!
If any key is down then returns one of those keys. Otherwise returns 0.
*/
virtual KeyButton isAnyKeyDown() const = 0;
//! Test if key is pressed
/*!
Returns true iff the given key is down. Half-duplex toggles
should always return false.
always return false.
*/
virtual bool isKeyDown(KeyButton) const = 0;
//! Test if modifier is a toggle
/*!
Returns true iff the given (single) modifier is a toggle.
*/
virtual bool isToggle(KeyModifierMask) const = 0;
//! Test if modifier is half-duplex
/*!
Returns true iff the given (single) modifier is a half-duplex
toggle key.
*/
virtual bool isHalfDuplex(KeyModifierMask) const = 0;
//! Test if modifier is active
/*!
Returns true iff the given (single) modifier is currently active.
*/
virtual bool isModifierActive(KeyModifierMask) const = 0;
//! Get the active modifiers
/*!
Returns the modifiers that are currently active.
@ -122,35 +105,25 @@ public:
virtual KeyModifierMask
getActiveModifiers() const = 0;
//! Get key events to change modifier state
//! Get name of key
/*!
Retrieves the key events necessary to activate (\c desireActive is true)
or deactivate (\c desireActive is false) the modifier given by \c mask
by pushing them onto the back of \c keys. \c mask must specify exactly
one modifier. \c undo receives the key events necessary to restore the
modifier's previous state. They're pushed onto \c undo in the reverse
order they should be executed. Returns true if the modifier can be
adjusted, false otherwise.
Return a string describing the given key.
*/
virtual bool mapModifier(Keystrokes& keys, Keystrokes& undo,
KeyModifierMask mask, bool desireActive) const = 0;
virtual const char* getKeyName(KeyButton) const = 0;
//! Get modifier mask for key
/*!
Returns the modifier mask for \c key. If \c key is not a modifier
key then returns 0.
*/
virtual KeyModifierMask
getMaskForKey(KeyButton) const = 0;
//! Get key down event type. Event data is CKeyInfo*, count == 1.
static CEvent::Type getKeyDownEvent();
//! Get key up event type. Event data is CKeyInfo*, count == 1.
static CEvent::Type getKeyUpEvent();
//! Get key repeat event type. Event data is CKeyInfo*.
static CEvent::Type getKeyRepeatEvent();
//@}
protected:
typedef UInt8 KeyState;
enum EKeyState {
kDown = 0x01, //!< Key is down
kToggled = 0x80 //!< Key is toggled on
};
private:
static CEvent::Type s_keyDownEvent;
static CEvent::Type s_keyUpEvent;
static CEvent::Type s_keyRepeatEvent;
};
#endif

View File

@ -18,11 +18,11 @@
#include "IScreen.h"
#include "IPrimaryScreen.h"
#include "ISecondaryScreen.h"
#include "IKeyState.h"
#include "ClipboardTypes.h"
#include "OptionTypes.h"
class IClipboard;
class IKeyState;
//! Screen interface
/*!
@ -31,18 +31,12 @@ screen implementations that are used by both primary and secondary
screens.
*/
class IPlatformScreen : public IScreen,
public IPrimaryScreen, public ISecondaryScreen {
public IPrimaryScreen, public ISecondaryScreen,
public IKeyState {
public:
//! @name manipulators
//@{
//! Set the key state
/*!
Sets the key state object. This object tracks keyboard state and
the screen is expected to keep it up to date.
*/
virtual void setKeyState(IKeyState*) = 0;
//! Enable screen
/*!
Enable the screen, preparing it to report system and user events.
@ -124,13 +118,6 @@ public:
*/
virtual void setOptions(const COptionsList& options) = 0;
//! Get keyboard state
/*!
Put the current keyboard state into the IKeyState passed to
\c setKeyState().
*/
virtual void updateKeys() = 0;
//! Set clipboard sequence number
/*!
Sets the sequence number to use in subsequent clipboard events.
@ -161,20 +148,27 @@ public:
virtual void warpCursor(SInt32 x, SInt32 y) = 0;
virtual SInt32 getJumpZoneSize() const = 0;
virtual bool isAnyMouseButtonDown() const = 0;
virtual KeyModifierMask getActiveModifiers() const = 0;
virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
virtual const char* getKeyName(KeyButton) const = 0;
// ISecondaryScreen overrides
virtual void fakeKeyEvent(KeyButton id, bool press) const = 0;
virtual bool fakeCtrlAltDel() const = 0;
virtual void fakeMouseButton(ButtonID id, bool press) const = 0;
virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0;
virtual void fakeMouseWheel(SInt32 delta) const = 0;
virtual KeyButton mapKey(IKeyState::Keystrokes&,
const IKeyState& keyState, KeyID id,
KeyModifierMask desiredMask,
bool isAutoRepeat) const = 0;
// IKeyState overrides
virtual void updateKeys() = 0;
virtual void setHalfDuplexMask(KeyModifierMask) = 0;
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button) = 0;
virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button) = 0;
virtual void fakeKeyUp(KeyButton button) = 0;
virtual void fakeToggle(KeyModifierMask modifier) = 0;
virtual bool isKeyDown(KeyButton) const = 0;
virtual KeyModifierMask
getActiveModifiers() const = 0;
virtual const char* getKeyName(KeyButton) const = 0;
protected:
//! Handle system event
@ -193,10 +187,10 @@ protected:
A primary screen has further responsibilities. It should post
the events in \c IPrimaryScreen as appropriate. It should also
call \c setKeyDown() on the \c IKeyState passed to \c setKeyState()
whenever a key is pressed or released (but not for key repeats).
And it should call \c updateKeys() on the \c IKeyState if necessary
when the keyboard mapping changes.
call \c setKeyDown() on its \c CKeyState whenever a key is pressed
or released (but not for key repeats). And it should call
\c updateKeys() on its \c CKeyState if necessary when the keyboard
mapping changes.
The target of all events should be the value returned by
\c getEventTarget().

View File

@ -18,9 +18,6 @@
// IPrimaryScreen
//
CEvent::Type IPrimaryScreen::s_keyDownEvent = CEvent::kUnknown;
CEvent::Type IPrimaryScreen::s_keyUpEvent = CEvent::kUnknown;
CEvent::Type IPrimaryScreen::s_keyRepeatEvent = CEvent::kUnknown;
CEvent::Type IPrimaryScreen::s_buttonDownEvent = CEvent::kUnknown;
CEvent::Type IPrimaryScreen::s_buttonUpEvent = CEvent::kUnknown;
CEvent::Type IPrimaryScreen::s_motionPrimaryEvent = CEvent::kUnknown;
@ -29,27 +26,6 @@ CEvent::Type IPrimaryScreen::s_wheelEvent = CEvent::kUnknown;
CEvent::Type IPrimaryScreen::s_ssActivatedEvent = CEvent::kUnknown;
CEvent::Type IPrimaryScreen::s_ssDeactivatedEvent = CEvent::kUnknown;
CEvent::Type
IPrimaryScreen::getKeyDownEvent()
{
return CEvent::registerTypeOnce(s_keyDownEvent,
"IPrimaryScreen::keyDown");
}
CEvent::Type
IPrimaryScreen::getKeyUpEvent()
{
return CEvent::registerTypeOnce(s_keyUpEvent,
"IPrimaryScreen::keyUp");
}
CEvent::Type
IPrimaryScreen::getKeyRepeatEvent()
{
return CEvent::registerTypeOnce(s_keyRepeatEvent,
"IPrimaryScreen::keyRepeat");
}
CEvent::Type
IPrimaryScreen::getButtonDownEvent()
{
@ -100,23 +76,6 @@ IPrimaryScreen::getScreensaverDeactivatedEvent()
}
//
// IPrimaryScreen::CKeyInfo
//
IPrimaryScreen::CKeyInfo*
IPrimaryScreen::CKeyInfo::alloc(KeyID id,
KeyModifierMask mask, KeyButton button, SInt32 count)
{
CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo));
info->m_key = id;
info->m_mask = mask;
info->m_button = button;
info->m_count = count;
return info;
}
//
// IPrimaryScreen::CButtonInfo
//

View File

@ -16,9 +16,8 @@
#define IPRIMARYSCREEN_H
#include "IInterface.h"
#include "IKeyState.h"
#include "CEvent.h"
#include "MouseTypes.h"
#include "CEvent.h"
//! Primary screen interface
/*!
@ -27,17 +26,6 @@ primary screen implementations.
*/
class IPrimaryScreen : public IInterface {
public:
//! Key event data
class CKeyInfo {
public:
static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count);
public:
KeyID m_key;
KeyModifierMask m_mask;
KeyButton m_button;
SInt32 m_count;
};
//! Button event data
class CButtonInfo {
public:
@ -103,14 +91,6 @@ public:
*/
virtual bool isAnyMouseButtonDown() const = 0;
//! Get current modifier key state
/*!
Returns the current modifier key state. Ideally, "current" means
up to the lat processed event but it can mean the current physical
modifier key state.
*/
virtual KeyModifierMask getActiveModifiers() const = 0;
//! Get cursor center position
/*!
Return the cursor center position which is where we park the
@ -119,18 +99,6 @@ public:
*/
virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
//! Get name of key
/*!
Return a string describing the given key.
*/
virtual const char* getKeyName(KeyButton) const = 0;
//! Get key down event type. Event data is CKeyInfo*, count == 1.
static CEvent::Type getKeyDownEvent();
//! Get key up event type. Event data is CKeyInfo*, count == 1.
static CEvent::Type getKeyUpEvent();
//! Get key repeat event type. Event data is CKeyInfo*.
static CEvent::Type getKeyRepeatEvent();
//! Get button down event type. Event data is CButtonInfo*.
static CEvent::Type getButtonDownEvent();
//! Get button up event type. Event data is CButtonInfo*.
@ -156,9 +124,6 @@ public:
//@}
private:
static CEvent::Type s_keyDownEvent;
static CEvent::Type s_keyUpEvent;
static CEvent::Type s_keyRepeatEvent;
static CEvent::Type s_buttonDownEvent;
static CEvent::Type s_buttonUpEvent;
static CEvent::Type s_motionPrimaryEvent;

View File

@ -16,7 +16,6 @@
#define ISECONDARYSCREEN_H
#include "IInterface.h"
#include "IKeyState.h"
#include "MouseTypes.h"
//! Secondary screen interface
@ -29,12 +28,6 @@ public:
//! @name accessors
//@{
//! Fake key press/release
/*!
Synthesize a press or release of key \c id.
*/
virtual void fakeKeyEvent(KeyButton id, bool press) const = 0;
//! Fake ctrl+alt+del
/*!
Synthesize a press of ctrl+alt+del. Return true if processing is
@ -60,22 +53,6 @@ public:
*/
virtual void fakeMouseWheel(SInt32 delta) const = 0;
//! Map key press/repeat to keystrokes
/*!
Convert a press/repeat of key \c id with the modifiers as given
in \c desiredMask into the keystrokes necessary to synthesize
that key event. This may expand into multiple keys due to modifiers
that don't match the current modifier state from \c keyState, or to
needing to compose a character using dead key, or to other reasons.
Return the platform specific code of the key being pressed. If \c id
cannot be mapped or if \c isAutoRepeat is true and the key does not
auto-repeat then return 0.
*/
virtual KeyButton mapKey(IKeyState::Keystrokes&,
const IKeyState& keyState, KeyID id,
KeyModifierMask desiredMask,
bool isAutoRepeat) const = 0;
//@}
};

View File

@ -26,16 +26,21 @@ MAINTAINERCLEANFILES = \
noinst_LIBRARIES = libsynergy.a
libsynergy_a_SOURCES = \
CClipboard.cpp \
CKeyState.cpp \
CPacketStreamFilter.cpp \
CPlatformScreen.cpp \
CProtocolUtil.cpp \
CScreen.cpp \
IClipboard.cpp \
IKeyState.cpp \
IPrimaryScreen.cpp \
IScreen.cpp \
XScreen.cpp \
XSynergy.cpp \
CClipboard.h \
CKeyState.h \
CPacketStreamFilter.h \
CPlatformScreen.h \
CProtocolUtil.h \
CScreen.h \
ClipboardTypes.h \