merged 1.4 r1043:1044 into trunk

This commit is contained in:
Nick Bolton 2011-06-18 23:44:23 +00:00
parent 7f4138a376
commit 2fe11744cf
28 changed files with 1101 additions and 267 deletions

View File

@ -0,0 +1,68 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsHookLibraryLoader.h"
#include "XScreen.h"
#include "CLog.h"
CMSWindowsHookLibraryLoader::CMSWindowsHookLibraryLoader() :
m_init(NULL),
m_cleanup(NULL),
m_setSides(NULL),
m_setZone(NULL),
m_setMode(NULL)
{
}
CMSWindowsHookLibraryLoader::~CMSWindowsHookLibraryLoader()
{
// TODO: take ownership of m_ and delete them.
}
HINSTANCE
CMSWindowsHookLibraryLoader::openHookLibrary(const char* name)
{
// load the hook library
HINSTANCE hookLibrary = LoadLibrary(name);
if (hookLibrary == NULL) {
LOG((CLOG_ERR "Failed to load hook library; %s.dll is missing", name));
throw XScreenOpenFailure();
}
// look up functions
m_setSides = (SetSidesFunc)GetProcAddress(hookLibrary, "setSides");
m_setZone = (SetZoneFunc)GetProcAddress(hookLibrary, "setZone");
m_setMode = (SetModeFunc)GetProcAddress(hookLibrary, "setMode");
m_init = (InitFunc)GetProcAddress(hookLibrary, "init");
m_cleanup = (CleanupFunc)GetProcAddress(hookLibrary, "cleanup");
if (m_setSides == NULL ||
m_setZone == NULL ||
m_setMode == NULL ||
m_init == NULL ||
m_cleanup == NULL) {
LOG((CLOG_ERR "Invalid hook library; use a newer %s.dll", name));
throw XScreenOpenFailure();
}
// initialize hook library
if (m_init(GetCurrentThreadId()) == 0) {
LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?"));
throw XScreenOpenFailure();
}
return hookLibrary;
}

View File

@ -0,0 +1,45 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSHOOKLIBRARYLOADER_H
#define CMSWINDOWSHOOKLIBRARYLOADER_H
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "CSynergyHook.h"
//! Loads Windows hook DLLs.
class CMSWindowsHookLibraryLoader
{
public:
CMSWindowsHookLibraryLoader();
virtual ~CMSWindowsHookLibraryLoader();
HINSTANCE openHookLibrary(const char* name);
// TODO: either make these private or expose properly
InitFunc m_init;
CleanupFunc m_cleanup;
SetSidesFunc m_setSides;
SetZoneFunc m_setZone;
SetModeFunc m_setMode;
private:
HINSTANCE m_hookLibrary;
};
#endif

View File

@ -574,8 +574,8 @@ static const CWin32Modifiers s_modifiers[] =
{ VK_RWIN, KeyModifierSuper }
};
CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks,
void* eventTarget) :
CMSWindowsKeyState::CMSWindowsKeyState(
CMSWindowsDesks* desks, void* eventTarget) :
m_is95Family(CArchMiscWindows::isWindows95Family()),
m_eventTarget(eventTarget),
m_desks(desks),
@ -584,11 +584,27 @@ CMSWindowsKeyState::CMSWindowsKeyState(CMSWindowsDesks* desks,
m_lastDown(0),
m_useSavedModifiers(false),
m_savedModifiers(0),
m_originalSavedModifiers(0)
m_originalSavedModifiers(0),
m_eventQueue(*EVENTQUEUE)
{
// look up symbol that's available on winNT family but not win95
HMODULE userModule = GetModuleHandle("user32.dll");
m_ToUnicodeEx = (ToUnicodeEx_t)GetProcAddress(userModule, "ToUnicodeEx");
init();
}
CMSWindowsKeyState::CMSWindowsKeyState(
CMSWindowsDesks* desks, void* eventTarget, IEventQueue& eventQueue, CKeyMap& keyMap) :
CKeyState(eventQueue, keyMap),
m_is95Family(CArchMiscWindows::isWindows95Family()),
m_eventTarget(eventTarget),
m_desks(desks),
m_keyLayout(GetKeyboardLayout(0)),
m_fixTimer(NULL),
m_lastDown(0),
m_useSavedModifiers(false),
m_savedModifiers(0),
m_originalSavedModifiers(0),
m_eventQueue(eventQueue)
{
init();
}
CMSWindowsKeyState::~CMSWindowsKeyState()
@ -596,12 +612,20 @@ CMSWindowsKeyState::~CMSWindowsKeyState()
disable();
}
void
CMSWindowsKeyState::init()
{
// look up symbol that's available on winNT family but not win95
HMODULE userModule = GetModuleHandle("user32.dll");
m_ToUnicodeEx = (ToUnicodeEx_t)GetProcAddress(userModule, "ToUnicodeEx");
}
void
CMSWindowsKeyState::disable()
{
if (m_fixTimer != NULL) {
EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer);
EVENTQUEUE->deleteTimer(m_fixTimer);
getEventQueue().removeHandler(CEvent::kTimer, m_fixTimer);
getEventQueue().deleteTimer(m_fixTimer);
m_fixTimer = NULL;
}
m_lastDown = 0;
@ -773,11 +797,11 @@ CMSWindowsKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask,
CKeyState::fakeKeyDown(id, mask, button);
}
void
bool
CMSWindowsKeyState::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button)
{
CKeyState::fakeKeyRepeat(id, mask, count, button);
return CKeyState::fakeKeyRepeat(id, mask, count, button);
}
bool

View File

