barrier/server/CMSWindowsPrimaryScreen.cpp

510 lines
11 KiB
C++

#include "CMSWindowsPrimaryScreen.h"
#include "CServer.h"
#include "CSynergyHook.h"
#include "CThread.h"
#include "CLog.h"
#include <assert.h>
//
// CMSWindowsPrimaryScreen
//
CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen() :
m_server(NULL),
m_active(false),
m_window(NULL),
m_hookLibrary(NULL),
m_mark(0),
m_markReceived(0)
{
// do nothing
}
CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen()
{
assert(m_window == NULL);
assert(m_hookLibrary == NULL);
}
static HWND s_debug = NULL;
static HWND s_debugLog = NULL;
static DWORD s_thread = 0;
static BOOL CALLBACK WINAPI debugProc(HWND, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
return TRUE;
case WM_CLOSE:
PostQuitMessage(0);
return TRUE;
}
return FALSE;
}
static void debugOutput(const char* msg)
{
if (s_thread != 0) {
const DWORD threadID = ::GetCurrentThreadId();
if (threadID != s_thread) {
GetDesktopWindow();
AttachThreadInput(threadID, s_thread, TRUE);
}
}
DWORD len = SendMessage(s_debugLog, WM_GETTEXTLENGTH, 0, 0);
if (len > 20000) {
SendMessage(s_debugLog, EM_SETSEL, -1, 0);
SendMessage(s_debugLog, WM_SETTEXT, FALSE, (LPARAM)(LPCTSTR)msg);
}
else {
SendMessage(s_debugLog, EM_SETSEL, -1, len);
SendMessage(s_debugLog, EM_REPLACESEL, FALSE, (LPARAM)(LPCTSTR)msg);
}
SendMessage(s_debugLog, EM_SCROLLCARET, 0, 0);
}
void CMSWindowsPrimaryScreen::run()
{
CLog::setOutputter(&debugOutput);
doRun();
CLog::setOutputter(NULL);
}
void CMSWindowsPrimaryScreen::stop()
{
doStop();
}
void CMSWindowsPrimaryScreen::open(CServer* server)
{
assert(m_server == NULL);
assert(server != NULL);
// set the server
m_server = server;
// open the display
openDisplay();
// enter the screen
doEnter();
}
void CMSWindowsPrimaryScreen::close()
{
assert(m_server != NULL);
// close the display
closeDisplay();
// done with server
m_server = NULL;
}
void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y)
{
log((CLOG_INFO "entering primary at %d,%d", x, y));
assert(m_active == true);
// do non-warp enter stuff
doEnter();
// warp to requested location
warpCursor(x, y);
}
void CMSWindowsPrimaryScreen::doEnter()
{
// set the zones that should cause a jump
SInt32 w, h;
getScreenSize(&w, &h);
SetZoneFunc setZone = (SetZoneFunc)GetProcAddress(
m_hookLibrary, "setZone");
setZone(m_server->getActivePrimarySides(), w, h, getJumpZoneSize());
// all messages prior to now are invalid
nextMark();
// not active anymore
m_active = false;
}
void CMSWindowsPrimaryScreen::leave()
{
log((CLOG_INFO "leaving primary"));
assert(m_active == false);
// all messages prior to now are invalid
nextMark();
// relay all mouse and keyboard events
SetRelayFunc setRelay = (SetRelayFunc)GetProcAddress(
m_hookLibrary, "setRelay");
setRelay();
// warp the mouse to the center of the screen
SInt32 w, h;
getScreenSize(&w, &h);
warpCursor(w >> 1, h >> 1);
// warp is also invalid
nextMark();
// local client now active
m_active = true;
}
void CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
{
SInt32 w, h;
getScreenSize(&w, &h);
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
(DWORD)((65535.99 * x) / (w - 1)),
(DWORD)((65535.99 * y) / (h - 1)),
0, 0);
}
void CMSWindowsPrimaryScreen::setClipboard(
const IClipboard* /*clipboard*/)
{
assert(m_window != NULL);
// FIXME -- should we retry until we get it?
if (!OpenClipboard(m_window))
return;
if (EmptyClipboard()) {
log((CLOG_DEBUG "grabbed clipboard"));
// FIXME -- set the clipboard data
}
CloseClipboard();
}
void CMSWindowsPrimaryScreen::getSize(
SInt32* width, SInt32* height) const
{
getScreenSize(width, height);
}
SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const
{
return 1;
}
void CMSWindowsPrimaryScreen::getClipboard(
IClipboard* clipboard) const
{
// FIXME -- put this in superclass?
// FIXME -- don't use CurrentTime
// getDisplayClipboard(clipboard, m_window, CurrentTime);
}
#include "resource.h" // FIXME
void CMSWindowsPrimaryScreen::onOpenDisplay()
{
assert(m_window == NULL);
assert(m_server != NULL);
// create debug dialog
s_thread = GetCurrentThreadId();;
s_debug = CreateDialog(getInstance(), MAKEINTRESOURCE(IDD_SYNERGY), NULL, &debugProc);
s_debugLog = ::GetDlgItem(s_debug, IDC_LOG);
CLog::setOutputter(&debugOutput);
ShowWindow(s_debug, SW_SHOWNORMAL);
// create the window
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(), "Synergy",
WS_POPUP,
0, 0, 1, 1, NULL, NULL,
getInstance(),
NULL);
// load the hook library
bool hooked = false;
m_hookLibrary = LoadLibrary("synrgyhk");
if (m_hookLibrary != NULL) {
// install input hooks
InstallFunc install = (InstallFunc)GetProcAddress(
m_hookLibrary, "install");
if (install != NULL) {
hooked = (install(m_window) != 0);
}
}
if (!hooked) {
DestroyWindow(m_window);
m_window = NULL;
// FIXME -- throw
}
// initialize marks
m_mark = 0;
m_markReceived = 0;
nextMark();
}
void CMSWindowsPrimaryScreen::onCloseDisplay()
{
assert(m_window != NULL);
// uninstall input hooks
UninstallFunc uninstall = (UninstallFunc)GetProcAddress(
m_hookLibrary, "uninstall");
if (uninstall != NULL) {
uninstall();
}
// done with hook library
FreeLibrary(m_hookLibrary);
m_hookLibrary = NULL;
// destroy window
DestroyWindow(m_window);
m_window = NULL;
CLog::setOutputter(NULL);
DestroyWindow(s_debug);
s_debug = NULL;
s_thread = 0;
}
bool CMSWindowsPrimaryScreen::onEvent(MSG* msg)
{
if (IsDialogMessage(s_debug, msg)) {
return true;
}
// handle event
switch (msg->message) {
// FIXME -- handle display changes
case WM_PAINT:
ValidateRect(m_window, NULL);
return true;
case SYNERGY_MSG_MARK:
m_markReceived = msg->wParam;
return true;
case SYNERGY_MSG_KEY:
// ignore if not at current mark
if (m_mark == m_markReceived) {
// FIXME -- vk code; key data
}
return true;
case SYNERGY_MSG_MOUSE_BUTTON:
// ignore if not at current mark
if (m_mark == m_markReceived) {
const ButtonID button = mapButton(msg->wParam);
switch (msg->wParam) {
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
log((CLOG_DEBUG "event: button press button=%d", button));
if (button != kButtonNone) {
m_server->onMouseDown(button);
}
break;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
log((CLOG_DEBUG "event: button release button=%d", button));
if (button != kButtonNone) {
m_server->onMouseUp(button);
}
break;
}
}
return true;
case SYNERGY_MSG_MOUSE_MOVE:
// ignore if not at current mark
if (m_mark == m_markReceived) {
SInt32 x = (SInt32)msg->wParam;
SInt32 y = (SInt32)msg->lParam;
if (!m_active) {
log((CLOG_DEBUG "event: inactive move %d,%d", x, y));
m_server->onMouseMovePrimary(x, y);
}
else {
log((CLOG_DEBUG "event: active move %d,%d", x, y));
// get screen size
SInt32 w, h;
getScreenSize(&w, &h);
// get center pixel
w >>= 1;
h >>= 1;
// ignore and discard message if motion is to center of
// screen. those are caused by us warping the mouse.
if (x != w || y != h) {
// get mouse deltas
x -= w;
y -= h;
// warp mouse back to center
warpCursor(w, h);
// send motion
m_server->onMouseMoveSecondary(x, y);
}
}
}
return true;
}
return false;
/*
case WM_MOUSEMOVE: {
if (!m_active) {
// mouse entered a jump zone window
POINT p;
p.x = (short)LOWORD(msg.lParam);
p.y = (short)HIWORD(msg.lParam);
ClientToScreen(msg.hwnd, &p);
log((CLOG_DEBUG "event: WM_MOUSEMOVE %d,%d", p.x, p.y));
m_server->onMouseMovePrimary((SInt32)p.x, (SInt32)p.y);
}
break;
}
case KeyPress: {
log((CLOG_DEBUG "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
const KeyID key = mapKey(xevent.xkey.keycode, mask);
if (key != kKeyNULL) {
m_server->onKeyDown(key, mask);
}
break;
}
// FIXME -- simulate key repeat. X sends press/release for
// repeat. must detect auto repeat and use kKeyRepeat.
case KeyRelease: {
log((CLOG_DEBUG "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
const KeyID key = mapKey(xevent.xkey.keycode, mask);
if (key != kKeyNULL) {
m_server->onKeyUp(key, mask);
}
break;
}
case ButtonPress: {
log((CLOG_DEBUG "event: ButtonPress button=%d", xevent.xbutton.button));
const ButtonID button = mapButton(xevent.xbutton.button);
if (button != kButtonNULL) {
m_server->onMouseDown(button);
}
break;
}
case ButtonRelease: {
log((CLOG_DEBUG "event: ButtonRelease button=%d", xevent.xbutton.button));
const ButtonID button = mapButton(xevent.xbutton.button);
if (button != kButtonNULL) {
m_server->onMouseUp(button);
}
break;
}
case SelectionClear:
target->XXX(xevent.xselectionclear.);
break;
case SelectionNotify:
target->XXX(xevent.xselection.);
break;
case SelectionRequest:
target->XXX(xevent.xselectionrequest.);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
*/
}
void CMSWindowsPrimaryScreen::nextMark()
{
assert(m_window != NULL);
PostMessage(m_window, SYNERGY_MSG_MARK, ++m_mark, 0);
}
#if 0
bool CMSWindowsPrimaryScreen::keyboardHook(
int /*code*/, WPARAM wParam, LPARAM lParam)
{
if (m_active) {
// handle keyboard events
const KeyID key = mapKey(wParam, lParam);
if (key != kKeyNone) {
const KeyModifierMask modifiers = mapModifier(wParam, lParam);
if ((lParam & KF_UP) == 0) {
log((CLOG_DEBUG "event: key press key=%d", key));
m_server->onKeyDown(key, modifiers);
}
else {
log((CLOG_DEBUG "event: key release key=%d", key));
m_server->onKeyUp(key, modifiers);
}
return true;
}
}
return false;
}
#endif
KeyModifierMask CMSWindowsPrimaryScreen::mapModifier(
WPARAM keycode, LPARAM info) const
{
// FIXME -- should be configurable
KeyModifierMask mask = 0;
if (GetKeyState(VK_SHIFT) < 0)
mask |= KeyModifierShift;
if ((GetKeyState(VK_CAPITAL) & 1) != 0)
mask |= KeyModifierCapsLock;
if (GetKeyState(VK_CONTROL) < 0)
mask |= KeyModifierControl;
if (GetKeyState(VK_MENU) < 0)
mask |= KeyModifierAlt;
if ((GetKeyState(VK_NUMLOCK) & 1) != 0)
mask |= KeyModifierNumLock;
if (GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0)
mask |= KeyModifierMeta;
if ((GetKeyState(VK_SCROLL) & 1) != 0)
mask |= KeyModifierScrollLock;
return mask;
}
KeyID CMSWindowsPrimaryScreen::mapKey(
WPARAM keycode, LPARAM info) const
{
// FIXME -- must convert to X keysyms
return keycode;
}
ButtonID CMSWindowsPrimaryScreen::mapButton(
WPARAM button) const
{
switch (button) {
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
return kButtonLeft;
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
return kButtonMiddle;
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
return kButtonRight;
default:
return kButtonNone;
}
}