From 2fe11744cf56821004749ce5534e022a7eef5c44 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Sat, 18 Jun 2011 23:44:23 +0000 Subject: [PATCH] merged 1.4 r1043:1044 into trunk --- .../platform/CMSWindowsHookLibraryLoader.cpp | 68 ++++ .../platform/CMSWindowsHookLibraryLoader.h | 45 +++ src/lib/platform/CMSWindowsKeyState.cpp | 44 ++- src/lib/platform/CMSWindowsKeyState.h | 13 +- src/lib/platform/CMSWindowsScreen.cpp | 61 +--- src/lib/platform/CMSWindowsScreen.h | 14 +- src/lib/platform/CMakeLists.txt | 2 + src/lib/platform/COSXKeyState.cpp | 41 ++- src/lib/platform/COSXKeyState.h | 3 + src/lib/platform/CXWindowsKeyState.cpp | 96 ++++-- src/lib/platform/CXWindowsKeyState.h | 3 + src/lib/synergy/CKeyMap.h | 2 +- src/lib/synergy/CKeyState.cpp | 14 +- src/lib/synergy/CKeyState.h | 4 +- src/lib/synergy/CPlatformScreen.cpp | 8 +- src/lib/synergy/CPlatformScreen.h | 4 +- src/lib/synergy/Global.h | 28 ++ src/lib/synergy/IKeyState.h | 4 +- src/lib/synergy/IPlatformScreen.h | 4 +- src/test/integtests/CMakeLists.txt | 5 + src/test/integtests/Main.cpp | 102 +----- .../platform/CMSWindowsKeyStateTests.cpp | 130 ++++++++ .../integtests/platform/COSXKeyStateTests.cpp | 95 ++++++ .../platform/CXWindowsKeyStateTests.cpp | 245 ++++++++++++++ src/test/unittests/CMakeLists.txt | 2 +- src/test/unittests/synergy/CKeyStateTests.cpp | 299 ++++++++++++++++-- .../{CKeyStateImpl.h => CKeyStateTests.h} | 29 +- src/test/unittests/synergy/CMockKeyMap.h | 3 + 28 files changed, 1101 insertions(+), 267 deletions(-) create mode 100644 src/lib/platform/CMSWindowsHookLibraryLoader.cpp create mode 100644 src/lib/platform/CMSWindowsHookLibraryLoader.h create mode 100644 src/lib/synergy/Global.h create mode 100644 src/test/integtests/platform/CMSWindowsKeyStateTests.cpp create mode 100644 src/test/integtests/platform/COSXKeyStateTests.cpp create mode 100644 src/test/integtests/platform/CXWindowsKeyStateTests.cpp rename src/test/unittests/synergy/{CKeyStateImpl.h => CKeyStateTests.h} (71%) diff --git a/src/lib/platform/CMSWindowsHookLibraryLoader.cpp b/src/lib/platform/CMSWindowsHookLibraryLoader.cpp new file mode 100644 index 00000000..33e8ee79 --- /dev/null +++ b/src/lib/platform/CMSWindowsHookLibraryLoader.cpp @@ -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 . + */ + +#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; +} \ No newline at end of file diff --git a/src/lib/platform/CMSWindowsHookLibraryLoader.h b/src/lib/platform/CMSWindowsHookLibraryLoader.h new file mode 100644 index 00000000..e37fd6e0 --- /dev/null +++ b/src/lib/platform/CMSWindowsHookLibraryLoader.h @@ -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 . + */ + +#ifndef CMSWINDOWSHOOKLIBRARYLOADER_H +#define CMSWINDOWSHOOKLIBRARYLOADER_H + +#define WIN32_LEAN_AND_MEAN +#include +#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 \ No newline at end of file diff --git a/src/lib/platform/CMSWindowsKeyState.cpp b/src/lib/platform/CMSWindowsKeyState.cpp index b615a071..13b62d7d 100644 --- a/src/lib/platform/CMSWindowsKeyState.cpp +++ b/src/lib/platform/CMSWindowsKeyState.cpp @@ -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 diff --git a/src/lib/platform/CMSWindowsKeyState.h b/src/lib/platform/CMSWindowsKeyState.h index 5196cf57..ee57474d 100644 --- a/src/lib/platform/CMSWindowsKeyState.h +++ b/src/lib/platform/CMSWindowsKeyState.h @@ -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; diff --git a/src/lib/platform/CMSWindowsScreen.cpp b/src/lib/platform/CMSWindowsScreen.cpp index 4005b157..b491d267 100644 --- a/src/lib/platform/CMSWindowsScreen.cpp +++ b/src/lib/platform/CMSWindowsScreen.cpp @@ -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()); } } diff --git a/src/lib/platform/CMSWindowsScreen.h b/src/lib/platform/CMSWindowsScreen.h index 0af50d39..9809f9a1 100644 --- a/src/lib/platform/CMSWindowsScreen.h +++ b/src/lib/platform/CMSWindowsScreen.h @@ -25,6 +25,7 @@ #include "CString.h" #define WIN32_LEAN_AND_MEAN #include +#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 diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index 8be5e3f2..f5bdd7af 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -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 diff --git a/src/lib/platform/COSXKeyState.cpp b/src/lib/platform/COSXKeyState.cpp index 2757b1ae..262357bf 100644 --- a/src/lib/platform/COSXKeyState.cpp +++ b/src/lib/platform/COSXKeyState.cpp @@ -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; } diff --git a/src/lib/platform/COSXKeyState.h b/src/lib/platform/COSXKeyState.h index 3c9f4275..0d90b1a0 100644 --- a/src/lib/platform/COSXKeyState.h +++ b/src/lib/platform/COSXKeyState.h @@ -39,6 +39,7 @@ public: typedef std::vector 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: diff --git a/src/lib/platform/CXWindowsKeyState.cpp b/src/lib/platform/CXWindowsKeyState.cpp index d8b4eee7..2956f440 100644 --- a/src/lib/platform/CXWindowsKeyState.cpp +++ b/src/lib/platform/CXWindowsKeyState.cpp @@ -20,6 +20,8 @@ #include "CLog.h" #include "CStringUtil.h" #include "stdmap.h" +#include +#include #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(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) { diff --git a/src/lib/platform/CXWindowsKeyState.h b/src/lib/platform/CXWindowsKeyState.h index 79045e32..37a5d082 100644 --- a/src/lib/platform/CXWindowsKeyState.h +++ b/src/lib/platform/CXWindowsKeyState.h @@ -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; diff --git a/src/lib/synergy/CKeyMap.h b/src/lib/synergy/CKeyMap.h index c1982587..de5961fe 100644 --- a/src/lib/synergy/CKeyMap.h +++ b/src/lib/synergy/CKeyMap.h @@ -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, diff --git a/src/lib/synergy/CKeyState.cpp b/src/lib/synergy/CKeyState.cpp index 1b36bf8d..d9d3ea6c 100644 --- a/src/lib/synergy/CKeyState.cpp +++ b/src/lib/synergy/CKeyState.cpp @@ -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 diff --git a/src/lib/synergy/CKeyState.h b/src/lib/synergy/CKeyState.h index 9536bd2f..bc6d2e69 100644 --- a/src/lib/synergy/CKeyState.h +++ b/src/lib/synergy/CKeyState.h @@ -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; diff --git a/src/lib/synergy/CPlatformScreen.cpp b/src/lib/synergy/CPlatformScreen.cpp index 7b640849..f74cc03c 100644 --- a/src/lib/synergy/CPlatformScreen.cpp +++ b/src/lib/synergy/CPlatformScreen.cpp @@ -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 diff --git a/src/lib/synergy/CPlatformScreen.h b/src/lib/synergy/CPlatformScreen.h index 355a9da4..eabdabae 100644 --- a/src/lib/synergy/CPlatformScreen.h +++ b/src/lib/synergy/CPlatformScreen.h @@ -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; diff --git a/src/lib/synergy/Global.h b/src/lib/synergy/Global.h new file mode 100644 index 00000000..6da57d61 --- /dev/null +++ b/src/lib/synergy/Global.h @@ -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 . + */ + +#ifndef GLOBAL_H +#define GLOBAL_H + +// Makes everything public for unit tests +#ifdef TEST_ENV +#define protected public +#define private public +#endif + +#endif + diff --git a/src/lib/synergy/IKeyState.h b/src/lib/synergy/IKeyState.h index f12d1b10..2966da84 100644 --- a/src/lib/synergy/IKeyState.h +++ b/src/lib/synergy/IKeyState.h @@ -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 /*! diff --git a/src/lib/synergy/IPlatformScreen.h b/src/lib/synergy/IPlatformScreen.h index 76058bb2..87682ff6 100644 --- a/src/lib/synergy/IPlatformScreen.h +++ b/src/lib/synergy/IPlatformScreen.h @@ -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; diff --git a/src/test/integtests/CMakeLists.txt b/src/test/integtests/CMakeLists.txt index 3711a382..ddc6f6b1 100644 --- a/src/test/integtests/CMakeLists.txt +++ b/src/test/integtests/CMakeLists.txt @@ -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) diff --git a/src/test/integtests/Main.cpp b/src/test/integtests/Main.cpp index c778e67a..c1e6e0f3 100644 --- a/src/test/integtests/Main.cpp +++ b/src/test/integtests/Main.cpp @@ -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 diff --git a/src/test/integtests/platform/CMSWindowsKeyStateTests.cpp b/src/test/integtests/platform/CMSWindowsKeyStateTests.cpp new file mode 100644 index 00000000..43a330e8 --- /dev/null +++ b/src/test/integtests/platform/CMSWindowsKeyStateTests.cpp @@ -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 . + */ + +#include +#include +#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( + this, &CMSWindowsKeyStateTests::updateKeysCB)); + } + + virtual void TearDown() + { + delete m_screensaver; + delete m_desks; + } + + CMSWindowsDesks* getDesks() const + { + return m_desks; + } + + void* getEventTarget() const + { + return const_cast(this); + } + +private: + void updateKeysCB(void*) { } + HINSTANCE m_hookLibrary; + IScreenSaver* m_screensaver; + CMSWindowsDesks* m_desks; + CMSWindowsHookLibraryLoader m_hookLibraryLoader; +}; + +TEST_F(CMSWindowsKeyStateTests, disable_nonWin95OS_eventQueueNotUsed) +{ + NiceMock 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 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 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 eventQueue; + CMockKeyMap keyMap; + CMSWindowsKeyState keyState(getDesks(), getEventTarget(), eventQueue, keyMap); + + keyState.saveModifiers(); + + ASSERT_EQ(0, keyState.getSavedModifiers()); +} + +TEST_F(CMSWindowsKeyStateTests, saveModifiers_shiftKeyDown_savedModifiers4) +{ + NiceMock eventQueue; + CMockKeyMap keyMap; + CMSWindowsKeyState keyState(getDesks(), getEventTarget(), eventQueue, keyMap); + getDesks()->enable(); + getDesks()->fakeKeyEvent(1, 1, true, false); + + keyState.saveModifiers(); + + ASSERT_EQ(1, keyState.getSavedModifiers()); +} diff --git a/src/test/integtests/platform/COSXKeyStateTests.cpp b/src/test/integtests/platform/COSXKeyStateTests.cpp new file mode 100644 index 00000000..7d8952b8 --- /dev/null +++ b/src/test/integtests/platform/COSXKeyStateTests.cpp @@ -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 . + */ + +#include +#include + +#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"; +} diff --git a/src/test/integtests/platform/CXWindowsKeyStateTests.cpp b/src/test/integtests/platform/CXWindowsKeyStateTests.cpp new file mode 100644 index 00000000..f2e9650e --- /dev/null +++ b/src/test/integtests/platform/CXWindowsKeyStateTests.cpp @@ -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 . + */ + +#include +#include + +#define TEST_ENV +#include "Global.h" + +#include "CMockKeyMap.h" +#include "CMockEventQueue.h" +#include "CXWindowsKeyState.h" +#include "CLog.h" +#include + +#define XK_LATIN1 +#define XK_MISCELLANY +#include "X11/keysymdef.h" + +#if HAVE_XKB_EXTENSION +# include +#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 +} + diff --git a/src/test/unittests/CMakeLists.txt b/src/test/unittests/CMakeLists.txt index c98f9cf6..78e73e1b 100644 --- a/src/test/unittests/CMakeLists.txt +++ b/src/test/unittests/CMakeLists.txt @@ -14,7 +14,7 @@ # along with this program. If not, see . set(h - synergy/CKeyStateImpl.h + synergy/CKeyStateTests.h synergy/CMockEventQueue.h synergy/CMockKeyMap.h ) diff --git a/src/test/unittests/synergy/CKeyStateTests.cpp b/src/test/unittests/synergy/CKeyStateTests.cpp index f73cff28..51e1caf5 100644 --- a/src/test/unittests/synergy/CKeyStateTests.cpp +++ b/src/test/unittests/synergy/CKeyStateTests.cpp @@ -17,7 +17,7 @@ #include #include -#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 keyMap; NiceMock 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 keyMap; NiceMock 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 keyMap; NiceMock 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 keyMap; NiceMock 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 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 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 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 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 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 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 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 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 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 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; +} diff --git a/src/test/unittests/synergy/CKeyStateImpl.h b/src/test/unittests/synergy/CKeyStateTests.h similarity index 71% rename from src/test/unittests/synergy/CKeyStateImpl.h rename to src/test/unittests/synergy/CKeyStateTests.h index 81bc2edd..fea2f6f8 100644 --- a/src/test/unittests/synergy/CKeyStateImpl.h +++ b/src/test/unittests/synergy/CKeyStateTests.h @@ -15,8 +15,8 @@ * along with this program. If not, see . */ -#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 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 diff --git a/src/test/unittests/synergy/CMockKeyMap.h b/src/test/unittests/synergy/CMockKeyMap.h index d5a8ac78..2fbf88d7 100644 --- a/src/test/unittests/synergy/CMockKeyMap.h +++ b/src/test/unittests/synergy/CMockKeyMap.h @@ -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