@ -27,6 +27,7 @@
class CEvent;
class CEventQueueTimer;
class CMSWindowsDesks;
class IEventQueue;
//! Microsoft Windows key mapper
/*!
@ -35,6 +36,7 @@ This class maps KeyIDs to keystrokes.
class CMSWindowsKeyState : public CKeyState {
public:
CMSWindowsKeyState(CMSWindowsDesks* desks, void* eventTarget);
CMSWindowsKeyState(CMSWindowsDesks* desks, void* eventTarget, IEventQueue& eventQueue, CKeyMap& keyMap);
virtual ~CMSWindowsKeyState();
//! @name manipulators
@ -126,7 +128,7 @@ public:
// IKeyState overrides
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button);
virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask,
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button);
virtual bool fakeCtrlAltDel();
virtual KeyModifierMask
@ -142,6 +144,12 @@ public:
KeyID key, KeyModifierMask mask,
SInt32 count, KeyButton button);
// Unit test accessors
KeyButton getLastDown() const { return m_lastDown; }
void setLastDown(KeyButton value) { m_lastDown = value; }
KeyModifierMask getSavedModifiers() const { return m_savedModifiers; }
void setSavedModifiers(KeyModifierMask value) { m_savedModifiers = value; }
protected:
// CKeyState overrides
virtual void getKeyMap(CKeyMap& keyMap);
@ -167,6 +175,8 @@ private:
void addKeyEntry(CKeyMap& keyMap, CKeyMap::KeyItem& item);
void init();
private:
// not implemented
CMSWindowsKeyState(const CMSWindowsKeyState&);
@ -184,6 +194,7 @@ private:
UINT m_buttonToNumpadVK[512];
KeyButton m_virtualKeyToButton[256];
KeyToVKMap m_keyToVKMap;
IEventQueue& m_eventQueue;
// the timer used to check for fixing key state
CEventQueueTimer* m_fixTimer;

View File

@ -104,11 +104,6 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, bool noHooks) :
m_ownClipboard(false),
m_desks(NULL),
m_hookLibrary(NULL),
m_init(NULL),
m_cleanup(NULL),
m_setSides(NULL),
m_setZone(NULL),
m_setMode(NULL),
m_keyState(NULL),
m_hasMouse(GetSystemMetrics(SM_MOUSEPRESENT) != 0),
m_showingMouse(false)
@ -205,10 +200,10 @@ CMSWindowsScreen::enable()
if (m_isPrimary) {
// set jump zones
m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
m_hookLibraryLoader.m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
// watch jump zones
m_setMode(kHOOK_WATCH_JUMP_ZONE);
m_hookLibraryLoader.m_setMode(kHOOK_WATCH_JUMP_ZONE);
}
else {
// prevent the system from entering power saving modes. if
@ -226,7 +221,7 @@ CMSWindowsScreen::disable()
if (m_isPrimary) {
// disable hooks
m_setMode(kHOOK_DISABLE);
m_hookLibraryLoader.m_setMode(kHOOK_DISABLE);
// enable special key sequences on win95 family
enableSpecialKeys(true);
@ -264,7 +259,7 @@ CMSWindowsScreen::enter()
enableSpecialKeys(true);
// watch jump zones
m_setMode(kHOOK_WATCH_JUMP_ZONE);
m_hookLibraryLoader.m_setMode(kHOOK_WATCH_JUMP_ZONE);
// all messages prior to now are invalid
nextMark();
@ -317,7 +312,7 @@ CMSWindowsScreen::leave()
m_keyState->saveModifiers();
// capture events
m_setMode(kHOOK_RELAY_EVENTS);
m_hookLibraryLoader.m_setMode(kHOOK_RELAY_EVENTS);
}
// now off screen
@ -471,7 +466,7 @@ CMSWindowsScreen::reconfigure(UInt32 activeSides)
assert(m_isPrimary);
LOG((CLOG_DEBUG "active sides: %x", activeSides));
m_setSides(activeSides);
m_hookLibraryLoader.m_setSides(activeSides);
}
void
@ -697,19 +692,21 @@ CMSWindowsScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
updateForceShowCursor();
}
void
bool
CMSWindowsScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button)
{
CPlatformScreen::fakeKeyRepeat(id, mask, count, button);
bool result = CPlatformScreen::fakeKeyRepeat(id, mask, count, button);
updateForceShowCursor();
return result;
}
void
bool
CMSWindowsScreen::fakeKeyUp(KeyButton button)
{
CPlatformScreen::fakeKeyUp(button);
bool result = CPlatformScreen::fakeKeyUp(button);
updateForceShowCursor();
return result;
}
void
@ -722,42 +719,14 @@ CMSWindowsScreen::fakeAllKeysUp()
HINSTANCE
CMSWindowsScreen::openHookLibrary(const char* name)
{
// load the hook library
HINSTANCE hookLibrary = LoadLibrary(name);
if (hookLibrary == NULL) {
LOG((CLOG_ERR "Failed to load hook library; %s.dll is missing", name));
throw XScreenOpenFailure();
}
// look up functions
m_setSides = (SetSidesFunc)GetProcAddress(hookLibrary, "setSides");
m_setZone = (SetZoneFunc)GetProcAddress(hookLibrary, "setZone");
m_setMode = (SetModeFunc)GetProcAddress(hookLibrary, "setMode");
m_init = (InitFunc)GetProcAddress(hookLibrary, "init");
m_cleanup = (CleanupFunc)GetProcAddress(hookLibrary, "cleanup");
if (m_setSides == NULL ||
m_setZone == NULL ||
m_setMode == NULL ||
m_init == NULL ||
m_cleanup == NULL) {
LOG((CLOG_ERR "Invalid hook library; use a newer %s.dll", name));
throw XScreenOpenFailure();
}
// initialize hook library
if (m_init(GetCurrentThreadId()) == 0) {
LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?"));
throw XScreenOpenFailure();
}
return hookLibrary;
return m_hookLibraryLoader.openHookLibrary(name);
}
void
CMSWindowsScreen::closeHookLibrary(HINSTANCE hookLibrary) const
{
if (hookLibrary != NULL) {
m_cleanup();
m_hookLibraryLoader.m_cleanup();
FreeLibrary(hookLibrary);
}
}
@ -1427,7 +1396,7 @@ CMSWindowsScreen::onDisplayChange()
// tell hook about resize if on screen
else {
m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
m_hookLibraryLoader.m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
}
}

View File

@ -25,6 +25,7 @@
#include "CString.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "CMSWindowsHookLibraryLoader.h"
class CEventQueueTimer;
class CMSWindowsDesks;
@ -89,9 +90,9 @@ public:
virtual void updateKeys();
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button);
virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask,
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button);
virtual void fakeKeyUp(KeyButton button);
virtual bool fakeKeyUp(KeyButton button);
virtual void fakeAllKeysUp();
// IPlatformScreen overrides
@ -274,11 +275,6 @@ private:
// hook library stuff
HINSTANCE m_hookLibrary;
InitFunc m_init;
CleanupFunc m_cleanup;
SetSidesFunc m_setSides;
SetZoneFunc m_setZone;
SetModeFunc m_setMode;
// keyboard stuff
CMSWindowsKeyState* m_keyState;
@ -308,6 +304,10 @@ private:
MOUSEKEYS m_mouseKeys;
MOUSEKEYS m_oldMouseKeys;
// loads synrgyhk.dll
CMSWindowsHookLibraryLoader
m_hookLibraryLoader;
static CMSWindowsScreen* s_screen;
// save last position of mouse to compute next delta movement

View File

@ -29,6 +29,7 @@ if (WIN32)
CMSWindowsScreenSaver.h
CMSWindowsUtil.h
CMSWindowsRelauncher.h
CMSWindowsHookLibraryLoader.h
IMSWindowsClipboardFacade.h
)
@ -48,6 +49,7 @@ if (WIN32)
CMSWindowsScreenSaver.cpp
CMSWindowsUtil.cpp
CMSWindowsRelauncher.cpp
CMSWindowsHookLibraryLoader.cpp
)
set(inc_hook

View File

@ -121,6 +121,23 @@ static const CKeyEntry s_controlKeys[] = {
COSXKeyState::COSXKeyState() :
m_deadKeyState(0)
{
init();
}
COSXKeyState::COSXKeyState(IEventQueue& eventQueue, CKeyMap& keyMap) :
CKeyState(eventQueue, keyMap),
m_deadKeyState(0)
{
init();
}
COSXKeyState::~COSXKeyState()
{
}
void
COSXKeyState::init()
{
// initialize modifier key values
shiftPressed = false;
@ -137,33 +154,31 @@ COSXKeyState::COSXKeyState() :
}
}
COSXKeyState::~COSXKeyState()
{
// do nothing
}
KeyModifierMask
COSXKeyState::mapModifiersFromOSX(UInt32 mask) const
{
LOG((CLOG_DEBUG1 "mask: %04x", mask));
// convert
LOG((CLOG_DEBUG1 "mask: %04x", mask));
// previously this used the kCGEventFlagMask* enums, which would
// not work, since GetCurrentKeyModifiers doesn't return a mask
// containing those values; instead *Key enums should be used.
KeyModifierMask outMask = 0;
if ((mask & kCGEventFlagMaskShift) != 0) {
if ((mask & shiftKey) != 0) {
outMask |= KeyModifierShift;
}
if ((mask & kCGEventFlagMaskControl) != 0) {
if ((mask & controlKey) != 0) {
outMask |= KeyModifierControl;
}
if ((mask & kCGEventFlagMaskAlternate) != 0) {
if ((mask & cmdKey) != 0) {
outMask |= KeyModifierAlt;
}
if ((mask & kCGEventFlagMaskCommand) != 0) {
if ((mask & optionKey) != 0) {
outMask |= KeyModifierSuper;
}
if ((mask & kCGEventFlagMaskAlphaShift) != 0) {
if ((mask & alphaLock) != 0) {
outMask |= KeyModifierCapsLock;
}
if ((mask & kCGEventFlagMaskNumericPad) != 0) {
if ((mask & s_osxNumLock) != 0) {
outMask |= KeyModifierNumLock;
}

View File

@ -39,6 +39,7 @@ public:
typedef std::vector<KeyID> CKeyIDs;
COSXKeyState();
COSXKeyState(IEventQueue& eventQueue, CKeyMap& keyMap);
virtual ~COSXKeyState();
//! @name modifiers
@ -147,6 +148,8 @@ private:
// mapVirtualKeyToKeyButton.
static UInt32 mapKeyButtonToVirtualKey(KeyButton keyButton);
void init();
private:
class CKeyResource : public IInterface {
public:

View File

@ -20,6 +20,8 @@
#include "CLog.h"
#include "CStringUtil.h"
#include "stdmap.h"
#include <cstddef>
#include <algorithm>
#if X_DISPLAY_MISSING
# error X11 is required to build synergy
#else
@ -33,8 +35,36 @@
#endif
#endif
static const size_t ModifiersFromXDefaultSize = 32;
CXWindowsKeyState::CXWindowsKeyState(Display* display, bool useXKB) :
m_display(display)
m_display(display),
m_modifierFromX(ModifiersFromXDefaultSize)
{
init(display, useXKB);
}
CXWindowsKeyState::CXWindowsKeyState(
Display* display, bool useXKB,
IEventQueue& eventQueue, CKeyMap& keyMap) :
CKeyState(eventQueue, keyMap),
m_display(display),
m_modifierFromX(ModifiersFromXDefaultSize)
{
init(display, useXKB);
}
CXWindowsKeyState::~CXWindowsKeyState()
{
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
XkbFreeKeyboard(m_xkb, 0, True);
}
#endif
}
void
CXWindowsKeyState::init(Display* display, bool useXKB)
{
XGetKeyboardControl(m_display, &m_keyboardState);
#if HAVE_XKB_EXTENSION
@ -49,19 +79,12 @@ CXWindowsKeyState::CXWindowsKeyState(Display* display, bool useXKB) :
setActiveGroup(kGroupPollAndSet);
}
CXWindowsKeyState::~CXWindowsKeyState()
{
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
XkbFreeKeyboard(m_xkb, 0, True);
}
#endif
}
void
CXWindowsKeyState::setActiveGroup(SInt32 group)
{
if (group == kGroupPollAndSet) {
// we need to set the group to -1 in order for pollActiveGroup() to
// actually poll for the group
m_group = -1;
m_group = pollActiveGroup();
}
@ -83,11 +106,18 @@ CXWindowsKeyState::setAutoRepeat(const XKeyboardState& state)
KeyModifierMask
CXWindowsKeyState::mapModifiersFromX(unsigned int state) const
{
LOG((CLOG_DEBUG2 "mapping state: %i", state));
UInt32 offset = 8 * getGroupFromState(state);
KeyModifierMask mask = 0;
for (int i = 0; i < 8; ++i) {
if ((state & (1u << i)) != 0) {
mask |= m_modifierFromX[offset + i];
LOG((CLOG_DEBUG2 "|= modifier: %i", offset + i));
if (offset + i >= m_modifierFromX.size()) {
LOG((CLOG_ERR "m_modifierFromX is too small (%d) for the "
"requested offset (%d)", m_modifierFromX.size(), offset+i));
} else {
mask |= m_modifierFromX[offset + i];
}
}
}
return mask;
@ -140,9 +170,9 @@ CXWindowsKeyState::pollActiveModifiers() const
{
Window root = DefaultRootWindow(m_display), window;
int xRoot, yRoot, xWindow, yWindow;
unsigned int state;
if (!XQueryPointer(m_display, root, &root, &window,
&xRoot, &yRoot, &xWindow, &yWindow, &state)) {
unsigned int state = 0;
if (XQueryPointer(m_display, root, &root, &window,
&xRoot, &yRoot, &xWindow, &yWindow, &state) == False) {
state = 0;
}
return mapModifiersFromX(state);
@ -151,15 +181,15 @@ CXWindowsKeyState::pollActiveModifiers() const
SInt32
CXWindowsKeyState::pollActiveGroup() const
{
if (m_group != -1) {
assert(m_group >= 0);
// fixed condition where any group < -1 would have undetermined behaviour
if (m_group >= 0) {
return m_group;
}
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
XkbStateRec state;
if (XkbGetState(m_display, XkbUseCoreKbd, &state)) {
if (XkbGetState(m_display, XkbUseCoreKbd, &state) == Success) {
return state.group;
}
}
@ -192,15 +222,14 @@ CXWindowsKeyState::getKeyMap(CKeyMap& keyMap)
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
XkbGetUpdatedMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask |
XkbAllClientInfoMask, m_xkb);
updateKeysymMapXKB(keyMap);
if (XkbGetUpdatedMap(m_display, XkbKeyActionsMask |
XkbKeyBehaviorsMask | XkbAllClientInfoMask, m_xkb) == Success) {
updateKeysymMapXKB(keyMap);
return;
}
}
else
#endif
{
updateKeysymMap(keyMap);
}
updateKeysymMap(keyMap);
}
void
@ -229,8 +258,10 @@ CXWindowsKeyState::fakeKey(const Keystroke& keystroke)
LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group));
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
XkbLockGroup(m_display, XkbUseCoreKbd,
keystroke.m_data.m_group.m_group);
if (XkbLockGroup(m_display, XkbUseCoreKbd,
keystroke.m_data.m_group.m_group) == False) {
LOG((CLOG_DEBUG1 "XkbLockGroup request not sent"));
}
}
else
#endif
@ -242,9 +273,11 @@ CXWindowsKeyState::fakeKey(const Keystroke& keystroke)
LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group));
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
XkbLockGroup(m_display, XkbUseCoreKbd,
if (XkbLockGroup(m_display, XkbUseCoreKbd,
getEffectiveGroup(pollActiveGroup(),
keystroke.m_data.m_group.m_group));
keystroke.m_data.m_group.m_group)) == False) {
LOG((CLOG_DEBUG1 "XkbLockGroup request not sent"));
}
}
else
#endif
@ -267,8 +300,7 @@ CXWindowsKeyState::updateKeysymMap(CKeyMap& keyMap)
// prepare map from X modifier to KeyModifierMask. certain bits
// are predefined.
m_modifierFromX.clear();
m_modifierFromX.resize(8);
std::fill(m_modifierFromX.begin(), m_modifierFromX.end(), 0);
m_modifierFromX[ShiftMapIndex] = KeyModifierShift;
m_modifierFromX[LockMapIndex] = KeyModifierCapsLock;
m_modifierFromX[ControlMapIndex] = KeyModifierControl;
@ -610,7 +642,7 @@ CXWindowsKeyState::updateKeysymMapXKB(CKeyMap& keyMap)
item.m_lock = false;
bool isModifier = false;
UInt32 modifierMask = m_xkb->map->modmap[keycode];
if (XkbKeyHasActions(m_xkb, keycode)) {
if (XkbKeyHasActions(m_xkb, keycode) == True) {
XkbAction* action =
XkbKeyActionEntry(m_xkb, keycode, level, eGroup);
if (action->type == XkbSA_SetMods ||
@ -761,7 +793,7 @@ CXWindowsKeyState::hasModifiersXKB() const
// iterate over all keycodes
for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
KeyCode keycode = static_cast<KeyCode>(i);
if (XkbKeyHasActions(m_xkb, keycode)) {
if (XkbKeyHasActions(m_xkb, keycode) == True) {
// iterate over all groups
int numGroups = XkbKeyNumGroups(m_xkb, keycode);
for (int group = 0; group < numGroups; ++group) {

View File

@ -48,6 +48,8 @@ public:
};
CXWindowsKeyState(Display*, bool useXKB);
CXWindowsKeyState(Display*, bool useXKB,
IEventQueue& eventQueue, CKeyMap& keyMap);
~CXWindowsKeyState();
//! @name modifiers
@ -111,6 +113,7 @@ protected:
virtual void fakeKey(const Keystroke& keystroke);
private:
void init(Display* display, bool useXKB);
void updateKeysymMap(CKeyMap&);
void updateKeysymMapXKB(CKeyMap&);
bool hasModifiersXKB() const;

View File

@ -215,7 +215,7 @@ public:
event in \p keys. It returns the \c KeyItem of the key being
pressed/repeated, or NULL if the key cannot be mapped.
*/
const KeyItem* mapKey(Keystrokes& keys, KeyID id, SInt32 group,
virtual const KeyItem* mapKey(Keystrokes& keys, KeyID id, SInt32 group,
ModifierToKeys& activeModifiers,
KeyModifierMask& currentState,
KeyModifierMask desiredMask,

View File

@ -583,7 +583,7 @@ CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID)
fakeKeys(keys, 1);
}
void
bool
CKeyState::fakeKeyRepeat(
KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton serverID)
@ -593,7 +593,7 @@ CKeyState::fakeKeyRepeat(
// if we haven't seen this button go down then ignore it
KeyButton oldLocalID = m_serverKeys[serverID];
if (oldLocalID == 0) {
return;
return false;
}
// get keys for key repeat
@ -603,11 +603,11 @@ CKeyState::fakeKeyRepeat(
m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
getActiveModifiersRValue(), mask, true);
if (keyItem == NULL) {
return;
return false;
}
KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask);
if (localID == 0) {
return;
return false;
}
// if the KeyButton for the auto-repeat is not the same as for the
@ -642,15 +642,16 @@ CKeyState::fakeKeyRepeat(
// generate key events
fakeKeys(keys, count);
return true;
}
void
bool
CKeyState::fakeKeyUp(KeyButton serverID)
{
// if we haven't seen this button go down then ignore it
KeyButton localID = m_serverKeys[serverID & kButtonMask];
if (localID == 0) {
return;
return false;
}
// get the sequence of keys to simulate key release
@ -682,6 +683,7 @@ CKeyState::fakeKeyUp(KeyButton serverID)
// generate key events
fakeKeys(keys, 1);
return true;
}
void

