migrate k/m hook handlers from synwinhk to platform/MSWindowsHook and delete synwinhk.dll. for ever and ever.
This commit is contained in:
parent
a3ec43999f
commit
be0fab1775
|
@ -25,7 +25,3 @@ add_subdirectory(net)
|
||||||
add_subdirectory(platform)
|
add_subdirectory(platform)
|
||||||
add_subdirectory(server)
|
add_subdirectory(server)
|
||||||
add_subdirectory(barrier)
|
add_subdirectory(barrier)
|
||||||
|
|
||||||
if (WIN32)
|
|
||||||
add_subdirectory(synwinhk)
|
|
||||||
endif()
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
file(GLOB headers "MSWindows*.h" "ImmuneKeysReader.h")
|
file(GLOB headers "MSWindows*.h" "ImmuneKeysReader.h" "synwinhk.h")
|
||||||
file(GLOB sources "MSWindows*.cpp" "ImmuneKeysReader.cpp")
|
file(GLOB sources "MSWindows*.cpp" "ImmuneKeysReader.cpp")
|
||||||
elseif (APPLE)
|
elseif (APPLE)
|
||||||
file(GLOB headers "OSX*.h" "IOSX*.h")
|
file(GLOB headers "OSX*.h" "IOSX*.h")
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* barrier -- mouse and keyboard sharing utility
|
* barrier -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2018 Debauchee Open Source Group
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
* Copyright (C) 2012-2016 Symless Ltd.
|
||||||
* Copyright (C) 2004 Chris Schoeneman
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
*
|
*
|
||||||
|
@ -18,7 +19,6 @@
|
||||||
|
|
||||||
#include "platform/MSWindowsDesks.h"
|
#include "platform/MSWindowsDesks.h"
|
||||||
|
|
||||||
#include "synwinhk/synwinhk.h"
|
|
||||||
#include "platform/MSWindowsScreen.h"
|
#include "platform/MSWindowsScreen.h"
|
||||||
#include "platform/ImmuneKeysReader.h"
|
#include "platform/ImmuneKeysReader.h"
|
||||||
#include "barrier/IScreenSaver.h"
|
#include "barrier/IScreenSaver.h"
|
||||||
|
@ -105,7 +105,7 @@ static std::vector<DWORD> immune_keys_list()
|
||||||
//
|
//
|
||||||
|
|
||||||
MSWindowsDesks::MSWindowsDesks(
|
MSWindowsDesks::MSWindowsDesks(
|
||||||
bool isPrimary, bool noHooks, HINSTANCE hookLibrary,
|
bool isPrimary, bool noHooks,
|
||||||
const IScreenSaver* screensaver, IEventQueue* events,
|
const IScreenSaver* screensaver, IEventQueue* events,
|
||||||
IJob* updateKeys, bool stopOnDeskSwitch) :
|
IJob* updateKeys, bool stopOnDeskSwitch) :
|
||||||
m_isPrimary(isPrimary),
|
m_isPrimary(isPrimary),
|
||||||
|
@ -128,9 +128,6 @@ MSWindowsDesks::MSWindowsDesks(
|
||||||
{
|
{
|
||||||
LOG((CLOG_DEBUG "Immune Keys Path: %s", ImmuneKeysPath.c_str()));
|
LOG((CLOG_DEBUG "Immune Keys Path: %s", ImmuneKeysPath.c_str()));
|
||||||
|
|
||||||
if (hookLibrary != NULL)
|
|
||||||
queryHookLibrary(hookLibrary);
|
|
||||||
|
|
||||||
m_cursor = createBlankCursor();
|
m_cursor = createBlankCursor();
|
||||||
m_deskClass = createDeskWindowClass(m_isPrimary);
|
m_deskClass = createDeskWindowClass(m_isPrimary);
|
||||||
m_keyLayout = GetKeyboardLayout(GetCurrentThreadId());
|
m_keyLayout = GetKeyboardLayout(GetCurrentThreadId());
|
||||||
|
@ -362,39 +359,6 @@ MSWindowsDesks::sendMessage(UINT msg, WPARAM wParam, LPARAM lParam) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
MSWindowsDesks::queryHookLibrary(HINSTANCE hookLibrary)
|
|
||||||
{
|
|
||||||
// look up functions
|
|
||||||
if (m_isPrimary && !m_noHooks) {
|
|
||||||
m_install = (InstallFunc)GetProcAddress(hookLibrary, "install");
|
|
||||||
m_uninstall = (UninstallFunc)GetProcAddress(hookLibrary, "uninstall");
|
|
||||||
//m_setImmuneKeys = (SetImmuneKeysFunc)GetProcAddress(hookLibrary, "setImmuneKeys");
|
|
||||||
m_installScreensaver =
|
|
||||||
(InstallScreenSaverFunc)GetProcAddress(
|
|
||||||
hookLibrary, "installScreenSaver");
|
|
||||||
m_uninstallScreensaver =
|
|
||||||
(UninstallScreenSaverFunc)GetProcAddress(
|
|
||||||
hookLibrary, "uninstallScreenSaver");
|
|
||||||
|
|
||||||
if (m_install == NULL ||
|
|
||||||
m_uninstall == NULL ||
|
|
||||||
//m_setImmuneKeys == NULL ||
|
|
||||||
m_installScreensaver == NULL ||
|
|
||||||
m_uninstallScreensaver == NULL) {
|
|
||||||
LOG((CLOG_ERR "Invalid hook library"));
|
|
||||||
throw XScreenOpenFailure();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_install = NULL;
|
|
||||||
m_uninstall = NULL;
|
|
||||||
//m_setImmuneKeys = NULL;
|
|
||||||
m_installScreensaver = NULL;
|
|
||||||
m_uninstallScreensaver = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HCURSOR
|
HCURSOR
|
||||||
MSWindowsDesks::createBlankCursor() const
|
MSWindowsDesks::createBlankCursor() const
|
||||||
{
|
{
|
||||||
|
@ -709,17 +673,17 @@ MSWindowsDesks::deskThread(void* vdesk)
|
||||||
|
|
||||||
case BARRIER_MSG_SWITCH:
|
case BARRIER_MSG_SWITCH:
|
||||||
if (m_isPrimary && !m_noHooks) {
|
if (m_isPrimary && !m_noHooks) {
|
||||||
m_uninstall();
|
MSWindowsHook::uninstall();
|
||||||
if (m_screensaverNotify) {
|
if (m_screensaverNotify) {
|
||||||
m_uninstallScreensaver();
|
MSWindowsHook::uninstallScreenSaver();
|
||||||
m_installScreensaver();
|
MSWindowsHook::installScreenSaver();
|
||||||
}
|
}
|
||||||
// populate immune keys list in the DLL's shared memory
|
// populate immune keys list in the DLL's shared memory
|
||||||
// before the hooks are activated
|
// before the hooks are activated
|
||||||
auto list = immune_keys_list();
|
auto list = immune_keys_list();
|
||||||
LOG((CLOG_DEBUG "Found %u immune keys", list.size()));
|
LOG((CLOG_DEBUG "Found %u immune keys", list.size()));
|
||||||
//m_setImmuneKeys(list.data(), list.size());
|
//m_setImmuneKeys(list.data(), list.size());
|
||||||
switch (m_install()) {
|
switch (MSWindowsHook::install()) {
|
||||||
case kHOOK_FAILED:
|
case kHOOK_FAILED:
|
||||||
// we won't work on this desk
|
// we won't work on this desk
|
||||||
desk->m_lowLevel = false;
|
desk->m_lowLevel = false;
|
||||||
|
@ -795,10 +759,10 @@ MSWindowsDesks::deskThread(void* vdesk)
|
||||||
case BARRIER_MSG_SCREENSAVER:
|
case BARRIER_MSG_SCREENSAVER:
|
||||||
if (!m_noHooks) {
|
if (!m_noHooks) {
|
||||||
if (msg.wParam != 0) {
|
if (msg.wParam != 0) {
|
||||||
m_installScreensaver();
|
MSWindowsHook::installScreenSaver();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_uninstallScreensaver();
|
MSWindowsHook::uninstallScreenSaver();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* barrier -- mouse and keyboard sharing utility
|
* barrier -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2018 Debauchee Open Source Group
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
* Copyright (C) 2012-2016 Symless Ltd.
|
||||||
* Copyright (C) 2004 Chris Schoeneman
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
*
|
*
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "synwinhk/synwinhk.h"
|
#include "platform/synwinhk.h"
|
||||||
#include "barrier/key_types.h"
|
#include "barrier/key_types.h"
|
||||||
#include "barrier/mouse_types.h"
|
#include "barrier/mouse_types.h"
|
||||||
#include "barrier/option_types.h"
|
#include "barrier/option_types.h"
|
||||||
|
@ -65,7 +66,7 @@ public:
|
||||||
\p hookLibrary must be a handle to the hook library.
|
\p hookLibrary must be a handle to the hook library.
|
||||||
*/
|
*/
|
||||||
MSWindowsDesks(
|
MSWindowsDesks(
|
||||||
bool isPrimary, bool noHooks, HINSTANCE hookLibrary,
|
bool isPrimary, bool noHooks,
|
||||||
const IScreenSaver* screensaver, IEventQueue* events,
|
const IScreenSaver* screensaver, IEventQueue* events,
|
||||||
IJob* updateKeys, bool stopOnDeskSwitch);
|
IJob* updateKeys, bool stopOnDeskSwitch);
|
||||||
~MSWindowsDesks();
|
~MSWindowsDesks();
|
||||||
|
@ -206,7 +207,6 @@ private:
|
||||||
typedef std::map<String, Desk*> Desks;
|
typedef std::map<String, Desk*> Desks;
|
||||||
|
|
||||||
// initialization and shutdown operations
|
// initialization and shutdown operations
|
||||||
void queryHookLibrary(HINSTANCE hookLibrary);
|
|
||||||
HCURSOR createBlankCursor() const;
|
HCURSOR createBlankCursor() const;
|
||||||
void destroyCursor(HCURSOR cursor) const;
|
void destroyCursor(HCURSOR cursor) const;
|
||||||
ATOM createDeskWindowClass(bool isPrimary) const;
|
ATOM createDeskWindowClass(bool isPrimary) const;
|
||||||
|
@ -283,15 +283,6 @@ private:
|
||||||
CondVar<bool> m_deskReady;
|
CondVar<bool> m_deskReady;
|
||||||
Desks m_desks;
|
Desks m_desks;
|
||||||
|
|
||||||
// hook library stuff
|
|
||||||
InstallFunc m_install;
|
|
||||||
UninstallFunc m_uninstall;
|
|
||||||
//SetImmuneKeysFunc m_setImmuneKeys;
|
|
||||||
InstallScreenSaverFunc
|
|
||||||
m_installScreensaver;
|
|
||||||
UninstallScreenSaverFunc
|
|
||||||
m_uninstallScreensaver;
|
|
||||||
|
|
||||||
// keyboard stuff
|
// keyboard stuff
|
||||||
IJob* m_updateKeys;
|
IJob* m_updateKeys;
|
||||||
HKL m_keyLayout;
|
HKL m_keyLayout;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* barrier -- mouse and keyboard sharing utility
|
* barrier -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2018 Debauchee Open Source Group
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
* Copyright (C) 2012-2016 Symless Ltd.
|
||||||
* Copyright (C) 2011 Chris Schoeneman
|
* Copyright (C) 2011 Chris Schoeneman
|
||||||
*
|
*
|
||||||
|
@ -17,19 +18,43 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "platform/MSWindowsHook.h"
|
#include "platform/MSWindowsHook.h"
|
||||||
|
#include "barrier/protocol_types.h"
|
||||||
#include "barrier/XScreen.h"
|
#include "barrier/XScreen.h"
|
||||||
#include "base/Log.h"
|
#include "base/Log.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// debugging compile flag. when not zero the server doesn't grab
|
||||||
|
// the keyboard when the mouse leaves the server screen. this
|
||||||
|
// makes it possible to use the debugger (via the keyboard) when
|
||||||
|
// all user input would normally be caught by the hook procedures.
|
||||||
|
//
|
||||||
|
#define NO_GRAB_KEYBOARD 0
|
||||||
|
|
||||||
static const char* g_name = "synwinhk";
|
static const char* g_name = "synwinhk";
|
||||||
|
|
||||||
MSWindowsHook::MSWindowsHook() :
|
static DWORD g_processID = 0;
|
||||||
m_initFunc(NULL),
|
static DWORD g_threadID = 0;
|
||||||
m_cleanupFunc(NULL),
|
static HHOOK g_getMessage = NULL;
|
||||||
m_setSidesFunc(NULL),
|
static HHOOK g_keyboardLL = NULL;
|
||||||
m_setZoneFunc(NULL),
|
static HHOOK g_mouseLL = NULL;
|
||||||
m_setModeFunc(NULL),
|
static bool g_screenSaver = false;
|
||||||
m_instance(NULL)
|
static EHookMode g_mode = kHOOK_DISABLE;
|
||||||
|
static UInt32 g_zoneSides = 0;
|
||||||
|
static SInt32 g_zoneSize = 0;
|
||||||
|
static SInt32 g_xScreen = 0;
|
||||||
|
static SInt32 g_yScreen = 0;
|
||||||
|
static SInt32 g_wScreen = 0;
|
||||||
|
static SInt32 g_hScreen = 0;
|
||||||
|
static WPARAM g_deadVirtKey = 0;
|
||||||
|
static WPARAM g_deadRelease = 0;
|
||||||
|
static LPARAM g_deadLParam = 0;
|
||||||
|
static BYTE g_deadKeyState[256] = { 0 };
|
||||||
|
static BYTE g_keyState[256] = { 0 };
|
||||||
|
static DWORD g_hookThread = 0;
|
||||||
|
static bool g_fakeServerInput = false;
|
||||||
|
static BOOL g_isPrimary = TRUE;
|
||||||
|
|
||||||
|
MSWindowsHook::MSWindowsHook()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,92 +62,693 @@ MSWindowsHook::~MSWindowsHook()
|
||||||
{
|
{
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|
||||||
if (m_instance != NULL) {
|
if (g_processID == GetCurrentProcessId()) {
|
||||||
FreeLibrary(m_instance);
|
uninstall();
|
||||||
|
uninstallScreenSaver();
|
||||||
|
g_processID = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MSWindowsHook::loadLibrary()
|
MSWindowsHook::loadLibrary()
|
||||||
{
|
{
|
||||||
// load library
|
if (g_processID == 0) {
|
||||||
m_instance = LoadLibrary(g_name);
|
g_processID = GetCurrentProcessId();
|
||||||
if (m_instance == NULL) {
|
|
||||||
LOG((CLOG_ERR "failed to load hook library, %s.dll is missing or invalid", g_name));
|
|
||||||
throw XScreenOpenFailure();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// look up functions
|
|
||||||
m_setSidesFunc = (SetSidesFunc)GetProcAddress(m_instance, "setSides");
|
|
||||||
m_setZoneFunc = (SetZoneFunc)GetProcAddress(m_instance, "setZone");
|
|
||||||
m_setModeFunc = (SetModeFunc)GetProcAddress(m_instance, "setMode");
|
|
||||||
m_initFunc = (InitFunc)GetProcAddress(m_instance, "init");
|
|
||||||
m_cleanupFunc = (CleanupFunc)GetProcAddress(m_instance, "cleanup");
|
|
||||||
|
|
||||||
if (m_setSidesFunc == NULL ||
|
|
||||||
m_setZoneFunc == NULL ||
|
|
||||||
m_setModeFunc == NULL ||
|
|
||||||
m_initFunc == NULL ||
|
|
||||||
m_cleanupFunc == NULL) {
|
|
||||||
LOG((CLOG_ERR "failed to load hook function, %s.dll could be out of date", g_name));
|
|
||||||
throw XScreenOpenFailure();
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize library
|
|
||||||
if (init(GetCurrentThreadId()) == 0) {
|
if (init(GetCurrentThreadId()) == 0) {
|
||||||
LOG((CLOG_ERR "failed to init %s.dll, another program may be using it", g_name));
|
LOG((CLOG_ERR "failed to init hooks handler"));
|
||||||
LOG((CLOG_INFO "restarting your computer may solve this error"));
|
|
||||||
throw XScreenOpenFailure();
|
throw XScreenOpenFailure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HINSTANCE
|
|
||||||
MSWindowsHook::getInstance() const
|
|
||||||
{
|
|
||||||
return m_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
MSWindowsHook::init(DWORD threadID)
|
MSWindowsHook::init(DWORD threadID)
|
||||||
{
|
{
|
||||||
if (m_initFunc == NULL) {
|
// try to open process that last called init() to see if it's
|
||||||
return NULL;
|
// still running or if it died without cleaning up.
|
||||||
|
if (g_processID != 0 && g_processID != GetCurrentProcessId()) {
|
||||||
|
HANDLE process = OpenProcess(STANDARD_RIGHTS_REQUIRED,
|
||||||
|
FALSE, g_processID);
|
||||||
|
if (process != NULL) {
|
||||||
|
// old process (probably) still exists so refuse to
|
||||||
|
// reinitialize this DLL (and thus steal it from the
|
||||||
|
// old process).
|
||||||
|
int result = CloseHandle(process);
|
||||||
|
if (result == false) {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return m_initFunc(threadID);
|
}
|
||||||
|
|
||||||
|
// clean up after old process. the system should've already
|
||||||
|
// removed the hooks so we just need to reset our state.
|
||||||
|
g_processID = GetCurrentProcessId();
|
||||||
|
g_threadID = 0;
|
||||||
|
g_getMessage = NULL;
|
||||||
|
g_keyboardLL = NULL;
|
||||||
|
g_mouseLL = NULL;
|
||||||
|
g_screenSaver = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save thread id. we'll post messages to this thread's
|
||||||
|
// message queue.
|
||||||
|
g_threadID = threadID;
|
||||||
|
|
||||||
|
// set defaults
|
||||||
|
g_mode = kHOOK_DISABLE;
|
||||||
|
g_zoneSides = 0;
|
||||||
|
g_zoneSize = 0;
|
||||||
|
g_xScreen = 0;
|
||||||
|
g_yScreen = 0;
|
||||||
|
g_wScreen = 0;
|
||||||
|
g_hScreen = 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
MSWindowsHook::cleanup()
|
MSWindowsHook::cleanup()
|
||||||
{
|
{
|
||||||
if (m_cleanupFunc == NULL) {
|
if (g_processID == GetCurrentProcessId()) {
|
||||||
return NULL;
|
g_threadID = 0;
|
||||||
}
|
}
|
||||||
return m_cleanupFunc();
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MSWindowsHook::setSides(UInt32 sides)
|
MSWindowsHook::setSides(UInt32 sides)
|
||||||
{
|
{
|
||||||
if (m_setSidesFunc == NULL) {
|
g_zoneSides = sides;
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_setSidesFunc(sides);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MSWindowsHook::setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize)
|
MSWindowsHook::setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize)
|
||||||
{
|
{
|
||||||
if (m_setZoneFunc == NULL) {
|
g_zoneSize = jumpZoneSize;
|
||||||
return;
|
g_xScreen = x;
|
||||||
}
|
g_yScreen = y;
|
||||||
m_setZoneFunc(x, y, w, h, jumpZoneSize);
|
g_wScreen = w;
|
||||||
|
g_hScreen = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MSWindowsHook::setMode(EHookMode mode)
|
MSWindowsHook::setMode(EHookMode mode)
|
||||||
{
|
{
|
||||||
if (m_setModeFunc == NULL) {
|
g_mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !NO_GRAB_KEYBOARD
|
||||||
|
static
|
||||||
|
void
|
||||||
|
keyboardGetState(BYTE keys[256], DWORD vkCode, bool kf_up)
|
||||||
|
{
|
||||||
|
// we have to use GetAsyncKeyState() rather than GetKeyState() because
|
||||||
|
// we don't pass through most keys so the event synchronous state
|
||||||
|
// doesn't get updated. we do that because certain modifier keys have
|
||||||
|
// side effects, like alt and the windows key.
|
||||||
|
if (vkCode < 0 || vkCode >= 256) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_setModeFunc(mode);
|
|
||||||
|
// Keep track of key state on our own in case GetAsyncKeyState() fails
|
||||||
|
g_keyState[vkCode] = kf_up ? 0 : 0x80;
|
||||||
|
g_keyState[VK_SHIFT] = g_keyState[VK_LSHIFT] | g_keyState[VK_RSHIFT];
|
||||||
|
|
||||||
|
SHORT key;
|
||||||
|
// Test whether GetAsyncKeyState() is being honest with us
|
||||||
|
key = GetAsyncKeyState(vkCode);
|
||||||
|
|
||||||
|
if (key & 0x80) {
|
||||||
|
// The only time we know for sure that GetAsyncKeyState() is working
|
||||||
|
// is when it tells us that the current key is down.
|
||||||
|
// In this case, update g_keyState to reflect what GetAsyncKeyState()
|
||||||
|
// is telling us, just in case we have gotten out of sync
|
||||||
|
|
||||||
|
for (int i = 0; i < 256; ++i) {
|
||||||
|
key = GetAsyncKeyState(i);
|
||||||
|
g_keyState[i] = (BYTE)((key < 0) ? 0x80u : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy g_keyState to keys
|
||||||
|
for (int i = 0; i < 256; ++i) {
|
||||||
|
keys[i] = g_keyState[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
key = GetKeyState(VK_CAPITAL);
|
||||||
|
keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
WPARAM
|
||||||
|
makeKeyMsg(UINT virtKey, char c, bool noAltGr)
|
||||||
|
{
|
||||||
|
return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), noAltGr ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
bool
|
||||||
|
keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
DWORD vkCode = static_cast<DWORD>(wParam);
|
||||||
|
bool kf_up = (lParam & (KF_UP << 16)) != 0;
|
||||||
|
|
||||||
|
// check for special events indicating if we should start or stop
|
||||||
|
// passing events through and not report them to the server. this
|
||||||
|
// is used to allow the server to synthesize events locally but
|
||||||
|
// not pick them up as user events.
|
||||||
|
if (wParam == BARRIER_HOOK_FAKE_INPUT_VIRTUAL_KEY &&
|
||||||
|
((lParam >> 16) & 0xffu) == BARRIER_HOOK_FAKE_INPUT_SCANCODE) {
|
||||||
|
// update flag
|
||||||
|
g_fakeServerInput = ((lParam & 0x80000000u) == 0);
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
||||||
|
0xff000000u | wParam, lParam);
|
||||||
|
|
||||||
|
// discard event
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're expecting fake input then just pass the event through
|
||||||
|
// and do not forward to the server
|
||||||
|
if (g_fakeServerInput) {
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
||||||
|
0xfe000000u | wParam, lParam);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VK_RSHIFT may be sent with an extended scan code but right shift
|
||||||
|
// is not an extended key so we reset that bit.
|
||||||
|
if (wParam == VK_RSHIFT) {
|
||||||
|
lParam &= ~0x01000000u;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tell server about event
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG, wParam, lParam);
|
||||||
|
|
||||||
|
// ignore dead key release
|
||||||
|
if ((g_deadVirtKey == wParam || g_deadRelease == wParam) &&
|
||||||
|
(lParam & 0x80000000u) != 0) {
|
||||||
|
g_deadRelease = 0;
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
||||||
|
wParam | 0x04000000, lParam);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need the keyboard state for ToAscii()
|
||||||
|
BYTE keys[256];
|
||||||
|
keyboardGetState(keys, vkCode, kf_up);
|
||||||
|
|
||||||
|
// ToAscii() maps ctrl+letter to the corresponding control code
|
||||||
|
// and ctrl+backspace to delete. we don't want those translations
|
||||||
|
// so clear the control modifier state. however, if we want to
|
||||||
|
// simulate AltGr (which is ctrl+alt) then we must not clear it.
|
||||||
|
UINT control = keys[VK_CONTROL] | keys[VK_LCONTROL] | keys[VK_RCONTROL];
|
||||||
|
UINT menu = keys[VK_MENU] | keys[VK_LMENU] | keys[VK_RMENU];
|
||||||
|
if ((control & 0x80) == 0 || (menu & 0x80) == 0) {
|
||||||
|
keys[VK_LCONTROL] = 0;
|
||||||
|
keys[VK_RCONTROL] = 0;
|
||||||
|
keys[VK_CONTROL] = 0;
|
||||||
|
} else {
|
||||||
|
keys[VK_LCONTROL] = 0x80;
|
||||||
|
keys[VK_RCONTROL] = 0x80;
|
||||||
|
keys[VK_CONTROL] = 0x80;
|
||||||
|
keys[VK_LMENU] = 0x80;
|
||||||
|
keys[VK_RMENU] = 0x80;
|
||||||
|
keys[VK_MENU] = 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToAscii() needs to know if a menu is active for some reason.
|
||||||
|
// we don't know and there doesn't appear to be any way to find
|
||||||
|
// out. so we'll just assume a menu is active if the menu key
|
||||||
|
// is down.
|
||||||
|
// FIXME -- figure out some way to check if a menu is active
|
||||||
|
UINT flags = 0;
|
||||||
|
if ((menu & 0x80) != 0)
|
||||||
|
flags |= 1;
|
||||||
|
|
||||||
|
// if we're on the server screen then just pass numpad keys with alt
|
||||||
|
// key down as-is. we won't pick up the resulting character but the
|
||||||
|
// local app will. if on a client screen then grab keys as usual;
|
||||||
|
// if the client is a windows system it'll synthesize the expected
|
||||||
|
// character. if not then it'll probably just do nothing.
|
||||||
|
if (g_mode != kHOOK_RELAY_EVENTS) {
|
||||||
|
// we don't use virtual keys because we don't know what the
|
||||||
|
// state of the numlock key is. we'll hard code the scan codes
|
||||||
|
// instead. hopefully this works across all keyboards.
|
||||||
|
UINT sc = (lParam & 0x01ff0000u) >> 16;
|
||||||
|
if (menu &&
|
||||||
|
(sc >= 0x47u && sc <= 0x52u && sc != 0x4au && sc != 0x4eu)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WORD c = 0;
|
||||||
|
|
||||||
|
// map the key event to a character. we have to put the dead
|
||||||
|
// key back first and this has the side effect of removing it.
|
||||||
|
if (g_deadVirtKey != 0) {
|
||||||
|
if (ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
|
||||||
|
g_deadKeyState, &c, flags) == 2) {
|
||||||
|
// If ToAscii returned 2, it means that we accidentally removed
|
||||||
|
// a double dead key instead of restoring it. Thus, we call
|
||||||
|
// ToAscii again with the same parameters to restore the
|
||||||
|
// internal dead key state.
|
||||||
|
ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
|
||||||
|
g_deadKeyState, &c, flags);
|
||||||
|
|
||||||
|
// We need to keep track of this because g_deadVirtKey will be
|
||||||
|
// cleared later on; this would cause the dead key release to
|
||||||
|
// incorrectly restore the dead key state.
|
||||||
|
g_deadRelease = g_deadVirtKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT scanCode = ((lParam & 0x10ff0000u) >> 16);
|
||||||
|
int n = ToAscii((UINT)wParam, scanCode, keys, &c, flags);
|
||||||
|
|
||||||
|
// if mapping failed and ctrl and alt are pressed then try again
|
||||||
|
// with both not pressed. this handles the case where ctrl and
|
||||||
|
// alt are being used as individual modifiers rather than AltGr.
|
||||||
|
// we note that's the case in the message sent back to barrier
|
||||||
|
// because there's no simple way to deduce it after the fact.
|
||||||
|
// we have to put the dead key back first, if there was one.
|
||||||
|
bool noAltGr = false;
|
||||||
|
if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) {
|
||||||
|
noAltGr = true;
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
||||||
|
wParam | 0x05000000, lParam);
|
||||||
|
if (g_deadVirtKey != 0) {
|
||||||
|
if (ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
|
||||||
|
g_deadKeyState, &c, flags) == 2) {
|
||||||
|
ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
|
||||||
|
g_deadKeyState, &c, flags);
|
||||||
|
g_deadRelease = g_deadVirtKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BYTE keys2[256];
|
||||||
|
for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) {
|
||||||
|
keys2[i] = keys[i];
|
||||||
|
}
|
||||||
|
keys2[VK_LCONTROL] = 0;
|
||||||
|
keys2[VK_RCONTROL] = 0;
|
||||||
|
keys2[VK_CONTROL] = 0;
|
||||||
|
keys2[VK_LMENU] = 0;
|
||||||
|
keys2[VK_RMENU] = 0;
|
||||||
|
keys2[VK_MENU] = 0;
|
||||||
|
n = ToAscii((UINT)wParam, scanCode, keys2, &c, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
||||||
|
wParam | ((c & 0xff) << 8) |
|
||||||
|
((n & 0xff) << 16) | 0x06000000,
|
||||||
|
lParam);
|
||||||
|
WPARAM charAndVirtKey = 0;
|
||||||
|
bool clearDeadKey = false;
|
||||||
|
switch (n) {
|
||||||
|
default:
|
||||||
|
// key is a dead key
|
||||||
|
|
||||||
|
if (lParam & 0x80000000u)
|
||||||
|
// This handles the obscure situation where a key has been
|
||||||
|
// pressed which is both a dead key and a normal character
|
||||||
|
// depending on which modifiers have been pressed. We
|
||||||
|
// break here to prevent it from being considered a dead
|
||||||
|
// key.
|
||||||
|
break;
|
||||||
|
|
||||||
|
g_deadVirtKey = wParam;
|
||||||
|
g_deadLParam = lParam;
|
||||||
|
for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) {
|
||||||
|
g_deadKeyState[i] = keys[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
// key doesn't map to a character. this can happen if
|
||||||
|
// non-character keys are pressed after a dead key.
|
||||||
|
charAndVirtKey = makeKeyMsg((UINT)wParam, (char)0, noAltGr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// key maps to a character composed with dead key
|
||||||
|
charAndVirtKey = makeKeyMsg((UINT)wParam, (char)LOBYTE(c), noAltGr);
|
||||||
|
clearDeadKey = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: {
|
||||||
|
// previous dead key not composed. send a fake key press
|
||||||
|
// and release for the dead key to our window.
|
||||||
|
WPARAM deadCharAndVirtKey =
|
||||||
|
makeKeyMsg((UINT)g_deadVirtKey, (char)LOBYTE(c), noAltGr);
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_KEY,
|
||||||
|
deadCharAndVirtKey, g_deadLParam & 0x7fffffffu);
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_KEY,
|
||||||
|
deadCharAndVirtKey, g_deadLParam | 0x80000000u);
|
||||||
|
|
||||||
|
// use uncomposed character
|
||||||
|
charAndVirtKey = makeKeyMsg((UINT)wParam, (char)HIBYTE(c), noAltGr);
|
||||||
|
clearDeadKey = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// put back the dead key, if any, for the application to use
|
||||||
|
if (g_deadVirtKey != 0) {
|
||||||
|
ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
|
||||||
|
g_deadKeyState, &c, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear out old dead key state
|
||||||
|
if (clearDeadKey) {
|
||||||
|
g_deadVirtKey = 0;
|
||||||
|
g_deadLParam = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// forward message to our window. do this whether or not we're
|
||||||
|
// forwarding events to clients because this'll keep our thread's
|
||||||
|
// key state table up to date. that's important for querying
|
||||||
|
// the scroll lock toggle state.
|
||||||
|
// XXX -- with hot keys for actions we may only need to do this when
|
||||||
|
// forwarding.
|
||||||
|
if (charAndVirtKey != 0) {
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
||||||
|
charAndVirtKey | 0x07000000, lParam);
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_KEY, charAndVirtKey, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_mode == kHOOK_RELAY_EVENTS) {
|
||||||
|
// let certain keys pass through
|
||||||
|
switch (wParam) {
|
||||||
|
case VK_CAPITAL:
|
||||||
|
case VK_NUMLOCK:
|
||||||
|
case VK_SCROLL:
|
||||||
|
// pass event on. we want to let these through to
|
||||||
|
// the window proc because otherwise the keyboard
|
||||||
|
// lights may not stay synchronized.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VK_HANGUL:
|
||||||
|
// pass these modifiers if using a low level hook, discard
|
||||||
|
// them if not.
|
||||||
|
if (g_hookThread == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// discard
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LRESULT CALLBACK
|
||||||
|
keyboardLLHook(int code, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
if (code >= 0) {
|
||||||
|
// decode the message
|
||||||
|
KBDLLHOOKSTRUCT* info = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||||
|
WPARAM wParam = info->vkCode;
|
||||||
|
LPARAM lParam = 1; // repeat code
|
||||||
|
lParam |= (info->scanCode << 16); // scan code
|
||||||
|
if (info->flags & LLKHF_EXTENDED) {
|
||||||
|
lParam |= (1lu << 24); // extended key
|
||||||
|
}
|
||||||
|
if (info->flags & LLKHF_ALTDOWN) {
|
||||||
|
lParam |= (1lu << 29); // context code
|
||||||
|
}
|
||||||
|
if (info->flags & LLKHF_UP) {
|
||||||
|
lParam |= (1lu << 31); // transition
|
||||||
|
}
|
||||||
|
// FIXME -- bit 30 should be set if key was already down but
|
||||||
|
// we don't know that info. as a result we'll never generate
|
||||||
|
// key repeat events.
|
||||||
|
|
||||||
|
// handle the message
|
||||||
|
if (keyboardHookHandler(wParam, lParam)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CallNextHookEx(g_keyboardLL, code, wParam, lParam);
|
||||||
|
}
|
||||||
|
#endif // !NO_GRAB_KEYBOARD
|
||||||
|
|
||||||
|
static
|
||||||
|
bool
|
||||||
|
mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data)
|
||||||
|
{
|
||||||
|
switch (wParam) {
|
||||||
|
case WM_LBUTTONDOWN:
|
||||||
|
case WM_MBUTTONDOWN:
|
||||||
|
case WM_RBUTTONDOWN:
|
||||||
|
case WM_XBUTTONDOWN:
|
||||||
|
case WM_LBUTTONDBLCLK:
|
||||||
|
case WM_MBUTTONDBLCLK:
|
||||||
|
case WM_RBUTTONDBLCLK:
|
||||||
|
case WM_XBUTTONDBLCLK:
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_MBUTTONUP:
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
case WM_XBUTTONUP:
|
||||||
|
case WM_NCLBUTTONDOWN:
|
||||||
|
case WM_NCMBUTTONDOWN:
|
||||||
|
case WM_NCRBUTTONDOWN:
|
||||||
|
case WM_NCXBUTTONDOWN:
|
||||||
|
case WM_NCLBUTTONDBLCLK:
|
||||||
|
case WM_NCMBUTTONDBLCLK:
|
||||||
|
case WM_NCRBUTTONDBLCLK:
|
||||||
|
case WM_NCXBUTTONDBLCLK:
|
||||||
|
case WM_NCLBUTTONUP:
|
||||||
|
case WM_NCMBUTTONUP:
|
||||||
|
case WM_NCRBUTTONUP:
|
||||||
|
case WM_NCXBUTTONUP:
|
||||||
|
// always relay the event. eat it if relaying.
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_BUTTON, wParam, data);
|
||||||
|
return (g_mode == kHOOK_RELAY_EVENTS);
|
||||||
|
|
||||||
|
case WM_MOUSEWHEEL:
|
||||||
|
if (g_mode == kHOOK_RELAY_EVENTS) {
|
||||||
|
// relay event
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_WHEEL, data, 0);
|
||||||
|
}
|
||||||
|
return (g_mode == kHOOK_RELAY_EVENTS);
|
||||||
|
|
||||||
|
case WM_NCMOUSEMOVE:
|
||||||
|
case WM_MOUSEMOVE:
|
||||||
|
if (g_mode == kHOOK_RELAY_EVENTS) {
|
||||||
|
// relay and eat event
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_MOVE, x, y);
|
||||||
|
return true;
|
||||||
|
} else if (g_mode == kHOOK_WATCH_JUMP_ZONE) {
|
||||||
|
// low level hooks can report bogus mouse positions that are
|
||||||
|
// outside of the screen. jeez. naturally we end up getting
|
||||||
|
// fake motion in the other direction to get the position back
|
||||||
|
// on the screen, which plays havoc with switch on double tap.
|
||||||
|
// Server deals with that. we'll clamp positions onto the
|
||||||
|
// screen. also, if we discard events for positions outside
|
||||||
|
// of the screen then the mouse appears to get a bit jerky
|
||||||
|
// near the edge. we can either accept that or pass the bogus
|
||||||
|
// events. we'll try passing the events.
|
||||||
|
bool bogus = false;
|
||||||
|
if (x < g_xScreen) {
|
||||||
|
x = g_xScreen;
|
||||||
|
bogus = true;
|
||||||
|
} else if (x >= g_xScreen + g_wScreen) {
|
||||||
|
x = g_xScreen + g_wScreen - 1;
|
||||||
|
bogus = true;
|
||||||
|
}
|
||||||
|
if (y < g_yScreen) {
|
||||||
|
y = g_yScreen;
|
||||||
|
bogus = true;
|
||||||
|
} else if (y >= g_yScreen + g_hScreen) {
|
||||||
|
y = g_yScreen + g_hScreen - 1;
|
||||||
|
bogus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for mouse inside jump zone
|
||||||
|
bool inside = false;
|
||||||
|
if (!inside && (g_zoneSides & kLeftMask) != 0) {
|
||||||
|
inside = (x < g_xScreen + g_zoneSize);
|
||||||
|
}
|
||||||
|
if (!inside && (g_zoneSides & kRightMask) != 0) {
|
||||||
|
inside = (x >= g_xScreen + g_wScreen - g_zoneSize);
|
||||||
|
}
|
||||||
|
if (!inside && (g_zoneSides & kTopMask) != 0) {
|
||||||
|
inside = (y < g_yScreen + g_zoneSize);
|
||||||
|
}
|
||||||
|
if (!inside && (g_zoneSides & kBottomMask) != 0) {
|
||||||
|
inside = (y >= g_yScreen + g_hScreen - g_zoneSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// relay the event
|
||||||
|
PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_MOVE, x, y);
|
||||||
|
|
||||||
|
// if inside and not bogus then eat the event
|
||||||
|
return inside && !bogus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass the event
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LRESULT CALLBACK
|
||||||
|
mouseLLHook(int code, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
if (code >= 0) {
|
||||||
|
// decode the message
|
||||||
|
MSLLHOOKSTRUCT* info = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
|
||||||
|
SInt32 x = static_cast<SInt32>(info->pt.x);
|
||||||
|
SInt32 y = static_cast<SInt32>(info->pt.y);
|
||||||
|
SInt32 w = static_cast<SInt16>(HIWORD(info->mouseData));
|
||||||
|
|
||||||
|
// handle the message
|
||||||
|
if (mouseHookHandler(wParam, x, y, w)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CallNextHookEx(g_mouseLL, code, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
EHookResult
|
||||||
|
MSWindowsHook::install()
|
||||||
|
{
|
||||||
|
assert(g_getMessage == NULL || g_screenSaver);
|
||||||
|
|
||||||
|
// must be initialized
|
||||||
|
if (g_threadID == 0) {
|
||||||
|
return kHOOK_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// discard old dead keys
|
||||||
|
g_deadVirtKey = 0;
|
||||||
|
g_deadLParam = 0;
|
||||||
|
|
||||||
|
// reset fake input flag
|
||||||
|
g_fakeServerInput = false;
|
||||||
|
|
||||||
|
// install low-level hooks. we require that they both get installed.
|
||||||
|
g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL,
|
||||||
|
&mouseLLHook,
|
||||||
|
NULL,
|
||||||
|
0);
|
||||||
|
#if !NO_GRAB_KEYBOARD
|
||||||
|
g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL,
|
||||||
|
&keyboardLLHook,
|
||||||
|
NULL,
|
||||||
|
0);
|
||||||
|
if (g_mouseLL == NULL || g_keyboardLL == NULL) {
|
||||||
|
if (g_keyboardLL != NULL) {
|
||||||
|
UnhookWindowsHookEx(g_keyboardLL);
|
||||||
|
g_keyboardLL = NULL;
|
||||||
|
}
|
||||||
|
if (g_mouseLL != NULL) {
|
||||||
|
UnhookWindowsHookEx(g_mouseLL);
|
||||||
|
g_mouseLL = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// check that we got all the hooks we wanted
|
||||||
|
if ((g_mouseLL == NULL)
|
||||||
|
#if !NO_GRAB_KEYBOARD
|
||||||
|
|| (g_keyboardLL == NULL)
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
uninstall();
|
||||||
|
return kHOOK_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_keyboardLL != NULL || g_mouseLL != NULL) {
|
||||||
|
g_hookThread = GetCurrentThreadId();
|
||||||
|
return kHOOK_OKAY_LL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kHOOK_OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
MSWindowsHook::uninstall()
|
||||||
|
{
|
||||||
|
// discard old dead keys
|
||||||
|
g_deadVirtKey = 0;
|
||||||
|
g_deadLParam = 0;
|
||||||
|
|
||||||
|
// uninstall hooks
|
||||||
|
if (g_keyboardLL != NULL) {
|
||||||
|
UnhookWindowsHookEx(g_keyboardLL);
|
||||||
|
g_keyboardLL = NULL;
|
||||||
|
}
|
||||||
|
if (g_mouseLL != NULL) {
|
||||||
|
UnhookWindowsHookEx(g_mouseLL);
|
||||||
|
g_mouseLL = NULL;
|
||||||
|
}
|
||||||
|
if (g_getMessage != NULL && !g_screenSaver) {
|
||||||
|
UnhookWindowsHookEx(g_getMessage);
|
||||||
|
g_getMessage = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
LRESULT CALLBACK
|
||||||
|
getMessageHook(int code, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
if (code >= 0) {
|
||||||
|
if (g_screenSaver) {
|
||||||
|
MSG* msg = reinterpret_cast<MSG*>(lParam);
|
||||||
|
if (msg->message == WM_SYSCOMMAND &&
|
||||||
|
msg->wParam == SC_SCREENSAVE) {
|
||||||
|
// broadcast screen saver started message
|
||||||
|
PostThreadMessage(g_threadID,
|
||||||
|
BARRIER_MSG_SCREEN_SAVER, TRUE, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CallNextHookEx(g_getMessage, code, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
MSWindowsHook::installScreenSaver()
|
||||||
|
{
|
||||||
|
// must be initialized
|
||||||
|
if (g_threadID == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate screen saver messages
|
||||||
|
g_screenSaver = true;
|
||||||
|
|
||||||
|
// install hook unless it's already installed
|
||||||
|
if (g_getMessage == NULL) {
|
||||||
|
g_getMessage = SetWindowsHookEx(WH_GETMESSAGE,
|
||||||
|
&getMessageHook,
|
||||||
|
NULL,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (g_getMessage != NULL) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
MSWindowsHook::uninstallScreenSaver()
|
||||||
|
{
|
||||||
|
// uninstall hook unless the mouse wheel hook is installed
|
||||||
|
if (g_getMessage != NULL) {
|
||||||
|
UnhookWindowsHookEx(g_getMessage);
|
||||||
|
g_getMessage = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// screen saver hook is no longer installed
|
||||||
|
g_screenSaver = false;
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* barrier -- mouse and keyboard sharing utility
|
* barrier -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2018 Debauchee Open Source Group
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
* Copyright (C) 2012-2016 Symless Ltd.
|
||||||
* Copyright (C) 2011 Chris Schoeneman
|
* Copyright (C) 2011 Chris Schoeneman
|
||||||
*
|
*
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "synwinhk/synwinhk.h"
|
#include "platform/synwinhk.h"
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
@ -31,18 +32,14 @@ public:
|
||||||
virtual ~MSWindowsHook();
|
virtual ~MSWindowsHook();
|
||||||
|
|
||||||
void loadLibrary();
|
void loadLibrary();
|
||||||
HINSTANCE getInstance() const;
|
|
||||||
int init(DWORD threadID);
|
int init(DWORD threadID);
|
||||||
int cleanup();
|
int cleanup();
|
||||||
void setSides(UInt32 sides);
|
void setSides(UInt32 sides);
|
||||||
void setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize);
|
void setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize);
|
||||||
void setMode(EHookMode mode);
|
void setMode(EHookMode mode);
|
||||||
|
|
||||||
private:
|
static EHookResult install();
|
||||||
InitFunc m_initFunc;
|
static int uninstall();
|
||||||
CleanupFunc m_cleanupFunc;
|
static int installScreenSaver();
|
||||||
SetSidesFunc m_setSidesFunc;
|
static int uninstallScreenSaver();
|
||||||
SetZoneFunc m_setZoneFunc;
|
|
||||||
SetModeFunc m_setModeFunc;
|
|
||||||
HINSTANCE m_instance;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* barrier -- mouse and keyboard sharing utility
|
* barrier -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2018 Debauchee Open Source Group
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
* Copyright (C) 2012-2016 Symless Ltd.
|
||||||
* Copyright (C) 2002 Chris Schoeneman
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
*
|
*
|
||||||
|
@ -136,7 +137,6 @@ MSWindowsScreen::MSWindowsScreen(
|
||||||
m_desks = new MSWindowsDesks(
|
m_desks = new MSWindowsDesks(
|
||||||
m_isPrimary,
|
m_isPrimary,
|
||||||
m_noHooks,
|
m_noHooks,
|
||||||
m_hook.getInstance(),
|
|
||||||
m_screensaver,
|
m_screensaver,
|
||||||
m_events,
|
m_events,
|
||||||
new TMethodJob<MSWindowsScreen>(
|
new TMethodJob<MSWindowsScreen>(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* barrier -- mouse and keyboard sharing utility
|
* barrier -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2018 Debauchee Open Source Group
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
* Copyright (C) 2012-2016 Symless Ltd.
|
||||||
* Copyright (C) 2002 Chris Schoeneman
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
*
|
*
|
||||||
|
@ -21,7 +22,7 @@
|
||||||
#include "platform/MSWindowsHook.h"
|
#include "platform/MSWindowsHook.h"
|
||||||
#include "barrier/PlatformScreen.h"
|
#include "barrier/PlatformScreen.h"
|
||||||
#include "barrier/DragInformation.h"
|
#include "barrier/DragInformation.h"
|
||||||
#include "synwinhk/synwinhk.h"
|
#include "platform/synwinhk.h"
|
||||||
#include "mt/CondVar.h"
|
#include "mt/CondVar.h"
|
||||||
#include "mt/Mutex.h"
|
#include "mt/Mutex.h"
|
||||||
#include "base/String.h"
|
#include "base/String.h"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* barrier -- mouse and keyboard sharing utility
|
* barrier -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2018 Debauchee Open Source Group
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
* Copyright (C) 2012-2016 Symless Ltd.
|
||||||
* Copyright (C) 2002 Chris Schoeneman
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
*
|
*
|
||||||
|
@ -59,27 +60,6 @@ enum EHookMode {
|
||||||
kHOOK_RELAY_EVENTS
|
kHOOK_RELAY_EVENTS
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int (*InitFunc)(DWORD targetQueueThreadID);
|
|
||||||
typedef int (*CleanupFunc)(void);
|
|
||||||
typedef EHookResult (*InstallFunc)(void);
|
|
||||||
typedef int (*UninstallFunc)(void);
|
|
||||||
typedef int (*InstallScreenSaverFunc)(void);
|
|
||||||
typedef int (*UninstallScreenSaverFunc)(void);
|
|
||||||
typedef void (*SetSidesFunc)(UInt32);
|
|
||||||
typedef void (*SetZoneFunc)(SInt32, SInt32, SInt32, SInt32, SInt32);
|
|
||||||
typedef void (*SetModeFunc)(int);
|
|
||||||
|
|
||||||
CBARRIERHOOK_API int init(DWORD);
|
|
||||||
CBARRIERHOOK_API int cleanup(void);
|
|
||||||
CBARRIERHOOK_API EHookResult install(void);
|
|
||||||
CBARRIERHOOK_API int uninstall(void);
|
|
||||||
CBARRIERHOOK_API int installScreenSaver(void);
|
|
||||||
CBARRIERHOOK_API int uninstallScreenSaver(void);
|
|
||||||
CBARRIERHOOK_API void setSides(UInt32 sides);
|
|
||||||
CBARRIERHOOK_API void setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h,
|
|
||||||
SInt32 jumpZoneSize);
|
|
||||||
CBARRIERHOOK_API void setMode(EHookMode mode);
|
|
||||||
|
|
||||||
/* REMOVED ImmuneKeys for migration of synwinhk out of DLL
|
/* REMOVED ImmuneKeys for migration of synwinhk out of DLL
|
||||||
|
|
||||||
typedef void (*SetImmuneKeysFunc)(const DWORD*, std::size_t);
|
typedef void (*SetImmuneKeysFunc)(const DWORD*, std::size_t);
|
|
@ -1,27 +0,0 @@
|
||||||
# barrier -- mouse and keyboard sharing utility
|
|
||||||
# Copyright (C) 2013-2016 Symless Ltd.
|
|
||||||
#
|
|
||||||
# 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 LICENSE 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/>.
|
|
||||||
|
|
||||||
file(GLOB headers "*.h")
|
|
||||||
file(GLOB sources "*.cpp")
|
|
||||||
|
|
||||||
if (BARRIER_ADD_HEADERS)
|
|
||||||
list(APPEND sources ${headers})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(synwinhk SHARED ${sources})
|
|
||||||
|
|
||||||
if (NOT MSVC_VERSION VERSION_LESS 1900)
|
|
||||||
target_link_libraries(synwinhk libucrt)
|
|
||||||
endif()
|
|
|
@ -1,873 +0,0 @@
|
||||||
/*
|
|
||||||
* barrier -- mouse and keyboard sharing utility
|
|
||||||
* Copyright (C) 2012-2016 Symless Ltd.
|
|
||||||
* Copyright (C) 2002 Chris Schoeneman
|
|
||||||
*
|
|
||||||
* This package is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* found in the file LICENSE 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/* REMOVED ImmuneKeys for migration of synwinhk out of DLL
|
|
||||||
|
|
||||||
// because all of our global data is shared between processes,
|
|
||||||
// allocating shared data on-the-fly is tricky. therefore let's
|
|
||||||
// store all of our immune keys in a pre-allocated array. the
|
|
||||||
// downside here is that we have to pick a maximum number of
|
|
||||||
// immune keys to store. who would ever want barrier to ignore
|
|
||||||
// more than 32 keys on their keyboard??
|
|
||||||
struct ImmuneKeys
|
|
||||||
{
|
|
||||||
static const std::size_t MaxKeys = 32;
|
|
||||||
DWORD list[MaxKeys];
|
|
||||||
std::size_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
static ImmuneKeys g_immuneKeys{ {0}, 0 };
|
|
||||||
|
|
||||||
inline static
|
|
||||||
bool is_immune_key(DWORD target)
|
|
||||||
{
|
|
||||||
for (std::size_t idx = 0; idx < g_immuneKeys.count; ++idx) {
|
|
||||||
if (g_immuneKeys.list[idx] == target)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow all immune keys to pass without filtering
|
|
||||||
if (is_immune_key(static_cast<DWORD>(wParam)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// do not call this while the hooks are active!
|
|
||||||
void
|
|
||||||
setImmuneKeys(const DWORD *list, std::size_t size)
|
|
||||||
{
|
|
||||||
if (size > ImmuneKeys::MaxKeys)
|
|
||||||
size = ImmuneKeys::MaxKeys;
|
|
||||||
g_immuneKeys.count = size;
|
|
||||||
if (size > 0) {
|
|
||||||
for (std::size_t idx = 0; idx < size; ++idx)
|
|
||||||
g_immuneKeys.list[idx] = list[idx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "synwinhk/synwinhk.h"
|
|
||||||
|
|
||||||
#include "barrier/protocol_types.h"
|
|
||||||
|
|
||||||
#include <tchar.h>
|
|
||||||
|
|
||||||
#undef assert
|
|
||||||
#if _DEBUG
|
|
||||||
#define assert(_X_) if (!(_X_)) __debugbreak()
|
|
||||||
#else
|
|
||||||
#define assert(_X_) __noop()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
|
||||||
// debugging compile flag. when not zero the server doesn't grab
|
|
||||||
// the keyboard when the mouse leaves the server screen. this
|
|
||||||
// makes it possible to use the debugger (via the keyboard) when
|
|
||||||
// all user input would normally be caught by the hook procedures.
|
|
||||||
//
|
|
||||||
#define NO_GRAB_KEYBOARD 0
|
|
||||||
|
|
||||||
//
|
|
||||||
// globals
|
|
||||||
//
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#pragma comment(linker, "-section:shared,rws")
|
|
||||||
#pragma data_seg("shared")
|
|
||||||
#endif
|
|
||||||
// all data in this shared section *must* be initialized
|
|
||||||
|
|
||||||
static HINSTANCE g_hinstance = NULL;
|
|
||||||
static DWORD g_processID = 0;
|
|
||||||
static DWORD g_threadID = 0;
|
|
||||||
static HHOOK g_getMessage = NULL;
|
|
||||||
static HHOOK g_keyboardLL = NULL;
|
|
||||||
static HHOOK g_mouseLL = NULL;
|
|
||||||
static bool g_screenSaver = false;
|
|
||||||
static EHookMode g_mode = kHOOK_DISABLE;
|
|
||||||
static UInt32 g_zoneSides = 0;
|
|
||||||
static SInt32 g_zoneSize = 0;
|
|
||||||
static SInt32 g_xScreen = 0;
|
|
||||||
static SInt32 g_yScreen = 0;
|
|
||||||
static SInt32 g_wScreen = 0;
|
|
||||||
static SInt32 g_hScreen = 0;
|
|
||||||
static WPARAM g_deadVirtKey = 0;
|
|
||||||
static WPARAM g_deadRelease = 0;
|
|
||||||
static LPARAM g_deadLParam = 0;
|
|
||||||
static BYTE g_deadKeyState[256] = { 0 };
|
|
||||||
static BYTE g_keyState[256] = { 0 };
|
|
||||||
static DWORD g_hookThread = 0;
|
|
||||||
static bool g_fakeInput = false;
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#pragma data_seg()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// keep linker quiet about floating point stuff. we don't use any
|
|
||||||
// floating point operations but our includes may define some
|
|
||||||
// (unused) floating point values.
|
|
||||||
#ifndef _DEBUG
|
|
||||||
extern "C" {
|
|
||||||
int _fltused=0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !NO_GRAB_KEYBOARD
|
|
||||||
static
|
|
||||||
WPARAM
|
|
||||||
makeKeyMsg(UINT virtKey, char c, bool noAltGr)
|
|
||||||
{
|
|
||||||
return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), noAltGr ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
void
|
|
||||||
keyboardGetState(BYTE keys[256], DWORD vkCode, bool kf_up)
|
|
||||||
{
|
|
||||||
// we have to use GetAsyncKeyState() rather than GetKeyState() because
|
|
||||||
// we don't pass through most keys so the event synchronous state
|
|
||||||
// doesn't get updated. we do that because certain modifier keys have
|
|
||||||
// side effects, like alt and the windows key.
|
|
||||||
if (vkCode < 0 || vkCode >= 256) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep track of key state on our own in case GetAsyncKeyState() fails
|
|
||||||
g_keyState[vkCode] = kf_up ? 0 : 0x80;
|
|
||||||
g_keyState[VK_SHIFT] = g_keyState[VK_LSHIFT] | g_keyState[VK_RSHIFT];
|
|
||||||
|
|
||||||
SHORT key;
|
|
||||||
// Test whether GetAsyncKeyState() is being honest with us
|
|
||||||
key = GetAsyncKeyState(vkCode);
|
|
||||||
|
|
||||||
if (key & 0x80) {
|
|
||||||
// The only time we know for sure that GetAsyncKeyState() is working
|
|
||||||
// is when it tells us that the current key is down.
|
|
||||||
// In this case, update g_keyState to reflect what GetAsyncKeyState()
|
|
||||||
// is telling us, just in case we have gotten out of sync
|
|
||||||
|
|
||||||
for (int i = 0; i < 256; ++i) {
|
|
||||||
key = GetAsyncKeyState(i);
|
|
||||||
g_keyState[i] = (BYTE)((key < 0) ? 0x80u : 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy g_keyState to keys
|
|
||||||
for (int i = 0; i < 256; ++i) {
|
|
||||||
keys[i] = g_keyState[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
key = GetKeyState(VK_CAPITAL);
|
|
||||||
keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
bool
|
|
||||||
keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
DWORD vkCode = static_cast<DWORD>(wParam);
|
|
||||||
bool kf_up = (lParam & (KF_UP << 16)) != 0;
|
|
||||||
|
|
||||||
// check for special events indicating if we should start or stop
|
|
||||||
// passing events through and not report them to the server. this
|
|
||||||
// is used to allow the server to synthesize events locally but
|
|
||||||
// not pick them up as user events.
|
|
||||||
if (wParam == BARRIER_HOOK_FAKE_INPUT_VIRTUAL_KEY &&
|
|
||||||
((lParam >> 16) & 0xffu) == BARRIER_HOOK_FAKE_INPUT_SCANCODE) {
|
|
||||||
// update flag
|
|
||||||
g_fakeInput = ((lParam & 0x80000000u) == 0);
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
|
||||||
0xff000000u | wParam, lParam);
|
|
||||||
|
|
||||||
// discard event
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we're expecting fake input then just pass the event through
|
|
||||||
// and do not forward to the server
|
|
||||||
if (g_fakeInput) {
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
|
||||||
0xfe000000u | wParam, lParam);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// VK_RSHIFT may be sent with an extended scan code but right shift
|
|
||||||
// is not an extended key so we reset that bit.
|
|
||||||
if (wParam == VK_RSHIFT) {
|
|
||||||
lParam &= ~0x01000000u;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tell server about event
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG, wParam, lParam);
|
|
||||||
|
|
||||||
// ignore dead key release
|
|
||||||
if ((g_deadVirtKey == wParam || g_deadRelease == wParam) &&
|
|
||||||
(lParam & 0x80000000u) != 0) {
|
|
||||||
g_deadRelease = 0;
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
|
||||||
wParam | 0x04000000, lParam);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we need the keyboard state for ToAscii()
|
|
||||||
BYTE keys[256];
|
|
||||||
keyboardGetState(keys, vkCode, kf_up);
|
|
||||||
|
|
||||||
// ToAscii() maps ctrl+letter to the corresponding control code
|
|
||||||
// and ctrl+backspace to delete. we don't want those translations
|
|
||||||
// so clear the control modifier state. however, if we want to
|
|
||||||
// simulate AltGr (which is ctrl+alt) then we must not clear it.
|
|
||||||
UINT control = keys[VK_CONTROL] | keys[VK_LCONTROL] | keys[VK_RCONTROL];
|
|
||||||
UINT menu = keys[VK_MENU] | keys[VK_LMENU] | keys[VK_RMENU];
|
|
||||||
if ((control & 0x80) == 0 || (menu & 0x80) == 0) {
|
|
||||||
keys[VK_LCONTROL] = 0;
|
|
||||||
keys[VK_RCONTROL] = 0;
|
|
||||||
keys[VK_CONTROL] = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
keys[VK_LCONTROL] = 0x80;
|
|
||||||
keys[VK_RCONTROL] = 0x80;
|
|
||||||
keys[VK_CONTROL] = 0x80;
|
|
||||||
keys[VK_LMENU] = 0x80;
|
|
||||||
keys[VK_RMENU] = 0x80;
|
|
||||||
keys[VK_MENU] = 0x80;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToAscii() needs to know if a menu is active for some reason.
|
|
||||||
// we don't know and there doesn't appear to be any way to find
|
|
||||||
// out. so we'll just assume a menu is active if the menu key
|
|
||||||
// is down.
|
|
||||||
// FIXME -- figure out some way to check if a menu is active
|
|
||||||
UINT flags = 0;
|
|
||||||
if ((menu & 0x80) != 0)
|
|
||||||
flags |= 1;
|
|
||||||
|
|
||||||
// if we're on the server screen then just pass numpad keys with alt
|
|
||||||
// key down as-is. we won't pick up the resulting character but the
|
|
||||||
// local app will. if on a client screen then grab keys as usual;
|
|
||||||
// if the client is a windows system it'll synthesize the expected
|
|
||||||
// character. if not then it'll probably just do nothing.
|
|
||||||
if (g_mode != kHOOK_RELAY_EVENTS) {
|
|
||||||
// we don't use virtual keys because we don't know what the
|
|
||||||
// state of the numlock key is. we'll hard code the scan codes
|
|
||||||
// instead. hopefully this works across all keyboards.
|
|
||||||
UINT sc = (lParam & 0x01ff0000u) >> 16;
|
|
||||||
if (menu &&
|
|
||||||
(sc >= 0x47u && sc <= 0x52u && sc != 0x4au && sc != 0x4eu)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WORD c = 0;
|
|
||||||
|
|
||||||
// map the key event to a character. we have to put the dead
|
|
||||||
// key back first and this has the side effect of removing it.
|
|
||||||
if (g_deadVirtKey != 0) {
|
|
||||||
if (ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
|
|
||||||
g_deadKeyState, &c, flags) == 2)
|
|
||||||
{
|
|
||||||
// If ToAscii returned 2, it means that we accidentally removed
|
|
||||||
// a double dead key instead of restoring it. Thus, we call
|
|
||||||
// ToAscii again with the same parameters to restore the
|
|
||||||
// internal dead key state.
|
|
||||||
ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
|
|
||||||
g_deadKeyState, &c, flags);
|
|
||||||
|
|
||||||
// We need to keep track of this because g_deadVirtKey will be
|
|
||||||
// cleared later on; this would cause the dead key release to
|
|
||||||
// incorrectly restore the dead key state.
|
|
||||||
g_deadRelease = g_deadVirtKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UINT scanCode = ((lParam & 0x10ff0000u) >> 16);
|
|
||||||
int n = ToAscii((UINT)wParam, scanCode, keys, &c, flags);
|
|
||||||
|
|
||||||
// if mapping failed and ctrl and alt are pressed then try again
|
|
||||||
// with both not pressed. this handles the case where ctrl and
|
|
||||||
// alt are being used as individual modifiers rather than AltGr.
|
|
||||||
// we note that's the case in the message sent back to barrier
|
|
||||||
// because there's no simple way to deduce it after the fact.
|
|
||||||
// we have to put the dead key back first, if there was one.
|
|
||||||
bool noAltGr = false;
|
|
||||||
if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) {
|
|
||||||
noAltGr = true;
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
|
||||||
wParam | 0x05000000, lParam);
|
|
||||||
if (g_deadVirtKey != 0) {
|
|
||||||
if (ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
|
|
||||||
g_deadKeyState, &c, flags) == 2)
|
|
||||||
{
|
|
||||||
ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
|
|
||||||
g_deadKeyState, &c, flags);
|
|
||||||
g_deadRelease = g_deadVirtKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BYTE keys2[256];
|
|
||||||
for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) {
|
|
||||||
keys2[i] = keys[i];
|
|
||||||
}
|
|
||||||
keys2[VK_LCONTROL] = 0;
|
|
||||||
keys2[VK_RCONTROL] = 0;
|
|
||||||
keys2[VK_CONTROL] = 0;
|
|
||||||
keys2[VK_LMENU] = 0;
|
|
||||||
keys2[VK_RMENU] = 0;
|
|
||||||
keys2[VK_MENU] = 0;
|
|
||||||
n = ToAscii((UINT)wParam, scanCode, keys2, &c, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
|
||||||
wParam | ((c & 0xff) << 8) |
|
|
||||||
((n & 0xff) << 16) | 0x06000000,
|
|
||||||
lParam);
|
|
||||||
WPARAM charAndVirtKey = 0;
|
|
||||||
bool clearDeadKey = false;
|
|
||||||
switch (n) {
|
|
||||||
default:
|
|
||||||
// key is a dead key
|
|
||||||
|
|
||||||
if (lParam & 0x80000000u)
|
|
||||||
// This handles the obscure situation where a key has been
|
|
||||||
// pressed which is both a dead key and a normal character
|
|
||||||
// depending on which modifiers have been pressed. We
|
|
||||||
// break here to prevent it from being considered a dead
|
|
||||||
// key.
|
|
||||||
break;
|
|
||||||
|
|
||||||
g_deadVirtKey = wParam;
|
|
||||||
g_deadLParam = lParam;
|
|
||||||
for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) {
|
|
||||||
g_deadKeyState[i] = keys[i];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0:
|
|
||||||
// key doesn't map to a character. this can happen if
|
|
||||||
// non-character keys are pressed after a dead key.
|
|
||||||
charAndVirtKey = makeKeyMsg((UINT)wParam, (char)0, noAltGr);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
// key maps to a character composed with dead key
|
|
||||||
charAndVirtKey = makeKeyMsg((UINT)wParam, (char)LOBYTE(c), noAltGr);
|
|
||||||
clearDeadKey = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: {
|
|
||||||
// previous dead key not composed. send a fake key press
|
|
||||||
// and release for the dead key to our window.
|
|
||||||
WPARAM deadCharAndVirtKey =
|
|
||||||
makeKeyMsg((UINT)g_deadVirtKey, (char)LOBYTE(c), noAltGr);
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_KEY,
|
|
||||||
deadCharAndVirtKey, g_deadLParam & 0x7fffffffu);
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_KEY,
|
|
||||||
deadCharAndVirtKey, g_deadLParam | 0x80000000u);
|
|
||||||
|
|
||||||
// use uncomposed character
|
|
||||||
charAndVirtKey = makeKeyMsg((UINT)wParam, (char)HIBYTE(c), noAltGr);
|
|
||||||
clearDeadKey = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// put back the dead key, if any, for the application to use
|
|
||||||
if (g_deadVirtKey != 0) {
|
|
||||||
ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
|
|
||||||
g_deadKeyState, &c, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear out old dead key state
|
|
||||||
if (clearDeadKey) {
|
|
||||||
g_deadVirtKey = 0;
|
|
||||||
g_deadLParam = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// forward message to our window. do this whether or not we're
|
|
||||||
// forwarding events to clients because this'll keep our thread's
|
|
||||||
// key state table up to date. that's important for querying
|
|
||||||
// the scroll lock toggle state.
|
|
||||||
// XXX -- with hot keys for actions we may only need to do this when
|
|
||||||
// forwarding.
|
|
||||||
if (charAndVirtKey != 0) {
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
|
|
||||||
charAndVirtKey | 0x07000000, lParam);
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_KEY, charAndVirtKey, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_mode == kHOOK_RELAY_EVENTS) {
|
|
||||||
// let certain keys pass through
|
|
||||||
switch (wParam) {
|
|
||||||
case VK_CAPITAL:
|
|
||||||
case VK_NUMLOCK:
|
|
||||||
case VK_SCROLL:
|
|
||||||
// pass event on. we want to let these through to
|
|
||||||
// the window proc because otherwise the keyboard
|
|
||||||
// lights may not stay synchronized.
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VK_HANGUL:
|
|
||||||
// pass these modifiers if using a low level hook, discard
|
|
||||||
// them if not.
|
|
||||||
if (g_hookThread == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// discard
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static
|
|
||||||
bool
|
|
||||||
mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data)
|
|
||||||
{
|
|
||||||
switch (wParam) {
|
|
||||||
case WM_LBUTTONDOWN:
|
|
||||||
case WM_MBUTTONDOWN:
|
|
||||||
case WM_RBUTTONDOWN:
|
|
||||||
case WM_XBUTTONDOWN:
|
|
||||||
case WM_LBUTTONDBLCLK:
|
|
||||||
case WM_MBUTTONDBLCLK:
|
|
||||||
case WM_RBUTTONDBLCLK:
|
|
||||||
case WM_XBUTTONDBLCLK:
|
|
||||||
case WM_LBUTTONUP:
|
|
||||||
case WM_MBUTTONUP:
|
|
||||||
case WM_RBUTTONUP:
|
|
||||||
case WM_XBUTTONUP:
|
|
||||||
case WM_NCLBUTTONDOWN:
|
|
||||||
case WM_NCMBUTTONDOWN:
|
|
||||||
case WM_NCRBUTTONDOWN:
|
|
||||||
case WM_NCXBUTTONDOWN:
|
|
||||||
case WM_NCLBUTTONDBLCLK:
|
|
||||||
case WM_NCMBUTTONDBLCLK:
|
|
||||||
case WM_NCRBUTTONDBLCLK:
|
|
||||||
case WM_NCXBUTTONDBLCLK:
|
|
||||||
case WM_NCLBUTTONUP:
|
|
||||||
case WM_NCMBUTTONUP:
|
|
||||||
case WM_NCRBUTTONUP:
|
|
||||||
case WM_NCXBUTTONUP:
|
|
||||||
// always relay the event. eat it if relaying.
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_BUTTON, wParam, data);
|
|
||||||
return (g_mode == kHOOK_RELAY_EVENTS);
|
|
||||||
|
|
||||||
case WM_MOUSEWHEEL:
|
|
||||||
if (g_mode == kHOOK_RELAY_EVENTS) {
|
|
||||||
// relay event
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_WHEEL, data, 0);
|
|
||||||
}
|
|
||||||
return (g_mode == kHOOK_RELAY_EVENTS);
|
|
||||||
|
|
||||||
case WM_NCMOUSEMOVE:
|
|
||||||
case WM_MOUSEMOVE:
|
|
||||||
if (g_mode == kHOOK_RELAY_EVENTS) {
|
|
||||||
// relay and eat event
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_MOVE, x, y);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (g_mode == kHOOK_WATCH_JUMP_ZONE) {
|
|
||||||
// low level hooks can report bogus mouse positions that are
|
|
||||||
// outside of the screen. jeez. naturally we end up getting
|
|
||||||
// fake motion in the other direction to get the position back
|
|
||||||
// on the screen, which plays havoc with switch on double tap.
|
|
||||||
// Server deals with that. we'll clamp positions onto the
|
|
||||||
// screen. also, if we discard events for positions outside
|
|
||||||
// of the screen then the mouse appears to get a bit jerky
|
|
||||||
// near the edge. we can either accept that or pass the bogus
|
|
||||||
// events. we'll try passing the events.
|
|
||||||
bool bogus = false;
|
|
||||||
if (x < g_xScreen) {
|
|
||||||
x = g_xScreen;
|
|
||||||
bogus = true;
|
|
||||||
}
|
|
||||||
else if (x >= g_xScreen + g_wScreen) {
|
|
||||||
x = g_xScreen + g_wScreen - 1;
|
|
||||||
bogus = true;
|
|
||||||
}
|
|
||||||
if (y < g_yScreen) {
|
|
||||||
y = g_yScreen;
|
|
||||||
bogus = true;
|
|
||||||
}
|
|
||||||
else if (y >= g_yScreen + g_hScreen) {
|
|
||||||
y = g_yScreen + g_hScreen - 1;
|
|
||||||
bogus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for mouse inside jump zone
|
|
||||||
bool inside = false;
|
|
||||||
if (!inside && (g_zoneSides & kLeftMask) != 0) {
|
|
||||||
inside = (x < g_xScreen + g_zoneSize);
|
|
||||||
}
|
|
||||||
if (!inside && (g_zoneSides & kRightMask) != 0) {
|
|
||||||
inside = (x >= g_xScreen + g_wScreen - g_zoneSize);
|
|
||||||
}
|
|
||||||
if (!inside && (g_zoneSides & kTopMask) != 0) {
|
|
||||||
inside = (y < g_yScreen + g_zoneSize);
|
|
||||||
}
|
|
||||||
if (!inside && (g_zoneSides & kBottomMask) != 0) {
|
|
||||||
inside = (y >= g_yScreen + g_hScreen - g_zoneSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// relay the event
|
|
||||||
PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_MOVE, x, y);
|
|
||||||
|
|
||||||
// if inside and not bogus then eat the event
|
|
||||||
return inside && !bogus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pass the event
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
LRESULT CALLBACK
|
|
||||||
getMessageHook(int code, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
if (code >= 0) {
|
|
||||||
if (g_screenSaver) {
|
|
||||||
MSG* msg = reinterpret_cast<MSG*>(lParam);
|
|
||||||
if (msg->message == WM_SYSCOMMAND &&
|
|
||||||
msg->wParam == SC_SCREENSAVE) {
|
|
||||||
// broadcast screen saver started message
|
|
||||||
PostThreadMessage(g_threadID,
|
|
||||||
BARRIER_MSG_SCREEN_SAVER, TRUE, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CallNextHookEx(g_getMessage, code, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// low-level keyboard hook -- this allows us to capture and handle
|
|
||||||
// alt+tab, alt+esc, ctrl+esc, and windows key hot keys. on the down
|
|
||||||
// side, key repeats are not reported to us.
|
|
||||||
//
|
|
||||||
|
|
||||||
#if !NO_GRAB_KEYBOARD
|
|
||||||
static
|
|
||||||
LRESULT CALLBACK
|
|
||||||
keyboardLLHook(int code, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
if (code >= 0) {
|
|
||||||
// decode the message
|
|
||||||
KBDLLHOOKSTRUCT* info = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
|
||||||
WPARAM wParam = info->vkCode;
|
|
||||||
LPARAM lParam = 1; // repeat code
|
|
||||||
lParam |= (info->scanCode << 16); // scan code
|
|
||||||
if (info->flags & LLKHF_EXTENDED) {
|
|
||||||
lParam |= (1lu << 24); // extended key
|
|
||||||
}
|
|
||||||
if (info->flags & LLKHF_ALTDOWN) {
|
|
||||||
lParam |= (1lu << 29); // context code
|
|
||||||
}
|
|
||||||
if (info->flags & LLKHF_UP) {
|
|
||||||
lParam |= (1lu << 31); // transition
|
|
||||||
}
|
|
||||||
// FIXME -- bit 30 should be set if key was already down but
|
|
||||||
// we don't know that info. as a result we'll never generate
|
|
||||||
// key repeat events.
|
|
||||||
|
|
||||||
// handle the message
|
|
||||||
if (keyboardHookHandler(wParam, lParam)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CallNextHookEx(g_keyboardLL, code, wParam, lParam);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
|
||||||
// low-level mouse hook -- this allows us to capture and handle mouse
|
|
||||||
// events very early. the earlier the better.
|
|
||||||
//
|
|
||||||
|
|
||||||
static
|
|
||||||
LRESULT CALLBACK
|
|
||||||
mouseLLHook(int code, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
if (code >= 0) {
|
|
||||||
// decode the message
|
|
||||||
MSLLHOOKSTRUCT* info = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
|
|
||||||
SInt32 x = static_cast<SInt32>(info->pt.x);
|
|
||||||
SInt32 y = static_cast<SInt32>(info->pt.y);
|
|
||||||
SInt32 w = static_cast<SInt16>(HIWORD(info->mouseData));
|
|
||||||
|
|
||||||
// handle the message
|
|
||||||
if (mouseHookHandler(wParam, x, y, w)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CallNextHookEx(g_mouseLL, code, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// external functions
|
|
||||||
//
|
|
||||||
|
|
||||||
BOOL WINAPI
|
|
||||||
DllMain(HINSTANCE instance, DWORD reason, LPVOID)
|
|
||||||
{
|
|
||||||
if (reason == DLL_PROCESS_ATTACH) {
|
|
||||||
DisableThreadLibraryCalls(instance);
|
|
||||||
if (g_processID == 0) {
|
|
||||||
g_hinstance = instance;
|
|
||||||
g_processID = GetCurrentProcessId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (reason == DLL_PROCESS_DETACH) {
|
|
||||||
if (g_processID == GetCurrentProcessId()) {
|
|
||||||
uninstall();
|
|
||||||
uninstallScreenSaver();
|
|
||||||
g_processID = 0;
|
|
||||||
g_hinstance = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
BOOL WINAPI _DllMainCRTStartup(
|
|
||||||
HINSTANCE instance, DWORD reason, LPVOID lpreserved)
|
|
||||||
{
|
|
||||||
return DllMain(instance, reason, lpreserved);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
init(DWORD threadID)
|
|
||||||
{
|
|
||||||
assert(g_hinstance != NULL);
|
|
||||||
|
|
||||||
// try to open process that last called init() to see if it's
|
|
||||||
// still running or if it died without cleaning up.
|
|
||||||
if (g_processID != 0 && g_processID != GetCurrentProcessId()) {
|
|
||||||
HANDLE process = OpenProcess(STANDARD_RIGHTS_REQUIRED,
|
|
||||||
FALSE, g_processID);
|
|
||||||
if (process != NULL) {
|
|
||||||
// old process (probably) still exists so refuse to
|
|
||||||
// reinitialize this DLL (and thus steal it from the
|
|
||||||
// old process).
|
|
||||||
int result = CloseHandle(process);
|
|
||||||
if (result == false) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up after old process. the system should've already
|
|
||||||
// removed the hooks so we just need to reset our state.
|
|
||||||
g_hinstance = GetModuleHandle(_T("synwinhk"));
|
|
||||||
g_processID = GetCurrentProcessId();
|
|
||||||
g_threadID = 0;
|
|
||||||
g_getMessage = NULL;
|
|
||||||
g_keyboardLL = NULL;
|
|
||||||
g_mouseLL = NULL;
|
|
||||||
g_screenSaver = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// save thread id. we'll post messages to this thread's
|
|
||||||
// message queue.
|
|
||||||
g_threadID = threadID;
|
|
||||||
|
|
||||||
// set defaults
|
|
||||||
g_mode = kHOOK_DISABLE;
|
|
||||||
g_zoneSides = 0;
|
|
||||||
g_zoneSize = 0;
|
|
||||||
g_xScreen = 0;
|
|
||||||
g_yScreen = 0;
|
|
||||||
g_wScreen = 0;
|
|
||||||
g_hScreen = 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
cleanup(void)
|
|
||||||
{
|
|
||||||
assert(g_hinstance != NULL);
|
|
||||||
|
|
||||||
if (g_processID == GetCurrentProcessId()) {
|
|
||||||
g_threadID = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
EHookResult
|
|
||||||
install()
|
|
||||||
{
|
|
||||||
assert(g_hinstance != NULL);
|
|
||||||
assert(g_getMessage == NULL || g_screenSaver);
|
|
||||||
|
|
||||||
// must be initialized
|
|
||||||
if (g_threadID == 0) {
|
|
||||||
return kHOOK_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// discard old dead keys
|
|
||||||
g_deadVirtKey = 0;
|
|
||||||
g_deadLParam = 0;
|
|
||||||
|
|
||||||
// reset fake input flag
|
|
||||||
g_fakeInput = false;
|
|
||||||
|
|
||||||
// install low-level hooks. we require that they both get installed.
|
|
||||||
g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL,
|
|
||||||
&mouseLLHook,
|
|
||||||
g_hinstance,
|
|
||||||
0);
|
|
||||||
#if !NO_GRAB_KEYBOARD
|
|
||||||
g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL,
|
|
||||||
&keyboardLLHook,
|
|
||||||
g_hinstance,
|
|
||||||
0);
|
|
||||||
if (g_mouseLL == NULL || g_keyboardLL == NULL) {
|
|
||||||
if (g_keyboardLL != NULL) {
|
|
||||||
UnhookWindowsHookEx(g_keyboardLL);
|
|
||||||
g_keyboardLL = NULL;
|
|
||||||
}
|
|
||||||
if (g_mouseLL != NULL) {
|
|
||||||
UnhookWindowsHookEx(g_mouseLL);
|
|
||||||
g_mouseLL = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// check that we got all the hooks we wanted
|
|
||||||
if ((g_mouseLL == NULL) ||
|
|
||||||
#if !NO_GRAB_KEYBOARD
|
|
||||||
(g_keyboardLL == NULL)
|
|
||||||
#endif
|
|
||||||
) {
|
|
||||||
uninstall();
|
|
||||||
return kHOOK_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_keyboardLL != NULL || g_mouseLL != NULL) {
|
|
||||||
g_hookThread = GetCurrentThreadId();
|
|
||||||
return kHOOK_OKAY_LL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return kHOOK_OKAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
uninstall(void)
|
|
||||||
{
|
|
||||||
assert(g_hinstance != NULL);
|
|
||||||
|
|
||||||
// discard old dead keys
|
|
||||||
g_deadVirtKey = 0;
|
|
||||||
g_deadLParam = 0;
|
|
||||||
|
|
||||||
// uninstall hooks
|
|
||||||
if (g_keyboardLL != NULL) {
|
|
||||||
UnhookWindowsHookEx(g_keyboardLL);
|
|
||||||
g_keyboardLL = NULL;
|
|
||||||
}
|
|
||||||
if (g_mouseLL != NULL) {
|
|
||||||
UnhookWindowsHookEx(g_mouseLL);
|
|
||||||
g_mouseLL = NULL;
|
|
||||||
}
|
|
||||||
if (g_getMessage != NULL && !g_screenSaver) {
|
|
||||||
UnhookWindowsHookEx(g_getMessage);
|
|
||||||
g_getMessage = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
installScreenSaver(void)
|
|
||||||
{
|
|
||||||
assert(g_hinstance != NULL);
|
|
||||||
|
|
||||||
// must be initialized
|
|
||||||
if (g_threadID == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate screen saver messages
|
|
||||||
g_screenSaver = true;
|
|
||||||
|
|
||||||
// install hook unless it's already installed
|
|
||||||
if (g_getMessage == NULL) {
|
|
||||||
g_getMessage = SetWindowsHookEx(WH_GETMESSAGE,
|
|
||||||
&getMessageHook,
|
|
||||||
g_hinstance,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (g_getMessage != NULL) ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
uninstallScreenSaver(void)
|
|
||||||
{
|
|
||||||
assert(g_hinstance != NULL);
|
|
||||||
|
|
||||||
// uninstall hook unless the mouse wheel hook is installed
|
|
||||||
if (g_getMessage != NULL) {
|
|
||||||
UnhookWindowsHookEx(g_getMessage);
|
|
||||||
g_getMessage = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// screen saver hook is no longer installed
|
|
||||||
g_screenSaver = false;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setSides(UInt32 sides)
|
|
||||||
{
|
|
||||||
g_zoneSides = sides;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize)
|
|
||||||
{
|
|
||||||
g_zoneSize = jumpZoneSize;
|
|
||||||
g_xScreen = x;
|
|
||||||
g_yScreen = y;
|
|
||||||
g_wScreen = w;
|
|
||||||
g_hScreen = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setMode(EHookMode mode)
|
|
||||||
{
|
|
||||||
if (mode == g_mode) {
|
|
||||||
// no change
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
g_mode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -52,7 +52,7 @@ protected:
|
||||||
MSWindowsDesks* newDesks(IEventQueue* eventQueue)
|
MSWindowsDesks* newDesks(IEventQueue* eventQueue)
|
||||||
{
|
{
|
||||||
return new MSWindowsDesks(
|
return new MSWindowsDesks(
|
||||||
true, false, m_hook.getInstance(), m_screensaver, eventQueue,
|
true, false, m_screensaver, eventQueue,
|
||||||
new TMethodJob<MSWindowsKeyStateTests>(
|
new TMethodJob<MSWindowsKeyStateTests>(
|
||||||
this, &MSWindowsKeyStateTests::updateKeysCB), false);
|
this, &MSWindowsKeyStateTests::updateKeysCB), false);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue