1634 lines
44 KiB
C++
1634 lines
44 KiB
C++
/*
|
|
* synergy -- mouse and keyboard sharing utility
|
|
* 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 COPYING that should have accompanied this file.
|
|
*
|
|
* This package is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include "CMSWindowsPrimaryScreen.h"
|
|
#include "CMSWindowsScreen.h"
|
|
#include "IPrimaryScreenReceiver.h"
|
|
#include "XScreen.h"
|
|
#include "CLog.h"
|
|
#include "CArch.h"
|
|
#include "CArchMiscWindows.h"
|
|
#include <cstring>
|
|
|
|
// X button stuff
|
|
#if !defined(WM_XBUTTONDOWN)
|
|
#define WM_XBUTTONDOWN 0x020B
|
|
#define WM_XBUTTONUP 0x020C
|
|
#define WM_XBUTTONDBLCLK 0x020D
|
|
#define WM_NCXBUTTONDOWN 0x00AB
|
|
#define WM_NCXBUTTONUP 0x00AC
|
|
#define WM_NCXBUTTONDBLCLK 0x00AD
|
|
#define MOUSEEVENTF_XDOWN 0x0100
|
|
#define MOUSEEVENTF_XUP 0x0200
|
|
#define XBUTTON1 0x0001
|
|
#define XBUTTON2 0x0002
|
|
#endif
|
|
|
|
//
|
|
// map virtual key id to a name
|
|
//
|
|
|
|
static const char* g_buttonToName[] = {
|
|
"button 0",
|
|
"Left Button",
|
|
"Middle Button",
|
|
"Right Button",
|
|
"X Button 1",
|
|
"X Button 2"
|
|
};
|
|
static const char* g_vkToName[] = {
|
|
"vk 0x00",
|
|
"Left Button",
|
|
"Right Button",
|
|
"VK_CANCEL",
|
|
"Middle Button",
|
|
"vk 0x05",
|
|
"vk 0x06",
|
|
"vk 0x07",
|
|
"VK_BACK",
|
|
"VK_TAB",
|
|
"vk 0x0a",
|
|
"vk 0x0b",
|
|
"VK_CLEAR",
|
|
"VK_RETURN",
|
|
"vk 0x0e",
|
|
"vk 0x0f",
|
|
"VK_SHIFT",
|
|
"VK_CONTROL",
|
|
"VK_MENU",
|
|
"VK_PAUSE",
|
|
"VK_CAPITAL",
|
|
"VK_KANA",
|
|
"vk 0x16",
|
|
"VK_JUNJA",
|
|
"VK_FINAL",
|
|
"VK_KANJI",
|
|
"vk 0x1a",
|
|
"VK_ESCAPE",
|
|
"VK_CONVERT",
|
|
"VK_NONCONVERT",
|
|
"VK_ACCEPT",
|
|
"VK_MODECHANGE",
|
|
"VK_SPACE",
|
|
"VK_PRIOR",
|
|
"VK_NEXT",
|
|
"VK_END",
|
|
"VK_HOME",
|
|
"VK_LEFT",
|
|
"VK_UP",
|
|
"VK_RIGHT",
|
|
"VK_DOWN",
|
|
"VK_SELECT",
|
|
"VK_PRINT",
|
|
"VK_EXECUTE",
|
|
"VK_SNAPSHOT",
|
|
"VK_INSERT",
|
|
"VK_DELETE",
|
|
"VK_HELP",
|
|
"VK_0",
|
|
"VK_1",
|
|
"VK_2",
|
|
"VK_3",
|
|
"VK_4",
|
|
"VK_5",
|
|
"VK_6",
|
|
"VK_7",
|
|
"VK_8",
|
|
"VK_9",
|
|
"vk 0x3a",
|
|
"vk 0x3b",
|
|
"vk 0x3c",
|
|
"vk 0x3d",
|
|
"vk 0x3e",
|
|
"vk 0x3f",
|
|
"vk 0x40",
|
|
"VK_A",
|
|
"VK_B",
|
|
"VK_C",
|
|
"VK_D",
|
|
"VK_E",
|
|
"VK_F",
|
|
"VK_G",
|
|
"VK_H",
|
|
"VK_I",
|
|
"VK_J",
|
|
"VK_K",
|
|
"VK_L",
|
|
"VK_M",
|
|
"VK_N",
|
|
"VK_O",
|
|
"VK_P",
|
|
"VK_Q",
|
|
"VK_R",
|
|
"VK_S",
|
|
"VK_T",
|
|
"VK_U",
|
|
"VK_V",
|
|
"VK_W",
|
|
"VK_X",
|
|
"VK_Y",
|
|
"VK_Z",
|
|
"VK_LWIN",
|
|
"VK_RWIN",
|
|
"VK_APPS",
|
|
"vk 0x5e",
|
|
"vk 0x5f",
|
|
"VK_NUMPAD0",
|
|
"VK_NUMPAD1",
|
|
"VK_NUMPAD2",
|
|
"VK_NUMPAD3",
|
|
"VK_NUMPAD4",
|
|
"VK_NUMPAD5",
|
|
"VK_NUMPAD6",
|
|
"VK_NUMPAD7",
|
|
"VK_NUMPAD8",
|
|
"VK_NUMPAD9",
|
|
"VK_MULTIPLY",
|
|
"VK_ADD",
|
|
"VK_SEPARATOR",
|
|
"VK_SUBTRACT",
|
|
"VK_DECIMAL",
|
|
"VK_DIVIDE",
|
|
"VK_F1",
|
|
"VK_F2",
|
|
"VK_F3",
|
|
"VK_F4",
|
|
"VK_F5",
|
|
"VK_F6",
|
|
"VK_F7",
|
|
"VK_F8",
|
|
"VK_F9",
|
|
"VK_F10",
|
|
"VK_F11",
|
|
"VK_F12",
|
|
"VK_F13",
|
|
"VK_F14",
|
|
"VK_F15",
|
|
"VK_F16",
|
|
"VK_F17",
|
|
"VK_F18",
|
|
"VK_F19",
|
|
"VK_F20",
|
|
"VK_F21",
|
|
"VK_F22",
|
|
"VK_F23",
|
|
"VK_F24",
|
|
"vk 0x88",
|
|
"vk 0x89",
|
|
"vk 0x8a",
|
|
"vk 0x8b",
|
|
"vk 0x8c",
|
|
"vk 0x8d",
|
|
"vk 0x8e",
|
|
"vk 0x8f",
|
|
"VK_NUMLOCK",
|
|
"VK_SCROLL",
|
|
"vk 0x92",
|
|
"vk 0x93",
|
|
"vk 0x94",
|
|
"vk 0x95",
|
|
"vk 0x96",
|
|
"vk 0x97",
|
|
"vk 0x98",
|
|
"vk 0x99",
|
|
"vk 0x9a",
|
|
"vk 0x9b",
|
|
"vk 0x9c",
|
|
"vk 0x9d",
|
|
"vk 0x9e",
|
|
"vk 0x9f",
|
|
"VK_LSHIFT",
|
|
"VK_RSHIFT",
|
|
"VK_LCONTROL",
|
|
"VK_RCONTROL",
|
|
"VK_LMENU",
|
|
"VK_RMENU",
|
|
"VK_BROWSER_BACK",
|
|
"VK_BROWSER_FORWARD",
|
|
"VK_BROWSER_REFRESH",
|
|
"VK_BROWSER_STOP",
|
|
"VK_BROWSER_SEARCH",
|
|
"VK_BROWSER_FAVORITES",
|
|
"VK_BROWSER_HOME",
|
|
"VK_VOLUME_MUTE",
|
|
"VK_VOLUME_DOWN",
|
|
"VK_VOLUME_UP",
|
|
"VK_MEDIA_NEXT_TRACK",
|
|
"VK_MEDIA_PREV_TRACK",
|
|
"VK_MEDIA_STOP",
|
|
"VK_MEDIA_PLAY_PAUSE",
|
|
"VK_LAUNCH_MAIL",
|
|
"VK_LAUNCH_MEDIA_SELECT",
|
|
"VK_LAUNCH_APP1",
|
|
"VK_LAUNCH_APP2",
|
|
"vk 0xb8",
|
|
"vk 0xb9",
|
|
"vk 0xba",
|
|
"vk 0xbb",
|
|
"vk 0xbc",
|
|
"vk 0xbd",
|
|
"vk 0xbe",
|
|
"vk 0xbf",
|
|
"vk 0xc0",
|
|
"vk 0xc1",
|
|
"vk 0xc2",
|
|
"vk 0xc3",
|
|
"vk 0xc4",
|
|
"vk 0xc5",
|
|
"vk 0xc6",
|
|
"vk 0xc7",
|
|
"vk 0xc8",
|
|
"vk 0xc9",
|
|
"vk 0xca",
|
|
"vk 0xcb",
|
|
"vk 0xcc",
|
|
"vk 0xcd",
|
|
"vk 0xce",
|
|
"vk 0xcf",
|
|
"vk 0xd0",
|
|
"vk 0xd1",
|
|
"vk 0xd2",
|
|
"vk 0xd3",
|
|
"vk 0xd4",
|
|
"vk 0xd5",
|
|
"vk 0xd6",
|
|
"vk 0xd7",
|
|
"vk 0xd8",
|
|
"vk 0xd9",
|
|
"vk 0xda",
|
|
"vk 0xdb",
|
|
"vk 0xdc",
|
|
"vk 0xdd",
|
|
"vk 0xde",
|
|
"vk 0xdf",
|
|
"vk 0xe0",
|
|
"vk 0xe1",
|
|
"vk 0xe2",
|
|
"vk 0xe3",
|
|
"vk 0xe4",
|
|
"VK_PROCESSKEY",
|
|
"vk 0xe6",
|
|
"vk 0xe7",
|
|
"vk 0xe8",
|
|
"vk 0xe9",
|
|
"vk 0xea",
|
|
"vk 0xeb",
|
|
"vk 0xec",
|
|
"vk 0xed",
|
|
"vk 0xee",
|
|
"vk 0xef",
|
|
"vk 0xf0",
|
|
"vk 0xf1",
|
|
"vk 0xf2",
|
|
"vk 0xf3",
|
|
"vk 0xf4",
|
|
"vk 0xf5",
|
|
"VK_ATTN",
|
|
"VK_CRSEL",
|
|
"VK_EXSEL",
|
|
"VK_EREOF",
|
|
"VK_PLAY",
|
|
"VK_ZOOM",
|
|
"VK_NONAME",
|
|
"VK_PA1",
|
|
"VK_OEM_CLEAR",
|
|
"vk 0xff"
|
|
};
|
|
|
|
//
|
|
// CMSWindowsPrimaryScreen
|
|
//
|
|
|
|
CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen(
|
|
IScreenReceiver* receiver,
|
|
IPrimaryScreenReceiver* primaryReceiver) :
|
|
CPrimaryScreen(receiver),
|
|
m_receiver(primaryReceiver),
|
|
m_is95Family(CArchMiscWindows::isWindows95Family()),
|
|
m_threadID(0),
|
|
m_mark(0),
|
|
m_markReceived(0),
|
|
m_lowLevel(false),
|
|
m_cursorThread(0)
|
|
{
|
|
assert(m_receiver != NULL);
|
|
|
|
// load the hook library
|
|
m_hookLibrary = LoadLibrary("synrgyhk");
|
|
if (m_hookLibrary == NULL) {
|
|
LOG((CLOG_ERR "Failed to load hook library; synrgyhk.dll is missing"));
|
|
throw XScreenOpenFailure();
|
|
}
|
|
m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides");
|
|
m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone");
|
|
m_setRelay = (SetRelayFunc)GetProcAddress(m_hookLibrary, "setRelay");
|
|
m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install");
|
|
m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall");
|
|
m_init = (InitFunc)GetProcAddress(m_hookLibrary, "init");
|
|
m_cleanup = (CleanupFunc)GetProcAddress(m_hookLibrary, "cleanup");
|
|
if (m_setSides == NULL ||
|
|
m_setZone == NULL ||
|
|
m_setRelay == NULL ||
|
|
m_install == NULL ||
|
|
m_uninstall == NULL ||
|
|
m_init == NULL ||
|
|
m_cleanup == NULL) {
|
|
LOG((CLOG_ERR "Invalid hook library; use a newer synrgyhk.dll"));
|
|
FreeLibrary(m_hookLibrary);
|
|
throw XScreenOpenFailure();
|
|
}
|
|
|
|
// create screen
|
|
m_screen = new CMSWindowsScreen(receiver, this);
|
|
}
|
|
|
|
CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen()
|
|
{
|
|
assert(m_hookLibrary != NULL);
|
|
|
|
delete m_screen;
|
|
FreeLibrary(m_hookLibrary);
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides)
|
|
{
|
|
m_setSides(activeSides);
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
|
|
{
|
|
// warp mouse
|
|
warpCursorNoFlush(x, y);
|
|
|
|
// remove all input events before and including warp
|
|
MSG msg;
|
|
while (PeekMessage(&msg, NULL, SYNERGY_MSG_INPUT_FIRST,
|
|
SYNERGY_MSG_INPUT_LAST, PM_REMOVE)) {
|
|
// do nothing
|
|
}
|
|
|
|
// save position as last position
|
|
m_x = x;
|
|
m_y = y;
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::resetOptions()
|
|
{
|
|
// no options
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::setOptions(const COptionsList& /*options*/)
|
|
{
|
|
// no options
|
|
}
|
|
|
|
UInt32
|
|
CMSWindowsPrimaryScreen::addOneShotTimer(double timeout)
|
|
{
|
|
return m_screen->addOneShotTimer(timeout);
|
|
}
|
|
|
|
KeyModifierMask
|
|
CMSWindowsPrimaryScreen::getToggleMask() const
|
|
{
|
|
KeyModifierMask mask = 0;
|
|
|
|
// get key state from our shadow state
|
|
if ((m_keys[VK_CAPITAL] & 0x01) != 0)
|
|
mask |= KeyModifierCapsLock;
|
|
if ((m_keys[VK_NUMLOCK] & 0x01) != 0)
|
|
mask |= KeyModifierNumLock;
|
|
if ((m_keys[VK_SCROLL] & 0x01) != 0)
|
|
mask |= KeyModifierScrollLock;
|
|
|
|
return mask;
|
|
}
|
|
|
|
bool
|
|
CMSWindowsPrimaryScreen::isLockedToScreen() const
|
|
{
|
|
// use shadow keyboard state in m_keys and m_buttons
|
|
for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) {
|
|
if ((m_buttons[i] & 0x80) != 0) {
|
|
LOG((CLOG_DEBUG "locked by \"%s\"", g_buttonToName[i]));
|
|
return true;
|
|
}
|
|
}
|
|
for (UInt32 i = 0; i < sizeof(m_keys) / sizeof(m_keys[0]); ++i) {
|
|
if ((m_keys[i] & 0x80) != 0) {
|
|
LOG((CLOG_DEBUG "locked by \"%s\"", g_vkToName[i]));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// not locked
|
|
return false;
|
|
}
|
|
|
|
IScreen*
|
|
CMSWindowsPrimaryScreen::getScreen() const
|
|
{
|
|
return m_screen;
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onScreensaver(bool activated)
|
|
{
|
|
m_receiver->onScreensaver(activated);
|
|
}
|
|
|
|
bool
|
|
CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
|
|
{
|
|
assert(event != NULL);
|
|
|
|
const MSG* msg = &event->m_msg;
|
|
|
|
// check if windows key is up but we think it's down. if so then
|
|
// synthesize a key release for it. we have to do this because
|
|
// if the user presses and releases a windows key without pressing
|
|
// any other key when its down then windows will eat the key
|
|
// release. if we don't detect that an synthesize the release
|
|
// then the user will be locked to the screen and the client won't
|
|
// take the usual windows key release action (which on windows is
|
|
// to show the start menu).
|
|
//
|
|
// we can use GetKeyState() to check the state of the windows keys
|
|
// because, event though the key release is not reported to us,
|
|
// the event is processed and the keyboard state updated by the
|
|
// system. since the key could go up at any time we'll check the
|
|
// state on every event. only check on windows 95 family since
|
|
// NT family reports the key release as usual. obviously we skip
|
|
// this if the event is for the windows key itself.
|
|
if (m_is95Family) {
|
|
if ((m_keys[VK_LWIN] & 0x80) != 0 &&
|
|
(GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 &&
|
|
!(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_LWIN)) {
|
|
// compute appropriate parameters for fake event
|
|
WPARAM wParam = VK_LWIN;
|
|
LPARAM lParam = 0xc1000000;
|
|
lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24));
|
|
|
|
// process as if it were a key up
|
|
KeyModifierMask mask;
|
|
KeyButton button = static_cast<KeyButton>(
|
|
(lParam & 0x00ff0000u) >> 16);
|
|
const KeyID key = mapKey(wParam, lParam, &mask);
|
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
m_receiver->onKeyUp(key, mask, button);
|
|
updateKey(wParam, false);
|
|
}
|
|
if ((m_keys[VK_RWIN] & 0x80) != 0 &&
|
|
(GetAsyncKeyState(VK_RWIN) & 0x8000) == 0 &&
|
|
!(msg->message == SYNERGY_MSG_KEY && msg->wParam == VK_RWIN)) {
|
|
// compute appropriate parameters for fake event
|
|
WPARAM wParam = VK_RWIN;
|
|
LPARAM lParam = 0xc1000000;
|
|
lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24));
|
|
|
|
// process as if it were a key up
|
|
KeyModifierMask mask;
|
|
KeyButton button = static_cast<KeyButton>(
|
|
(lParam & 0x00ff0000u) >> 16);
|
|
const KeyID key = mapKey(wParam, lParam, &mask);
|
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
m_receiver->onKeyUp(key, mask, button);
|
|
updateKey(wParam, false);
|
|
}
|
|
}
|
|
|
|
// handle event
|
|
switch (msg->message) {
|
|
case SYNERGY_MSG_MARK:
|
|
m_markReceived = msg->wParam;
|
|
return true;
|
|
|
|
case SYNERGY_MSG_KEY:
|
|
// ignore message if posted prior to last mark change
|
|
if (!ignore()) {
|
|
KeyModifierMask mask;
|
|
const KeyID key = mapKey(msg->wParam, msg->lParam, &mask);
|
|
KeyButton button = static_cast<KeyButton>(
|
|
(msg->lParam & 0x00ff0000u) >> 16);
|
|
if (key != kKeyNone && key != kKeyMultiKey) {
|
|
if ((msg->lParam & 0x80000000) == 0) {
|
|
// key press
|
|
const bool wasDown = ((msg->lParam & 0x40000000) != 0);
|
|
const SInt32 repeat = (SInt32)(msg->lParam & 0xffff);
|
|
if (repeat >= 2 || wasDown) {
|
|
LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button));
|
|
m_receiver->onKeyRepeat(key, mask, repeat, button);
|
|
}
|
|
else {
|
|
LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
m_receiver->onKeyDown(key, mask, button);
|
|
}
|
|
}
|
|
else {
|
|
// key release. if the key isn't down according to
|
|
// our table then we never got the key press event
|
|
// for it. if it's not a modifier key then we'll
|
|
// synthesize the press first. only do this on
|
|
// the windows 95 family, which eats certain special
|
|
// keys like alt+tab, ctrl+esc, etc.
|
|
if (m_is95Family && !isModifier(msg->wParam) &&
|
|
(m_keys[msg->wParam] & 0x80) == 0) {
|
|
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
m_receiver->onKeyDown(key, mask, button);
|
|
updateKey(msg->wParam, true);
|
|
}
|
|
|
|
// do key up
|
|
LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
m_receiver->onKeyUp(key, mask, button);
|
|
}
|
|
}
|
|
else {
|
|
LOG((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", msg->wParam, msg->lParam));
|
|
}
|
|
}
|
|
|
|
// keep our shadow key state up to date
|
|
updateKey(msg->wParam, ((msg->lParam & 0x80000000) == 0));
|
|
|
|
return true;
|
|
|
|
case SYNERGY_MSG_MOUSE_BUTTON: {
|
|
// get which button
|
|
bool pressed = false;
|
|
const ButtonID button = mapButton(msg->wParam, msg->lParam);
|
|
|
|
// ignore message if posted prior to last mark change
|
|
if (!ignore()) {
|
|
switch (msg->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_NCLBUTTONDOWN:
|
|
case WM_NCMBUTTONDOWN:
|
|
case WM_NCRBUTTONDOWN:
|
|
case WM_NCXBUTTONDOWN:
|
|
case WM_NCLBUTTONDBLCLK:
|
|
case WM_NCMBUTTONDBLCLK:
|
|
case WM_NCRBUTTONDBLCLK:
|
|
case WM_NCXBUTTONDBLCLK:
|
|
LOG((CLOG_DEBUG1 "event: button press button=%d", button));
|
|
if (button != kButtonNone) {
|
|
m_receiver->onMouseDown(button);
|
|
}
|
|
pressed = true;
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
case WM_XBUTTONUP:
|
|
case WM_NCLBUTTONUP:
|
|
case WM_NCMBUTTONUP:
|
|
case WM_NCRBUTTONUP:
|
|
case WM_NCXBUTTONUP:
|
|
LOG((CLOG_DEBUG1 "event: button release button=%d", button));
|
|
if (button != kButtonNone) {
|
|
m_receiver->onMouseUp(button);
|
|
}
|
|
pressed = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// keep our shadow key state up to date
|
|
if (button >= kButtonLeft && button <= kButtonExtra0 + 1) {
|
|
if (pressed) {
|
|
m_buttons[button] |= 0x80;
|
|
}
|
|
else {
|
|
m_buttons[button] &= ~0x80;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
case SYNERGY_MSG_MOUSE_WHEEL:
|
|
// ignore message if posted prior to last mark change
|
|
if (!ignore()) {
|
|
LOG((CLOG_DEBUG1 "event: button wheel delta=%d %d", msg->wParam, msg->lParam));
|
|
m_receiver->onMouseWheel(msg->wParam);
|
|
}
|
|
return true;
|
|
|
|
case SYNERGY_MSG_PRE_WARP:
|
|
{
|
|
// save position to compute delta of next motion
|
|
m_x = static_cast<SInt32>(msg->wParam);
|
|
m_y = static_cast<SInt32>(msg->lParam);
|
|
|
|
// we warped the mouse. discard events until we find the
|
|
// matching post warp event. see warpCursorNoFlush() for
|
|
// where the events are sent. we discard the matching
|
|
// post warp event and can be sure we've skipped the warp
|
|
// event.
|
|
MSG msg;
|
|
do {
|
|
GetMessage(&msg, NULL, SYNERGY_MSG_MOUSE_MOVE,
|
|
SYNERGY_MSG_POST_WARP);
|
|
} while (msg.message != SYNERGY_MSG_POST_WARP);
|
|
|
|
return true;
|
|
}
|
|
|
|
case SYNERGY_MSG_POST_WARP:
|
|
LOG((CLOG_WARN "unmatched post warp"));
|
|
return true;
|
|
|
|
case SYNERGY_MSG_MOUSE_MOVE:
|
|
// ignore message if posted prior to last mark change
|
|
if (!ignore()) {
|
|
// compute motion delta (relative to the last known
|
|
// mouse position)
|
|
SInt32 x = static_cast<SInt32>(msg->wParam) - m_x;
|
|
SInt32 y = static_cast<SInt32>(msg->lParam) - m_y;
|
|
|
|
// save position to compute delta of next motion
|
|
m_x = static_cast<SInt32>(msg->wParam);
|
|
m_y = static_cast<SInt32>(msg->lParam);
|
|
|
|
if (!isActive()) {
|
|
// motion on primary screen
|
|
if (x != 0 || y != 0) {
|
|
m_receiver->onMouseMovePrimary(m_x, m_y);
|
|
}
|
|
}
|
|
else {
|
|
// motion on secondary screen. warp mouse back to
|
|
// center.
|
|
if (x != 0 || y != 0) {
|
|
// back to center
|
|
warpCursorNoFlush(m_xCenter, m_yCenter);
|
|
|
|
// examine the motion. if it's about the distance
|
|
// from the center of the screen to an edge then
|
|
// it's probably a bogus motion that we want to
|
|
// ignore (see warpCursorNoFlush() for a further
|
|
// description).
|
|
static SInt32 bogusZoneSize = 10;
|
|
SInt32 x0, y0, w0, h0;
|
|
m_screen->getShape(x0, y0, w0, h0);
|
|
if (-x + bogusZoneSize > m_xCenter - x0 ||
|
|
x + bogusZoneSize > x0 + w0 - m_xCenter ||
|
|
-y + bogusZoneSize > m_yCenter - y0 ||
|
|
y + bogusZoneSize > y0 + h0 - m_yCenter) {
|
|
LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y));
|
|
}
|
|
else {
|
|
// send motion
|
|
m_receiver->onMouseMoveSecondary(x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
CMSWindowsPrimaryScreen::onEvent(CEvent* event)
|
|
{
|
|
assert(event != NULL);
|
|
|
|
const MSG& msg = event->m_msg;
|
|
switch (msg.message) {
|
|
case WM_DISPLAYCHANGE:
|
|
// recompute center pixel of primary screen
|
|
m_screen->getCursorCenter(m_xCenter, m_yCenter);
|
|
|
|
// warp mouse to center if active
|
|
if (isActive()) {
|
|
warpCursorToCenter();
|
|
}
|
|
|
|
// tell hook about resize if not active
|
|
else {
|
|
SInt32 x, y, w, h;
|
|
m_screen->getShape(x, y, w, h);
|
|
m_setZone(x, y, w, h, getJumpZoneSize());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onOneShotTimerExpired(UInt32 id)
|
|
{
|
|
m_receiver->onOneShotTimerExpired(id);
|
|
}
|
|
|
|
SInt32
|
|
CMSWindowsPrimaryScreen::getJumpZoneSize() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::postCreateWindow(HWND)
|
|
{
|
|
// install hooks
|
|
switch (m_install()) {
|
|
case kHOOK_FAILED:
|
|
// FIXME -- can't install hook so we won't work; report error
|
|
m_lowLevel = false;
|
|
break;
|
|
|
|
case kHOOK_OKAY:
|
|
m_lowLevel = false;
|
|
break;
|
|
|
|
case kHOOK_OKAY_LL:
|
|
m_lowLevel = true;
|
|
break;
|
|
}
|
|
|
|
if (!isActive()) {
|
|
// watch jump zones
|
|
m_setRelay(false);
|
|
|
|
// all messages prior to now are invalid
|
|
nextMark();
|
|
}
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::preDestroyWindow(HWND)
|
|
{
|
|
// uninstall hooks
|
|
m_uninstall();
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onAccessibleDesktop()
|
|
{
|
|
// get the current keyboard state
|
|
updateKeys();
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onPreMainLoop()
|
|
{
|
|
// must call mainLoop() from same thread as open()
|
|
assert(m_threadID == GetCurrentThreadId());
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onPreOpen()
|
|
{
|
|
// initialize hook library
|
|
m_threadID = GetCurrentThreadId();
|
|
if (m_init(m_threadID) == 0) {
|
|
LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?"));
|
|
throw XScreenOpenFailure();
|
|
}
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onPostOpen()
|
|
{
|
|
// get cursor info
|
|
m_screen->getCursorPos(m_x, m_y);
|
|
m_screen->getCursorCenter(m_xCenter, m_yCenter);
|
|
|
|
// set jump zones
|
|
SInt32 x, y, w, h;
|
|
m_screen->getShape(x, y, w, h);
|
|
m_setZone(x, y, w, h, getJumpZoneSize());
|
|
|
|
// initialize marks
|
|
m_mark = 0;
|
|
m_markReceived = 0;
|
|
nextMark();
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onPostClose()
|
|
{
|
|
m_cleanup();
|
|
m_threadID = 0;
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onPreEnter()
|
|
{
|
|
// show cursor if we hid it
|
|
if (m_cursorThread != 0) {
|
|
if (m_threadID != m_cursorThread) {
|
|
AttachThreadInput(m_threadID, m_cursorThread, TRUE);
|
|
}
|
|
ShowCursor(TRUE);
|
|
if (m_threadID != m_cursorThread) {
|
|
AttachThreadInput(m_threadID, m_cursorThread, FALSE);
|
|
}
|
|
m_cursorThread = 0;
|
|
}
|
|
|
|
// enable ctrl+alt+del, alt+tab, etc
|
|
if (m_is95Family) {
|
|
DWORD dummy = 0;
|
|
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0);
|
|
}
|
|
|
|
// watch jump zones
|
|
m_setRelay(false);
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onPostEnter()
|
|
{
|
|
// all messages prior to now are invalid
|
|
nextMark();
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onPreLeave()
|
|
{
|
|
// all messages prior to now are invalid
|
|
nextMark();
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::onPostLeave(bool success)
|
|
{
|
|
if (success) {
|
|
// relay all mouse and keyboard events
|
|
m_setRelay(true);
|
|
|
|
// disable ctrl+alt+del, alt+tab, etc
|
|
if (m_is95Family) {
|
|
DWORD dummy = 0;
|
|
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0);
|
|
}
|
|
|
|
// hide the cursor if using low level hooks
|
|
if (m_lowLevel) {
|
|
HWND hwnd = GetForegroundWindow();
|
|
m_cursorThread = GetWindowThreadProcessId(hwnd, NULL);
|
|
if (m_threadID != m_cursorThread) {
|
|
AttachThreadInput(m_threadID, m_cursorThread, TRUE);
|
|
}
|
|
ShowCursor(FALSE);
|
|
if (m_threadID != m_cursorThread) {
|
|
AttachThreadInput(m_threadID, m_cursorThread, FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::createWindow()
|
|
{
|
|
// open the desktop and the window
|
|
m_window = m_screen->openDesktop();
|
|
if (m_window == NULL) {
|
|
throw XScreenOpenFailure();
|
|
}
|
|
|
|
// we don't ever want our window to activate
|
|
EnableWindow(m_window, FALSE);
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::destroyWindow()
|
|
{
|
|
// close the desktop and the window
|
|
m_screen->closeDesktop();
|
|
}
|
|
|
|
bool
|
|
CMSWindowsPrimaryScreen::showWindow()
|
|
{
|
|
// we don't need a window to capture input but we need a window
|
|
// to hide the cursor when using low-level hooks. do not try to
|
|
// take the activation; we want the currently active window to
|
|
// stay active.
|
|
if (m_lowLevel) {
|
|
SetWindowPos(m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1,
|
|
SWP_NOACTIVATE);
|
|
ShowWindow(m_window, SW_SHOWNA);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::hideWindow()
|
|
{
|
|
// hide our window
|
|
if (m_lowLevel) {
|
|
ShowWindow(m_window, SW_HIDE);
|
|
}
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::warpCursorToCenter()
|
|
{
|
|
warpCursor(m_xCenter, m_yCenter);
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::warpCursorNoFlush(SInt32 x, SInt32 y)
|
|
{
|
|
// send an event that we can recognize before the mouse warp
|
|
PostThreadMessage(m_threadID, SYNERGY_MSG_PRE_WARP, x, y);
|
|
|
|
// warp mouse. hopefully this inserts a mouse motion event
|
|
// between the previous message and the following message.
|
|
SetCursorPos(x, y);
|
|
|
|
// yield the CPU. there's a race condition when warping:
|
|
// a hardware mouse event occurs
|
|
// the mouse hook is not called because that process doesn't have the CPU
|
|
// we send PRE_WARP, SetCursorPos(), send POST_WARP
|
|
// we process all of those events and update m_x, m_y
|
|
// we finish our time slice
|
|
// the hook is called
|
|
// the hook sends us a mouse event from the pre-warp position
|
|
// we get the CPU
|
|
// we compute a bogus warp
|
|
// we need the hook to process all mouse events that occur
|
|
// before we warp before we do the warp but i'm not sure how
|
|
// to guarantee that. yielding the CPU here may reduce the
|
|
// chance of undesired behavior. we'll also check for very
|
|
// large motions that look suspiciously like about half width
|
|
// or height of the screen.
|
|
ARCH->sleep(0.0);
|
|
|
|
// send an event that we can recognize after the mouse warp
|
|
PostThreadMessage(m_threadID, SYNERGY_MSG_POST_WARP, 0, 0);
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::nextMark()
|
|
{
|
|
// next mark
|
|
++m_mark;
|
|
|
|
// mark point in message queue where the mark was changed
|
|
PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0);
|
|
}
|
|
|
|
bool
|
|
CMSWindowsPrimaryScreen::ignore() const
|
|
{
|
|
return (m_mark != m_markReceived);
|
|
}
|
|
|
|
// map virtual keys to synergy key enumeration. use extended keyboard
|
|
// bit to distinguish some keys.
|
|
static const KeyID g_virtualKey[][2] =
|
|
{
|
|
/* 0x00 */ kKeyNone, kKeyNone, // reserved
|
|
/* 0x01 */ kKeyNone, kKeyNone, // VK_LBUTTON
|
|
/* 0x02 */ kKeyNone, kKeyNone, // VK_RBUTTON
|
|
/* 0x03 */ kKeyNone, kKeyBreak, // VK_CANCEL
|
|
/* 0x04 */ kKeyNone, kKeyNone, // VK_MBUTTON
|
|
/* 0x05 */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x06 */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x07 */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x08 */ kKeyBackSpace, kKeyNone, // VK_BACK
|
|
/* 0x09 */ kKeyTab, kKeyNone, // VK_TAB
|
|
/* 0x0a */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x0b */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x0c */ kKeyClear, kKeyClear, // VK_CLEAR
|
|
/* 0x0d */ kKeyReturn, kKeyKP_Enter, // VK_RETURN
|
|
/* 0x0e */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x0f */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x10 */ kKeyShift_L, kKeyShift_R, // VK_SHIFT
|
|
/* 0x11 */ kKeyControl_L, kKeyControl_R, // VK_CONTROL
|
|
/* 0x12 */ kKeyAlt_L, kKeyAlt_R, // VK_MENU
|
|
/* 0x13 */ kKeyPause, kKeyNone, // VK_PAUSE
|
|
/* 0x14 */ kKeyCapsLock, kKeyNone, // VK_CAPITAL
|
|
/* 0x15 */ kKeyNone, kKeyNone, // VK_KANA
|
|
/* 0x16 */ kKeyNone, kKeyNone, // VK_HANGUL
|
|
/* 0x17 */ kKeyNone, kKeyNone, // VK_JUNJA
|
|
/* 0x18 */ kKeyNone, kKeyNone, // VK_FINAL
|
|
/* 0x19 */ kKeyNone, kKeyNone, // VK_KANJI
|
|
/* 0x1a */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x1b */ kKeyEscape, kKeyNone, // VK_ESCAPE
|
|
/* 0x1c */ kKeyNone, kKeyNone, // VK_CONVERT
|
|
/* 0x1d */ kKeyNone, kKeyNone, // VK_NONCONVERT
|
|
/* 0x1e */ kKeyNone, kKeyNone, // VK_ACCEPT
|
|
/* 0x1f */ kKeyNone, kKeyNone, // VK_MODECHANGE
|
|
/* 0x20 */ 0x0020, kKeyNone, // VK_SPACE
|
|
/* 0x21 */ kKeyKP_PageUp, kKeyPageUp, // VK_PRIOR
|
|
/* 0x22 */ kKeyKP_PageDown, kKeyPageDown, // VK_NEXT
|
|
/* 0x23 */ kKeyKP_End, kKeyEnd, // VK_END
|
|
/* 0x24 */ kKeyKP_Home, kKeyHome, // VK_HOME
|
|
/* 0x25 */ kKeyKP_Left, kKeyLeft, // VK_LEFT
|
|
/* 0x26 */ kKeyKP_Up, kKeyUp, // VK_UP
|
|
/* 0x27 */ kKeyKP_Right, kKeyRight, // VK_RIGHT
|
|
/* 0x28 */ kKeyKP_Down, kKeyDown, // VK_DOWN
|
|
/* 0x29 */ kKeySelect, kKeySelect, // VK_SELECT
|
|
/* 0x2a */ kKeyNone, kKeyNone, // VK_PRINT
|
|
/* 0x2b */ kKeyExecute, kKeyExecute, // VK_EXECUTE
|
|
/* 0x2c */ kKeyPrint, kKeyPrint, // VK_SNAPSHOT
|
|
/* 0x2d */ kKeyKP_Insert, kKeyInsert, // VK_INSERT
|
|
/* 0x2e */ kKeyKP_Delete, kKeyDelete, // VK_DELETE
|
|
/* 0x2f */ kKeyHelp, kKeyHelp, // VK_HELP
|
|
/* 0x30 */ kKeyNone, kKeyNone, // VK_0
|
|
/* 0x31 */ kKeyNone, kKeyNone, // VK_1
|
|
/* 0x32 */ kKeyNone, kKeyNone, // VK_2
|
|
/* 0x33 */ kKeyNone, kKeyNone, // VK_3
|
|
/* 0x34 */ kKeyNone, kKeyNone, // VK_4
|
|
/* 0x35 */ kKeyNone, kKeyNone, // VK_5
|
|
/* 0x36 */ kKeyNone, kKeyNone, // VK_6
|
|
/* 0x37 */ kKeyNone, kKeyNone, // VK_7
|
|
/* 0x38 */ kKeyNone, kKeyNone, // VK_8
|
|
/* 0x39 */ kKeyNone, kKeyNone, // VK_9
|
|
/* 0x3a */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x3b */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x3c */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x3d */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x3e */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x3f */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x40 */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x41 */ kKeyNone, kKeyNone, // VK_A
|
|
/* 0x42 */ kKeyNone, kKeyNone, // VK_B
|
|
/* 0x43 */ kKeyNone, kKeyNone, // VK_C
|
|
/* 0x44 */ kKeyNone, kKeyNone, // VK_D
|
|
/* 0x45 */ kKeyNone, kKeyNone, // VK_E
|
|
/* 0x46 */ kKeyNone, kKeyNone, // VK_F
|
|
/* 0x47 */ kKeyNone, kKeyNone, // VK_G
|
|
/* 0x48 */ kKeyNone, kKeyNone, // VK_H
|
|
/* 0x49 */ kKeyNone, kKeyNone, // VK_I
|
|
/* 0x4a */ kKeyNone, kKeyNone, // VK_J
|
|
/* 0x4b */ kKeyNone, kKeyNone, // VK_K
|
|
/* 0x4c */ kKeyNone, kKeyNone, // VK_L
|
|
/* 0x4d */ kKeyNone, kKeyNone, // VK_M
|
|
/* 0x4e */ kKeyNone, kKeyNone, // VK_N
|
|
/* 0x4f */ kKeyNone, kKeyNone, // VK_O
|
|
/* 0x50 */ kKeyNone, kKeyNone, // VK_P
|
|
/* 0x51 */ kKeyNone, kKeyNone, // VK_Q
|
|
/* 0x52 */ kKeyNone, kKeyNone, // VK_R
|
|
/* 0x53 */ kKeyNone, kKeyNone, // VK_S
|
|
/* 0x54 */ kKeyNone, kKeyNone, // VK_T
|
|
/* 0x55 */ kKeyNone, kKeyNone, // VK_U
|
|
/* 0x56 */ kKeyNone, kKeyNone, // VK_V
|
|
/* 0x57 */ kKeyNone, kKeyNone, // VK_W
|
|
/* 0x58 */ kKeyNone, kKeyNone, // VK_X
|
|
/* 0x59 */ kKeyNone, kKeyNone, // VK_Y
|
|
/* 0x5a */ kKeyNone, kKeyNone, // VK_Z
|
|
/* 0x5b */ kKeyNone, kKeySuper_L, // VK_LWIN
|
|
/* 0x5c */ kKeyNone, kKeySuper_R, // VK_RWIN
|
|
/* 0x5d */ kKeyMenu, kKeyMenu, // VK_APPS
|
|
/* 0x5e */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x5f */ kKeyNone, kKeyNone, // undefined
|
|
/* 0x60 */ kKeyKP_0, kKeyNone, // VK_NUMPAD0
|
|
/* 0x61 */ kKeyKP_1, kKeyNone, // VK_NUMPAD1
|
|
/* 0x62 */ kKeyKP_2, kKeyNone, // VK_NUMPAD2
|
|
/* 0x63 */ kKeyKP_3, kKeyNone, // VK_NUMPAD3
|
|
/* 0x64 */ kKeyKP_4, kKeyNone, // VK_NUMPAD4
|
|
/* 0x65 */ kKeyKP_5, kKeyNone, // VK_NUMPAD5
|
|
/* 0x66 */ kKeyKP_6, kKeyNone, // VK_NUMPAD6
|
|
/* 0x67 */ kKeyKP_7, kKeyNone, // VK_NUMPAD7
|
|
/* 0x68 */ kKeyKP_8, kKeyNone, // VK_NUMPAD8
|
|
/* 0x69 */ kKeyKP_9, kKeyNone, // VK_NUMPAD9
|
|
/* 0x6a */ kKeyKP_Multiply, kKeyNone, // VK_MULTIPLY
|
|
/* 0x6b */ kKeyKP_Add, kKeyNone, // VK_ADD
|
|
/* 0x6c */ kKeyKP_Separator,kKeyKP_Separator,// VK_SEPARATOR
|
|
/* 0x6d */ kKeyKP_Subtract, kKeyNone, // VK_SUBTRACT
|
|
/* 0x6e */ kKeyKP_Decimal, kKeyNone, // VK_DECIMAL
|
|
/* 0x6f */ kKeyNone, kKeyKP_Divide, // VK_DIVIDE
|
|
/* 0x70 */ kKeyF1, kKeyNone, // VK_F1
|
|
/* 0x71 */ kKeyF2, kKeyNone, // VK_F2
|
|
/* 0x72 */ kKeyF3, kKeyNone, // VK_F3
|
|
/* 0x73 */ kKeyF4, kKeyNone, // VK_F4
|
|
/* 0x74 */ kKeyF5, kKeyNone, // VK_F5
|
|
/* 0x75 */ kKeyF6, kKeyNone, // VK_F6
|
|
/* 0x76 */ kKeyF7, kKeyNone, // VK_F7
|
|
/* 0x77 */ kKeyF8, kKeyNone, // VK_F8
|
|
/* 0x78 */ kKeyF9, kKeyNone, // VK_F9
|
|
/* 0x79 */ kKeyF10, kKeyNone, // VK_F10
|
|
/* 0x7a */ kKeyF11, kKeyNone, // VK_F11
|
|
/* 0x7b */ kKeyF12, kKeyNone, // VK_F12
|
|
/* 0x7c */ kKeyF13, kKeyF13, // VK_F13
|
|
/* 0x7d */ kKeyF14, kKeyF14, // VK_F14
|
|
/* 0x7e */ kKeyF15, kKeyF15, // VK_F15
|
|
/* 0x7f */ kKeyF16, kKeyF16, // VK_F16
|
|
/* 0x80 */ kKeyF17, kKeyF17, // VK_F17
|
|
/* 0x81 */ kKeyF18, kKeyF18, // VK_F18
|
|
/* 0x82 */ kKeyF19, kKeyF19, // VK_F19
|
|
/* 0x83 */ kKeyF20, kKeyF20, // VK_F20
|
|
/* 0x84 */ kKeyF21, kKeyF21, // VK_F21
|
|
/* 0x85 */ kKeyF22, kKeyF22, // VK_F22
|
|
/* 0x86 */ kKeyF23, kKeyF23, // VK_F23
|
|
/* 0x87 */ kKeyF24, kKeyF24, // VK_F24
|
|
/* 0x88 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x89 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x8a */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x8b */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x8c */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x8d */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x8e */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x8f */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x90 */ kKeyNumLock, kKeyNumLock, // VK_NUMLOCK
|
|
/* 0x91 */ kKeyScrollLock, kKeyNone, // VK_SCROLL
|
|
/* 0x92 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x93 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x94 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x95 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x96 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x97 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x98 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x99 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x9a */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x9b */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x9c */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x9d */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x9e */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0x9f */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xa0 */ kKeyShift_L, kKeyShift_L, // VK_LSHIFT
|
|
/* 0xa1 */ kKeyShift_R, kKeyShift_R, // VK_RSHIFT
|
|
/* 0xa2 */ kKeyControl_L, kKeyControl_L, // VK_LCONTROL
|
|
/* 0xa3 */ kKeyControl_R, kKeyControl_R, // VK_RCONTROL
|
|
/* 0xa4 */ kKeyAlt_L, kKeyAlt_L, // VK_LMENU
|
|
/* 0xa5 */ kKeyAlt_R, kKeyAlt_R, // VK_RMENU
|
|
/* 0xa6 */ kKeyNone, kKeyWWWBack, // VK_BROWSER_BACK
|
|
/* 0xa7 */ kKeyNone, kKeyWWWForward, // VK_BROWSER_FORWARD
|
|
/* 0xa8 */ kKeyNone, kKeyWWWRefresh, // VK_BROWSER_REFRESH
|
|
/* 0xa9 */ kKeyNone, kKeyWWWStop, // VK_BROWSER_STOP
|
|
/* 0xaa */ kKeyNone, kKeyWWWSearch, // VK_BROWSER_SEARCH
|
|
/* 0xab */ kKeyNone, kKeyWWWFavorites, // VK_BROWSER_FAVORITES
|
|
/* 0xac */ kKeyNone, kKeyWWWHome, // VK_BROWSER_HOME
|
|
/* 0xad */ kKeyNone, kKeyAudioMute, // VK_VOLUME_MUTE
|
|
/* 0xae */ kKeyNone, kKeyAudioDown, // VK_VOLUME_DOWN
|
|
/* 0xaf */ kKeyNone, kKeyAudioUp, // VK_VOLUME_UP
|
|
/* 0xb0 */ kKeyNone, kKeyAudioNext, // VK_MEDIA_NEXT_TRACK
|
|
/* 0xb1 */ kKeyNone, kKeyAudioPrev, // VK_MEDIA_PREV_TRACK
|
|
/* 0xb2 */ kKeyNone, kKeyAudioStop, // VK_MEDIA_STOP
|
|
/* 0xb3 */ kKeyNone, kKeyAudioPlay, // VK_MEDIA_PLAY_PAUSE
|
|
/* 0xb4 */ kKeyNone, kKeyAppMail, // VK_LAUNCH_MAIL
|
|
/* 0xb5 */ kKeyNone, kKeyAppMedia, // VK_LAUNCH_MEDIA_SELECT
|
|
/* 0xb6 */ kKeyNone, kKeyAppUser1, // VK_LAUNCH_APP1
|
|
/* 0xb7 */ kKeyNone, kKeyAppUser2, // VK_LAUNCH_APP2
|
|
/* 0xb8 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xb9 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xba */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xbb */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xbc */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xbd */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xbe */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xbf */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xc0 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xc1 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xc2 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xc3 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xc4 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xc5 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xc6 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xc7 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xc8 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xc9 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xca */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xcb */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xcc */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xcd */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xce */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xcf */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xd0 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xd1 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xd2 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xd3 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xd4 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xd5 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xd6 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xd7 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xd8 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xd9 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xda */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xdb */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xdc */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xdd */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xde */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xdf */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xe0 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xe1 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xe2 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xe3 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xe4 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xe5 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xe6 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xe7 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xe8 */ kKeyNone, kKeyNone, // unassigned
|
|
/* 0xe9 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xea */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xeb */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xec */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xed */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xee */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xef */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xf0 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xf1 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xf2 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xf3 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xf4 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xf5 */ kKeyNone, kKeyNone, // OEM specific
|
|
/* 0xf6 */ kKeyNone, kKeyNone, // VK_ATTN
|
|
/* 0xf7 */ kKeyNone, kKeyNone, // VK_CRSEL
|
|
/* 0xf8 */ kKeyNone, kKeyNone, // VK_EXSEL
|
|
/* 0xf9 */ kKeyNone, kKeyNone, // VK_EREOF
|
|
/* 0xfa */ kKeyNone, kKeyNone, // VK_PLAY
|
|
/* 0xfb */ kKeyNone, kKeyNone, // VK_ZOOM
|
|
/* 0xfc */ kKeyNone, kKeyNone, // reserved
|
|
/* 0xfd */ kKeyNone, kKeyNone, // VK_PA1
|
|
/* 0xfe */ kKeyNone, kKeyNone, // VK_OEM_CLEAR
|
|
/* 0xff */ kKeyNone, kKeyNone // reserved
|
|
};
|
|
|
|
KeyID
|
|
CMSWindowsPrimaryScreen::mapKey(
|
|
WPARAM vkCode,
|
|
LPARAM info,
|
|
KeyModifierMask* maskOut)
|
|
{
|
|
// note: known microsoft bugs
|
|
// Q72583 -- MapVirtualKey() maps keypad keys incorrectly
|
|
// 95,98: num pad vk code -> invalid scan code
|
|
// 95,98,NT4: num pad scan code -> bad vk code except
|
|
// SEPARATOR, MULTIPLY, SUBTRACT, ADD
|
|
|
|
assert(maskOut != NULL);
|
|
|
|
// map modifier key
|
|
KeyModifierMask mask = 0;
|
|
if (((m_keys[VK_LSHIFT] |
|
|
m_keys[VK_RSHIFT] |
|
|
m_keys[VK_SHIFT]) & 0x80) != 0) {
|
|
mask |= KeyModifierShift;
|
|
}
|
|
if (((m_keys[VK_LCONTROL] |
|
|
m_keys[VK_RCONTROL] |
|
|
m_keys[VK_CONTROL]) & 0x80) != 0) {
|
|
mask |= KeyModifierControl;
|
|
}
|
|
if ((m_keys[VK_RMENU] & 0x80) != 0) {
|
|
// right alt => AltGr on windows
|
|
mask |= KeyModifierModeSwitch;
|
|
}
|
|
else if (((m_keys[VK_LMENU] |
|
|
m_keys[VK_MENU]) & 0x80) != 0) {
|
|
mask |= KeyModifierAlt;
|
|
}
|
|
if (((m_keys[VK_LWIN] |
|
|
m_keys[VK_RWIN]) & 0x80) != 0) {
|
|
mask |= KeyModifierSuper;
|
|
}
|
|
if ((m_keys[VK_CAPITAL] & 0x01) != 0) {
|
|
mask |= KeyModifierCapsLock;
|
|
}
|
|
if ((m_keys[VK_NUMLOCK] & 0x01) != 0) {
|
|
mask |= KeyModifierNumLock;
|
|
}
|
|
if ((m_keys[VK_SCROLL] & 0x01) != 0) {
|
|
mask |= KeyModifierScrollLock;
|
|
}
|
|
// ctrl+alt => AltGr on windows
|
|
/* don't convert ctrl+alt to mode switch. if we do that then we can
|
|
* never send ctrl+alt+[key] from windows to some platform that
|
|
* doesn't treat ctrl+alt as mode switch (i.e. all other platforms).
|
|
* instead, let windows clients automatically treat ctrl+alt as
|
|
* AltGr and let other clients use ctrl+alt as is. the right alt
|
|
* key serves as a mode switch key.
|
|
if ((mask & (KeyModifierControl | KeyModifierAlt)) ==
|
|
(KeyModifierControl | KeyModifierAlt)) {
|
|
mask |= KeyModifierModeSwitch;
|
|
mask &= ~(KeyModifierControl | KeyModifierAlt);
|
|
}
|
|
*/
|
|
*maskOut = mask;
|
|
LOG((CLOG_DEBUG2 "key in vk=%d info=0x%08x mask=0x%04x", vkCode, info, mask));
|
|
|
|
// get the scan code and the extended keyboard flag
|
|
UINT scanCode = static_cast<UINT>((info & 0x00ff0000u) >> 16);
|
|
int extended = ((info & 0x01000000) == 0) ? 0 : 1;
|
|
LOG((CLOG_DEBUG1 "key vk=%d ext=%d scan=%d", vkCode, extended, scanCode));
|
|
|
|
// handle some keys via table lookup
|
|
KeyID id = g_virtualKey[vkCode][extended];
|
|
if (id != kKeyNone) {
|
|
return id;
|
|
}
|
|
|
|
// save the control state then clear it. ToAscii() maps ctrl+letter
|
|
// to the corresponding control code and ctrl+backspace to delete.
|
|
// we don't want that translation so we clear the control modifier
|
|
// state. however, if we want to simulate AltGr (which is ctrl+alt)
|
|
// then we must not clear it.
|
|
BYTE lControl = m_keys[VK_LCONTROL];
|
|
BYTE rControl = m_keys[VK_RCONTROL];
|
|
BYTE control = m_keys[VK_CONTROL];
|
|
BYTE lMenu = m_keys[VK_LMENU];
|
|
BYTE menu = m_keys[VK_MENU];
|
|
if ((mask & KeyModifierModeSwitch) == 0) {
|
|
m_keys[VK_LCONTROL] = 0;
|
|
m_keys[VK_RCONTROL] = 0;
|
|
m_keys[VK_CONTROL] = 0;
|
|
}
|
|
else {
|
|
m_keys[VK_LCONTROL] = 0x80;
|
|
m_keys[VK_CONTROL] = 0x80;
|
|
m_keys[VK_LMENU] = 0x80;
|
|
m_keys[VK_MENU] = 0x80;
|
|
}
|
|
|
|
// convert to ascii
|
|
WORD ascii;
|
|
int result = ToAscii(vkCode, scanCode, m_keys, &ascii, 0);
|
|
|
|
// restore control state
|
|
m_keys[VK_LCONTROL] = lControl;
|
|
m_keys[VK_RCONTROL] = rControl;
|
|
m_keys[VK_CONTROL] = control;
|
|
m_keys[VK_LMENU] = lMenu;
|
|
m_keys[VK_MENU] = menu;
|
|
|
|
// if result is less than zero then it was a dead key. leave it
|
|
// there.
|
|
if (result < 0) {
|
|
return kKeyMultiKey;
|
|
}
|
|
|
|
// if result is 1 then the key was succesfully converted
|
|
else if (result == 1) {
|
|
if (ascii >= 0x80) {
|
|
// character is not really ASCII. instead it's some
|
|
// character in the current ANSI code page. try to
|
|
// convert that to a Unicode character. if we fail
|
|
// then use the single byte character as is.
|
|
char src = static_cast<char>(ascii & 0xff);
|
|
wchar_t unicode;
|
|
if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED,
|
|
&src, 1, &unicode, 1) > 0) {
|
|
return static_cast<KeyID>(unicode);
|
|
}
|
|
}
|
|
return static_cast<KeyID>(ascii & 0x00ff);
|
|
}
|
|
|
|
// if result is 2 then a previous dead key could not be composed.
|
|
else if (result == 2) {
|
|
// if the two characters are the same and this is a key release
|
|
// then this event is the dead key being released. we put the
|
|
// dead key back in that case, otherwise we discard both key
|
|
// events because we can't compose the character. alternatively
|
|
// we could generate key events for both keys.
|
|
if (((ascii & 0xff00) >> 8) != (ascii & 0x00ff) ||
|
|
(info & 0x80000000) == 0) {
|
|
// cannot compose key
|
|
return kKeyNone;
|
|
}
|
|
|
|
// get the scan code of the dead key and the shift state
|
|
// required to generate it.
|
|
vkCode = VkKeyScan(static_cast<TCHAR>(ascii & 0x00ff));
|
|
|
|
// set shift state required to generate key
|
|
BYTE keys[256];
|
|
memset(keys, 0, sizeof(keys));
|
|
if (vkCode & 0x0100) {
|
|
keys[VK_SHIFT] = 0x80;
|
|
}
|
|
if (vkCode & 0x0200) {
|
|
keys[VK_CONTROL] = 0x80;
|
|
}
|
|
if (vkCode & 0x0400) {
|
|
keys[VK_MENU] = 0x80;
|
|
}
|
|
|
|
// strip shift state off of virtual key code
|
|
vkCode &= 0x00ff;
|
|
|
|
// get the scan code for the key
|
|
scanCode = MapVirtualKey(vkCode, 0);
|
|
|
|
// put it back
|
|
ToAscii(vkCode, scanCode, keys, &ascii, 0);
|
|
return kKeyMultiKey;
|
|
}
|
|
|
|
// cannot convert key
|
|
return kKeyNone;
|
|
}
|
|
|
|
ButtonID
|
|
CMSWindowsPrimaryScreen::mapButton(WPARAM msg, LPARAM button) const
|
|
{
|
|
switch (msg) {
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_LBUTTONUP:
|
|
case WM_NCLBUTTONDOWN:
|
|
case WM_NCLBUTTONDBLCLK:
|
|
case WM_NCLBUTTONUP:
|
|
return kButtonLeft;
|
|
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONDBLCLK:
|
|
case WM_MBUTTONUP:
|
|
case WM_NCMBUTTONDOWN:
|
|
case WM_NCMBUTTONDBLCLK:
|
|
case WM_NCMBUTTONUP:
|
|
return kButtonMiddle;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONDBLCLK:
|
|
case WM_RBUTTONUP:
|
|
case WM_NCRBUTTONDOWN:
|
|
case WM_NCRBUTTONDBLCLK:
|
|
case WM_NCRBUTTONUP:
|
|
return kButtonRight;
|
|
|
|
case WM_XBUTTONDOWN:
|
|
case WM_XBUTTONDBLCLK:
|
|
case WM_XBUTTONUP:
|
|
case WM_NCXBUTTONDOWN:
|
|
case WM_NCXBUTTONDBLCLK:
|
|
case WM_NCXBUTTONUP:
|
|
switch (button) {
|
|
case XBUTTON1:
|
|
return kButtonExtra0 + 0;
|
|
|
|
case XBUTTON2:
|
|
return kButtonExtra0 + 1;
|
|
}
|
|
return kButtonNone;
|
|
|
|
default:
|
|
return kButtonNone;
|
|
}
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::updateKeys()
|
|
{
|
|
// not using GetKeyboardState() because that doesn't seem to give
|
|
// up-to-date results. i don't know why that is or why GetKeyState()
|
|
// should give different results.
|
|
|
|
// clear key and button state
|
|
memset(m_keys, 0, sizeof(m_keys));
|
|
memset(m_buttons, 0, sizeof(m_buttons));
|
|
|
|
// we only care about the modifier key states. other keys and the
|
|
// mouse buttons should be up.
|
|
m_keys[VK_LSHIFT] = static_cast<BYTE>(GetKeyState(VK_LSHIFT) & 0x80);
|
|
m_keys[VK_RSHIFT] = static_cast<BYTE>(GetKeyState(VK_RSHIFT) & 0x80);
|
|
m_keys[VK_SHIFT] = static_cast<BYTE>(GetKeyState(VK_SHIFT) & 0x80);
|
|
m_keys[VK_LCONTROL] = static_cast<BYTE>(GetKeyState(VK_LCONTROL) & 0x80);
|
|
m_keys[VK_RCONTROL] = static_cast<BYTE>(GetKeyState(VK_RCONTROL) & 0x80);
|
|
m_keys[VK_CONTROL] = static_cast<BYTE>(GetKeyState(VK_CONTROL) & 0x80);
|
|
m_keys[VK_LMENU] = static_cast<BYTE>(GetKeyState(VK_LMENU) & 0x80);
|
|
m_keys[VK_RMENU] = static_cast<BYTE>(GetKeyState(VK_RMENU) & 0x80);
|
|
m_keys[VK_MENU] = static_cast<BYTE>(GetKeyState(VK_MENU) & 0x80);
|
|
m_keys[VK_LWIN] = static_cast<BYTE>(GetKeyState(VK_LWIN) & 0x80);
|
|
m_keys[VK_RWIN] = static_cast<BYTE>(GetKeyState(VK_RWIN) & 0x80);
|
|
m_keys[VK_APPS] = static_cast<BYTE>(GetKeyState(VK_APPS) & 0x80);
|
|
m_keys[VK_CAPITAL] = static_cast<BYTE>(GetKeyState(VK_CAPITAL));
|
|
m_keys[VK_NUMLOCK] = static_cast<BYTE>(GetKeyState(VK_NUMLOCK));
|
|
m_keys[VK_SCROLL] = static_cast<BYTE>(GetKeyState(VK_SCROLL));
|
|
}
|
|
|
|
void
|
|
CMSWindowsPrimaryScreen::updateKey(UINT vkCode, bool press)
|
|
{
|
|
if (press) {
|
|
switch (vkCode) {
|
|
case VK_LSHIFT:
|
|
case VK_RSHIFT:
|
|
case VK_SHIFT:
|
|
m_keys[vkCode] |= 0x80;
|
|
m_keys[VK_SHIFT] |= 0x80;
|
|
break;
|
|
|
|
case VK_LCONTROL:
|
|
case VK_RCONTROL:
|
|
case VK_CONTROL:
|
|
m_keys[vkCode] |= 0x80;
|
|
m_keys[VK_CONTROL] |= 0x80;
|
|
break;
|
|
|
|
case VK_LMENU:
|
|
case VK_RMENU:
|
|
case VK_MENU:
|
|
m_keys[vkCode] |= 0x80;
|
|
m_keys[VK_MENU] |= 0x80;
|
|
break;
|
|
|
|
case VK_CAPITAL:
|
|
case VK_NUMLOCK:
|
|
case VK_SCROLL:
|
|
// toggle keys
|
|
m_keys[vkCode] |= 0x80;
|
|
break;
|
|
|
|
default:
|
|
case VK_LWIN:
|
|
case VK_RWIN:
|
|
case VK_APPS:
|
|
m_keys[vkCode] |= 0x80;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (vkCode) {
|
|
case VK_LSHIFT:
|
|
case VK_RSHIFT:
|
|
case VK_SHIFT:
|
|
m_keys[vkCode] &= ~0x80;
|
|
if (((m_keys[VK_LSHIFT] | m_keys[VK_RSHIFT]) & 0x80) == 0) {
|
|
m_keys[VK_SHIFT] &= ~0x80;
|
|
}
|
|
break;
|
|
|
|
case VK_LCONTROL:
|
|
case VK_RCONTROL:
|
|
case VK_CONTROL:
|
|
m_keys[vkCode] &= ~0x80;
|
|
if (((m_keys[VK_LCONTROL] | m_keys[VK_RCONTROL]) & 0x80) == 0) {
|
|
m_keys[VK_CONTROL] &= ~0x80;
|
|
}
|
|
break;
|
|
|
|
case VK_LMENU:
|
|
case VK_RMENU:
|
|
case VK_MENU:
|
|
m_keys[vkCode] &= ~0x80;
|
|
if (((m_keys[VK_LMENU] | m_keys[VK_RMENU]) & 0x80) == 0) {
|
|
m_keys[VK_MENU] &= ~0x80;
|
|
}
|
|
break;
|
|
|
|
case VK_CAPITAL:
|
|
case VK_NUMLOCK:
|
|
case VK_SCROLL:
|
|
// toggle keys
|
|
m_keys[vkCode] &= ~0x80;
|
|
m_keys[vkCode] ^= 0x01;
|
|
break;
|
|
|
|
default:
|
|
case VK_LWIN:
|
|
case VK_RWIN:
|
|
case VK_APPS:
|
|
m_keys[vkCode] &= ~0x80;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
CMSWindowsPrimaryScreen::isModifier(UINT vkCode) const
|
|
|
|
{
|
|
switch (vkCode) {
|
|
case VK_LSHIFT:
|
|
case VK_RSHIFT:
|
|
case VK_SHIFT:
|
|
case VK_LCONTROL:
|
|
case VK_RCONTROL:
|
|
case VK_CONTROL:
|
|
case VK_LMENU:
|
|
case VK_RMENU:
|
|
case VK_MENU:
|
|
case VK_CAPITAL:
|
|
case VK_NUMLOCK:
|
|
case VK_SCROLL:
|
|
case VK_LWIN:
|
|
case VK_RWIN:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|