View File

@ -68,9 +68,9 @@ public:
virtual void setHalfDuplexMask(KeyModifierMask);
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button);
virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask,
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button);
virtual void fakeKeyUp(KeyButton button);
virtual bool fakeKeyUp(KeyButton button);
virtual void fakeAllKeysUp();
virtual bool fakeCtrlAltDel() = 0;
virtual bool isKeyDown(KeyButton) const;

View File

@ -53,17 +53,17 @@ CPlatformScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
getKeyState()->fakeKeyDown(id, mask, button);
}
void
bool
CPlatformScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button)
{
getKeyState()->fakeKeyRepeat(id, mask, count, button);
return getKeyState()->fakeKeyRepeat(id, mask, count, button);
}
void
bool
CPlatformScreen::fakeKeyUp(KeyButton button)
{
getKeyState()->fakeKeyUp(button);
return getKeyState()->fakeKeyUp(button);
}
void

View File

@ -62,9 +62,9 @@ public:
virtual void setHalfDuplexMask(KeyModifierMask);
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button);
virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask,
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button);
virtual void fakeKeyUp(KeyButton button);
virtual bool fakeKeyUp(KeyButton button);
virtual void fakeAllKeysUp();
virtual bool fakeCtrlAltDel();
virtual bool isKeyDown(KeyButton) const;

