Converted win32 to new keyboard state tracking design. Also
changed locking to screen so that keys no longer count (only mouse buttons and scroll lock toggled on). This is to deal with the unreliability of key event reporting which can leave us locked to a screen with no key physically pressed. The result of this is that clients get key repeats and releases without the corresponding key press. CKeyState handles this by discarding repeat/release events on keys it hasn't seen go down. Also made a few other minor fixes to win32 keyboard handling.
This commit is contained in:
parent
ab11ebea01
commit
e2a31e8b66
|
@ -0,0 +1,865 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2004 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file COPYING that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "CMSWindowsDesks.h"
|
||||
#include "CMSWindowsDesktop.h"
|
||||
#include "CMSWindowsScreen.h"
|
||||
#include "IScreenSaver.h"
|
||||
#include "XScreen.h"
|
||||
#include "CLock.h"
|
||||
#include "CThread.h"
|
||||
#include "CLog.h"
|
||||
#include "IEventQueue.h"
|
||||
#include "IJob.h"
|
||||
#include "TMethodEventJob.h"
|
||||
#include "TMethodJob.h"
|
||||
#include "CArchMiscWindows.h"
|
||||
#include <malloc.h>
|
||||
|
||||
// these are only defined when WINVER >= 0x0500
|
||||
#if !defined(SPI_GETMOUSESPEED)
|
||||
#define SPI_GETMOUSESPEED 112
|
||||
#endif
|
||||
#if !defined(SPI_SETMOUSESPEED)
|
||||
#define SPI_SETMOUSESPEED 113
|
||||
#endif
|
||||
|
||||
// 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
|
||||
#if !defined(VK_XBUTTON1)
|
||||
#define VK_XBUTTON1 0x05
|
||||
#define VK_XBUTTON2 0x06
|
||||
#endif
|
||||
|
||||
// <unused>; <unused>
|
||||
#define SYNERGY_MSG_SWITCH SYNERGY_HOOK_LAST_MSG + 1
|
||||
// <unused>; <unused>
|
||||
#define SYNERGY_MSG_ENTER SYNERGY_HOOK_LAST_MSG + 2
|
||||
// <unused>; <unused>
|
||||
#define SYNERGY_MSG_LEAVE SYNERGY_HOOK_LAST_MSG + 3
|
||||
// wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code
|
||||
#define SYNERGY_MSG_FAKE_KEY SYNERGY_HOOK_LAST_MSG + 4
|
||||
// flags, XBUTTON id
|
||||
#define SYNERGY_MSG_FAKE_BUTTON SYNERGY_HOOK_LAST_MSG + 5
|
||||
// x; y
|
||||
#define SYNERGY_MSG_FAKE_MOVE SYNERGY_HOOK_LAST_MSG + 6
|
||||
// delta; <unused>
|
||||
#define SYNERGY_MSG_FAKE_WHEEL SYNERGY_HOOK_LAST_MSG + 7
|
||||
// POINT*; <unused>
|
||||
#define SYNERGY_MSG_CURSOR_POS SYNERGY_HOOK_LAST_MSG + 8
|
||||
// IKeyState*; <unused>
|
||||
#define SYNERGY_MSG_SYNC_KEYS SYNERGY_HOOK_LAST_MSG + 9
|
||||
// install; <unused>
|
||||
#define SYNERGY_MSG_SCREENSAVER SYNERGY_HOOK_LAST_MSG + 10
|
||||
|
||||
//
|
||||
// CMSWindowsDesks
|
||||
//
|
||||
|
||||
CMSWindowsDesks::CMSWindowsDesks(
|
||||
bool isPrimary, HINSTANCE hookLibrary,
|
||||
const IScreenSaver* screensaver, IJob* updateKeys) :
|
||||
m_isPrimary(isPrimary),
|
||||
m_is95Family(CArchMiscWindows::isWindows95Family()),
|
||||
m_isOnScreen(m_isPrimary),
|
||||
m_x(0), m_y(0),
|
||||
m_w(0), m_h(0),
|
||||
m_xCenter(0), m_yCenter(0),
|
||||
m_multimon(false),
|
||||
m_timer(NULL),
|
||||
m_screensaver(screensaver),
|
||||
m_screensaverNotify(false),
|
||||
m_activeDesk(NULL),
|
||||
m_activeDeskName(),
|
||||
m_mutex(),
|
||||
m_deskReady(&m_mutex, false),
|
||||
m_updateKeys(updateKeys)
|
||||
{
|
||||
queryHookLibrary(hookLibrary);
|
||||
m_cursor = createBlankCursor();
|
||||
m_deskClass = createDeskWindowClass(m_isPrimary);
|
||||
m_keyLayout = GetKeyboardLayout(GetCurrentThreadId());
|
||||
}
|
||||
|
||||
CMSWindowsDesks::~CMSWindowsDesks()
|
||||
{
|
||||
disable();
|
||||
destroyClass(m_deskClass);
|
||||
destroyCursor(m_cursor);
|
||||
delete m_updateKeys;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::enable()
|
||||
{
|
||||
// set the active desk and (re)install the hooks
|
||||
checkDesk();
|
||||
|
||||
// install the desk timer. this timer periodically checks
|
||||
// which desk is active and reinstalls the hooks as necessary.
|
||||
// we wouldn't need this if windows notified us of a desktop
|
||||
// change but as far as i can tell it doesn't.
|
||||
m_timer = EVENTQUEUE->newTimer(0.2, NULL);
|
||||
EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer,
|
||||
new TMethodEventJob<CMSWindowsDesks>(
|
||||
this, &CMSWindowsDesks::handleCheckDesk));
|
||||
|
||||
updateKeys();
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::disable()
|
||||
{
|
||||
// remove timer
|
||||
if (m_timer != NULL) {
|
||||
EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer);
|
||||
EVENTQUEUE->deleteTimer(m_timer);
|
||||
m_timer = NULL;
|
||||
}
|
||||
|
||||
// destroy desks
|
||||
removeDesks();
|
||||
|
||||
m_isOnScreen = m_isPrimary;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::enter()
|
||||
{
|
||||
sendMessage(SYNERGY_MSG_ENTER, 0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::leave(HKL keyLayout)
|
||||
{
|
||||
sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)keyLayout, 0);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::updateKeys()
|
||||
{
|
||||
sendMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::setShape(SInt32 x, SInt32 y,
|
||||
SInt32 width, SInt32 height,
|
||||
SInt32 xCenter, SInt32 yCenter, bool isMultimon)
|
||||
{
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
m_w = width;
|
||||
m_h = height;
|
||||
m_xCenter = xCenter;
|
||||
m_yCenter = yCenter;
|
||||
m_multimon = isMultimon;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::installScreensaverHooks(bool install)
|
||||
{
|
||||
if (m_isPrimary && m_screensaverNotify != install) {
|
||||
m_screensaverNotify = install;
|
||||
sendMessage(SYNERGY_MSG_SCREENSAVER, install, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::getCursorPos(SInt32& x, SInt32& y) const
|
||||
{
|
||||
POINT pos;
|
||||
sendMessage(SYNERGY_MSG_CURSOR_POS, reinterpret_cast<WPARAM>(&pos), 0);
|
||||
x = pos.x;
|
||||
y = pos.y;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::fakeKeyEvent(
|
||||
KeyButton button, UINT virtualKey,
|
||||
bool press, bool /*isAutoRepeat*/) const
|
||||
{
|
||||
DWORD flags = 0;
|
||||
if (((button & 0x100u) != 0)) {
|
||||
flags |= KEYEVENTF_EXTENDEDKEY;
|
||||
}
|
||||
if (!press) {
|
||||
flags |= KEYEVENTF_KEYUP;
|
||||
}
|
||||
sendMessage(SYNERGY_MSG_FAKE_KEY, flags,
|
||||
MAKEWORD(static_cast<BYTE>(button & 0xffu),
|
||||
static_cast<BYTE>(virtualKey & 0xffu)));
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::fakeMouseButton(ButtonID button, bool press) const
|
||||
{
|
||||
// the system will swap the meaning of left/right for us if
|
||||
// the user has configured a left-handed mouse but we don't
|
||||
// want it to swap since we want the handedness of the
|
||||
// server's mouse. so pre-swap for a left-handed mouse.
|
||||
if (GetSystemMetrics(SM_SWAPBUTTON)) {
|
||||
switch (button) {
|
||||
case kButtonLeft:
|
||||
button = kButtonRight;
|
||||
break;
|
||||
|
||||
case kButtonRight:
|
||||
button = kButtonLeft;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// map button id to button flag and button data
|
||||
DWORD data = 0;
|
||||
DWORD flags;
|
||||
switch (button) {
|
||||
case kButtonLeft:
|
||||
flags = press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
|
||||
break;
|
||||
|
||||
case kButtonMiddle:
|
||||
flags = press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
|
||||
break;
|
||||
|
||||
case kButtonRight:
|
||||
flags = press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
|
||||
break;
|
||||
|
||||
case kButtonExtra0 + 0:
|
||||
data = XBUTTON1;
|
||||
flags = press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
|
||||
break;
|
||||
|
||||
case kButtonExtra0 + 1:
|
||||
data = XBUTTON2;
|
||||
flags = press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// do it
|
||||
sendMessage(SYNERGY_MSG_FAKE_BUTTON, flags, data);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::fakeMouseMove(SInt32 x, SInt32 y) const
|
||||
{
|
||||
sendMessage(SYNERGY_MSG_FAKE_MOVE,
|
||||
static_cast<WPARAM>(x),
|
||||
static_cast<LPARAM>(y));
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::fakeMouseWheel(SInt32 delta) const
|
||||
{
|
||||
sendMessage(SYNERGY_MSG_FAKE_WHEEL, delta, 0);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::sendMessage(UINT msg, WPARAM wParam, LPARAM lParam) const
|
||||
{
|
||||
if (m_activeDesk != NULL && m_activeDesk->m_window != NULL) {
|
||||
PostThreadMessage(m_activeDesk->m_threadID, msg, wParam, lParam);
|
||||
waitForDesk();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::queryHookLibrary(HINSTANCE hookLibrary)
|
||||
{
|
||||
// look up functions
|
||||
if (m_isPrimary) {
|
||||
m_install = (InstallFunc)GetProcAddress(hookLibrary, "install");
|
||||
m_uninstall = (UninstallFunc)GetProcAddress(hookLibrary, "uninstall");
|
||||
m_installScreensaver =
|
||||
(InstallScreenSaverFunc)GetProcAddress(
|
||||
hookLibrary, "installScreenSaver");
|
||||
m_uninstallScreensaver =
|
||||
(UninstallScreenSaverFunc)GetProcAddress(
|
||||
hookLibrary, "uninstallScreenSaver");
|
||||
if (m_install == NULL ||
|
||||
m_uninstall == NULL ||
|
||||
m_installScreensaver == NULL ||
|
||||
m_uninstallScreensaver == NULL) {
|
||||
LOG((CLOG_ERR "Invalid hook library"));
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_install = NULL;
|
||||
m_uninstall = NULL;
|
||||
m_installScreensaver = NULL;
|
||||
m_uninstallScreensaver = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HCURSOR
|
||||
CMSWindowsDesks::createBlankCursor() const
|
||||
{
|
||||
// create a transparent cursor
|
||||
int cw = GetSystemMetrics(SM_CXCURSOR);
|
||||
int ch = GetSystemMetrics(SM_CYCURSOR);
|
||||
UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)];
|
||||
UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)];
|
||||
memset(cursorAND, 0xff, ch * ((cw + 31) >> 2));
|
||||
memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2));
|
||||
HCURSOR c = CreateCursor(CMSWindowsScreen::getInstance(),
|
||||
0, 0, cw, ch, cursorAND, cursorXOR);
|
||||
delete[] cursorXOR;
|
||||
delete[] cursorAND;
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::destroyCursor(HCURSOR cursor) const
|
||||
{
|
||||
if (cursor != NULL) {
|
||||
DestroyCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
ATOM
|
||||
CMSWindowsDesks::createDeskWindowClass(bool isPrimary) const
|
||||
{
|
||||
WNDCLASSEX classInfo;
|
||||
classInfo.cbSize = sizeof(classInfo);
|
||||
classInfo.style = CS_DBLCLKS | CS_NOCLOSE;
|
||||
classInfo.lpfnWndProc = isPrimary ?
|
||||
&CMSWindowsDesks::primaryDeskProc :
|
||||
&CMSWindowsDesks::secondaryDeskProc;
|
||||
classInfo.cbClsExtra = 0;
|
||||
classInfo.cbWndExtra = 0;
|
||||
classInfo.hInstance = CMSWindowsScreen::getInstance();
|
||||
classInfo.hIcon = NULL;
|
||||
classInfo.hCursor = m_cursor;
|
||||
classInfo.hbrBackground = NULL;
|
||||
classInfo.lpszMenuName = NULL;
|
||||
classInfo.lpszClassName = "SynergyDesk";
|
||||
classInfo.hIconSm = NULL;
|
||||
return RegisterClassEx(&classInfo);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::destroyClass(ATOM windowClass) const
|
||||
{
|
||||
if (windowClass != 0) {
|
||||
UnregisterClass((LPCTSTR)windowClass, CMSWindowsScreen::getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
HWND
|
||||
CMSWindowsDesks::createWindow(ATOM windowClass, const char* name) const
|
||||
{
|
||||
HWND window = CreateWindowEx(WS_EX_TOPMOST |
|
||||
WS_EX_TRANSPARENT |
|
||||
WS_EX_TOOLWINDOW,
|
||||
(LPCTSTR)windowClass,
|
||||
name,
|
||||
WS_POPUP,
|
||||
0, 0, 1, 1,
|
||||
NULL, NULL,
|
||||
CMSWindowsScreen::getInstance(),
|
||||
NULL);
|
||||
if (window == NULL) {
|
||||
LOG((CLOG_ERR "failed to create window: %d", GetLastError()));
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::destroyWindow(HWND hwnd) const
|
||||
{
|
||||
if (hwnd != NULL) {
|
||||
DestroyWindow(hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK
|
||||
CMSWindowsDesks::primaryDeskProc(
|
||||
HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK
|
||||
CMSWindowsDesks::secondaryDeskProc(
|
||||
HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
// would like to detect any local user input and hide the hider
|
||||
// window but for now we just detect mouse motion.
|
||||
bool hide = false;
|
||||
switch (msg) {
|
||||
case WM_MOUSEMOVE:
|
||||
if (LOWORD(lParam) != 0 || HIWORD(lParam) != 0) {
|
||||
hide = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (hide && IsWindowVisible(hwnd)) {
|
||||
ReleaseCapture();
|
||||
SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE |
|
||||
SWP_NOACTIVATE | SWP_HIDEWINDOW);
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::deskMouseMove(SInt32 x, SInt32 y) const
|
||||
{
|
||||
// motion is simple (i.e. it's on the primary monitor) if there
|
||||
// is only one monitor.
|
||||
bool simple = !m_multimon;
|
||||
if (!simple) {
|
||||
// also simple if motion is within the primary monitor
|
||||
simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) &&
|
||||
y >= 0 && y < GetSystemMetrics(SM_CYSCREEN));
|
||||
}
|
||||
|
||||
// move the mouse directly to target position if motion is simple
|
||||
if (simple) {
|
||||
// when using absolute positioning with mouse_event(),
|
||||
// the normalized device coordinates range over only
|
||||
// the primary screen.
|
||||
SInt32 w = GetSystemMetrics(SM_CXSCREEN);
|
||||
SInt32 h = GetSystemMetrics(SM_CYSCREEN);
|
||||
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
|
||||
(DWORD)((65535.0f * x) / (w - 1) + 0.5f),
|
||||
(DWORD)((65535.0f * y) / (h - 1) + 0.5f),
|
||||
0, 0);
|
||||
}
|
||||
|
||||
// windows 98 (and Me?) is broken. you cannot set the absolute
|
||||
// position of the mouse except on the primary monitor but you
|
||||
// can do relative moves onto any monitor. this is, in microsoft's
|
||||
// words, "by design." apparently the designers of windows 2000
|
||||
// we're a little less lazy and did it right.
|
||||
//
|
||||
// microsoft recommends in Q193003 to absolute position the cursor
|
||||
// somewhere on the primary monitor then relative move to the
|
||||
// desired location. this doesn't work for us because when the
|
||||
// user drags a scrollbar, a window, etc. it causes the dragged
|
||||
// item to jump back and forth between the position on the primary
|
||||
// monitor and the desired position. while it always ends up in
|
||||
// the right place, the effect is disconcerting.
|
||||
//
|
||||
// instead we'll get the cursor's current position and do just a
|
||||
// relative move from there to the desired position. relative
|
||||
// moves are subject to cursor acceleration which we don't want.
|
||||
// so we disable acceleration, do the relative move, then restore
|
||||
// acceleration. there's a slight chance we'll end up in the
|
||||
// wrong place if the user moves the cursor using this system's
|
||||
// mouse while simultaneously moving the mouse on the server
|
||||
// system. that defeats the purpose of synergy so we'll assume
|
||||
// that won't happen. even if it does, the next mouse move will
|
||||
// correct the position.
|
||||
else {
|
||||
// save mouse speed & acceleration
|
||||
int oldSpeed[4];
|
||||
bool accelChanged =
|
||||
SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) &&
|
||||
SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0);
|
||||
|
||||
// use 1:1 motion
|
||||
if (accelChanged) {
|
||||
int newSpeed[4] = { 0, 0, 0, 1 };
|
||||
accelChanged =
|
||||
SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) ||
|
||||
SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0);
|
||||
}
|
||||
|
||||
// move relative to mouse position
|
||||
POINT pos;
|
||||
GetCursorPos(&pos);
|
||||
mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0);
|
||||
|
||||
// restore mouse speed & acceleration
|
||||
if (accelChanged) {
|
||||
SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0);
|
||||
SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::deskEnter(CDesk* desk)
|
||||
{
|
||||
if (!m_isPrimary) {
|
||||
ReleaseCapture();
|
||||
}
|
||||
ShowCursor(TRUE);
|
||||
SetWindowPos(desk->m_window, HWND_BOTTOM, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE |
|
||||
SWP_NOACTIVATE | SWP_HIDEWINDOW);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout)
|
||||
{
|
||||
ShowCursor(FALSE);
|
||||
if (m_isPrimary) {
|
||||
// update key state
|
||||
m_updateKeys->run();
|
||||
|
||||
// map a window to hide the cursor and to use whatever keyboard
|
||||
// layout we choose rather than the keyboard layout of the last
|
||||
// active window.
|
||||
int x, y, w, h;
|
||||
if (desk->m_lowLevel) {
|
||||
// with a low level hook the cursor will never budge so
|
||||
// just a 1x1 window is sufficient.
|
||||
x = m_xCenter;
|
||||
y = m_yCenter;
|
||||
w = 1;
|
||||
h = 1;
|
||||
}
|
||||
else {
|
||||
// with regular hooks the cursor will jitter as it's moved
|
||||
// by the user then back to the center by us. to be sure
|
||||
// we never lose it, cover all the monitors with the window.
|
||||
x = m_x;
|
||||
y = m_y;
|
||||
w = m_w;
|
||||
h = m_h;
|
||||
}
|
||||
SetWindowPos(desk->m_window, HWND_TOPMOST, x, y, w, h,
|
||||
SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
|
||||
// switch to requested keyboard layout
|
||||
ActivateKeyboardLayout(keyLayout, 0);
|
||||
}
|
||||
else {
|
||||
// move hider window under the cursor center, raise, and show it
|
||||
SetWindowPos(desk->m_window, HWND_TOPMOST,
|
||||
m_xCenter, m_yCenter, 1, 1,
|
||||
SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
|
||||
// watch for mouse motion. if we see any then we hide the
|
||||
// hider window so the user can use the physically attached
|
||||
// mouse if desired. we'd rather not capture the mouse but
|
||||
// we aren't notified when the mouse leaves our window.
|
||||
SetCapture(desk->m_window);
|
||||
|
||||
// warp the mouse to the cursor center
|
||||
deskMouseMove(m_xCenter, m_yCenter);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::deskThread(void* vdesk)
|
||||
{
|
||||
MSG msg;
|
||||
|
||||
// use given desktop for this thread
|
||||
CDesk* desk = reinterpret_cast<CDesk*>(vdesk);
|
||||
desk->m_threadID = GetCurrentThreadId();
|
||||
desk->m_window = NULL;
|
||||
if (desk->m_desk != NULL && SetThreadDesktop(desk->m_desk) != 0) {
|
||||
// create a message queue
|
||||
PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE);
|
||||
|
||||
// create a window. we use this window to hide the cursor.
|
||||
try {
|
||||
desk->m_window = createWindow(m_deskClass, "SynergyDesk");
|
||||
LOG((CLOG_DEBUG "desk %s window is 0x%08x", desk->m_name.c_str(), desk->m_window));
|
||||
}
|
||||
catch (...) {
|
||||
// ignore
|
||||
LOG((CLOG_DEBUG "can't create desk window for %s", desk->m_name.c_str()));
|
||||
}
|
||||
|
||||
// a window on the primary screen should never activate
|
||||
if (m_isPrimary && desk->m_window != NULL) {
|
||||
EnableWindow(desk->m_window, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
// tell main thread that we're ready
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
m_deskReady = true;
|
||||
m_deskReady.broadcast();
|
||||
}
|
||||
|
||||
while (GetMessage(&msg, NULL, 0, 0)) {
|
||||
switch (msg.message) {
|
||||
default:
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
continue;
|
||||
|
||||
case SYNERGY_MSG_SWITCH:
|
||||
if (m_isPrimary) {
|
||||
m_uninstall();
|
||||
if (m_screensaverNotify) {
|
||||
m_uninstallScreensaver();
|
||||
m_installScreensaver();
|
||||
}
|
||||
switch (m_install()) {
|
||||
case kHOOK_FAILED:
|
||||
// we won't work on this desk
|
||||
desk->m_lowLevel = false;
|
||||
break;
|
||||
|
||||
case kHOOK_OKAY:
|
||||
desk->m_lowLevel = false;
|
||||
break;
|
||||
|
||||
case kHOOK_OKAY_LL:
|
||||
desk->m_lowLevel = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SYNERGY_MSG_ENTER:
|
||||
m_isOnScreen = true;
|
||||
deskEnter(desk);
|
||||
break;
|
||||
|
||||
case SYNERGY_MSG_LEAVE:
|
||||
m_isOnScreen = false;
|
||||
m_keyLayout = (HKL)msg.wParam;
|
||||
deskLeave(desk, m_keyLayout);
|
||||
break;
|
||||
|
||||
case SYNERGY_MSG_FAKE_KEY:
|
||||
keybd_event(HIBYTE(msg.lParam), LOBYTE(msg.lParam), msg.wParam, 0);
|
||||
break;
|
||||
|
||||
case SYNERGY_MSG_FAKE_BUTTON:
|
||||
if (msg.wParam != 0) {
|
||||
mouse_event(msg.wParam, 0, 0, msg.lParam, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case SYNERGY_MSG_FAKE_MOVE:
|
||||
deskMouseMove(static_cast<SInt32>(msg.wParam),
|
||||
static_cast<SInt32>(msg.lParam));
|
||||
break;
|
||||
|
||||
case SYNERGY_MSG_FAKE_WHEEL:
|
||||
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, msg.wParam, 0);
|
||||
break;
|
||||
|
||||
case SYNERGY_MSG_CURSOR_POS: {
|
||||
POINT* pos = reinterpret_cast<POINT*>(msg.wParam);
|
||||
if (!GetCursorPos(pos)) {
|
||||
pos->x = m_xCenter;
|
||||
pos->y = m_yCenter;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SYNERGY_MSG_SYNC_KEYS:
|
||||
m_updateKeys->run();
|
||||
break;
|
||||
|
||||
case SYNERGY_MSG_SCREENSAVER:
|
||||
if (msg.wParam != 0) {
|
||||
m_installScreensaver();
|
||||
}
|
||||
else {
|
||||
m_uninstallScreensaver();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// notify that message was processed
|
||||
CLock lock(&m_mutex);
|
||||
m_deskReady = true;
|
||||
m_deskReady.broadcast();
|
||||
}
|
||||
|
||||
// clean up
|
||||
deskEnter(desk);
|
||||
if (desk->m_window != NULL) {
|
||||
DestroyWindow(desk->m_window);
|
||||
}
|
||||
if (desk->m_desk != NULL) {
|
||||
closeDesktop(desk->m_desk);
|
||||
}
|
||||
}
|
||||
|
||||
CMSWindowsDesks::CDesk*
|
||||
CMSWindowsDesks::addDesk(const CString& name, HDESK hdesk)
|
||||
{
|
||||
CDesk* desk = new CDesk;
|
||||
desk->m_name = name;
|
||||
desk->m_desk = hdesk;
|
||||
desk->m_targetID = GetCurrentThreadId();
|
||||
desk->m_thread = new CThread(new TMethodJob<CMSWindowsDesks>(
|
||||
this, &CMSWindowsDesks::deskThread, desk));
|
||||
waitForDesk();
|
||||
m_desks.insert(std::make_pair(name, desk));
|
||||
return desk;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::removeDesks()
|
||||
{
|
||||
for (CDesks::iterator index = m_desks.begin();
|
||||
index != m_desks.end(); ++index) {
|
||||
CDesk* desk = index->second;
|
||||
PostThreadMessage(desk->m_threadID, WM_QUIT, 0, 0);
|
||||
desk->m_thread->wait();
|
||||
delete desk->m_thread;
|
||||
delete desk;
|
||||
}
|
||||
m_desks.clear();
|
||||
m_activeDesk = NULL;
|
||||
m_activeDeskName = "";
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::checkDesk()
|
||||
{
|
||||
// get current desktop. if we already know about it then return.
|
||||
CDesk* desk;
|
||||
HDESK hdesk = openInputDesktop();
|
||||
CString name = getDesktopName(hdesk);
|
||||
CDesks::const_iterator index = m_desks.find(name);
|
||||
if (index == m_desks.end()) {
|
||||
desk = addDesk(name, hdesk);
|
||||
// hold on to hdesk until thread exits so the desk can't
|
||||
// be removed by the system
|
||||
}
|
||||
else {
|
||||
closeDesktop(hdesk);
|
||||
desk = index->second;
|
||||
}
|
||||
|
||||
// if active desktop changed then tell the old and new desk threads
|
||||
// about the change. don't switch desktops when the screensaver is
|
||||
// active becaue we'd most likely switch to the screensaver desktop
|
||||
// which would have the side effect of forcing the screensaver to
|
||||
// stop.
|
||||
if (name != m_activeDeskName && !m_screensaver->isActive()) {
|
||||
// show cursor on previous desk
|
||||
bool wasOnScreen = m_isOnScreen;
|
||||
if (!wasOnScreen) {
|
||||
sendMessage(SYNERGY_MSG_ENTER, 0, 0);
|
||||
}
|
||||
|
||||
// check for desk accessibility change. we don't get events
|
||||
// from an inaccessible desktop so when we switch from an
|
||||
// inaccessible desktop to an accessible one we have to
|
||||
// update the keyboard state.
|
||||
LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str()));
|
||||
bool isAccessible = isDeskAccessible(desk);
|
||||
if (isDeskAccessible(m_activeDesk) != isAccessible) {
|
||||
if (isAccessible) {
|
||||
LOG((CLOG_DEBUG "desktop is now accessible"));
|
||||
sendMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0);
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_DEBUG "desktop is now inaccessible"));
|
||||
}
|
||||
}
|
||||
|
||||
// switch desk
|
||||
m_activeDesk = desk;
|
||||
m_activeDeskName = name;
|
||||
sendMessage(SYNERGY_MSG_SWITCH, 0, 0);
|
||||
|
||||
// hide cursor on new desk
|
||||
if (!wasOnScreen) {
|
||||
sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CMSWindowsDesks::isDeskAccessible(const CDesk* desk) const
|
||||
{
|
||||
return (desk != NULL && desk->m_desk != NULL);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::waitForDesk() const
|
||||
{
|
||||
CMSWindowsDesks* self = const_cast<CMSWindowsDesks*>(this);
|
||||
|
||||
CLock lock(&m_mutex);
|
||||
while (!(bool)m_deskReady) {
|
||||
m_deskReady.wait();
|
||||
}
|
||||
self->m_deskReady = false;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::handleCheckDesk(const CEvent&, void*)
|
||||
{
|
||||
checkDesk();
|
||||
}
|
||||
|
||||
HDESK
|
||||
CMSWindowsDesks::openInputDesktop()
|
||||
{
|
||||
if (m_is95Family) {
|
||||
// there's only one desktop on windows 95 et al.
|
||||
return GetThreadDesktop(GetCurrentThreadId());
|
||||
}
|
||||
else {
|
||||
return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE,
|
||||
DESKTOP_CREATEWINDOW |
|
||||
DESKTOP_HOOKCONTROL |
|
||||
GENERIC_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesks::closeDesktop(HDESK desk)
|
||||
{
|
||||
// on 95/98/me we don't need to close the desktop returned by
|
||||
// openInputDesktop().
|
||||
if (desk != NULL && !m_is95Family) {
|
||||
CloseDesktop(desk);
|
||||
}
|
||||
}
|
||||
|
||||
CString
|
||||
CMSWindowsDesks::getDesktopName(HDESK desk)
|
||||
{
|
||||
if (desk == NULL) {
|
||||
return CString();
|
||||
}
|
||||
else if (m_is95Family) {
|
||||
return "desktop";
|
||||
}
|
||||
else {
|
||||
DWORD size;
|
||||
GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size);
|
||||
TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR));
|
||||
GetUserObjectInformation(desk, UOI_NAME, name, size, &size);
|
||||
CString result(name);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2004 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file COPYING that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef CMSWINDOWSDESKS_H
|
||||
#define CMSWINDOWSDESKS_H
|
||||
|
||||
#include "CSynergyHook.h"
|
||||
#include "KeyTypes.h"
|
||||
#include "MouseTypes.h"
|
||||
#include "CCondVar.h"
|
||||
#include "CMutex.h"
|
||||
#include "CString.h"
|
||||
#include "stdmap.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
class CEvent;
|
||||
class CEventQueueTimer;
|
||||
class CThread;
|
||||
class IJob;
|
||||
class IScreenSaver;
|
||||
|
||||
//! Microsoft Windows desk handling
|
||||
/*!
|
||||
Desks in Microsoft Windows are only remotely like desktops on X11
|
||||
systems. A desk is another virtual surface for windows but desks
|
||||
impose serious restrictions: a thread can interact with only one
|
||||
desk at a time, you can't switch desks if the thread has any hooks
|
||||
installed or owns any windows, windows cannot exist on multiple
|
||||
desks at once, etc. Basically, they're useless except for running
|
||||
the login window or the screensaver, which is what they're used
|
||||
for. Synergy must deal with them mainly because of the login
|
||||
window and screensaver but users can create their own desks and
|
||||
synergy should work on those too.
|
||||
|
||||
This class encapsulates all the desk nastiness. Clients of this
|
||||
object don't have to know anything about desks.
|
||||
*/
|
||||
class CMSWindowsDesks {
|
||||
public:
|
||||
//! Constructor
|
||||
/*!
|
||||
\p isPrimary is true iff the desk is for a primary screen.
|
||||
\p screensaver points to a screensaver object and it's used
|
||||
only to check if the screensaver is active. The \p updateKeys
|
||||
job is adopted and is called when the key state should be
|
||||
updated in a thread attached to the current desk.
|
||||
\p hookLibrary must be a handle to the hook library.
|
||||
*/
|
||||
CMSWindowsDesks(bool isPrimary, HINSTANCE hookLibrary,
|
||||
const IScreenSaver* screensaver, IJob* updateKeys);
|
||||
~CMSWindowsDesks();
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Enable desk tracking
|
||||
/*!
|
||||
Enables desk tracking. While enabled, this object checks to see
|
||||
if the desk has changed and ensures that the hooks are installed
|
||||
on the new desk. \c setShape should be called at least once
|
||||
before calling \c enable.
|
||||
*/
|
||||
void enable();
|
||||
|
||||
//! Disable desk tracking
|
||||
/*!
|
||||
Disables desk tracking. \sa enable.
|
||||
*/
|
||||
void disable();
|
||||
|
||||
//! Notify of entering a desk
|
||||
/*!
|
||||
Prepares a desk for when the cursor enters it.
|
||||
*/
|
||||
void enter();
|
||||
|
||||
//! Notify of leaving a desk
|
||||
/*!
|
||||
Prepares a desk for when the cursor leaves it.
|
||||
*/
|
||||
void leave(HKL keyLayout);
|
||||
|
||||
//! Update the key state
|
||||
/*!
|
||||
Causes the key state to get updated to reflect the physical keyboard
|
||||
state and current keyboard mapping.
|
||||
*/
|
||||
void updateKeys();
|
||||
|
||||
//! Tell desk about new size
|
||||
/*!
|
||||
This tells the desks that the display size has changed.
|
||||
*/
|
||||
void setShape(SInt32 x, SInt32 y,
|
||||
SInt32 width, SInt32 height,
|
||||
SInt32 xCenter, SInt32 yCenter, bool isMultimon);
|
||||
|
||||
//! Install/uninstall screensaver hooks
|
||||
/*!
|
||||
If \p install is true then the screensaver hooks are installed and,
|
||||
if desk tracking is enabled, updated whenever the desk changes. If
|
||||
\p install is false then the screensaver hooks are uninstalled.
|
||||
*/
|
||||
void installScreensaverHooks(bool install);
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Get cursor position
|
||||
/*!
|
||||
Return the current position of the cursor in \c x and \c y.
|
||||
*/
|
||||
void getCursorPos(SInt32& x, SInt32& y) const;
|
||||
|
||||
//! Fake key press/release
|
||||
/*!
|
||||
Synthesize a press or release of key \c button.
|
||||
*/
|
||||
void fakeKeyEvent(KeyButton button, UINT virtualKey,
|
||||
bool press, bool isAutoRepeat) const;
|
||||
|
||||
//! Fake mouse press/release
|
||||
/*!
|
||||
Synthesize a press or release of mouse button \c id.
|
||||
*/
|
||||
void fakeMouseButton(ButtonID id, bool press) const;
|
||||
|
||||
//! Fake mouse move
|
||||
/*!
|
||||
Synthesize a mouse move to the absolute coordinates \c x,y.
|
||||
*/
|
||||
void fakeMouseMove(SInt32 x, SInt32 y) const;
|
||||
|
||||
//! Fake mouse wheel
|
||||
/*!
|
||||
Synthesize a mouse wheel event of amount \c delta.
|
||||
*/
|
||||
void fakeMouseWheel(SInt32 delta) const;
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
class CDesk {
|
||||
public:
|
||||
CString m_name;
|
||||
CThread* m_thread;
|
||||
DWORD m_threadID;
|
||||
DWORD m_targetID;
|
||||
HDESK m_desk;
|
||||
HWND m_window;
|
||||
bool m_lowLevel;
|
||||
};
|
||||
typedef std::map<CString, CDesk*> CDesks;
|
||||
|
||||
// initialization and shutdown operations
|
||||
void queryHookLibrary(HINSTANCE hookLibrary);
|
||||
HCURSOR createBlankCursor() const;
|
||||
void destroyCursor(HCURSOR cursor) const;
|
||||
ATOM createDeskWindowClass(bool isPrimary) const;
|
||||
void destroyClass(ATOM windowClass) const;
|
||||
HWND createWindow(ATOM windowClass, const char* name) const;
|
||||
void destroyWindow(HWND) const;
|
||||
|
||||
// message handlers
|
||||
void deskMouseMove(SInt32 x, SInt32 y) const;
|
||||
void deskEnter(CDesk* desk);
|
||||
void deskLeave(CDesk* desk, HKL keyLayout);
|
||||
void deskThread(void* vdesk);
|
||||
|
||||
// desk switch checking and handling
|
||||
CDesk* addDesk(const CString& name, HDESK hdesk);
|
||||
void removeDesks();
|
||||
void checkDesk();
|
||||
bool isDeskAccessible(const CDesk* desk) const;
|
||||
void handleCheckDesk(const CEvent& event, void*);
|
||||
|
||||
// communication with desk threads
|
||||
void waitForDesk() const;
|
||||
void sendMessage(UINT, WPARAM, LPARAM) const;
|
||||
|
||||
// desk API wrappers
|
||||
HDESK openInputDesktop();
|
||||
void closeDesktop(HDESK);
|
||||
CString getDesktopName(HDESK);
|
||||
|
||||
// our desk window procs
|
||||
static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM);
|
||||
static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
private:
|
||||
// true if screen is being used as a primary screen, false otherwise
|
||||
bool m_isPrimary;
|
||||
|
||||
// true if windows 95/98/me
|
||||
bool m_is95Family;
|
||||
|
||||
// true if mouse has entered the screen
|
||||
bool m_isOnScreen;
|
||||
|
||||
// our resources
|
||||
ATOM m_deskClass;
|
||||
HCURSOR m_cursor;
|
||||
|
||||
// screen shape stuff
|
||||
SInt32 m_x, m_y;
|
||||
SInt32 m_w, m_h;
|
||||
SInt32 m_xCenter, m_yCenter;
|
||||
|
||||
// true if system appears to have multiple monitors
|
||||
bool m_multimon;
|
||||
|
||||
// the timer used to check for desktop switching
|
||||
CEventQueueTimer* m_timer;
|
||||
|
||||
// screen saver stuff
|
||||
const IScreenSaver* m_screensaver;
|
||||
bool m_screensaverNotify;
|
||||
|
||||
// the current desk and it's name
|
||||
CDesk* m_activeDesk;
|
||||
CString m_activeDeskName;
|
||||
|
||||
// one desk per desktop and a cond var to communicate with it
|
||||
CMutex m_mutex;
|
||||
CCondVar<bool> m_deskReady;
|
||||
CDesks m_desks;
|
||||
|
||||
// hook library stuff
|
||||
InstallFunc m_install;
|
||||
UninstallFunc m_uninstall;
|
||||
InstallScreenSaverFunc m_installScreensaver;
|
||||
UninstallScreenSaverFunc m_uninstallScreensaver;
|
||||
|
||||
// keyboard stuff
|
||||
IJob* m_updateKeys;
|
||||
HKL m_keyLayout;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* 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 "CMSWindowsDesktop.h"
|
||||
#include "CLog.h"
|
||||
#include "CArchMiscWindows.h"
|
||||
#include <malloc.h>
|
||||
|
||||
//
|
||||
// CMSWindowsDesktop
|
||||
//
|
||||
|
||||
HDESK
|
||||
CMSWindowsDesktop::openInputDesktop()
|
||||
{
|
||||
if (CArchMiscWindows::isWindows95Family()) {
|
||||
// there's only one desktop on windows 95 et al.
|
||||
return GetThreadDesktop(GetCurrentThreadId());
|
||||
}
|
||||
else {
|
||||
return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE,
|
||||
DESKTOP_CREATEWINDOW |
|
||||
DESKTOP_HOOKCONTROL |
|
||||
GENERIC_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsDesktop::closeDesktop(HDESK desk)
|
||||
{
|
||||
// on 95/98/me we don't need to close the desktop returned by
|
||||
// openInputDesktop().
|
||||
if (desk != NULL && !CArchMiscWindows::isWindows95Family()) {
|
||||
CloseDesktop(desk);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CMSWindowsDesktop::setDesktop(HDESK desk)
|
||||
{
|
||||
// 95/98/me doesn't support multiple desktops so just return
|
||||
// true on those platforms.
|
||||
return (CArchMiscWindows::isWindows95Family() ||
|
||||
SetThreadDesktop(desk) != 0);
|
||||
}
|
||||
|
||||
CString
|
||||
CMSWindowsDesktop::getDesktopName(HDESK desk)
|
||||
{
|
||||
if (desk == NULL) {
|
||||
return CString();
|
||||
}
|
||||
else if (CArchMiscWindows::isWindows95Family()) {
|
||||
return "desktop";
|
||||
}
|
||||
else {
|
||||
DWORD size;
|
||||
GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size);
|
||||
TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR));
|
||||
GetUserObjectInformation(desk, UOI_NAME, name, size, &size);
|
||||
CString result(name);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CMSWINDOWSDESKTOP_H
|
||||
#define CMSWINDOWSDESKTOP_H
|
||||
|
||||
#include "CString.h"
|
||||
#include <windows.h>
|
||||
|
||||
//! Encapsulate Microsoft Windows desktop
|
||||
class CMSWindowsDesktop {
|
||||
public:
|
||||
//! Open the desktop
|
||||
/*!
|
||||
Opens the desktop named \p name. The caller must close the desktop.
|
||||
*/
|
||||
static HDESK openDesktop(const CString& name);
|
||||
|
||||
//! Open the input desktop
|
||||
/*!
|
||||
Opens the input desktop. The caller must close the desktop.
|
||||
*/
|
||||
static HDESK openInputDesktop();
|
||||
|
||||
//! Close a desktop
|
||||
/*!
|
||||
Closes the given desktop.
|
||||
*/
|
||||
static void closeDesktop(HDESK);
|
||||
|
||||
//! Change current desktop
|
||||
/*!
|
||||
Changes the calling thread's desktop, return true iff successful.
|
||||
The call will fail if the calling thread has any windows or a hooks
|
||||
on the current desktop.
|
||||
*/
|
||||
static bool setDesktop(HDESK);
|
||||
|
||||
//! Get the desktop's name.
|
||||
/*!
|
||||
Returns the current desktop's name. Returns a constant string
|
||||
on 95/98/Me.
|
||||
*/
|
||||
static CString getDesktopName(HDESK);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,196 +0,0 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2003 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file COPYING that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef CMSWINDOWSKEYMAPPER_H
|
||||
#define CMSWINDOWSKEYMAPPER_H
|
||||
|
||||
#include "IKeyState.h"
|
||||
#include "CString.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
//! Microsoft Windows key mapper
|
||||
/*!
|
||||
This class maps KeyIDs to keystrokes.
|
||||
*/
|
||||
class CMSWindowsKeyMapper {
|
||||
public:
|
||||
CMSWindowsKeyMapper();
|
||||
~CMSWindowsKeyMapper();
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Update key mapper
|
||||
/*!
|
||||
Updates the key mapper's internal tables according to the
|
||||
current keyboard mapping and updates \c keyState.
|
||||
*/
|
||||
void update(IKeyState* keyState);
|
||||
|
||||
//! Update shadow key state
|
||||
/*!
|
||||
Updates the shadow keyboard state.
|
||||
*/
|
||||
void updateKey(LPARAM eventLParam);
|
||||
|
||||
//! Set the active keyboard layout
|
||||
/*!
|
||||
Uses \p keyLayout when finding scan codes via \c keyToScanCode()
|
||||
and mapping keys in \c mapKeyFromEvent().
|
||||
*/
|
||||
void setKeyLayout(HKL keyLayout);
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Map key press/repeat to keystrokes
|
||||
/*!
|
||||
Converts a press/repeat of key \c id with the modifiers as given
|
||||
in \c desiredMask into the keystrokes necessary to synthesize
|
||||
that key event. Returns the platform specific code of the key
|
||||
being pressed, or 0 if the key cannot be mapped or \c isAutoRepeat
|
||||
is true and the key does not auto-repeat.
|
||||
*/
|
||||
KeyButton mapKey(IKeyState::Keystrokes&,
|
||||
const IKeyState& keyState, KeyID id,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const;
|
||||
|
||||
//! Map key event to a key
|
||||
/*!
|
||||
Converts a key event into a KeyID and the shadow modifier state
|
||||
to a modifier mask. If \c altgr is non-NULL it's set to true if
|
||||
the key requires AltGr and false otherwise.
|
||||
*/
|
||||
KeyID mapKeyFromEvent(WPARAM charAndVirtKey, LPARAM info,
|
||||
KeyModifierMask* maskOut, bool* altgr) const;
|
||||
|
||||
//! Check if virtual key is a modifier
|
||||
/*!
|
||||
Returns true iff \p virtKey refers to a modifier key.
|
||||
*/
|
||||
bool isModifier(UINT virtKey) const;
|
||||
|
||||
//! Test shadow key state
|
||||
/*!
|
||||
Returns true iff the shadow state indicates the key is pressed.
|
||||
*/
|
||||
bool isPressed(UINT virtKey) const;
|
||||
|
||||
//! Map button to a virtual key
|
||||
/*!
|
||||
Returns the virtual key for \c button.
|
||||
*/
|
||||
UINT buttonToVirtualKey(KeyButton button) const;
|
||||
|
||||
//! Map virtual key to a button
|
||||
/*!
|
||||
Returns the button for virtual key \c virtKey.
|
||||
*/
|
||||
KeyButton virtualKeyToButton(UINT virtKey) const;
|
||||
|
||||
//! Check for extended key
|
||||
/*!
|
||||
Returns true iff \c key is an extended key
|
||||
*/
|
||||
bool isExtendedKey(KeyButton button) const;
|
||||
|
||||
//! Get current modifier key state
|
||||
/*!
|
||||
Returns the current modifier key state.
|
||||
*/
|
||||
KeyModifierMask getActiveModifiers() const;
|
||||
|
||||
//! Get name of key
|
||||
/*!
|
||||
Return a string describing the given key.
|
||||
*/
|
||||
const char* getKeyName(KeyButton) const;
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
// convert a language ID to a code page
|
||||
UINT getCodePageFromLangID(LANGID langid) const;
|
||||
|
||||
// map character \c c given keyboard layout \c hkl to the keystrokes
|
||||
// to generate it.
|
||||
KeyButton mapCharacter(IKeyState::Keystrokes& keys,
|
||||
const IKeyState& keyState, char c, HKL hkl,
|
||||
bool isAutoRepeat) const;
|
||||
|
||||
// map \c virtualKey to the keystrokes to generate it, along with
|
||||
// keystrokes to update and restore the modifier state.
|
||||
KeyButton mapToKeystrokes(IKeyState::Keystrokes& keys,
|
||||
const IKeyState& keyState, KeyButton button,
|
||||
KeyModifierMask desiredMask,
|
||||
KeyModifierMask requiredMask,
|
||||
bool isAutoRepeat) const;
|
||||
|
||||
// get keystrokes to get modifiers in a desired state
|
||||
bool adjustModifiers(IKeyState::Keystrokes& keys,
|
||||
IKeyState::Keystrokes& undo,
|
||||
const IKeyState& keyState,
|
||||
KeyModifierMask desiredMask,
|
||||
KeyModifierMask requiredMask) const;
|
||||
|
||||
//! Test shadow key toggle state
|
||||
/*!
|
||||
Returns true iff the shadow state indicates the key is toggled on.
|
||||
*/
|
||||
bool isToggled(UINT virtKey) const;
|
||||
|
||||
//! Get shadow modifier key state
|
||||
/*!
|
||||
Returns the shadow modifier key state.
|
||||
*/
|
||||
KeyModifierMask getShadowModifiers(bool needAltGr) const;
|
||||
|
||||
// pass character to ToAsciiEx(), returning what it returns
|
||||
int toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const;
|
||||
|
||||
// return true iff \c c is a dead character
|
||||
bool isDeadChar(TCHAR c, HKL hkl, bool menu) const;
|
||||
|
||||
private:
|
||||
class CModifierKeys {
|
||||
public:
|
||||
enum { s_maxKeys = 2 };
|
||||
KeyModifierMask m_mask;
|
||||
KeyButton m_keys[s_maxKeys];
|
||||
};
|
||||
|
||||
// map of key state for each scan code. this would be 8 bits
|
||||
// except windows reuses some scan codes for "extended" keys
|
||||
// we actually need 9 bits. an example is the left and right
|
||||
// alt keys; they share the same scan code but the right key
|
||||
// is "extended".
|
||||
BYTE m_keys[512];
|
||||
UINT m_scanCodeToVirtKey[512];
|
||||
KeyButton m_virtKeyToScanCode[256];
|
||||
mutable TCHAR m_deadKey;
|
||||
HKL m_keyLayout;
|
||||
CString m_keyName;
|
||||
|
||||
static const CModifierKeys s_modifiers[];
|
||||
static const char* s_vkToName[];
|
||||
static const KeyID s_virtualKey[][2];
|
||||
static const UINT s_mapE000[];
|
||||
static const UINT s_mapEE00[];
|
||||
static const UINT s_mapEF00[];
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2003 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file COPYING that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef CMSWINDOWSKEYSTATE_H
|
||||
#define CMSWINDOWSKEYSTATE_H
|
||||
|
||||
#include "CKeyState.h"
|
||||
#include "CString.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
class CMSWindowsDesks;
|
||||
|
||||
//! Microsoft Windows key mapper
|
||||
/*!
|
||||
This class maps KeyIDs to keystrokes.
|
||||
*/
|
||||
class CMSWindowsKeyState : public CKeyState {
|
||||
public:
|
||||
CMSWindowsKeyState(CMSWindowsDesks* desks);
|
||||
virtual ~CMSWindowsKeyState();
|
||||
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Set the active keyboard layout
|
||||
/*!
|
||||
Uses \p keyLayout when querying the keyboard.
|
||||
*/
|
||||
void setKeyLayout(HKL keyLayout);
|
||||
|
||||
//! Check the named virtual key for release
|
||||
/*!
|
||||
If \p virtualKey isn't really pressed but we think it is then
|
||||
update our state and post a key release event to \p eventTarget.
|
||||
*/
|
||||
void fixKey(void* eventTarget, UINT virtualKey);
|
||||
|
||||
//! Map key event to a key
|
||||
/*!
|
||||
Converts a key event into a KeyID and the shadow modifier state
|
||||
to a modifier mask.
|
||||
*/
|
||||
KeyID mapKeyFromEvent(WPARAM charAndVirtKey,
|
||||
LPARAM info, KeyModifierMask* maskOut) const;
|
||||
|
||||
//! Map a virtual key to a button
|
||||
/*!
|
||||
Returns the button for the \p virtualKey.
|
||||
*/
|
||||
KeyButton virtualKeyToButton(UINT virtualKey) const;
|
||||
|
||||
//@}
|
||||
|
||||
// IKeyState overrides
|
||||
virtual void setKeyDown(KeyButton button, bool down);
|
||||
virtual void sendKeyEvent(void* target,
|
||||
bool press, bool isAutoRepeat,
|
||||
KeyID key, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button);
|
||||
virtual bool fakeCtrlAltDel();
|
||||
virtual const char* getKeyName(KeyButton) const;
|
||||
|
||||
protected:
|
||||
// IKeyState overrides
|
||||
virtual void doUpdateKeys();
|
||||
virtual void doFakeKeyEvent(KeyButton button,
|
||||
bool press, bool isAutoRepeat);
|
||||
virtual KeyButton mapKey(Keystrokes& keys, KeyID id,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const;
|
||||
|
||||
private:
|
||||
// send ctrl+alt+del hotkey event on NT family
|
||||
static void ctrlAltDelThread(void*);
|
||||
|
||||
// convert a language ID to a code page
|
||||
UINT getCodePageFromLangID(LANGID langid) const;
|
||||
|
||||
// map character \c c given keyboard layout \c hkl to the keystrokes
|
||||
// to generate it.
|
||||
KeyButton mapCharacter(Keystrokes& keys,
|
||||
char c, HKL hkl, bool isAutoRepeat) const;
|
||||
|
||||
// map \c virtualKey to the keystrokes to generate it, along with
|
||||
// keystrokes to update and restore the modifier state.
|
||||
KeyButton mapToKeystrokes(Keystrokes& keys, KeyButton button,
|
||||
KeyModifierMask desiredMask,
|
||||
KeyModifierMask requiredMask,
|
||||
bool isAutoRepeat) const;
|
||||
|
||||
// get keystrokes to get modifiers in a desired state
|
||||
bool adjustModifiers(Keystrokes& keys,
|
||||
Keystrokes& undo,
|
||||
KeyModifierMask desiredMask,
|
||||
KeyModifierMask requiredMask) const;
|
||||
|
||||
// pass character to ToAsciiEx(), returning what it returns
|
||||
int toAscii(TCHAR c, HKL hkl, bool menu, WORD* chars) const;
|
||||
|
||||
// return true iff \c c is a dead character
|
||||
bool isDeadChar(TCHAR c, HKL hkl, bool menu) const;
|
||||
|
||||
private:
|
||||
bool m_is95Family;
|
||||
CMSWindowsDesks* m_desks;
|
||||
HKL m_keyLayout;
|
||||
CString m_keyName;
|
||||
UINT m_scanCodeToVirtKey[512];
|
||||
KeyButton m_virtKeyToScanCode[256];
|
||||
|
||||
static const char* s_vkToName[];
|
||||
static const KeyID s_virtualKey[][2];
|
||||
static const UINT s_mapE000[];
|
||||
static const UINT s_mapEE00[];
|
||||
static const UINT s_mapEF00[];
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -15,8 +15,7 @@
|
|||
#ifndef CMSWINDOWSSCREEN_H
|
||||
#define CMSWINDOWSSCREEN_H
|
||||
|
||||
#include "IPlatformScreen.h"
|
||||
#include "CMSWindowsKeyMapper.h"
|
||||
#include "CPlatformScreen.h"
|
||||
#include "CSynergyHook.h"
|
||||
#include "CCondVar.h"
|
||||
#include "CMutex.h"
|
||||
|
@ -24,13 +23,14 @@
|
|||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
class CEventQueueTimer;
|
||||
class CMSWindowsDesks;
|
||||
class CMSWindowsKeyState;
|
||||
class CMSWindowsScreenSaver;
|
||||
class CThread;
|
||||
class IJob;
|
||||
|
||||
//! Implementation of IPlatformScreen for Microsoft Windows
|
||||
class CMSWindowsScreen : public IPlatformScreen {
|
||||
class CMSWindowsScreen : public CPlatformScreen {
|
||||
public:
|
||||
CMSWindowsScreen(bool isPrimary, IJob* suspend, IJob* resume);
|
||||
virtual ~CMSWindowsScreen();
|
||||
|
@ -57,23 +57,6 @@ public:
|
|||
|
||||
//@}
|
||||
|
||||
// IPlatformScreen overrides
|
||||
virtual void setKeyState(IKeyState*);
|
||||
virtual void enable();
|
||||
virtual void disable();
|
||||
virtual void enter();
|
||||
virtual bool leave();
|
||||
virtual bool setClipboard(ClipboardID, const IClipboard*);
|
||||
virtual void checkClipboards();
|
||||
virtual void openScreensaver(bool notify);
|
||||
virtual void closeScreensaver();
|
||||
virtual void screensaver(bool activate);
|
||||
virtual void resetOptions();
|
||||
virtual void setOptions(const COptionsList& options);
|
||||
virtual void updateKeys();
|
||||
virtual void setSequenceNumber(UInt32);
|
||||
virtual bool isPrimary() const;
|
||||
|
||||
// IScreen overrides
|
||||
virtual void* getEventTarget() const;
|
||||
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
|
||||
|
@ -86,34 +69,38 @@ public:
|
|||
virtual void warpCursor(SInt32 x, SInt32 y);
|
||||
virtual SInt32 getJumpZoneSize() const;
|
||||
virtual bool isAnyMouseButtonDown() const;
|
||||
virtual KeyModifierMask getActiveModifiers() const;
|
||||
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
|
||||
virtual const char* getKeyName(KeyButton) const;
|
||||
|
||||
// ISecondaryScreen overrides
|
||||
virtual void fakeKeyEvent(KeyButton id, bool press) const;
|
||||
virtual bool fakeCtrlAltDel() const;
|
||||
virtual void fakeMouseButton(ButtonID id, bool press) const;
|
||||
virtual void fakeMouseMove(SInt32 x, SInt32 y) const;
|
||||
virtual void fakeMouseWheel(SInt32 delta) const;
|
||||
virtual KeyButton mapKey(IKeyState::Keystrokes&,
|
||||
const IKeyState& keyState, KeyID id,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const;
|
||||
|
||||
// IKeyState overrides
|
||||
virtual void updateKeys();
|
||||
|
||||
// IPlatformScreen overrides
|
||||
virtual void enable();
|
||||
virtual void disable();
|
||||
virtual void enter();
|
||||
virtual bool leave();
|
||||
virtual bool setClipboard(ClipboardID, const IClipboard*);
|
||||
virtual void checkClipboards();
|
||||
virtual void openScreensaver(bool notify);
|
||||
virtual void closeScreensaver();
|
||||
virtual void screensaver(bool activate);
|
||||
virtual void resetOptions();
|
||||
virtual void setOptions(const COptionsList& options);
|
||||
virtual void setSequenceNumber(UInt32);
|
||||
virtual bool isPrimary() const;
|
||||
|
||||
protected:
|
||||
// IPlatformScreen overrides
|
||||
virtual void handleSystemEvent(const CEvent&, void*);
|
||||
virtual void updateButtons();
|
||||
virtual IKeyState* getKeyState() const;
|
||||
|
||||
private:
|
||||
class CDesk {
|
||||
public:
|
||||
CString m_name;
|
||||
CThread* m_thread;
|
||||
DWORD m_threadID;
|
||||
DWORD m_targetID;
|
||||
HDESK m_desk;
|
||||
HWND m_window;
|
||||
bool m_lowLevel;
|
||||
};
|
||||
typedef std::map<CString, CDesk*> CDesks;
|
||||
|
||||
// initialization and shutdown operations
|
||||
HINSTANCE openHookLibrary(const char* name);
|
||||
void closeHookLibrary(HINSTANCE hookLibrary) const;
|
||||
|
@ -129,9 +116,6 @@ private:
|
|||
void sendEvent(CEvent::Type type, void* = NULL);
|
||||
void sendClipboardEvent(CEvent::Type type, ClipboardID id);
|
||||
|
||||
// system event handler (does DispatchMessage)
|
||||
void handleSystemEvent(const CEvent& event, void*);
|
||||
|
||||
// handle message before it gets dispatched. returns true iff
|
||||
// the message should not be dispatched.
|
||||
bool onPreDispatch(HWND, UINT, WPARAM, LPARAM);
|
||||
|
@ -169,42 +153,15 @@ private:
|
|||
// enable/disable special key combinations so we can catch/pass them
|
||||
void enableSpecialKeys(bool) const;
|
||||
|
||||
// send fake key up if shadow state says virtualKey is down but
|
||||
// system says it isn't.
|
||||
void fixKey(UINT virtualKey);
|
||||
|
||||
// map a button ID and action to a mouse event
|
||||
DWORD mapButtonToEvent(ButtonID button,
|
||||
bool press, DWORD* inData) const;
|
||||
|
||||
// map a button event to a button ID
|
||||
ButtonID mapButtonFromEvent(WPARAM msg, LPARAM button) const;
|
||||
|
||||
// return true iff the given virtual key is a modifier
|
||||
bool isModifier(UINT vkCode) const;
|
||||
|
||||
// send ctrl+alt+del hotkey event
|
||||
static void ctrlAltDelThread(void*);
|
||||
// job to update the key state
|
||||
void updateKeysCB(void*);
|
||||
|
||||
// our window proc
|
||||
static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
// our desk window procs
|
||||
static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM);
|
||||
static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
void deskMouseMove(SInt32 x, SInt32 y) const;
|
||||
void deskEnter(CDesk* desk);
|
||||
void deskLeave(CDesk* desk, HKL keyLayout);
|
||||
void deskThread(void* vdesk);
|
||||
CDesk* addDesk(const CString& name, HDESK hdesk);
|
||||
void removeDesks();
|
||||
void checkDesk();
|
||||
bool isDeskAccessible(const CDesk* desk) const;
|
||||
void sendDeskMessage(UINT, WPARAM, LPARAM) const;
|
||||
void waitForDesk() const;
|
||||
void handleCheckDesk(const CEvent& event, void*);
|
||||
|
||||
private:
|
||||
static HINSTANCE s_instance;
|
||||
|
||||
|
@ -219,8 +176,6 @@ private:
|
|||
|
||||
// our resources
|
||||
ATOM m_class;
|
||||
ATOM m_deskClass;
|
||||
HCURSOR m_cursor;
|
||||
|
||||
// screen shape stuff
|
||||
SInt32 m_x, m_y;
|
||||
|
@ -245,9 +200,6 @@ private:
|
|||
// the keyboard layout to use when off primary screen
|
||||
HKL m_keyLayout;
|
||||
|
||||
// the timer used to check for desktop switching
|
||||
CEventQueueTimer* m_timer;
|
||||
|
||||
// screen saver stuff
|
||||
CMSWindowsScreenSaver* m_screensaver;
|
||||
bool m_screensaverNotify;
|
||||
|
@ -258,33 +210,22 @@ private:
|
|||
HWND m_nextClipboardWindow;
|
||||
bool m_ownClipboard;
|
||||
|
||||
// the current desk and it's name
|
||||
CDesk* m_activeDesk;
|
||||
CString m_activeDeskName;
|
||||
|
||||
// one desk per desktop and a cond var to communicate with it
|
||||
CMutex m_mutex;
|
||||
CCondVar<bool> m_deskReady;
|
||||
CDesks m_desks;
|
||||
CMSWindowsDesks* m_desks;
|
||||
|
||||
// hook library stuff
|
||||
HINSTANCE m_hookLibrary;
|
||||
InitFunc m_init;
|
||||
CleanupFunc m_cleanup;
|
||||
InstallFunc m_install;
|
||||
UninstallFunc m_uninstall;
|
||||
SetSidesFunc m_setSides;
|
||||
SetZoneFunc m_setZone;
|
||||
SetModeFunc m_setMode;
|
||||
InstallScreenSaverFunc m_installScreensaver;
|
||||
UninstallScreenSaverFunc m_uninstallScreensaver;
|
||||
|
||||
// keyboard stuff
|
||||
IKeyState* m_keyState;
|
||||
CMSWindowsKeyMapper m_keyMapper;
|
||||
CMSWindowsKeyState* m_keyState;
|
||||
|
||||
// map of button state
|
||||
BYTE m_buttons[1 + kButtonExtra0 + 1];
|
||||
bool m_buttons[1 + kButtonExtra0 + 1];
|
||||
|
||||
// suspend/resume callbacks
|
||||
IJob* m_suspend;
|
||||
|
|
|
@ -219,8 +219,8 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|||
// 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.
|
||||
BYTE control = keys[VK_CONTROL];
|
||||
BYTE menu = keys[VK_MENU];
|
||||
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;
|
||||
|
@ -228,8 +228,10 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -237,7 +239,7 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
|
|||
// 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.
|
||||
// XXX -- figure out some way to check if a menu is active
|
||||
// FIXME -- figure out some way to check if a menu is active
|
||||
UINT flags = 0;
|
||||
if ((menu & 0x80) != 0)
|
||||
flags |= 1;
|
||||
|
|
|
@ -174,6 +174,13 @@ CXWindowsKeyState::mapModifiersFromX(unsigned int state) const
|
|||
return mask;
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsKeyState::fakeCtrlAltDel()
|
||||
{
|
||||
// pass keys through unchanged
|
||||
return false;
|
||||
}
|
||||
|
||||
const char*
|
||||
CXWindowsKeyState::getKeyName(KeyButton keycode) const
|
||||
{
|
||||
|
@ -864,7 +871,7 @@ CXWindowsKeyState::findBestKeyIndex(KeySymIndex keyIndex,
|
|||
// if the action is an auto-repeat then we don't call this
|
||||
// method since we just need to synthesize a key repeat on the
|
||||
// same keycode that we pressed.
|
||||
// XXX -- do this right
|
||||
// FIXME -- do this right
|
||||
for (unsigned int i = 0; i < 4; ++i) {
|
||||
if (keyIndex->second.m_keycode[i] != 0) {
|
||||
return i;
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
//@}
|
||||
|
||||
// IKeyState overrides
|
||||
virtual bool fakeCtrlAltDel();
|
||||
virtual const char* getKeyName(KeyButton) const;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -241,9 +241,8 @@ CXWindowsScreen::enable()
|
|||
// warp the mouse to the cursor center
|
||||
fakeMouseMove(m_xCenter, m_yCenter);
|
||||
}
|
||||
else {
|
||||
m_keyState->updateKeys();
|
||||
}
|
||||
|
||||
updateKeys();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -545,13 +544,6 @@ CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
|
|||
y = m_yCenter;
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsScreen::fakeCtrlAltDel() const
|
||||
{
|
||||
// pass keys through unchanged
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsScreen::fakeMouseButton(ButtonID button, bool press) const
|
||||
{
|
||||
|
|
|
@ -53,7 +53,6 @@ public:
|
|||
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
|
||||
|
||||
// ISecondaryScreen overrides
|
||||
virtual bool fakeCtrlAltDel() const;
|
||||
virtual void fakeMouseButton(ButtonID id, bool press) const;
|
||||
virtual void fakeMouseMove(SInt32 x, SInt32 y) const;
|
||||
virtual void fakeMouseWheel(SInt32 delta) const;
|
||||
|
|
|
@ -103,7 +103,7 @@ SOURCE=.\CMSWindowsClipboardUTF16Converter.cpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CMSWindowsDesktop.cpp
|
||||
SOURCE=.\CMSWindowsDesks.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
|
@ -111,7 +111,7 @@ SOURCE=.\CMSWindowsEventQueueBuffer.cpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CMSWindowsKeyMapper.cpp
|
||||
SOURCE=.\CMSWindowsKeyState.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
|
@ -147,7 +147,7 @@ SOURCE=.\CMSWindowsClipboardUTF16Converter.h
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CMSWindowsDesktop.h
|
||||
SOURCE=.\CMSWindowsDesks.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
|
@ -155,7 +155,7 @@ SOURCE=.\CMSWindowsEventQueueBuffer.h
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CMSWindowsKeyMapper.h
|
||||
SOURCE=.\CMSWindowsKeyState.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
|
|
|
@ -294,7 +294,7 @@ UInt32
|
|||
CServer::getActivePrimarySides() const
|
||||
{
|
||||
UInt32 sides = 0;
|
||||
if (!isLockedToScreen()) {
|
||||
if (!isLockedToScreenServer()) {
|
||||
if (getNeighbor(m_primaryClient, kLeft) != NULL) {
|
||||
sides |= kLeftMask;
|
||||
}
|
||||
|
@ -312,7 +312,7 @@ CServer::getActivePrimarySides() const
|
|||
}
|
||||
|
||||
bool
|
||||
CServer::isLockedToScreen() const
|
||||
CServer::isLockedToScreenServer() const
|
||||
{
|
||||
// locked if scroll-lock is toggled on
|
||||
if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) {
|
||||
|
@ -320,6 +320,18 @@ CServer::isLockedToScreen() const
|
|||
return true;
|
||||
}
|
||||
|
||||
// not locked
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CServer::isLockedToScreen() const
|
||||
{
|
||||
// locked if we say we're locked
|
||||
if (isLockedToScreenServer()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// locked if primary says we're locked
|
||||
if (m_primaryClient->isLockedToScreen()) {
|
||||
return true;
|
||||
|
|
|
@ -120,6 +120,12 @@ private:
|
|||
UInt32 getActivePrimarySides() const;
|
||||
|
||||
// returns true iff mouse should be locked to the current screen
|
||||
// according to this object only, ignoring what the primary client
|
||||
// says.
|
||||
bool isLockedToScreenServer() const;
|
||||
|
||||
// returns true iff mouse should be locked to the current screen
|
||||
// according to this object or the primary client.
|
||||
bool isLockedToScreen() const;
|
||||
|
||||
// returns the jump zone of the client
|
||||
|
|
|
@ -39,14 +39,7 @@ void
|
|||
CKeyState::setKeyDown(KeyButton button, bool down)
|
||||
{
|
||||
button &= kButtonMask;
|
||||
if (button != 0) {
|
||||
if (down) {
|
||||
m_keys[button] |= kDown;
|
||||
}
|
||||
else {
|
||||
m_keys[button] &= ~kDown;
|
||||
}
|
||||
}
|
||||
updateKeyState(button, button, down);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -144,7 +137,8 @@ CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button)
|
|||
// get the sequence of keys to simulate key press and the final
|
||||
// modifier state.
|
||||
Keystrokes keys;
|
||||
KeyButton localID = (mapKey(keys, id, mask, false) & kButtonMask);
|
||||
KeyButton localID =
|
||||
(KeyButton)(mapKey(keys, id, mask, false) & kButtonMask);
|
||||
if (keys.empty()) {
|
||||
// do nothing if there are no associated keys
|
||||
LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id));
|
||||
|
@ -155,7 +149,7 @@ CKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button)
|
|||
fakeKeyEvents(keys, 1);
|
||||
|
||||
// note that key is down
|
||||
updateKeyState(button & kButtonMask, localID, true);
|
||||
updateKeyState((KeyButton)(button & kButtonMask), localID, true);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -174,7 +168,7 @@ CKeyState::fakeKeyRepeat(
|
|||
// get the sequence of keys to simulate key repeat and the final
|
||||
// modifier state.
|
||||
Keystrokes keys;
|
||||
KeyButton localID = (mapKey(keys, id, mask, true) & kButtonMask);
|
||||
KeyButton localID = (KeyButton)(mapKey(keys, id, mask, true) & kButtonMask);
|
||||
if (localID == 0) {
|
||||
LOG((CLOG_DEBUG2 "cannot map key 0x%08x", id));
|
||||
return;
|
||||
|
@ -511,12 +505,16 @@ CKeyState::updateKeyState(KeyButton serverID, KeyButton localID, bool press)
|
|||
KeyModifierMask mask = m_keyToMask[localID];
|
||||
if (mask != 0) {
|
||||
if (isToggle(mask)) {
|
||||
m_keys[localID] ^= kToggled;
|
||||
m_mask ^= mask;
|
||||
|
||||
// never report half-duplex keys as down
|
||||
if (isHalfDuplex(mask)) {
|
||||
m_keys[localID] &= ~kDown;
|
||||
press = true;
|
||||
}
|
||||
|
||||
// toggle on the press
|
||||
if (press) {
|
||||
m_keys[localID] ^= kToggled;
|
||||
m_mask ^= mask;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -33,9 +33,10 @@ public:
|
|||
|
||||
//! Mark key as being down
|
||||
/*!
|
||||
Sets the state of \p button to down or up.
|
||||
Sets the state of \p button to down or up. If this is overridden
|
||||
it must forward to the superclass.
|
||||
*/
|
||||
void setKeyDown(KeyButton button, bool down);
|
||||
virtual void setKeyDown(KeyButton button, bool down);
|
||||
|
||||
//! Mark modifier as being toggled on
|
||||
/*!
|
||||
|
@ -47,9 +48,10 @@ public:
|
|||
//! Post a key event
|
||||
/*!
|
||||
Posts a key event. This may adjust the event or post additional
|
||||
events in some circumstances.
|
||||
events in some circumstances. If this is overridden it must forward
|
||||
to the superclass.
|
||||
*/
|
||||
void sendKeyEvent(void* target,
|
||||
virtual void sendKeyEvent(void* target,
|
||||
bool press, bool isAutoRepeat,
|
||||
KeyID key, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button);
|
||||
|
@ -69,6 +71,7 @@ public:
|
|||
SInt32 count, KeyButton button);
|
||||
virtual void fakeKeyUp(KeyButton button);
|
||||
virtual void fakeToggle(KeyModifierMask modifier);
|
||||
virtual bool fakeCtrlAltDel() = 0;
|
||||
virtual bool isKeyDown(KeyButton) const;
|
||||
virtual KeyModifierMask
|
||||
getActiveModifiers() const;
|
||||
|
|
|
@ -63,6 +63,12 @@ CPlatformScreen::fakeToggle(KeyModifierMask modifier)
|
|||
getKeyState()->fakeToggle(modifier);
|
||||
}
|
||||
|
||||
bool
|
||||
CPlatformScreen::fakeCtrlAltDel()
|
||||
{
|
||||
return getKeyState()->fakeCtrlAltDel();
|
||||
}
|
||||
|
||||
bool
|
||||
CPlatformScreen::isKeyDown(KeyButton button) const
|
||||
{
|
||||
|
|
|
@ -43,7 +43,6 @@ public:
|
|||
virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
|
||||
|
||||
// ISecondaryScreen overrides
|
||||
virtual bool fakeCtrlAltDel() const = 0;
|
||||
virtual void fakeMouseButton(ButtonID id, bool press) const = 0;
|
||||
virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0;
|
||||
virtual void fakeMouseWheel(SInt32 delta) const = 0;
|
||||
|
@ -57,6 +56,7 @@ public:
|
|||
SInt32 count, KeyButton button);
|
||||
virtual void fakeKeyUp(KeyButton button);
|
||||
virtual void fakeToggle(KeyModifierMask modifier);
|
||||
virtual bool fakeCtrlAltDel();
|
||||
virtual bool isKeyDown(KeyButton) const;
|
||||
virtual KeyModifierMask
|
||||
getActiveModifiers() const;
|
||||
|
|
|
@ -98,13 +98,13 @@ CScreen::enter(KeyModifierMask toggleMask)
|
|||
// now on screen
|
||||
m_entered = true;
|
||||
|
||||
m_screen->enter();
|
||||
if (m_isPrimary) {
|
||||
enterPrimary();
|
||||
}
|
||||
else {
|
||||
enterSecondary(toggleMask);
|
||||
}
|
||||
m_screen->enter();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -315,6 +315,16 @@ CScreen::isLockedToScreen() const
|
|||
return true;
|
||||
}
|
||||
|
||||
// note -- we don't lock to the screen if a key is down. key
|
||||
// reporting is simply not reliable enough to trust. the effect
|
||||
// of switching screens with a key down is that the client will
|
||||
// receive key repeats and key releases for keys that it hasn't
|
||||
// see go down. that's okay because CKeyState will ignore those
|
||||
// events. the user might be surprised that any modifier keys
|
||||
// held while crossing to another screen don't apply on the
|
||||
// target screen. if that ends up being a problem we can try
|
||||
// to synthesize a key press for those modifiers on entry.
|
||||
/*
|
||||
// check for any pressed key
|
||||
KeyButton key = isAnyKeyDown();
|
||||
if (key != 0) {
|
||||
|
@ -332,6 +342,7 @@ CScreen::isLockedToScreen() const
|
|||
LOG((CLOG_DEBUG "spuriously locked by %s", m_screen->getKeyName(key)));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// not locked
|
||||
return false;
|
||||
|
@ -431,9 +442,6 @@ CScreen::enterPrimary()
|
|||
void
|
||||
CScreen::enterSecondary(KeyModifierMask toggleMask)
|
||||
{
|
||||
// update our keyboard state to reflect the local state
|
||||
m_screen->updateKeys();
|
||||
|
||||
// remember toggle key state. we'll restore this when we leave.
|
||||
m_toggleKeys = getActiveModifiers();
|
||||
|
||||
|
|
|
@ -87,6 +87,13 @@ public:
|
|||
*/
|
||||
virtual void fakeToggle(KeyModifierMask modifier) = 0;
|
||||
|
||||
//! Fake ctrl+alt+del
|
||||
/*!
|
||||
Synthesize a press of ctrl+alt+del. Return true if processing is
|
||||
complete and false if normal key processing should continue.
|
||||
*/
|
||||
virtual bool fakeCtrlAltDel() = 0;
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
|
|
@ -151,7 +151,6 @@ public:
|
|||
virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
|
||||
|
||||
// ISecondaryScreen overrides
|
||||
virtual bool fakeCtrlAltDel() const = 0;
|
||||
virtual void fakeMouseButton(ButtonID id, bool press) const = 0;
|
||||
virtual void fakeMouseMove(SInt32 x, SInt32 y) const = 0;
|
||||
virtual void fakeMouseWheel(SInt32 delta) const = 0;
|
||||
|
@ -165,6 +164,7 @@ public:
|
|||
SInt32 count, KeyButton button) = 0;
|
||||
virtual void fakeKeyUp(KeyButton button) = 0;
|
||||
virtual void fakeToggle(KeyModifierMask modifier) = 0;
|
||||
virtual bool fakeCtrlAltDel() = 0;
|
||||
virtual bool isKeyDown(KeyButton) const = 0;
|
||||
virtual KeyModifierMask
|
||||
getActiveModifiers() const = 0;
|
||||
|
|
|
@ -28,13 +28,6 @@ public:
|
|||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Fake ctrl+alt+del
|
||||
/*!
|
||||
Synthesize a press of ctrl+alt+del. Return true if processing is
|
||||
complete and false if normal key processing should continue.
|
||||
*/
|
||||
virtual bool fakeCtrlAltDel() const = 0;
|
||||
|
||||
//! Fake mouse press/release
|
||||
/*!
|
||||
Synthesize a press or release of mouse button \c id.
|
||||
|
|
|
@ -91,10 +91,18 @@ SOURCE=.\CClipboard.cpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CKeyState.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CPacketStreamFilter.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CPlatformScreen.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CProtocolUtil.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -107,6 +115,10 @@ SOURCE=.\IClipboard.cpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\IKeyState.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\IPrimaryScreen.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -131,6 +143,10 @@ SOURCE=.\CClipboard.h
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CKeyState.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ClipboardTypes.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -139,6 +155,10 @@ SOURCE=.\CPacketStreamFilter.h
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CPlatformScreen.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\CProtocolUtil.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
|
Loading…
Reference in New Issue