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:
crs 2004-03-26 20:59:26 +00:00
parent ab11ebea01
commit e2a31e8b66
26 changed files with 1974 additions and 1761 deletions

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -50,6 +50,7 @@ public:
//@}
// IKeyState overrides
virtual bool fakeCtrlAltDel();
virtual const char* getKeyName(KeyButton) const;
protected:

View File

@ -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
{

View File

@ -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;

View File

@ -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

View 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;

View File

@ -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

View File

@ -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 {

View File

@ -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;

View File

@ -63,6 +63,12 @@ CPlatformScreen::fakeToggle(KeyModifierMask modifier)
getKeyState()->fakeToggle(modifier);
}
bool
CPlatformScreen::fakeCtrlAltDel()
{
return getKeyState()->fakeCtrlAltDel();
}
bool
CPlatformScreen::isKeyDown(KeyButton button) const
{

View File

@ -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;

View File

@ -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();

View File

@ -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
//@{

View File

@ -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;

View File

@ -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.

View File

@ -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