28
src/lib/synergy/Global.h Normal file
View File

@ -0,0 +1,28 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLOBAL_H
#define GLOBAL_H
// Makes everything public for unit tests
#ifdef TEST_ENV
#define protected public
#define private public
#endif
#endif

View File

@ -100,14 +100,14 @@ public:
/*!
Synthesizes a key repeat event and updates the key state.
*/
virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask,
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button) = 0;
//! Fake a key release
/*!
Synthesizes a key release event and updates the key state.
*/
virtual void fakeKeyUp(KeyButton button) = 0;
virtual bool fakeKeyUp(KeyButton button) = 0;
//! Fake key releases for all fake pressed keys
/*!

View File

@ -169,9 +169,9 @@ public:
virtual void setHalfDuplexMask(KeyModifierMask) = 0;
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button) = 0;
virtual void fakeKeyRepeat(KeyID id, KeyModifierMask mask,
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button) = 0;
virtual void fakeKeyUp(KeyButton button) = 0;
virtual bool fakeKeyUp(KeyButton button) = 0;
virtual void fakeAllKeysUp() = 0;
virtual bool fakeCtrlAltDel() = 0;
virtual bool isKeyDown(KeyButton) const = 0;

View File

@ -22,6 +22,7 @@ if (WIN32)
# windows
list(APPEND src
platform/CMSWindowsClipboardTests.cpp
platform/CMSWindowsKeyStateTests.cpp
)
elseif (APPLE)
@ -29,6 +30,7 @@ elseif (APPLE)
# mac
list(APPEND src
platform/COSXClipboardTests.cpp
platform/COSXKeyStateTests.cpp
)
elseif (UNIX)
@ -36,6 +38,7 @@ elseif (UNIX)
# unix/linux
list(APPEND src
platform/CXWindowsClipboardTests.cpp
platform/CXWindowsKeyStateTests.cpp
)
endif()
@ -52,6 +55,8 @@ set(inc
../../lib/synergy
../../../tools/gtest-1.6.0/include
../../../tools/gmock-1.6.0/include
../unittests
../unittests/synergy
)
if (UNIX)

View File

@ -36,9 +36,6 @@
using namespace std;
int
ensureSingleInstance();
#if SYSAPI_UNIX
void
@ -52,15 +49,12 @@ removeLock();
int
main(int argc, char **argv)
{
// make sure integ tests don't run in parallel.
int err = ensureSingleInstance();
if (err != 0)
return err;
#if SYSAPI_UNIX
// register SIGINT handling (to delete lock file)
signal(SIGINT, signalHandler);
atexit(removeLock);
#if SYSAPI_WIN32
if (CArchMiscWindows::isWindows95Family())
{
std::cerr << "Windows 95 family not supported." << std::endl;
return 1;
}
#endif
#if SYSAPI_WIN32
@ -76,87 +70,3 @@ main(int argc, char **argv)
return RUN_ALL_TESTS();
}
int
ensureSingleInstance()
{
#if SYSAPI_WIN32
// get info for current process (we'll use the name later).
PROCESSENTRY32 selfEntry;
if (!CArchMiscWindows::getSelfProcessEntry(selfEntry))
cerr << "could not process info for self "
<< "(error: " << GetLastError() << ")" << endl;
// get current task list.
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE)
cerr << "could not get process snapshot "
<< "(error: " << GetLastError() << ")" << endl;
PROCESSENTRY32 entry;
BOOL gotEntry = Process32First(snapshot, &entry);
if (!gotEntry)
cerr << "could not get first process entry "
<< "(error: " << GetLastError() << ")" << endl;
while (gotEntry)
{
string checkName(entry.szExeFile);
// if entry has the same name as this process, but is not
// the current process...
if ((checkName.find(selfEntry.szExeFile) != string::npos) &&
(entry.th32ProcessID != selfEntry.th32ProcessID))
{
cerr << "error: process already running: "
<< entry.th32ProcessID << " -> " << entry.szExeFile << endl;
return ERROR_ALREADY_RUNNING;
}
gotEntry = Process32Next(snapshot, &entry);
}
#elif SYSAPI_UNIX
// fail if lock file exists
struct stat info;
int statResult = stat(LOCK_FILE, &info);
if (statResult == 0)
{
cerr << "error: lock file exists: " << LOCK_FILE << endl;
return ERROR_ALREADY_RUNNING;
}
// write an empty lock file
cout << "creating lock: " << LOCK_FILE << endl;
ofstream stream;
stream.open(LOCK_FILE);
if (!stream.is_open())
cerr << "error: could not create lock" << endl;
stream << "";
stream.close();
#endif
return 0;
}
#if SYSAPI_UNIX
void
signalHandler(int signal)
{
removeLock();
}
void
removeLock()
{
// remove lock file so other instances can run.
cout << "removing lock: " << LOCK_FILE << endl;
unlink(LOCK_FILE);
}
#endif

View File

@ -0,0 +1,130 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "CMSWindowsKeyState.h"
#include "CMSWindowsDesks.h"
#include "CMSWindowsScreen.h"
#include "CMSWindowsScreenSaver.h"
#include "TMethodJob.h"
#include "CMockEventQueue.h"
#include "CMockKeyMap.h"
// wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code
#define SYNERGY_MSG_FAKE_KEY SYNERGY_HOOK_LAST_MSG + 4
using ::testing::_;
using ::testing::NiceMock;
class CMSWindowsKeyStateTests : public ::testing::Test
{
protected:
virtual void SetUp()
{
// load synrgyhk.dll
m_hookLibrary = m_hookLibraryLoader.openHookLibrary("synrgyhk");
m_screensaver = new CMSWindowsScreenSaver();
m_desks = new CMSWindowsDesks(
true, false, m_hookLibrary, m_screensaver,
new TMethodJob<CMSWindowsKeyStateTests>(
this, &CMSWindowsKeyStateTests::updateKeysCB));
}
virtual void TearDown()
{
delete m_screensaver;
delete m_desks;
}
CMSWindowsDesks* getDesks() const
{
return m_desks;
}
void* getEventTarget() const
{
return const_cast<CMSWindowsKeyStateTests*>(this);
}
private:
void updateKeysCB(void*) { }
HINSTANCE m_hookLibrary;
IScreenSaver* m_screensaver;
CMSWindowsDesks* m_desks;
CMSWindowsHookLibraryLoader m_hookLibraryLoader;
};
TEST_F(CMSWindowsKeyStateTests, disable_nonWin95OS_eventQueueNotUsed)
{
NiceMock<CMockEventQueue> eventQueue;
CMockKeyMap keyMap;
CMSWindowsKeyState keyState(getDesks(), getEventTarget(), eventQueue, keyMap);
// in anything above win95-family, event handler should not be called.
EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(0);
keyState.disable();
}
TEST_F(CMSWindowsKeyStateTests, testAutoRepeat_noRepeatAndButtonIsZero_resultIsTrue)
{
NiceMock<CMockEventQueue> eventQueue;
CMockKeyMap keyMap;
CMSWindowsKeyState keyState(getDesks(), getEventTarget(), eventQueue, keyMap);
keyState.setLastDown(1);
bool actual = keyState.testAutoRepeat(true, false, 1);
ASSERT_TRUE(actual);
}
TEST_F(CMSWindowsKeyStateTests, testAutoRepeat_pressFalse_lastDownIsZero)
{
NiceMock<CMockEventQueue> eventQueue;
CMockKeyMap keyMap;
CMSWindowsKeyState keyState(getDesks(), getEventTarget(), eventQueue, keyMap);
keyState.setLastDown(1);
keyState.testAutoRepeat(false, false, 1);
ASSERT_EQ(0, keyState.getLastDown());
}
TEST_F(CMSWindowsKeyStateTests, saveModifiers_noModifiers_savedModifiers0)
{
NiceMock<CMockEventQueue> eventQueue;
CMockKeyMap keyMap;
CMSWindowsKeyState keyState(getDesks(), getEventTarget(), eventQueue, keyMap);
keyState.saveModifiers();
ASSERT_EQ(0, keyState.getSavedModifiers());
}
TEST_F(CMSWindowsKeyStateTests, saveModifiers_shiftKeyDown_savedModifiers4)
{
NiceMock<CMockEventQueue> eventQueue;
CMockKeyMap keyMap;
CMSWindowsKeyState keyState(getDesks(), getEventTarget(), eventQueue, keyMap);
getDesks()->enable();
getDesks()->fakeKeyEvent(1, 1, true, false);
keyState.saveModifiers();
ASSERT_EQ(1, keyState.getSavedModifiers());
}

View File

@ -0,0 +1,95 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "COSXKeyState.h"
#include "CMockKeyMap.h"
#include "CMockEventQueue.h"
CGKeyCode escKeyCode = 53;
CGKeyCode shiftKeyCode = 56;
CGKeyCode controlKeyCode = 59;
TEST(COSXKeyStateTests, pollActiveModifiers_shiftKeyDownThenUp_masksAreCorrect)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
COSXKeyState keyState((IEventQueue&)keyMap, (CKeyMap&)eventQueue);
// fake shift key down (without using synergy). this is a bit weird;
// looks like you need to create a shift down event *and* set the
// shift modifier.
CGEventRef shiftDown = CGEventCreateKeyboardEvent(NULL, shiftKeyCode, true);
CGEventSetFlags(shiftDown, kCGEventFlagMaskShift);
CGEventPost(kCGHIDEventTap, shiftDown);
CFRelease(shiftDown);
// function under test (1st call)
KeyModifierMask downMask = keyState.pollActiveModifiers();
// fake shift key up (without using synergy). also as weird as the
// shift down; use a non-shift key down and reset the pressed modifiers.
CGEventRef shiftUp = CGEventCreateKeyboardEvent(NULL, escKeyCode, true);
CGEventSetFlags(shiftUp, 0);
CGEventPost(kCGHIDEventTap, shiftUp);
CFRelease(shiftUp);
// function under test (2nd call)
KeyModifierMask upMask = keyState.pollActiveModifiers();
EXPECT_TRUE((downMask & KeyModifierShift) == KeyModifierShift)
<< "shift key not in mask (" << downMask << ") - key was not pressed";
EXPECT_TRUE((upMask & KeyModifierShift) == 0)
<< "shift key still in mask (" << upMask << ") - make sure no keys are being held down";
}
TEST(COSXKeyStateTests, pollActiveModifiers_controlKeyDownThenUp_masksAreCorrect)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
COSXKeyState keyState((IEventQueue&)keyMap, (CKeyMap&)eventQueue);
// fake control key down (without using synergy). this is a bit weird;
// looks like you need to create a shift down event *and* set the
// shift modifier.
CGEventRef controlDown = CGEventCreateKeyboardEvent(NULL, controlKeyCode, true);
CGEventSetFlags(controlDown, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, controlDown);
CFRelease(controlDown);
// function under test (1st call)
KeyModifierMask downMask = keyState.pollActiveModifiers();
// fake control key up (without using synergy). also as weird as the
// shift down; use a non-shift key down and reset the pressed modifiers.
CGEventRef controlUp = CGEventCreateKeyboardEvent(NULL, escKeyCode, true);
CGEventSetFlags(controlUp, 0);
CGEventPost(kCGHIDEventTap, controlUp);
CFRelease(controlUp);
// function under test (2nd call)
KeyModifierMask upMask = keyState.pollActiveModifiers();
EXPECT_TRUE((downMask & KeyModifierControl) == KeyModifierControl)
<< "control key not in mask (" << downMask << ") - key was not pressed";
EXPECT_TRUE((upMask & KeyModifierControl) == 0)
<< "control key still in mask (" << upMask << ") - make sure no keys are being held down";
}

View File

@ -0,0 +1,245 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#define TEST_ENV
#include "Global.h"
#include "CMockKeyMap.h"
#include "CMockEventQueue.h"
#include "CXWindowsKeyState.h"
#include "CLog.h"
#include <errno.h>
#define XK_LATIN1
#define XK_MISCELLANY
#include "X11/keysymdef.h"
#if HAVE_XKB_EXTENSION
# include <X11/XKBlib.h>
#endif
class CXWindowsKeyStateTests : public ::testing::Test
{
protected:
CXWindowsKeyStateTests() :
m_display(NULL)
{
}
~CXWindowsKeyStateTests()
{
if (m_display != NULL) {
LOG((CLOG_DEBUG "closing display"));
XCloseDisplay(m_display);
}
}
virtual void
SetUp()
{
// open the display only once for the entire test suite
if (this->m_display == NULL) {
LOG((CLOG_DEBUG "opening display"));
this->m_display = XOpenDisplay(NULL);
ASSERT_TRUE(this->m_display != NULL)
<< "unable to open display: " << errno;
}
}
virtual void
TearDown()
{
}
Display* m_display;
};
TEST_F(CXWindowsKeyStateTests, setActiveGroup_pollAndSet_groupIsZero)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
keyState.setActiveGroup(CXWindowsKeyState::kGroupPollAndSet);
ASSERT_EQ(0, keyState.m_group);
}
TEST_F(CXWindowsKeyStateTests, setActiveGroup_poll_groupIsNotSet)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
keyState.setActiveGroup(CXWindowsKeyState::kGroupPoll);
ASSERT_LE(-1, keyState.m_group);
}
TEST_F(CXWindowsKeyStateTests, setActiveGroup_customGroup_groupWasSet)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
keyState.setActiveGroup(1);
ASSERT_EQ(1, keyState.m_group);
}
TEST_F(CXWindowsKeyStateTests, mapModifiersFromX_zeroState_zeroMask)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
int mask = keyState.mapModifiersFromX(0);
ASSERT_EQ(0, mask);
}
TEST_F(CXWindowsKeyStateTests, mapModifiersToX_zeroMask_resultIsTrue)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
unsigned int modifiers = 0;
bool result = keyState.mapModifiersToX(0, modifiers);
ASSERT_TRUE(result);
}
TEST_F(CXWindowsKeyStateTests, fakeCtrlAltDel_default_returnsFalse)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
bool result = keyState.fakeCtrlAltDel();
ASSERT_FALSE(result);
}
TEST_F(CXWindowsKeyStateTests, pollActiveModifiers_defaultState_returnsZero)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
KeyModifierMask actual = keyState.pollActiveModifiers();
ASSERT_EQ(0, actual);
}
TEST_F(CXWindowsKeyStateTests, pollActiveModifiers_shiftKeyDownThenUp_masksAreCorrect)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
// set mock modifier mapping
std::fill(
keyState.m_modifierFromX.begin(), keyState.m_modifierFromX.end(), 0);
keyState.m_modifierFromX[ShiftMapIndex] = KeyModifierShift;
KeyCode key = XKeysymToKeycode(m_display, XK_Shift_L);
// fake shift key down (without using synergy)
XTestFakeKeyEvent(m_display, key, true, CurrentTime);
// function under test (1st call)
KeyModifierMask modDown = keyState.pollActiveModifiers();
// fake shift key up (without using synergy)
XTestFakeKeyEvent(m_display, key, false, CurrentTime);
// function under test (2nd call)
KeyModifierMask modUp = keyState.pollActiveModifiers();
EXPECT_TRUE((modDown & KeyModifierShift) == KeyModifierShift)
<< "shift key not in mask - key was not pressed";
EXPECT_TRUE((modUp & KeyModifierShift) == 0)
<< "shift key still in mask - make sure no keys are being held down";
}
TEST_F(CXWindowsKeyStateTests, pollActiveGroup_defaultState_returnsZero)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
SInt32 actual = keyState.pollActiveGroup();
ASSERT_EQ(0, actual);
}
TEST_F(CXWindowsKeyStateTests, pollActiveGroup_positiveGroup_returnsGroup)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
keyState.m_group = 3;
SInt32 actual = keyState.pollActiveGroup();
ASSERT_EQ(3, actual);
}
TEST_F(CXWindowsKeyStateTests, pollActiveGroup_xkb_areEqual)
{
#if HAVE_XKB_EXTENSION
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CXWindowsKeyState keyState(
m_display, true, (IEventQueue&)keyMap, (CKeyMap&)eventQueue);
// reset the group
keyState.m_group = -1;
XkbStateRec state;
// compare pollActiveGroup() with XkbGetState()
if (XkbGetState(m_display, XkbUseCoreKbd, &state) == Success) {
SInt32 actual = keyState.pollActiveGroup();
ASSERT_EQ(state.group, actual);
}
else {
FAIL() << "XkbGetState() returned error " << errno;
}
#else
SUCCEED() << "Xkb extension not installed";
#endif
}

View File

@ -14,7 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set(h
synergy/CKeyStateImpl.h
synergy/CKeyStateTests.h
synergy/CMockEventQueue.h
synergy/CMockKeyMap.h
)

View File

@ -17,7 +17,7 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "CKeyStateImpl.h"
#include "CKeyStateTests.h"
#include "CMockEventQueue.h"
#include "CMockKeyMap.h"
@ -27,19 +27,15 @@ using ::testing::Invoke;
using ::testing::Return;
using ::testing::SaveArg;
enum {
kAKey = 30
};
TEST(CKeyStateTests, onKey_aKeyDown_keyStateOne)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
keyState.onKey(kAKey, true, KeyModifierAlt);
keyState.onKey(1, true, KeyModifierAlt);
EXPECT_EQ(1, keyState.getKeyState(kAKey));
EXPECT_EQ(1, keyState.getKeyState(1));
}
TEST(CKeyStateTests, onKey_aKeyUp_keyStateZero)
@ -48,9 +44,9 @@ TEST(CKeyStateTests, onKey_aKeyUp_keyStateZero)
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
keyState.onKey(kAKey, false, KeyModifierAlt);
keyState.onKey(1, false, KeyModifierAlt);
EXPECT_EQ(0, keyState.getKeyState(kAKey));
EXPECT_EQ(0, keyState.getKeyState(1));
}
TEST(CKeyStateTests, onKey_invalidKey_keyStateZero)
@ -78,7 +74,7 @@ TEST(CKeyStateTests, sendKeyEvent_halfDuplexAndRepeat_addEventNotCalled)
TEST(CKeyStateTests, sendKeyEvent_halfDuplex_addEventCalledTwice)
{
CMockKeyMap keyMap;
NiceMock<CMockKeyMap> keyMap;
NiceMock<CMockEventQueue> eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
ON_CALL(keyMap, isHalfDuplex(_, _)).WillByDefault(Return(true));
@ -90,55 +86,49 @@ TEST(CKeyStateTests, sendKeyEvent_halfDuplex_addEventCalledTwice)
TEST(CKeyStateTests, sendKeyEvent_keyRepeat_addEventCalledOnce)
{
CMockKeyMap keyMap;
NiceMock<CMockKeyMap> keyMap;
NiceMock<CMockEventQueue> eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
EXPECT_CALL(eventQueue, addEvent(_)).Times(1);
keyState.sendKeyEvent(NULL, false, true, kAKey, 0, 0, 0);
keyState.sendKeyEvent(NULL, false, true, 1, 0, 0, 0);
}
TEST(CKeyStateTests, sendKeyEvent_keyDown_addEventCalledOnce)
{
CMockKeyMap keyMap;
NiceMock<CMockKeyMap> keyMap;
NiceMock<CMockEventQueue> eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
EXPECT_CALL(eventQueue, addEvent(_)).Times(1);
keyState.sendKeyEvent(NULL, true, false, kAKey, 0, 0, 0);
keyState.sendKeyEvent(NULL, true, false, 1, 0, 0, 0);
}
TEST(CKeyStateTests, sendKeyEvent_keyUp_addEventCalledOnce)
{
CMockKeyMap keyMap;
NiceMock<CMockKeyMap> keyMap;
NiceMock<CMockEventQueue> eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
EXPECT_CALL(eventQueue, addEvent(_)).Times(1);
keyState.sendKeyEvent(NULL, false, false, kAKey, 0, 0, 0);
keyState.sendKeyEvent(NULL, false, false, 1, 0, 0, 0);
}
TEST(CKeyStateTests, updateKeyMap_mockKeyMap_keyMapGotMock)
{
CMockKeyMap keyMap;
NiceMock<CMockKeyMap> keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
// key map member gets a new key map via swap()
EXPECT_CALL(keyMap, swap(_));
EXPECT_CALL(keyMap, finish());
keyState.updateKeyMap();
}
void
stubPollPressedKeys(IKeyState::KeyButtonSet& pressedKeys)
{
pressedKeys.insert(kAKey);
}
TEST(CKeyStateTests, updateKeyState_pollInsertsSingleKey_keyIsDown)
{
NiceMock<CMockKeyMap> keyMap;
@ -148,7 +138,7 @@ TEST(CKeyStateTests, updateKeyState_pollInsertsSingleKey_keyIsDown)
keyState.updateKeyState();
bool actual = keyState.isKeyDown(kAKey);
bool actual = keyState.isKeyDown(1);
ASSERT_TRUE(actual);
}
@ -160,7 +150,7 @@ TEST(CKeyStateTests, updateKeyState_pollDoesNothing_keyNotSet)
keyState.updateKeyState();
bool actual = keyState.isKeyDown(kAKey);
bool actual = keyState.isKeyDown(1);
ASSERT_FALSE(actual);
}
@ -189,22 +179,17 @@ TEST(CKeyStateTests, updateKeyState_activeModifiers_maskNotSet)
ASSERT_EQ(0, actual);
}
void
assertMaskIsOne(ForeachKeyCallback cb, void* userData)
{
ASSERT_EQ(1, ((CKeyState::CAddActiveModifierContext*)userData)->m_mask);
}
TEST(CKeyStateTests, updateKeyState_activeModifiers_keyMapGotModifers)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
EXPECT_CALL(keyMap, foreachKey(_, _));
ON_CALL(keyState, pollActiveModifiers()).WillByDefault(Return(1));
ON_CALL(keyMap, foreachKey(_, _)).WillByDefault(Invoke(assertMaskIsOne));
// key map gets new modifiers via foreachKey()
EXPECT_CALL(keyMap, foreachKey(_, _));
keyState.updateKeyState();
}
@ -218,3 +203,249 @@ TEST(CKeyStateTests, setHalfDuplexMask_capsLock_halfDuplexCapsLockAdded)
keyState.setHalfDuplexMask(KeyModifierCapsLock);
}
TEST(CKeyStateTests, setHalfDuplexMask_numLock_halfDuplexNumLockAdded)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
EXPECT_CALL(keyMap, addHalfDuplexModifier(kKeyNumLock));
keyState.setHalfDuplexMask(KeyModifierNumLock);
}
TEST(CKeyStateTests, setHalfDuplexMask_scrollLock_halfDuplexScollLockAdded)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
EXPECT_CALL(keyMap, addHalfDuplexModifier(kKeyScrollLock));
keyState.setHalfDuplexMask(KeyModifierScrollLock);
}
TEST(CKeyStateTests, fakeKeyDown_serverKeyAlreadyDown_fakeKeyCalledTwice)
{
NiceMock<CMockKeyMap> keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
s_stubKeyItem.m_client = 0;
s_stubKeyItem.m_button = 1;
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
// 2 calls to fakeKeyDown should still call fakeKey, even though
// repeated keys are handled differently.
EXPECT_CALL(keyState, fakeKey(_)).Times(2);
// call twice to simulate server key already down (a misreported autorepeat).
keyState.fakeKeyDown(1, 0, 0);
keyState.fakeKeyDown(1, 0, 0);
}
TEST(CKeyStateTests, fakeKeyDown_isIgnoredKey_fakeKeyNotCalled)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
EXPECT_CALL(keyState, fakeKey(_)).Times(0);
keyState.fakeKeyDown(kKeyCapsLock, 0, 0);
}
TEST(CKeyStateTests, fakeKeyDown_mapReturnsKeystrokes_fakeKeyCalled)
{
NiceMock<CMockKeyMap> keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
s_stubKeyItem.m_button = 0;
s_stubKeyItem.m_client = 0;
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
EXPECT_CALL(keyState, fakeKey(_)).Times(1);
keyState.fakeKeyDown(1, 0, 0);
}
TEST(CKeyStateTests, fakeKeyRepeat_invalidKey_returnsFalse)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
bool actual = keyState.fakeKeyRepeat(0, 0, 0, 0);
ASSERT_FALSE(actual);
}
TEST(CKeyStateTests, fakeKeyRepeat_nullKey_returnsFalse)
{
NiceMock<CMockKeyMap> keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
// set the key to down (we need to make mapKey return a valid key to do this).
CKeyMap::KeyItem keyItem;
keyItem.m_client = 0;
keyItem.m_button = 1;
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
keyState.fakeKeyDown(1, 0, 0);
// change mapKey to return NULL so that fakeKeyRepeat exits early.
CKeyMap::KeyItem* nullKeyItem = NULL;
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(nullKeyItem));
bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0);
ASSERT_FALSE(actual);
}
TEST(CKeyStateTests, fakeKeyRepeat_invalidButton_returnsFalse)
{
NiceMock<CMockKeyMap> keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
// set the key to down (we need to make mapKey return a valid key to do this).
CKeyMap::KeyItem keyItem;
keyItem.m_client = 0;
keyItem.m_button = 1; // set to 1 to make fakeKeyDown work.
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
keyState.fakeKeyDown(1, 0, 0);
// change button to 0 so that fakeKeyRepeat will return early.
keyItem.m_button = 0;
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0);
ASSERT_FALSE(actual);
}
TEST(CKeyStateTests, fakeKeyRepeat_validKey_returnsTrue)
{
NiceMock<CMockKeyMap> keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
s_stubKeyItem.m_client = 0;
s_stubKeystroke.m_type = CKeyMap::Keystroke::kButton;
s_stubKeystroke.m_data.m_button.m_button = 2;
// set the button to 1 for fakeKeyDown call
s_stubKeyItem.m_button = 1;
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
keyState.fakeKeyDown(1, 0, 0);
// change the button to 2
s_stubKeyItem.m_button = 2;
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0);
ASSERT_TRUE(actual);
}
TEST(CKeyStateTests, fakeKeyUp_buttonNotDown_returnsFalse)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
bool actual = keyState.fakeKeyUp(0);
ASSERT_FALSE(actual);
}
TEST(CKeyStateTests, fakeKeyUp_buttonAlreadyDown_returnsTrue)
{
NiceMock<CMockKeyMap> keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
// press alt down so we get full coverage.
ON_CALL(keyState, pollActiveModifiers()).WillByDefault(Return(KeyModifierAlt));
keyState.updateKeyState();
// press button 1 down.
s_stubKeyItem.m_button = 1;
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
keyState.fakeKeyDown(1, 0, 1);
// this takes the button id, which is the 3rd arg of fakeKeyDown
bool actual = keyState.fakeKeyUp(1);
ASSERT_TRUE(actual);
}
TEST(CKeyStateTests, fakeAllKeysUp_keysWereDown_keysAreUp)
{
NiceMock<CMockKeyMap> keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
// press button 1 down.
s_stubKeyItem.m_button = 1;
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
keyState.fakeKeyDown(1, 0, 1);
// method under test
keyState.fakeAllKeysUp();
bool actual = keyState.isKeyDown(1);
ASSERT_FALSE(actual);
}
TEST(CKeyStateTests, isKeyDown_keyDown_returnsTrue)
{
NiceMock<CMockKeyMap> keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
// press button 1 down.
s_stubKeyItem.m_button = 1;
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
keyState.fakeKeyDown(1, 0, 1);
// method under test
bool actual = keyState.isKeyDown(1);
ASSERT_TRUE(actual);
}
TEST(CKeyStateTests, isKeyDown_noKeysDown_returnsFalse)
{
CMockKeyMap keyMap;
CMockEventQueue eventQueue;
CKeyStateImpl keyState(eventQueue, keyMap);
// method under test
bool actual = keyState.isKeyDown(1);
ASSERT_FALSE(actual);
}
void
stubPollPressedKeys(IKeyState::KeyButtonSet& pressedKeys)
{
pressedKeys.insert(1);
}
void
assertMaskIsOne(ForeachKeyCallback cb, void* userData)
{
ASSERT_EQ(1, ((CKeyState::CAddActiveModifierContext*)userData)->m_mask);
}
const CKeyMap::KeyItem*
stubMapKey(
CKeyMap::Keystrokes& keys, KeyID id, SInt32 group,
CKeyMap::ModifierToKeys& activeModifiers,
KeyModifierMask& currentState,
KeyModifierMask desiredMask,
bool isAutoRepeat)
{
keys.push_back(s_stubKeystroke);
return &s_stubKeyItem;
}

View File

@ -15,8 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CKEYSTATEIMPL_H
#define CKEYSTATEIMPL_H
#ifndef CKEYSTATETESTS_H
#define CKEYSTATETESTS_H
#include "CKeyState.h"
#include "gmock/gmock.h"
@ -24,9 +24,8 @@
class CMockKeyMap;
class CMockEventQueue;
// while the class name indicates that this is actually a mock, we use a
// typedef later to rename it (so the name matches the compilation unit)
// so the tests are less confusing.
// NOTE: do not mock methods that are not pure virtual. this mock exists only
// to provide an implementation of the CKeyState abstract class.
class CMockKeyState : public CKeyState
{
public:
@ -47,9 +46,6 @@ public:
MOCK_CONST_METHOD1(pollPressedKeys, void(KeyButtonSet&));
};
// hide that we're actually testing a mock to make the unit tests less
// confusing. use NiceMock so that we don't get warnings for unexpected
// calls.
typedef ::testing::NiceMock<CMockKeyState> CKeyStateImpl;
typedef UInt32 KeyID;
@ -57,4 +53,21 @@ typedef UInt32 KeyID;
typedef void (*ForeachKeyCallback)(
KeyID, SInt32 group, CKeyMap::KeyItem&, void* userData);
void
stubPollPressedKeys(IKeyState::KeyButtonSet& pressedKeys);
void
assertMaskIsOne(ForeachKeyCallback cb, void* userData);
const CKeyMap::KeyItem*
stubMapKey(
CKeyMap::Keystrokes& keys, KeyID id, SInt32 group,
CKeyMap::ModifierToKeys& activeModifiers,
KeyModifierMask& currentState,
KeyModifierMask desiredMask,
bool isAutoRepeat);
CKeyMap::Keystroke s_stubKeystroke(1, false, false);
CKeyMap::KeyItem s_stubKeyItem;
#endif

View File

@ -29,6 +29,9 @@ public:
MOCK_METHOD2(foreachKey, void(ForeachKeyCallback, void*));
MOCK_METHOD1(addHalfDuplexModifier, void(KeyID));
MOCK_CONST_METHOD2(isHalfDuplex, bool(KeyID, KeyButton));
MOCK_CONST_METHOD7(mapKey, const CKeyMap::KeyItem*(
Keystrokes&, KeyID, SInt32, ModifierToKeys&, KeyModifierMask&,
KeyModifierMask, bool));
};
#endif