2002-08-02 19:57:46 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2001-11-19 00:33:36 +00:00
|
|
|
#include "CMSWindowsScreen.h"
|
2002-07-15 15:01:36 +00:00
|
|
|
#include "CMSWindowsClipboard.h"
|
2003-09-02 22:05:47 +00:00
|
|
|
#include "CMSWindowsDesktop.h"
|
2002-06-23 21:53:31 +00:00
|
|
|
#include "CMSWindowsScreenSaver.h"
|
2002-07-15 15:01:36 +00:00
|
|
|
#include "CClipboard.h"
|
|
|
|
#include "IScreenReceiver.h"
|
2003-09-02 22:05:47 +00:00
|
|
|
#include "IPrimaryScreenReceiver.h"
|
|
|
|
#include "XScreen.h"
|
2001-11-19 00:33:36 +00:00
|
|
|
#include "CThread.h"
|
|
|
|
#include "CLock.h"
|
2003-09-02 22:05:47 +00:00
|
|
|
#include "CFunctionJob.h"
|
2001-11-19 00:33:36 +00:00
|
|
|
#include "CLog.h"
|
|
|
|
#include "CString.h"
|
2003-01-04 22:01:32 +00:00
|
|
|
#include "CStringUtil.h"
|
2003-09-02 22:05:47 +00:00
|
|
|
#include "TMethodJob.h"
|
|
|
|
#include "CArch.h"
|
2003-01-04 22:01:32 +00:00
|
|
|
#include "CArchMiscWindows.h"
|
2002-06-10 22:06:45 +00:00
|
|
|
#include <cstring>
|
2003-02-16 19:50:36 +00:00
|
|
|
#include <malloc.h>
|
|
|
|
#include <tchar.h>
|
2001-11-19 00:33:36 +00:00
|
|
|
|
2002-06-19 17:03:29 +00:00
|
|
|
//
|
2002-07-15 15:01:36 +00:00
|
|
|
// add backwards compatible multihead support (and suppress bogus warning)
|
2002-06-19 17:03:29 +00:00
|
|
|
//
|
|
|
|
#pragma warning(push)
|
|
|
|
#pragma warning(disable: 4706) // assignment within conditional
|
|
|
|
#define COMPILE_MULTIMON_STUBS
|
|
|
|
#include <multimon.h>
|
|
|
|
#pragma warning(pop)
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// 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
|
|
|
|
|
|
|
|
// multimedia keys
|
|
|
|
#if !defined(VK_BROWSER_BACK)
|
|
|
|
#define VK_BROWSER_BACK 0xA6
|
|
|
|
#define VK_BROWSER_FORWARD 0xA7
|
|
|
|
#define VK_BROWSER_REFRESH 0xA8
|
|
|
|
#define VK_BROWSER_STOP 0xA9
|
|
|
|
#define VK_BROWSER_SEARCH 0xAA
|
|
|
|
#define VK_BROWSER_FAVORITES 0xAB
|
|
|
|
#define VK_BROWSER_HOME 0xAC
|
|
|
|
#define VK_VOLUME_MUTE 0xAD
|
|
|
|
#define VK_VOLUME_DOWN 0xAE
|
|
|
|
#define VK_VOLUME_UP 0xAF
|
|
|
|
#define VK_MEDIA_NEXT_TRACK 0xB0
|
|
|
|
#define VK_MEDIA_PREV_TRACK 0xB1
|
|
|
|
#define VK_MEDIA_STOP 0xB2
|
|
|
|
#define VK_MEDIA_PLAY_PAUSE 0xB3
|
|
|
|
#define VK_LAUNCH_MAIL 0xB4
|
|
|
|
#define VK_LAUNCH_MEDIA_SELECT 0xB5
|
|
|
|
#define VK_LAUNCH_APP1 0xB6
|
|
|
|
#define VK_LAUNCH_APP2 0xB7
|
|
|
|
#endif
|
|
|
|
|
2001-11-19 00:33:36 +00:00
|
|
|
//
|
|
|
|
// CMSWindowsScreen
|
|
|
|
//
|
|
|
|
|
|
|
|
HINSTANCE CMSWindowsScreen::s_instance = NULL;
|
2001-11-25 18:32:41 +00:00
|
|
|
CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL;
|
2001-11-19 00:33:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::CMSWindowsScreen(
|
|
|
|
IScreenReceiver* receiver,
|
|
|
|
IPrimaryScreenReceiver* primaryReceiver) :
|
|
|
|
m_isPrimary(primaryReceiver != NULL),
|
|
|
|
m_is95Family(CArchMiscWindows::isWindows95Family()),
|
2002-07-15 15:01:36 +00:00
|
|
|
m_receiver(receiver),
|
2003-09-02 22:05:47 +00:00
|
|
|
m_primaryReceiver(primaryReceiver),
|
|
|
|
m_isOnScreen(m_isPrimary),
|
|
|
|
m_class(0),
|
2002-06-10 22:06:45 +00:00
|
|
|
m_cursor(NULL),
|
2002-07-15 15:01:36 +00:00
|
|
|
m_window(NULL),
|
2002-06-19 17:03:29 +00:00
|
|
|
m_x(0), m_y(0),
|
2002-06-10 22:06:45 +00:00
|
|
|
m_w(0), m_h(0),
|
2003-09-02 22:05:47 +00:00
|
|
|
m_xCenter(0), m_yCenter(0),
|
2002-07-15 15:01:36 +00:00
|
|
|
m_multimon(false),
|
2003-09-02 22:05:47 +00:00
|
|
|
m_xCursor(0), m_yCursor(0),
|
|
|
|
m_mark(0),
|
|
|
|
m_markReceived(0),
|
2002-07-15 15:01:36 +00:00
|
|
|
m_threadID(0),
|
|
|
|
m_lastThreadID(0),
|
2003-09-02 22:05:47 +00:00
|
|
|
m_timer(0),
|
|
|
|
m_oneShotTimer(0),
|
|
|
|
m_screensaver(NULL),
|
|
|
|
m_screensaverNotify(false),
|
2002-07-15 15:01:36 +00:00
|
|
|
m_nextClipboardWindow(NULL),
|
Fixed several win32 bugs. First, synergy wasn't forwarding mouse
events to other hook functions, which broke some tools like objectbar.
Second, windows key processing was fixed. Previously pressing and
release the key would only send a press event, locking the user onto
the client window; also, the win32 server treated as a Meta modifier
instead of a Super modifier, which broke any use of it as any kind of
modifier key. Third, added hacks to support several key combinations
on windows 95/98/me that are treated specially by windows, including
Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any
combination using the windows key like Win+E and Win+F but not
Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which
only happened when using a synergy server on windows) has been fixed;
unfortunately the solution causes a lot of screen redraws for some
reason. Finally, there's been a fix to clipboard handling that may
or may not fix a problem where the clipboard would stop transferring
between systems after a little while. I can't be sure if it fixes
the problem because I can't reproduce the problem.
2003-04-13 14:59:53 +00:00
|
|
|
m_ownClipboard(false),
|
2002-07-15 15:01:36 +00:00
|
|
|
m_desk(NULL),
|
|
|
|
m_deskName(),
|
2003-09-02 22:05:47 +00:00
|
|
|
m_inaccessibleDesktop(false),
|
2002-07-15 15:01:36 +00:00
|
|
|
m_hookLibrary(NULL),
|
2003-09-02 22:05:47 +00:00
|
|
|
m_lowLevel(false),
|
|
|
|
m_keyState(NULL)
|
2001-11-19 00:33:36 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
assert(s_screen == NULL);
|
|
|
|
assert(m_receiver != NULL);
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2001-11-25 18:32:41 +00:00
|
|
|
s_screen = this;
|
2002-07-15 15:01:36 +00:00
|
|
|
|
|
|
|
// make sure this thread has a message queue
|
|
|
|
MSG dummy;
|
|
|
|
PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE);
|
2001-11-19 00:33:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CMSWindowsScreen::~CMSWindowsScreen()
|
|
|
|
{
|
2002-07-15 15:01:36 +00:00
|
|
|
assert(s_screen != NULL);
|
|
|
|
assert(m_class == 0);
|
|
|
|
|
2001-11-25 18:32:41 +00:00
|
|
|
s_screen = NULL;
|
2001-11-19 00:33:36 +00:00
|
|
|
}
|
|
|
|
|
2002-06-10 22:06:45 +00:00
|
|
|
void
|
2002-06-17 13:31:21 +00:00
|
|
|
CMSWindowsScreen::init(HINSTANCE instance)
|
2001-11-19 00:33:36 +00:00
|
|
|
{
|
|
|
|
s_instance = instance;
|
|
|
|
}
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
HINSTANCE
|
|
|
|
CMSWindowsScreen::getInstance()
|
2001-11-19 00:33:36 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
return s_instance;
|
|
|
|
}
|
2002-05-22 17:08:37 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::open(IKeyState* keyState)
|
|
|
|
{
|
|
|
|
assert(s_instance != NULL);
|
|
|
|
assert(m_class == 0);
|
|
|
|
assert(m_hookLibrary == NULL);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// load the hook library
|
|
|
|
m_hookLibrary = LoadLibrary("synrgyhk");
|
|
|
|
if (m_hookLibrary == NULL) {
|
|
|
|
LOG((CLOG_ERR "Failed to load hook library; synrgyhk.dll is missing"));
|
|
|
|
throw XScreenOpenFailure();
|
|
|
|
}
|
|
|
|
m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides");
|
|
|
|
m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone");
|
|
|
|
m_setMode = (SetModeFunc)GetProcAddress(m_hookLibrary, "setMode");
|
|
|
|
m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install");
|
|
|
|
m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall");
|
|
|
|
m_init = (InitFunc)GetProcAddress(m_hookLibrary, "init");
|
|
|
|
m_cleanup = (CleanupFunc)GetProcAddress(m_hookLibrary, "cleanup");
|
|
|
|
m_installScreensaver =
|
|
|
|
(InstallScreenSaverFunc)GetProcAddress(
|
|
|
|
m_hookLibrary, "installScreenSaver");
|
|
|
|
m_uninstallScreensaver =
|
|
|
|
(UninstallScreenSaverFunc)GetProcAddress(
|
|
|
|
m_hookLibrary, "uninstallScreenSaver");
|
|
|
|
if (m_setSides == NULL ||
|
|
|
|
m_setZone == NULL ||
|
|
|
|
m_setMode == NULL ||
|
|
|
|
m_install == NULL ||
|
|
|
|
m_uninstall == NULL ||
|
|
|
|
m_init == NULL ||
|
|
|
|
m_cleanup == NULL ||
|
|
|
|
m_installScreensaver == NULL ||
|
|
|
|
m_uninstallScreensaver == NULL) {
|
|
|
|
LOG((CLOG_ERR "Invalid hook library; use a newer synrgyhk.dll"));
|
|
|
|
throw XScreenOpenFailure();
|
|
|
|
}
|
2001-11-19 00:33:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// save thread id. this is mainly to ensure that mainLoop()
|
|
|
|
// is called by the same thread that called open(). these
|
|
|
|
// threads must be the same to get the right message queue.
|
|
|
|
m_threadID = GetCurrentThreadId();
|
|
|
|
|
|
|
|
// initialize hook library
|
|
|
|
if (m_isPrimary && m_init(m_threadID) == 0) {
|
|
|
|
LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?"));
|
|
|
|
throw XScreenOpenFailure();
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the transparent cursor
|
|
|
|
m_cursor = createBlankCursor();
|
|
|
|
|
|
|
|
// register a window class
|
|
|
|
WNDCLASSEX classInfo;
|
|
|
|
classInfo.cbSize = sizeof(classInfo);
|
|
|
|
classInfo.style = CS_DBLCLKS | CS_NOCLOSE;
|
|
|
|
classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc;
|
|
|
|
classInfo.cbClsExtra = 0;
|
|
|
|
classInfo.cbWndExtra = 0;
|
|
|
|
classInfo.hInstance = s_instance;
|
|
|
|
classInfo.hIcon = NULL;
|
|
|
|
classInfo.hCursor = m_cursor;
|
|
|
|
classInfo.hbrBackground = NULL;
|
|
|
|
classInfo.lpszMenuName = NULL;
|
|
|
|
classInfo.lpszClassName = "Synergy";
|
|
|
|
classInfo.hIconSm = NULL;
|
|
|
|
m_class = RegisterClassEx(&classInfo);
|
|
|
|
|
|
|
|
// get screen shape
|
|
|
|
updateScreenShape();
|
|
|
|
|
|
|
|
// initialize the screen saver
|
|
|
|
m_screensaver = new CMSWindowsScreenSaver();
|
|
|
|
|
|
|
|
// initialize marks
|
|
|
|
m_mark = 0;
|
|
|
|
m_markReceived = 0;
|
|
|
|
}
|
|
|
|
catch (...) {
|
|
|
|
close();
|
|
|
|
throw;
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// save the IKeyState
|
|
|
|
m_keyState = keyState;
|
2001-11-19 00:33:36 +00:00
|
|
|
}
|
|
|
|
|
2002-06-10 22:06:45 +00:00
|
|
|
void
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::close()
|
2001-11-19 00:33:36 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
assert(s_instance != NULL);
|
|
|
|
|
|
|
|
// done with m_keyState
|
|
|
|
m_keyState = NULL;
|
|
|
|
|
|
|
|
// done with screen saver
|
|
|
|
delete m_screensaver;
|
|
|
|
|
|
|
|
// unregister the window class
|
|
|
|
if (m_class != 0) {
|
|
|
|
UnregisterClass((LPCTSTR)m_class, s_instance);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// done with cursor
|
|
|
|
if (m_cursor != NULL) {
|
|
|
|
DestroyCursor(m_cursor);
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// done with hook library
|
|
|
|
if (m_hookLibrary != NULL) {
|
|
|
|
if (m_isPrimary) {
|
|
|
|
m_cleanup();
|
|
|
|
}
|
|
|
|
FreeLibrary(m_hookLibrary);
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// reset state
|
|
|
|
m_screensaver = NULL;
|
|
|
|
m_cursor = NULL;
|
|
|
|
m_class = 0;
|
|
|
|
m_hookLibrary = NULL;
|
|
|
|
m_threadID = 0;
|
2001-11-19 00:33:36 +00:00
|
|
|
}
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::enable()
|
2003-03-12 22:34:07 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
assert(m_isOnScreen == m_isPrimary);
|
|
|
|
|
|
|
|
if (m_isPrimary) {
|
|
|
|
// update shadow key state
|
|
|
|
m_keyMapper.update(NULL);
|
|
|
|
|
|
|
|
// set jump zones
|
|
|
|
m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
|
|
|
|
|
|
|
|
// watch jump zones
|
|
|
|
m_setMode(kHOOK_WATCH_JUMP_ZONE);
|
2003-03-12 22:34:07 +00:00
|
|
|
}
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// create the window
|
|
|
|
if (!switchDesktop(CMSWindowsDesktop::openInputDesktop())) {
|
|
|
|
throw XScreenOpenFailure();
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// poll input desktop to see if it changes. windows doesn't
|
|
|
|
// inform us when the desktop has changed but we need to
|
|
|
|
// open a new window when that happens so we poll. this is
|
|
|
|
// also used for polling other stuff.
|
|
|
|
m_timer = SetTimer(NULL, 0, 200, NULL);
|
2002-07-12 20:41:23 +00:00
|
|
|
}
|
|
|
|
|
2002-06-10 22:06:45 +00:00
|
|
|
void
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::disable()
|
2001-11-19 00:33:36 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
// remove timers
|
|
|
|
if (m_timer != 0) {
|
|
|
|
KillTimer(NULL, m_timer);
|
|
|
|
}
|
|
|
|
if (m_oneShotTimer != 0) {
|
|
|
|
KillTimer(NULL, m_oneShotTimer);
|
|
|
|
}
|
2001-11-19 00:33:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// reset state
|
|
|
|
m_timer = 0;
|
|
|
|
m_oneShotTimer = 0;
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// done with window
|
|
|
|
switchDesktop(NULL);
|
|
|
|
assert(m_window == NULL);
|
|
|
|
assert(m_desk == NULL);
|
2001-11-19 00:33:36 +00:00
|
|
|
}
|
|
|
|
|
2002-06-10 22:06:45 +00:00
|
|
|
void
|
2002-07-15 15:01:36 +00:00
|
|
|
CMSWindowsScreen::mainLoop()
|
|
|
|
{
|
|
|
|
// must call mainLoop() from same thread as openDesktop()
|
|
|
|
assert(m_threadID == GetCurrentThreadId());
|
|
|
|
|
|
|
|
// event loop
|
2003-09-02 22:05:47 +00:00
|
|
|
MSG msg;
|
2002-07-15 15:01:36 +00:00
|
|
|
for (;;) {
|
|
|
|
// wait for an event in a cancellable way
|
2003-03-12 22:34:07 +00:00
|
|
|
if (CThread::getCurrentThread().waitForEvent(-1.0) != CThread::kEvent) {
|
|
|
|
continue;
|
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
2003-03-12 22:34:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
|
|
|
// handle quit message
|
2003-09-02 22:05:47 +00:00
|
|
|
if (msg.message == WM_QUIT) {
|
|
|
|
if (msg.wParam == 0) {
|
2002-12-15 19:58:41 +00:00
|
|
|
// force termination
|
|
|
|
CThread::getCurrentThread().cancel();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// just exit the main loop
|
|
|
|
break;
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// dispatch message
|
2003-09-02 22:05:47 +00:00
|
|
|
if (!onPreDispatch(msg.hwnd, msg.message, msg.wParam, msg.lParam)) {
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CMSWindowsScreen::exitMainLoop()
|
|
|
|
{
|
2002-12-15 19:58:41 +00:00
|
|
|
// close down cleanly
|
|
|
|
PostThreadMessage(m_threadID, WM_QUIT, 1, 0);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::enter()
|
2001-11-19 00:33:36 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
if (m_isPrimary) {
|
|
|
|
// show the cursor
|
|
|
|
showCursor(true);
|
|
|
|
m_cursorThread = 0;
|
2002-06-23 21:53:31 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// enable special key sequences on win95 family
|
|
|
|
enableSpecialKeys(true);
|
|
|
|
|
|
|
|
// watch jump zones
|
|
|
|
m_setMode(kHOOK_WATCH_JUMP_ZONE);
|
|
|
|
|
|
|
|
// all messages prior to now are invalid
|
|
|
|
nextMark();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// show the cursor
|
|
|
|
ShowWindow(m_window, SW_HIDE);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// now on screen
|
|
|
|
m_isOnScreen = true;
|
|
|
|
}
|
2001-11-19 00:33:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
bool
|
|
|
|
CMSWindowsScreen::leave()
|
|
|
|
{
|
|
|
|
if (m_isPrimary) {
|
|
|
|
// show window
|
|
|
|
/* XXX
|
|
|
|
if (m_lowLevel) {
|
|
|
|
SetWindowPos(m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1,
|
|
|
|
SWP_NOACTIVATE);
|
|
|
|
ShowWindow(m_window, SW_SHOWNA);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
// update keys
|
|
|
|
/* XXX
|
|
|
|
m_keyMapper.update(NULL);
|
|
|
|
*/
|
|
|
|
|
|
|
|
// warp to center
|
|
|
|
warpCursor(m_xCenter, m_yCenter);
|
|
|
|
|
|
|
|
// disable special key sequences on win95 family
|
|
|
|
enableSpecialKeys(false);
|
|
|
|
|
|
|
|
// all messages prior to now are invalid
|
|
|
|
nextMark();
|
|
|
|
|
|
|
|
// watch jump zones
|
|
|
|
m_setMode(kHOOK_RELAY_EVENTS);
|
|
|
|
|
|
|
|
// hide the cursor if using low level hooks
|
|
|
|
if (m_lowLevel) {
|
|
|
|
HWND hwnd = GetForegroundWindow();
|
|
|
|
m_cursorThread = GetWindowThreadProcessId(hwnd, NULL);
|
|
|
|
showCursor(false);
|
|
|
|
}
|
2002-07-11 18:58:49 +00:00
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
else {
|
|
|
|
// move hider window under the cursor center
|
|
|
|
MoveWindow(m_window, m_xCenter, m_yCenter, 1, 1, FALSE);
|
2001-11-19 00:33:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// raise and show the hider window
|
|
|
|
ShowWindow(m_window, SW_SHOWNA);
|
|
|
|
|
|
|
|
// warp the mouse to the cursor center
|
|
|
|
fakeMouseMove(m_xCenter, m_yCenter);
|
2002-07-11 18:58:49 +00:00
|
|
|
}
|
2001-11-19 00:33:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// now off screen
|
|
|
|
m_isOnScreen = false;
|
|
|
|
|
|
|
|
return true;
|
2001-11-19 00:33:36 +00:00
|
|
|
}
|
|
|
|
|
2002-07-15 15:01:36 +00:00
|
|
|
bool
|
|
|
|
CMSWindowsScreen::setClipboard(ClipboardID, const IClipboard* src)
|
2001-11-19 00:33:36 +00:00
|
|
|
{
|
2002-07-15 15:01:36 +00:00
|
|
|
CMSWindowsClipboard dst(m_window);
|
|
|
|
if (src != NULL) {
|
|
|
|
// save clipboard data
|
|
|
|
return CClipboard::copy(&dst, src);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// assert clipboard ownership
|
|
|
|
if (!dst.open(0)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
dst.empty();
|
|
|
|
dst.close();
|
|
|
|
return true;
|
|
|
|
}
|
2001-11-19 00:33:36 +00:00
|
|
|
}
|
|
|
|
|
2002-07-15 15:01:36 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::checkClipboards()
|
2001-11-19 00:33:36 +00:00
|
|
|
{
|
2002-07-15 15:01:36 +00:00
|
|
|
// if we think we own the clipboard but we don't then somebody
|
|
|
|
// grabbed the clipboard on this screen without us knowing.
|
|
|
|
// tell the server that this screen grabbed the clipboard.
|
|
|
|
//
|
|
|
|
// this works around bugs in the clipboard viewer chain.
|
|
|
|
// sometimes NT will simply never send WM_DRAWCLIPBOARD
|
|
|
|
// messages for no apparent reason and rebooting fixes the
|
|
|
|
// problem. since we don't want a broken clipboard until the
|
|
|
|
// next reboot we do this double check. clipboard ownership
|
|
|
|
// won't be reflected on other screens until we leave but at
|
|
|
|
// least the clipboard itself will work.
|
Fixed several win32 bugs. First, synergy wasn't forwarding mouse
events to other hook functions, which broke some tools like objectbar.
Second, windows key processing was fixed. Previously pressing and
release the key would only send a press event, locking the user onto
the client window; also, the win32 server treated as a Meta modifier
instead of a Super modifier, which broke any use of it as any kind of
modifier key. Third, added hacks to support several key combinations
on windows 95/98/me that are treated specially by windows, including
Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any
combination using the windows key like Win+E and Win+F but not
Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which
only happened when using a synergy server on windows) has been fixed;
unfortunately the solution causes a lot of screen redraws for some
reason. Finally, there's been a fix to clipboard handling that may
or may not fix a problem where the clipboard would stop transferring
between systems after a little while. I can't be sure if it fixes
the problem because I can't reproduce the problem.
2003-04-13 14:59:53 +00:00
|
|
|
if (m_ownClipboard && !CMSWindowsClipboard::isOwnedBySynergy()) {
|
2003-09-02 22:05:47 +00:00
|
|
|
LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received"));
|
Fixed several win32 bugs. First, synergy wasn't forwarding mouse
events to other hook functions, which broke some tools like objectbar.
Second, windows key processing was fixed. Previously pressing and
release the key would only send a press event, locking the user onto
the client window; also, the win32 server treated as a Meta modifier
instead of a Super modifier, which broke any use of it as any kind of
modifier key. Third, added hacks to support several key combinations
on windows 95/98/me that are treated specially by windows, including
Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any
combination using the windows key like Win+E and Win+F but not
Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which
only happened when using a synergy server on windows) has been fixed;
unfortunately the solution causes a lot of screen redraws for some
reason. Finally, there's been a fix to clipboard handling that may
or may not fix a problem where the clipboard would stop transferring
between systems after a little while. I can't be sure if it fixes
the problem because I can't reproduce the problem.
2003-04-13 14:59:53 +00:00
|
|
|
m_ownClipboard = false;
|
|
|
|
m_receiver->onGrabClipboard(kClipboardClipboard);
|
|
|
|
m_receiver->onGrabClipboard(kClipboardSelection);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
2001-11-19 00:33:36 +00:00
|
|
|
}
|
|
|
|
|
2002-06-10 22:06:45 +00:00
|
|
|
void
|
2002-07-15 15:01:36 +00:00
|
|
|
CMSWindowsScreen::openScreensaver(bool notify)
|
2002-05-24 17:54:28 +00:00
|
|
|
{
|
2002-07-15 15:01:36 +00:00
|
|
|
assert(m_screensaver != NULL);
|
|
|
|
|
|
|
|
m_screensaverNotify = notify;
|
|
|
|
if (m_screensaverNotify) {
|
2003-09-02 22:05:47 +00:00
|
|
|
m_installScreensaver();
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_screensaver->disable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CMSWindowsScreen::closeScreensaver()
|
|
|
|
{
|
|
|
|
if (m_screensaver != NULL) {
|
|
|
|
if (m_screensaverNotify) {
|
2003-09-02 22:05:47 +00:00
|
|
|
m_uninstallScreensaver();
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_screensaver->enable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_screensaverNotify = false;
|
2002-05-24 17:54:28 +00:00
|
|
|
}
|
|
|
|
|
2002-06-10 22:06:45 +00:00
|
|
|
void
|
2002-07-15 15:01:36 +00:00
|
|
|
CMSWindowsScreen::screensaver(bool activate)
|
|
|
|
{
|
|
|
|
assert(m_screensaver != NULL);
|
2003-09-02 22:05:47 +00:00
|
|
|
|
2002-07-15 15:01:36 +00:00
|
|
|
if (activate) {
|
|
|
|
m_screensaver->activate();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_screensaver->deactivate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::resetOptions()
|
2002-07-15 15:01:36 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
// no options
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::setOptions(const COptionsList&)
|
|
|
|
{
|
|
|
|
// no options
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CMSWindowsScreen::updateKeys()
|
|
|
|
{
|
|
|
|
syncDesktop();
|
|
|
|
m_keyMapper.update(m_keyState);
|
|
|
|
memset(m_buttons, 0, sizeof(m_buttons));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::isPrimary() const
|
|
|
|
{
|
|
|
|
return m_isPrimary;
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const
|
|
|
|
{
|
|
|
|
CMSWindowsClipboard src(m_window);
|
|
|
|
CClipboard::copy(dst, &src);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
|
2001-11-19 00:33:36 +00:00
|
|
|
{
|
|
|
|
assert(m_class != 0);
|
|
|
|
|
2002-06-19 17:03:29 +00:00
|
|
|
x = m_x;
|
|
|
|
y = m_y;
|
|
|
|
w = m_w;
|
|
|
|
h = m_h;
|
2001-11-19 00:33:36 +00:00
|
|
|
}
|
|
|
|
|
2002-07-11 18:58:49 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
|
|
|
|
{
|
|
|
|
POINT pos;
|
2003-09-02 22:05:47 +00:00
|
|
|
syncDesktop();
|
2002-07-12 20:41:23 +00:00
|
|
|
if (GetCursorPos(&pos)) {
|
|
|
|
x = pos.x;
|
|
|
|
y = pos.y;
|
|
|
|
}
|
|
|
|
else {
|
2003-09-02 22:05:47 +00:00
|
|
|
x = m_xCenter;
|
|
|
|
y = m_yCenter;
|
2002-07-12 20:41:23 +00:00
|
|
|
}
|
2002-07-11 18:58:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::reconfigure(UInt32 activeSides)
|
2002-07-11 18:58:49 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
assert(m_isPrimary);
|
|
|
|
|
|
|
|
m_setSides(activeSides);
|
2002-07-11 18:58:49 +00:00
|
|
|
}
|
|
|
|
|
2002-07-15 15:01:36 +00:00
|
|
|
void
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::warpCursor(SInt32 x, SInt32 y)
|
2002-07-11 18:58:49 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
// warp mouse
|
|
|
|
warpCursorNoFlush(x, y);
|
|
|
|
|
|
|
|
// remove all input events before and including warp
|
|
|
|
MSG msg;
|
|
|
|
while (PeekMessage(&msg, NULL, SYNERGY_MSG_INPUT_FIRST,
|
|
|
|
SYNERGY_MSG_INPUT_LAST, PM_REMOVE)) {
|
|
|
|
// do nothing
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// save position as last position
|
|
|
|
m_xCursor = x;
|
|
|
|
m_yCursor = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt32
|
|
|
|
CMSWindowsScreen::addOneShotTimer(double timeout)
|
|
|
|
{
|
|
|
|
// FIXME -- support multiple one-shot timers
|
|
|
|
if (m_oneShotTimer != 0) {
|
|
|
|
KillTimer(NULL, m_oneShotTimer);
|
|
|
|
}
|
|
|
|
m_oneShotTimer = SetTimer(NULL, 0,
|
|
|
|
static_cast<UINT>(1000.0 * timeout), NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SInt32
|
|
|
|
CMSWindowsScreen::getJumpZoneSize() const
|
|
|
|
{
|
|
|
|
return 1;
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::isAnyMouseButtonDown() const
|
2002-07-15 15:01:36 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
static const char* buttonToName[] = {
|
|
|
|
"button 0",
|
|
|
|
"Left Button",
|
|
|
|
"Middle Button",
|
|
|
|
"Right Button",
|
|
|
|
"X Button 1",
|
|
|
|
"X Button 2"
|
|
|
|
};
|
|
|
|
|
|
|
|
for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) {
|
|
|
|
if ((m_buttons[i] & 0x80) != 0) {
|
|
|
|
LOG((CLOG_DEBUG "locked by \"%s\"", buttonToName[i]));
|
2002-07-18 16:58:08 +00:00
|
|
|
return true;
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
return false;
|
|
|
|
}
|
Fixed several win32 bugs. First, synergy wasn't forwarding mouse
events to other hook functions, which broke some tools like objectbar.
Second, windows key processing was fixed. Previously pressing and
release the key would only send a press event, locking the user onto
the client window; also, the win32 server treated as a Meta modifier
instead of a Super modifier, which broke any use of it as any kind of
modifier key. Third, added hacks to support several key combinations
on windows 95/98/me that are treated specially by windows, including
Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any
combination using the windows key like Win+E and Win+F but not
Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which
only happened when using a synergy server on windows) has been fixed;
unfortunately the solution causes a lot of screen redraws for some
reason. Finally, there's been a fix to clipboard handling that may
or may not fix a problem where the clipboard would stop transferring
between systems after a little while. I can't be sure if it fixes
the problem because I can't reproduce the problem.
2003-04-13 14:59:53 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
const char*
|
|
|
|
CMSWindowsScreen::getKeyName(KeyButton virtualKey) const
|
|
|
|
{
|
|
|
|
return m_keyMapper.getKeyName(virtualKey);
|
|
|
|
}
|
Fixed several win32 bugs. First, synergy wasn't forwarding mouse
events to other hook functions, which broke some tools like objectbar.
Second, windows key processing was fixed. Previously pressing and
release the key would only send a press event, locking the user onto
the client window; also, the win32 server treated as a Meta modifier
instead of a Super modifier, which broke any use of it as any kind of
modifier key. Third, added hacks to support several key combinations
on windows 95/98/me that are treated specially by windows, including
Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any
combination using the windows key like Win+E and Win+F but not
Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which
only happened when using a synergy server on windows) has been fixed;
unfortunately the solution causes a lot of screen redraws for some
reason. Finally, there's been a fix to clipboard handling that may
or may not fix a problem where the clipboard would stop transferring
between systems after a little while. I can't be sure if it fixes
the problem because I can't reproduce the problem.
2003-04-13 14:59:53 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::fakeKeyEvent(KeyButton virtualKey, bool press) const
|
|
|
|
{
|
|
|
|
DWORD flags = 0;
|
|
|
|
if (m_keyMapper.isExtendedKey(virtualKey)) {
|
|
|
|
flags |= KEYEVENTF_EXTENDEDKEY;
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
if (!press) {
|
|
|
|
flags |= KEYEVENTF_KEYUP;
|
|
|
|
}
|
|
|
|
const UINT code = m_keyMapper.keyToScanCode(&virtualKey);
|
|
|
|
syncDesktop();
|
|
|
|
keybd_event(static_cast<BYTE>(virtualKey & 0xffu),
|
|
|
|
static_cast<BYTE>(code), flags, 0);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::fakeCtrlAltDel() const
|
2002-07-15 15:01:36 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
if (!m_is95Family) {
|
|
|
|
// to fake ctrl+alt+del on the NT family we broadcast a suitable
|
|
|
|
// hotkey to all windows on the winlogon desktop. however, we
|
|
|
|
// the current thread must be on that desktop to do the broadcast
|
|
|
|
// and we can't switch just any thread because some own windows
|
|
|
|
// or hooks. so start a new thread to do the real work.
|
|
|
|
CThread cad(new CFunctionJob(&CMSWindowsScreen::ctrlAltDelThread));
|
|
|
|
cad.wait();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// get the sequence of keys to simulate ctrl+alt+del
|
|
|
|
IKeyState::Keystrokes keys;
|
|
|
|
KeyID key = kKeyDelete;
|
|
|
|
KeyModifierMask mask = KeyModifierControl | KeyModifierAlt;
|
|
|
|
if (mapKey(keys, *m_keyState, key, mask, false) == 0) {
|
|
|
|
keys.clear();
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// do it
|
|
|
|
for (IKeyState::Keystrokes::const_iterator k = keys.begin();
|
|
|
|
k != keys.end(); ++k) {
|
|
|
|
fakeKeyEvent(k->m_key, k->m_press);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::fakeMouseButton(ButtonID id, bool press) const
|
|
|
|
{
|
|
|
|
// map button id to button flag
|
|
|
|
DWORD data;
|
|
|
|
DWORD flags = mapButtonToEvent(id, press, &data);
|
|
|
|
|
|
|
|
// send event
|
|
|
|
if (flags != 0) {
|
|
|
|
syncDesktop();
|
|
|
|
mouse_event(flags, 0, 0, data, 0);
|
|
|
|
}
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::fakeMouseMove(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));
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// move the mouse directly to target position if motion is simple
|
|
|
|
syncDesktop();
|
|
|
|
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)((65536.0 * x) / w),
|
|
|
|
(DWORD)((65536.0 * y) / h),
|
|
|
|
0, 0);
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// 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 a 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);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// get current mouse position
|
|
|
|
POINT pos;
|
|
|
|
GetCursorPos(&pos);
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// move relative to mouse position
|
|
|
|
mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0);
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// restore mouse speed & acceleration
|
|
|
|
if (accelChanged) {
|
|
|
|
SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0);
|
|
|
|
SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
}
|
2002-07-11 18:58:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::fakeMouseWheel(SInt32 delta) const
|
2002-07-11 18:58:49 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
syncDesktop();
|
|
|
|
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyButton
|
|
|
|
CMSWindowsScreen::mapKey(IKeyState::Keystrokes& keys,
|
|
|
|
const IKeyState& keyState, KeyID id,
|
|
|
|
KeyModifierMask desiredMask,
|
|
|
|
bool isAutoRepeat) const
|
|
|
|
{
|
|
|
|
return m_keyMapper.mapKey(keys, keyState, id, desiredMask, isAutoRepeat);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CMSWindowsScreen::updateScreenShape()
|
|
|
|
{
|
|
|
|
// get shape
|
|
|
|
m_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
|
|
|
m_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
|
|
|
m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
|
|
|
m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
|
|
|
LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h));
|
|
|
|
|
|
|
|
// get center for cursor
|
|
|
|
m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1;
|
|
|
|
m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1;
|
|
|
|
|
|
|
|
// check for multiple monitors
|
|
|
|
m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) ||
|
|
|
|
m_h != GetSystemMetrics(SM_CYSCREEN));
|
2002-07-11 18:58:49 +00:00
|
|
|
}
|
|
|
|
|
2002-07-15 15:01:36 +00:00
|
|
|
bool
|
|
|
|
CMSWindowsScreen::switchDesktop(HDESK desk)
|
|
|
|
{
|
Fixed several win32 bugs. First, synergy wasn't forwarding mouse
events to other hook functions, which broke some tools like objectbar.
Second, windows key processing was fixed. Previously pressing and
release the key would only send a press event, locking the user onto
the client window; also, the win32 server treated as a Meta modifier
instead of a Super modifier, which broke any use of it as any kind of
modifier key. Third, added hacks to support several key combinations
on windows 95/98/me that are treated specially by windows, including
Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any
combination using the windows key like Win+E and Win+F but not
Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which
only happened when using a synergy server on windows) has been fixed;
unfortunately the solution causes a lot of screen redraws for some
reason. Finally, there's been a fix to clipboard handling that may
or may not fix a problem where the clipboard would stop transferring
between systems after a little while. I can't be sure if it fixes
the problem because I can't reproduce the problem.
2003-04-13 14:59:53 +00:00
|
|
|
// assume we don't own the clipboard until later
|
|
|
|
m_ownClipboard = false;
|
2002-07-15 15:01:36 +00:00
|
|
|
|
|
|
|
// destroy old window
|
|
|
|
if (m_window != NULL) {
|
|
|
|
// first remove clipboard snooper
|
|
|
|
ChangeClipboardChain(m_window, m_nextClipboardWindow);
|
|
|
|
m_nextClipboardWindow = NULL;
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// uninstall hooks. we can't change the thread desktop
|
|
|
|
// with hooks installed.
|
|
|
|
if (m_isPrimary) {
|
|
|
|
m_uninstall();
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
// now destroy window. we can't change the thread desktop
|
|
|
|
// with a window.
|
2002-07-15 15:01:36 +00:00
|
|
|
DestroyWindow(m_window);
|
|
|
|
m_window = NULL;
|
|
|
|
|
|
|
|
// done with desk
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsDesktop::closeDesktop(m_desk);
|
2002-07-15 15:01:36 +00:00
|
|
|
m_desk = NULL;
|
|
|
|
m_deskName = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
// if no new desktop then we're done
|
|
|
|
if (desk == NULL) {
|
2003-07-12 17:57:31 +00:00
|
|
|
LOG((CLOG_DEBUG "disconnecting desktop"));
|
2002-07-15 15:01:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// uninstall screen saver hooks
|
|
|
|
if (m_screensaverNotify) {
|
2003-09-02 22:05:47 +00:00
|
|
|
m_uninstallScreensaver();
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// set the desktop. can only do this when there are no windows
|
|
|
|
// and hooks on the current desktop owned by this thread.
|
2003-09-02 22:05:47 +00:00
|
|
|
if (!CMSWindowsDesktop::setDesktop(desk)) {
|
2002-10-15 21:29:44 +00:00
|
|
|
LOG((CLOG_ERR "failed to set desktop: %d", GetLastError()));
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsDesktop::closeDesktop(desk);
|
2002-07-15 15:01:36 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the window
|
|
|
|
m_window = CreateWindowEx(WS_EX_TOPMOST |
|
|
|
|
WS_EX_TRANSPARENT |
|
|
|
|
WS_EX_TOOLWINDOW,
|
|
|
|
(LPCTSTR)m_class,
|
|
|
|
"Synergy",
|
|
|
|
WS_POPUP,
|
|
|
|
0, 0, 1, 1,
|
|
|
|
NULL, NULL,
|
|
|
|
getInstance(),
|
|
|
|
NULL);
|
|
|
|
if (m_window == NULL) {
|
2002-10-15 21:29:44 +00:00
|
|
|
LOG((CLOG_ERR "failed to create window: %d", GetLastError()));
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsDesktop::closeDesktop(desk);
|
2002-07-15 15:01:36 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reinstall screen saver hooks
|
|
|
|
if (m_screensaverNotify) {
|
2003-09-02 22:05:47 +00:00
|
|
|
m_installScreensaver();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_isPrimary) {
|
|
|
|
// we don't ever want our window to activate
|
|
|
|
EnableWindow(m_window, FALSE);
|
|
|
|
|
|
|
|
// install hooks
|
|
|
|
switch (m_install()) {
|
|
|
|
case kHOOK_FAILED:
|
|
|
|
// FIXME -- can't install hook so we won't work; report error
|
|
|
|
m_lowLevel = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kHOOK_OKAY:
|
|
|
|
m_lowLevel = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kHOOK_OKAY_LL:
|
|
|
|
m_lowLevel = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_isOnScreen) {
|
|
|
|
// all messages prior to now are invalid
|
|
|
|
// FIXME -- is this necessary; couldn't we lose key releases?
|
|
|
|
nextMark();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// update key state
|
|
|
|
updateKeys();
|
|
|
|
|
|
|
|
// hide cursor if this screen isn't active
|
|
|
|
if (!m_isOnScreen) {
|
|
|
|
// move hider window under the cursor center
|
|
|
|
MoveWindow(m_window, m_xCenter, m_yCenter, 1, 1, FALSE);
|
|
|
|
|
|
|
|
// raise and show the hider window
|
|
|
|
ShowWindow(m_window, SW_SHOWNA);
|
|
|
|
|
|
|
|
// warp the mouse to the cursor center
|
|
|
|
fakeMouseMove(m_xCenter, m_yCenter);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// install our clipboard snooper
|
|
|
|
m_nextClipboardWindow = SetClipboardViewer(m_window);
|
|
|
|
|
Fixed several win32 bugs. First, synergy wasn't forwarding mouse
events to other hook functions, which broke some tools like objectbar.
Second, windows key processing was fixed. Previously pressing and
release the key would only send a press event, locking the user onto
the client window; also, the win32 server treated as a Meta modifier
instead of a Super modifier, which broke any use of it as any kind of
modifier key. Third, added hacks to support several key combinations
on windows 95/98/me that are treated specially by windows, including
Alt+Tab, Alt+Shift+Tab, Alt+Esc, Alt+Shift+Esc, Ctrl+Esc, and any
combination using the windows key like Win+E and Win+F but not
Ctrl+Alt+Del. Fourth, scroll lock only locking to the client (which
only happened when using a synergy server on windows) has been fixed;
unfortunately the solution causes a lot of screen redraws for some
reason. Finally, there's been a fix to clipboard handling that may
or may not fix a problem where the clipboard would stop transferring
between systems after a little while. I can't be sure if it fixes
the problem because I can't reproduce the problem.
2003-04-13 14:59:53 +00:00
|
|
|
// check if we own the clipboard
|
|
|
|
m_ownClipboard = CMSWindowsClipboard::isOwnedBySynergy();
|
2002-07-15 15:01:36 +00:00
|
|
|
|
|
|
|
// save new desktop
|
|
|
|
m_desk = desk;
|
2003-09-02 22:05:47 +00:00
|
|
|
m_deskName = CMSWindowsDesktop::getDesktopName(desk);
|
|
|
|
LOG((CLOG_DEBUG "switched to desktop \"%s\" with window 0x%08x", m_deskName.c_str(), (UInt32)m_window));
|
2002-07-15 15:01:36 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::syncDesktop() const
|
|
|
|
{
|
|
|
|
// change calling thread's desktop
|
|
|
|
if (!CMSWindowsDesktop::setDesktop(m_desk)) {
|
|
|
|
// LOG((CLOG_WARN "failed to set desktop: %d", GetLastError()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// attach input queues if not already attached. this has a habit
|
|
|
|
// of sucking up more and more CPU each time it's called (even if
|
|
|
|
// the threads are already attached). since we only expect one
|
|
|
|
// thread to call this more than once we can save just the last
|
|
|
|
// attached thread.
|
|
|
|
DWORD threadID = GetCurrentThreadId();
|
|
|
|
if (threadID != m_lastThreadID && threadID != m_threadID) {
|
|
|
|
CMSWindowsScreen* self = const_cast<CMSWindowsScreen*>(this);
|
|
|
|
self->m_lastThreadID = threadID;
|
|
|
|
AttachThreadInput(threadID, m_threadID, TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onPreDispatch(HWND hwnd,
|
|
|
|
UINT message, WPARAM wParam, LPARAM lParam)
|
2002-06-08 21:48:00 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
// handle event
|
|
|
|
switch (message) {
|
|
|
|
case SYNERGY_MSG_SCREEN_SAVER:
|
|
|
|
return onScreensaver(wParam != 0);
|
|
|
|
|
|
|
|
case WM_TIMER:
|
|
|
|
return onTimer(static_cast<UINT>(wParam));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_isPrimary) {
|
|
|
|
return onPreDispatchPrimary(hwnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onPreDispatchPrimary(HWND,
|
|
|
|
UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
// check if windows key is up but we think it's down. if so then
|
|
|
|
// synthesize a key release for it. we have to do this because
|
|
|
|
// if the user presses and releases a windows key without pressing
|
|
|
|
// any other key while it's down then windows will eat the key
|
|
|
|
// release. if we don't detect that and synthesize the release
|
|
|
|
// then the user will be locked to the screen and the client won't
|
|
|
|
// take the usual windows key release action (which on windows is
|
|
|
|
// to show the start menu).
|
|
|
|
//
|
|
|
|
// we can use GetKeyState() to check the state of the windows keys
|
|
|
|
// because, event though the key release is not reported to us,
|
|
|
|
// the event is processed and the keyboard state updated by the
|
|
|
|
// system. since the key could go up at any time we'll check the
|
|
|
|
// state on every event. only check on windows 95 family since
|
|
|
|
// NT family reports the key release as usual. obviously we skip
|
|
|
|
// this if the event is for the windows key itself.
|
2002-07-15 15:01:36 +00:00
|
|
|
if (m_is95Family) {
|
2003-09-02 22:05:47 +00:00
|
|
|
if (m_keyMapper.isPressed(VK_LWIN) &&
|
|
|
|
(GetAsyncKeyState(VK_LWIN) & 0x8000) == 0 &&
|
|
|
|
!(message == SYNERGY_MSG_KEY && wParam == VK_LWIN)) {
|
|
|
|
// compute appropriate parameters for fake event
|
|
|
|
WPARAM wParam = VK_LWIN;
|
|
|
|
LPARAM lParam = 0xc1000000;
|
|
|
|
lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24));
|
|
|
|
|
|
|
|
// process as if it were a key up
|
|
|
|
KeyModifierMask mask;
|
|
|
|
KeyButton button = static_cast<KeyButton>(
|
|
|
|
(lParam & 0x00ff0000u) >> 16);
|
|
|
|
KeyID key = m_keyMapper.mapKeyFromEvent(wParam,
|
|
|
|
lParam, &mask, NULL);
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
|
|
m_primaryReceiver->onKeyUp(key, mask, button);
|
|
|
|
m_keyMapper.updateKey(static_cast<KeyButton>(wParam), false);
|
|
|
|
}
|
|
|
|
if (m_keyMapper.isPressed(VK_RWIN) &&
|
|
|
|
(GetAsyncKeyState(VK_RWIN) & 0x8000) == 0 &&
|
|
|
|
!(message == SYNERGY_MSG_KEY && wParam == VK_RWIN)) {
|
|
|
|
// compute appropriate parameters for fake event
|
|
|
|
WPARAM wParam = VK_RWIN;
|
|
|
|
LPARAM lParam = 0xc1000000;
|
|
|
|
lParam |= (0x00ff0000 & (MapVirtualKey(wParam, 0) << 24));
|
|
|
|
|
|
|
|
// process as if it were a key up
|
|
|
|
KeyModifierMask mask;
|
|
|
|
KeyButton button = static_cast<KeyButton>(
|
|
|
|
(lParam & 0x00ff0000u) >> 16);
|
|
|
|
KeyID key = m_keyMapper.mapKeyFromEvent(wParam,
|
|
|
|
lParam, &mask, NULL);
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
|
|
m_primaryReceiver->onKeyUp(key, mask, button);
|
|
|
|
m_keyMapper.updateKey(static_cast<KeyButton>(wParam), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handle event
|
|
|
|
switch (message) {
|
|
|
|
case SYNERGY_MSG_MARK:
|
|
|
|
return onMark(static_cast<UInt32>(wParam));
|
|
|
|
|
|
|
|
case SYNERGY_MSG_KEY:
|
|
|
|
return onKey(wParam, lParam);
|
|
|
|
|
|
|
|
case SYNERGY_MSG_MOUSE_BUTTON:
|
|
|
|
return onMouseButton(wParam, lParam);
|
|
|
|
|
|
|
|
case SYNERGY_MSG_MOUSE_MOVE:
|
|
|
|
return onMouseMove(static_cast<SInt32>(wParam),
|
|
|
|
static_cast<SInt32>(lParam));
|
|
|
|
|
|
|
|
case SYNERGY_MSG_MOUSE_WHEEL:
|
|
|
|
return onMouseWheel(static_cast<SInt32>(wParam));
|
|
|
|
|
|
|
|
case SYNERGY_MSG_PRE_WARP:
|
|
|
|
{
|
|
|
|
// save position to compute delta of next motion
|
|
|
|
m_xCursor = static_cast<SInt32>(wParam);
|
|
|
|
m_yCursor = static_cast<SInt32>(lParam);
|
|
|
|
|
|
|
|
// we warped the mouse. discard events until we find the
|
|
|
|
// matching post warp event. see warpCursorNoFlush() for
|
|
|
|
// where the events are sent. we discard the matching
|
|
|
|
// post warp event and can be sure we've skipped the warp
|
|
|
|
// event.
|
|
|
|
MSG msg;
|
|
|
|
do {
|
|
|
|
GetMessage(&msg, NULL, SYNERGY_MSG_MOUSE_MOVE,
|
|
|
|
SYNERGY_MSG_POST_WARP);
|
|
|
|
} while (msg.message != SYNERGY_MSG_POST_WARP);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case SYNERGY_MSG_POST_WARP:
|
|
|
|
LOG((CLOG_WARN "unmatched post warp"));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onEvent(HWND hwnd,
|
|
|
|
UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result)
|
|
|
|
{
|
|
|
|
switch (message) {
|
|
|
|
case WM_QUERYENDSESSION:
|
|
|
|
if (m_is95Family) {
|
|
|
|
*result = TRUE;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_ENDSESSION:
|
|
|
|
if (m_is95Family) {
|
|
|
|
if (wParam == TRUE && lParam == 0) {
|
|
|
|
exitMainLoop();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_PAINT:
|
|
|
|
ValidateRect(hwnd, NULL);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case WM_DRAWCLIPBOARD:
|
|
|
|
LOG((CLOG_DEBUG "clipboard was taken"));
|
|
|
|
|
|
|
|
// first pass on the message
|
|
|
|
if (m_nextClipboardWindow != NULL) {
|
|
|
|
SendMessage(m_nextClipboardWindow, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
// now handle the message
|
|
|
|
return onClipboardChange();
|
|
|
|
|
|
|
|
case WM_CHANGECBCHAIN:
|
|
|
|
if (m_nextClipboardWindow == (HWND)wParam) {
|
|
|
|
m_nextClipboardWindow = (HWND)lParam;
|
|
|
|
LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow));
|
|
|
|
}
|
|
|
|
else if (m_nextClipboardWindow != NULL) {
|
|
|
|
LOG((CLOG_DEBUG "clipboard chain: forward: %d 0x%08x 0x%08x", message, wParam, lParam));
|
|
|
|
SendMessage(m_nextClipboardWindow, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
|
|
return onDisplayChange();
|
|
|
|
|
|
|
|
case WM_ACTIVATEAPP:
|
|
|
|
return onActivate(wParam != FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onMark(UInt32 mark)
|
|
|
|
{
|
|
|
|
m_markReceived = mark;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
// ignore message if posted prior to last mark change
|
|
|
|
if (!ignore()) {
|
|
|
|
// check for ctrl+alt+del emulation
|
|
|
|
if ((wParam == VK_PAUSE || wParam == VK_CANCEL) &&
|
|
|
|
(m_keyMapper.isPressed(VK_CONTROL) &&
|
|
|
|
m_keyMapper.isPressed(VK_MENU))) {
|
|
|
|
LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
|
|
|
|
wParam = VK_DELETE;
|
|
|
|
lParam &= 0xffff0000;
|
|
|
|
lParam |= 0x00000001;
|
|
|
|
}
|
|
|
|
|
|
|
|
// process key normally
|
|
|
|
bool altgr;
|
|
|
|
KeyModifierMask mask;
|
|
|
|
const KeyID key = m_keyMapper.mapKeyFromEvent(wParam,
|
|
|
|
lParam, &mask, &altgr);
|
|
|
|
KeyButton button = static_cast<KeyButton>(
|
|
|
|
(lParam & 0x00ff0000u) >> 16);
|
|
|
|
if (key != kKeyNone && key != kKeyMultiKey) {
|
|
|
|
if ((lParam & 0x80000000) == 0) {
|
|
|
|
// key press
|
|
|
|
|
|
|
|
// if AltGr required for this key then make sure
|
|
|
|
// the ctrl and alt keys are *not* down on the
|
|
|
|
// client. windows simulates AltGr with ctrl and
|
|
|
|
// alt for some inexplicable reason and clients
|
|
|
|
// will get confused if they see mode switch and
|
|
|
|
// ctrl and alt. we'll also need to put ctrl and
|
|
|
|
// alt back the way they were after we simulate
|
|
|
|
// the key.
|
|
|
|
bool ctrlL = m_keyMapper.isPressed(VK_LCONTROL);
|
|
|
|
bool ctrlR = m_keyMapper.isPressed(VK_RCONTROL);
|
|
|
|
bool altL = m_keyMapper.isPressed(VK_LMENU);
|
|
|
|
bool altR = m_keyMapper.isPressed(VK_RMENU);
|
|
|
|
if (altgr) {
|
|
|
|
KeyID key;
|
|
|
|
KeyButton button;
|
|
|
|
UINT scanCode;
|
|
|
|
KeyModifierMask mask2 = (mask &
|
|
|
|
~(KeyModifierControl |
|
|
|
|
KeyModifierAlt |
|
|
|
|
KeyModifierModeSwitch));
|
|
|
|
if (ctrlL) {
|
|
|
|
key = kKeyControl_L;
|
|
|
|
button = VK_LCONTROL;
|
|
|
|
scanCode = m_keyMapper.keyToScanCode(&button);
|
|
|
|
button = static_cast<KeyButton>(scanCode);
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
|
|
|
m_primaryReceiver->onKeyUp(key, mask2, button);
|
|
|
|
}
|
|
|
|
if (ctrlR) {
|
|
|
|
key = kKeyControl_R;
|
|
|
|
button = VK_RCONTROL;
|
|
|
|
scanCode = m_keyMapper.keyToScanCode(&button);
|
|
|
|
button = static_cast<KeyButton>(scanCode);
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
|
|
|
m_primaryReceiver->onKeyUp(key, mask2, button);
|
|
|
|
}
|
|
|
|
if (altL) {
|
|
|
|
key = kKeyAlt_L;
|
|
|
|
button = VK_LMENU;
|
|
|
|
scanCode = m_keyMapper.keyToScanCode(&button);
|
|
|
|
button = static_cast<KeyButton>(scanCode);
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
|
|
|
m_primaryReceiver->onKeyUp(key, mask2, button);
|
|
|
|
}
|
|
|
|
if (altR) {
|
|
|
|
key = kKeyAlt_R;
|
|
|
|
button = VK_RMENU;
|
|
|
|
scanCode = m_keyMapper.keyToScanCode(&button);
|
|
|
|
button = static_cast<KeyButton>(scanCode);
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
|
|
|
m_primaryReceiver->onKeyUp(key, mask2, button);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// send key
|
|
|
|
const bool wasDown = ((lParam & 0x40000000) != 0);
|
|
|
|
SInt32 repeat = (SInt32)(lParam & 0xffff);
|
|
|
|
if (!wasDown) {
|
|
|
|
LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
|
|
m_primaryReceiver->onKeyDown(key, mask, button);
|
|
|
|
if (repeat > 0) {
|
|
|
|
--repeat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (repeat >= 1) {
|
|
|
|
LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button));
|
|
|
|
m_primaryReceiver->onKeyRepeat(key, mask, repeat, button);
|
|
|
|
}
|
|
|
|
|
|
|
|
// restore ctrl and alt state
|
|
|
|
if (altgr) {
|
|
|
|
KeyID key;
|
|
|
|
KeyButton button;
|
|
|
|
UINT scanCode;
|
|
|
|
KeyModifierMask mask2 = (mask &
|
|
|
|
~(KeyModifierControl |
|
|
|
|
KeyModifierAlt |
|
|
|
|
KeyModifierModeSwitch));
|
|
|
|
if (ctrlL) {
|
|
|
|
key = kKeyControl_L;
|
|
|
|
button = VK_LCONTROL;
|
|
|
|
scanCode = m_keyMapper.keyToScanCode(&button);
|
|
|
|
button = static_cast<KeyButton>(scanCode);
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
|
|
|
m_primaryReceiver->onKeyDown(key, mask2, button);
|
|
|
|
mask2 |= KeyModifierControl;
|
|
|
|
}
|
|
|
|
if (ctrlR) {
|
|
|
|
key = kKeyControl_R;
|
|
|
|
button = VK_RCONTROL;
|
|
|
|
scanCode = m_keyMapper.keyToScanCode(&button);
|
|
|
|
button = static_cast<KeyButton>(scanCode);
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
|
|
|
m_primaryReceiver->onKeyDown(key, mask2, button);
|
|
|
|
mask2 |= KeyModifierControl;
|
|
|
|
}
|
|
|
|
if (altL) {
|
|
|
|
key = kKeyAlt_L;
|
|
|
|
button = VK_LMENU;
|
|
|
|
scanCode = m_keyMapper.keyToScanCode(&button);
|
|
|
|
button = static_cast<KeyButton>(scanCode);
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
|
|
|
m_primaryReceiver->onKeyDown(key, mask2, button);
|
|
|
|
mask2 |= KeyModifierAlt;
|
|
|
|
}
|
|
|
|
if (altR) {
|
|
|
|
key = kKeyAlt_R;
|
|
|
|
button = VK_RMENU;
|
|
|
|
scanCode = m_keyMapper.keyToScanCode(&button);
|
|
|
|
button = static_cast<KeyButton>(scanCode);
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button));
|
|
|
|
m_primaryReceiver->onKeyDown(key, mask2, button);
|
|
|
|
mask2 |= KeyModifierAlt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// key release. if the key isn't down according to
|
|
|
|
// our table then we never got the key press event
|
|
|
|
// for it. if it's not a modifier key then we'll
|
|
|
|
// synthesize the press first. only do this on
|
|
|
|
// the windows 95 family, which eats certain special
|
|
|
|
// keys like alt+tab, ctrl+esc, etc.
|
|
|
|
if (m_is95Family && !isModifier(wParam) &&
|
|
|
|
m_keyMapper.isPressed(static_cast<KeyButton>(wParam))) {
|
|
|
|
LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
|
|
m_primaryReceiver->onKeyDown(key, mask, button);
|
|
|
|
m_keyMapper.updateKey(static_cast<KeyButton>(wParam), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// do key up
|
|
|
|
LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button));
|
|
|
|
m_primaryReceiver->onKeyUp(key, mask, button);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOG((CLOG_DEBUG2 "event: cannot map key wParam=%d lParam=0x%08x", wParam, lParam));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// keep our shadow key state up to date
|
|
|
|
m_keyMapper.updateKey(static_cast<KeyButton>(wParam),
|
|
|
|
((lParam & 0x80000000) == 0));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
// get which button
|
|
|
|
bool pressed = false;
|
|
|
|
const ButtonID button = mapButtonFromEvent(wParam, lParam);
|
|
|
|
|
|
|
|
// ignore message if posted prior to last mark change
|
|
|
|
if (!ignore()) {
|
|
|
|
switch (wParam) {
|
|
|
|
case WM_LBUTTONDOWN:
|
|
|
|
case WM_MBUTTONDOWN:
|
|
|
|
case WM_RBUTTONDOWN:
|
|
|
|
case WM_XBUTTONDOWN:
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
|
|
case WM_MBUTTONDBLCLK:
|
|
|
|
case WM_RBUTTONDBLCLK:
|
|
|
|
case WM_XBUTTONDBLCLK:
|
|
|
|
case WM_NCLBUTTONDOWN:
|
|
|
|
case WM_NCMBUTTONDOWN:
|
|
|
|
case WM_NCRBUTTONDOWN:
|
|
|
|
case WM_NCXBUTTONDOWN:
|
|
|
|
case WM_NCLBUTTONDBLCLK:
|
|
|
|
case WM_NCMBUTTONDBLCLK:
|
|
|
|
case WM_NCRBUTTONDBLCLK:
|
|
|
|
case WM_NCXBUTTONDBLCLK:
|
|
|
|
LOG((CLOG_DEBUG1 "event: button press button=%d", button));
|
|
|
|
if (button != kButtonNone) {
|
|
|
|
m_primaryReceiver->onMouseDown(button);
|
|
|
|
}
|
|
|
|
pressed = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_LBUTTONUP:
|
|
|
|
case WM_MBUTTONUP:
|
|
|
|
case WM_RBUTTONUP:
|
|
|
|
case WM_XBUTTONUP:
|
|
|
|
case WM_NCLBUTTONUP:
|
|
|
|
case WM_NCMBUTTONUP:
|
|
|
|
case WM_NCRBUTTONUP:
|
|
|
|
case WM_NCXBUTTONUP:
|
|
|
|
LOG((CLOG_DEBUG1 "event: button release button=%d", button));
|
|
|
|
if (button != kButtonNone) {
|
|
|
|
m_primaryReceiver->onMouseUp(button);
|
|
|
|
}
|
|
|
|
pressed = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// keep our shadow key state up to date
|
|
|
|
if (button >= kButtonLeft && button <= kButtonExtra0 + 1) {
|
|
|
|
if (pressed) {
|
|
|
|
m_buttons[button] |= 0x80;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_buttons[button] &= ~0x80;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my)
|
|
|
|
{
|
|
|
|
// compute motion delta (relative to the last known
|
|
|
|
// mouse position)
|
|
|
|
SInt32 x = mx - m_xCursor;
|
|
|
|
SInt32 y = my - m_yCursor;
|
|
|
|
|
|
|
|
// ignore if the mouse didn't move or if message posted prior
|
|
|
|
// to last mark change.
|
|
|
|
if (ignore() || (x == 0 && y == 0)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// save position to compute delta of next motion
|
|
|
|
m_xCursor = mx;
|
|
|
|
m_yCursor = my;
|
|
|
|
|
|
|
|
if (m_isOnScreen) {
|
|
|
|
// motion on primary screen
|
|
|
|
m_primaryReceiver->onMouseMovePrimary(m_xCursor, m_yCursor);
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
|
|
|
else {
|
2003-09-02 22:05:47 +00:00
|
|
|
// motion on secondary screen. warp mouse back to
|
|
|
|
// center.
|
|
|
|
warpCursorNoFlush(m_xCenter, m_yCenter);
|
|
|
|
|
|
|
|
// examine the motion. if it's about the distance
|
|
|
|
// from the center of the screen to an edge then
|
|
|
|
// it's probably a bogus motion that we want to
|
|
|
|
// ignore (see warpCursorNoFlush() for a further
|
|
|
|
// description).
|
|
|
|
static SInt32 bogusZoneSize = 10;
|
|
|
|
if (-x + bogusZoneSize > m_xCenter - m_x ||
|
|
|
|
x + bogusZoneSize > m_x + m_w - m_xCenter ||
|
|
|
|
-y + bogusZoneSize > m_yCenter - m_y ||
|
|
|
|
y + bogusZoneSize > m_y + m_h - m_yCenter) {
|
|
|
|
LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// send motion
|
|
|
|
m_primaryReceiver->onMouseMoveSecondary(x, y);
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
|
|
|
|
return true;
|
2002-06-08 21:48:00 +00:00
|
|
|
}
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onMouseWheel(SInt32 delta)
|
2002-06-08 21:48:00 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
// ignore message if posted prior to last mark change
|
|
|
|
if (!ignore()) {
|
|
|
|
LOG((CLOG_DEBUG1 "event: button wheel delta=%d", delta));
|
|
|
|
m_primaryReceiver->onMouseWheel(delta);
|
2002-06-08 21:48:00 +00:00
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onScreensaver(bool activated)
|
|
|
|
{
|
|
|
|
// ignore this message if there are any other screen saver
|
|
|
|
// messages already in the queue. this is important because
|
|
|
|
// our checkStarted() function has a deliberate delay, so it
|
|
|
|
// can't respond to events at full CPU speed and will fall
|
|
|
|
// behind if a lot of screen saver events are generated.
|
|
|
|
// that can easily happen because windows will continually
|
|
|
|
// send SC_SCREENSAVE until the screen saver starts, even if
|
|
|
|
// the screen saver is disabled!
|
|
|
|
MSG msg;
|
|
|
|
if (PeekMessage(&msg, NULL, SYNERGY_MSG_SCREEN_SAVER,
|
|
|
|
SYNERGY_MSG_SCREEN_SAVER, PM_NOREMOVE)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (activated) {
|
|
|
|
if (m_screensaver->checkStarted(SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) {
|
|
|
|
m_primaryReceiver->onScreensaver(true);
|
|
|
|
}
|
2002-07-15 15:01:36 +00:00
|
|
|
}
|
2002-06-08 21:48:00 +00:00
|
|
|
else {
|
2003-09-02 22:05:47 +00:00
|
|
|
m_primaryReceiver->onScreensaver(false);
|
2002-06-08 21:48:00 +00:00
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
|
|
|
|
return true;
|
2002-06-08 21:48:00 +00:00
|
|
|
}
|
|
|
|
|
2002-06-10 22:06:45 +00:00
|
|
|
bool
|
2003-09-02 22:05:47 +00:00
|
|
|
CMSWindowsScreen::onTimer(UINT timerID)
|
2002-06-08 21:48:00 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
if (timerID == m_timer) {
|
|
|
|
// if current desktop is not the input desktop then switch to it.
|
|
|
|
HDESK desk = CMSWindowsDesktop::openInputDesktop();
|
|
|
|
if (desk == m_desk ||
|
|
|
|
CMSWindowsDesktop::getDesktopName(desk) == m_deskName ||
|
|
|
|
m_screensaver->isActive()) {
|
|
|
|
// same desktop or screensaver is active. don't switch
|
|
|
|
// desktops when the screensaver is active. we'd most
|
|
|
|
// likely switch to the screensaver desktop which would
|
|
|
|
// have the side effect of forcing the screensaver to stop.
|
|
|
|
CMSWindowsDesktop::closeDesktop(desk);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
switchDesktop(desk);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the desktop was inaccessible and isn't anymore then
|
|
|
|
// update our key state.
|
|
|
|
if (desk != NULL && m_inaccessibleDesktop) {
|
|
|
|
LOG((CLOG_DEBUG "desktop is now accessible"));
|
|
|
|
m_inaccessibleDesktop = false;
|
|
|
|
updateKeys();
|
|
|
|
}
|
|
|
|
|
|
|
|
// note if desktop was accessible but isn't anymore
|
|
|
|
else if (desk == NULL && !m_inaccessibleDesktop) {
|
|
|
|
m_inaccessibleDesktop = true;
|
|
|
|
LOG((CLOG_DEBUG "desktop is now inaccessible"));
|
|
|
|
}
|
2003-02-16 19:50:36 +00:00
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
|
|
|
|
else if (timerID == m_oneShotTimer) {
|
|
|
|
// one shot timer expired
|
|
|
|
KillTimer(NULL, m_oneShotTimer);
|
|
|
|
m_oneShotTimer = 0;
|
|
|
|
m_primaryReceiver->onOneShotTimerExpired(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onDisplayChange()
|
|
|
|
{
|
|
|
|
// screen resolution may have changed. save old shape.
|
|
|
|
SInt32 xOld = m_x, yOld = m_y, wOld = m_w, hOld = m_h;
|
|
|
|
|
|
|
|
// update shape
|
|
|
|
updateScreenShape();
|
|
|
|
|
|
|
|
// do nothing if resolution hasn't changed
|
|
|
|
if (xOld != m_x || yOld != m_y || wOld != m_w || hOld != m_h) {
|
|
|
|
if (m_isPrimary) {
|
|
|
|
// warp mouse to center if off screen
|
|
|
|
if (!m_isOnScreen) {
|
|
|
|
warpCursor(m_xCenter, m_yCenter);
|
|
|
|
}
|
|
|
|
|
|
|
|
// tell hook about resize if on screen
|
|
|
|
else {
|
|
|
|
m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// collect new screen info
|
|
|
|
CClientInfo info;
|
|
|
|
info.m_x = m_x;
|
|
|
|
info.m_y = m_y;
|
|
|
|
info.m_w = m_w;
|
|
|
|
info.m_h = m_h;
|
|
|
|
info.m_zoneSize = getJumpZoneSize();
|
|
|
|
getCursorPos(info.m_mx, info.m_my);
|
|
|
|
|
|
|
|
// send new screen info
|
|
|
|
m_receiver->onInfoChanged(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onClipboardChange()
|
|
|
|
{
|
|
|
|
// now notify client that somebody changed the clipboard (unless
|
|
|
|
// we're the owner).
|
|
|
|
if (!CMSWindowsClipboard::isOwnedBySynergy()) {
|
|
|
|
LOG((CLOG_DEBUG "clipboard changed: foreign owned"));
|
|
|
|
if (m_ownClipboard) {
|
|
|
|
LOG((CLOG_DEBUG "clipboard changed: lost ownership"));
|
|
|
|
m_ownClipboard = false;
|
|
|
|
m_receiver->onGrabClipboard(kClipboardClipboard);
|
|
|
|
m_receiver->onGrabClipboard(kClipboardSelection);
|
|
|
|
}
|
2003-02-16 19:50:36 +00:00
|
|
|
}
|
|
|
|
else {
|
2003-09-02 22:05:47 +00:00
|
|
|
LOG((CLOG_DEBUG "clipboard changed: synergy owned"));
|
|
|
|
m_ownClipboard = true;
|
2003-02-16 19:50:36 +00:00
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
|
|
|
|
return true;
|
2002-06-23 21:53:31 +00:00
|
|
|
}
|
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
bool
|
|
|
|
CMSWindowsScreen::onActivate(bool activated)
|
2001-11-19 00:33:36 +00:00
|
|
|
{
|
2003-09-02 22:05:47 +00:00
|
|
|
if (!m_isPrimary && activated) {
|
|
|
|
// some other app activated. hide the hider window.
|
|
|
|
ShowWindow(m_window, SW_HIDE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CMSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y)
|
|
|
|
{
|
|
|
|
// send an event that we can recognize before the mouse warp
|
|
|
|
PostThreadMessage(m_threadID, SYNERGY_MSG_PRE_WARP, x, y);
|
|
|
|
|
|
|
|
// warp mouse. hopefully this inserts a mouse motion event
|
|
|
|
// between the previous message and the following message.
|
|
|
|
SetCursorPos(x, y);
|
|
|
|
|
|
|
|
// yield the CPU. there's a race condition when warping:
|
|
|
|
// a hardware mouse event occurs
|
|
|
|
// the mouse hook is not called because that process doesn't have the CPU
|
|
|
|
// we send PRE_WARP, SetCursorPos(), send POST_WARP
|
|
|
|
// we process all of those events and update m_x, m_y
|
|
|
|
// we finish our time slice
|
|
|
|
// the hook is called
|
|
|
|
// the hook sends us a mouse event from the pre-warp position
|
|
|
|
// we get the CPU
|
|
|
|
// we compute a bogus warp
|
|
|
|
// we need the hook to process all mouse events that occur
|
|
|
|
// before we warp before we do the warp but i'm not sure how
|
|
|
|
// to guarantee that. yielding the CPU here may reduce the
|
|
|
|
// chance of undesired behavior. we'll also check for very
|
|
|
|
// large motions that look suspiciously like about half width
|
|
|
|
// or height of the screen.
|
|
|
|
ARCH->sleep(0.0);
|
|
|
|
|
|
|
|
// send an event that we can recognize after the mouse warp
|
|
|
|
PostThreadMessage(m_threadID, SYNERGY_MSG_POST_WARP, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CMSWindowsScreen::nextMark()
|
|
|
|
{
|
|
|
|
// next mark
|
|
|
|
++m_mark;
|
|
|
|
|
|
|
|
// mark point in message queue where the mark was changed
|
|
|
|
PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::ignore() const
|
|
|
|
{
|
|
|
|
return (m_mark != m_markReceived);
|
|
|
|
}
|
|
|
|
|
|
|
|
HCURSOR
|
|
|
|
CMSWindowsScreen::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(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR);
|
|
|
|
delete[] cursorXOR;
|
|
|
|
delete[] cursorAND;
|
|
|
|
return c;
|
|
|
|
}
|
2002-07-12 20:41:23 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::showCursor(bool show) const
|
|
|
|
{
|
|
|
|
if (m_cursorThread != 0) {
|
|
|
|
if (m_threadID != m_cursorThread) {
|
|
|
|
AttachThreadInput(m_threadID, m_cursorThread, TRUE);
|
|
|
|
}
|
|
|
|
ShowCursor(show ? TRUE : FALSE);
|
|
|
|
if (m_threadID != m_cursorThread) {
|
|
|
|
AttachThreadInput(m_threadID, m_cursorThread, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2002-07-12 20:41:23 +00:00
|
|
|
|
2003-09-02 22:05:47 +00:00
|
|
|
void
|
|
|
|
CMSWindowsScreen::enableSpecialKeys(bool enable) const
|
|
|
|
{
|
|
|
|
// enable/disable ctrl+alt+del, alt+tab, etc on win95 family.
|
|
|
|
// since the win95 family doesn't support low-level hooks, we
|
|
|
|
// use this undocumented feature to suppress normal handling
|
|
|
|
// of certain key combinations.
|
|
|
|
if (m_is95Family) {
|
|
|
|
DWORD dummy = 0;
|
|
|
|
SystemParametersInfo(SPI_SETSCREENSAVERRUNNING,
|
|
|
|
enable ? FALSE : TRUE, &dummy, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
|
|
CMSWindowsScreen::mapButtonToEvent(ButtonID button,
|
|
|
|
bool press, DWORD* inData) const
|
|
|
|
{
|
|
|
|
DWORD dummy;
|
|
|
|
DWORD* data = (inData != NULL) ? inData : &dummy;
|
|
|
|
|
|
|
|
// 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
|
|
|
|
*data = 0;
|
|
|
|
switch (button) {
|
|
|
|
case kButtonLeft:
|
|
|
|
return press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
|
|
|
|
|
|
|
|
case kButtonMiddle:
|
|
|
|
return press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
|
|
|
|
|
|
|
|
case kButtonRight:
|
|
|
|
return press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
|
|
|
|
|
|
|
|
case kButtonExtra0 + 0:
|
|
|
|
*data = XBUTTON1;
|
|
|
|
return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
|
|
|
|
|
|
|
|
case kButtonExtra0 + 1:
|
|
|
|
*data = XBUTTON2;
|
|
|
|
return press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ButtonID
|
|
|
|
CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const
|
|
|
|
{
|
|
|
|
switch (msg) {
|
|
|
|
case WM_LBUTTONDOWN:
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
|
|
case WM_LBUTTONUP:
|
|
|
|
case WM_NCLBUTTONDOWN:
|
|
|
|
case WM_NCLBUTTONDBLCLK:
|
|
|
|
case WM_NCLBUTTONUP:
|
|
|
|
return kButtonLeft;
|
|
|
|
|
|
|
|
case WM_MBUTTONDOWN:
|
|
|
|
case WM_MBUTTONDBLCLK:
|
|
|
|
case WM_MBUTTONUP:
|
|
|
|
case WM_NCMBUTTONDOWN:
|
|
|
|
case WM_NCMBUTTONDBLCLK:
|
|
|
|
case WM_NCMBUTTONUP:
|
|
|
|
return kButtonMiddle;
|
|
|
|
|
|
|
|
case WM_RBUTTONDOWN:
|
|
|
|
case WM_RBUTTONDBLCLK:
|
|
|
|
case WM_RBUTTONUP:
|
|
|
|
case WM_NCRBUTTONDOWN:
|
|
|
|
case WM_NCRBUTTONDBLCLK:
|
|
|
|
case WM_NCRBUTTONUP:
|
|
|
|
return kButtonRight;
|
|
|
|
|
|
|
|
case WM_XBUTTONDOWN:
|
|
|
|
case WM_XBUTTONDBLCLK:
|
|
|
|
case WM_XBUTTONUP:
|
|
|
|
case WM_NCXBUTTONDOWN:
|
|
|
|
case WM_NCXBUTTONDBLCLK:
|
|
|
|
case WM_NCXBUTTONUP:
|
|
|
|
switch (button) {
|
|
|
|
case XBUTTON1:
|
|
|
|
return kButtonExtra0 + 0;
|
|
|
|
|
|
|
|
case XBUTTON2:
|
|
|
|
return kButtonExtra0 + 1;
|
|
|
|
}
|
|
|
|
return kButtonNone;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return kButtonNone;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CMSWindowsScreen::isModifier(UINT vkCode) const
|
|
|
|
{
|
|
|
|
switch (vkCode) {
|
|
|
|
case VK_LSHIFT:
|
|
|
|
case VK_RSHIFT:
|
|
|
|
case VK_SHIFT:
|
|
|
|
case VK_LCONTROL:
|
|
|
|
case VK_RCONTROL:
|
|
|
|
case VK_CONTROL:
|
|
|
|
case VK_LMENU:
|
|
|
|
case VK_RMENU:
|
|
|
|
case VK_MENU:
|
|
|
|
case VK_CAPITAL:
|
|
|
|
case VK_NUMLOCK:
|
|
|
|
case VK_SCROLL:
|
|
|
|
case VK_LWIN:
|
|
|
|
case VK_RWIN:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CMSWindowsScreen::ctrlAltDelThread(void*)
|
|
|
|
{
|
|
|
|
// get the Winlogon desktop at whatever privilege we can
|
|
|
|
HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED);
|
|
|
|
if (desk != NULL) {
|
|
|
|
if (SetThreadDesktop(desk)) {
|
|
|
|
PostMessage(HWND_BROADCAST, WM_HOTKEY, 0,
|
|
|
|
MAKELPARAM(MOD_CONTROL | MOD_ALT, VK_DELETE));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOG((CLOG_DEBUG "can't switch to Winlogon desk: %d", GetLastError()));
|
|
|
|
}
|
|
|
|
CloseDesktop(desk);
|
2002-07-12 20:41:23 +00:00
|
|
|
}
|
|
|
|
else {
|
2003-09-02 22:05:47 +00:00
|
|
|
LOG((CLOG_DEBUG "can't open Winlogon desk: %d", GetLastError()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CALLBACK
|
|
|
|
CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
assert(s_screen != NULL);
|
|
|
|
|
|
|
|
LRESULT result = 0;
|
|
|
|
if (!s_screen->onEvent(hwnd, msg, wParam, lParam, &result)) {
|
|
|
|
result = DefWindowProc(hwnd, msg, wParam, lParam);
|
2002-07-12 20:41:23 +00:00
|
|
|
}
|
2003-09-02 22:05:47 +00:00
|
|
|
|
|
|
|
return result;
|
2001-11-19 00:33:36 +00:00
|
|
|
}
|