543 lines
11 KiB
C++
543 lines
11 KiB
C++
/*
|
|
* synergy -- mouse and keyboard sharing utility
|
|
* Copyright (C) 2002 Chris Schoeneman
|
|
*
|
|
* This package is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* found in the file COPYING that should have accompanied this file.
|
|
*
|
|
* This package is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include "CServerProxy.h"
|
|
#include "CProtocolUtil.h"
|
|
#include "IClient.h"
|
|
#include "ProtocolTypes.h"
|
|
#include "IInputStream.h"
|
|
#include "IOutputStream.h"
|
|
#include "CLock.h"
|
|
#include "CLog.h"
|
|
#include "CStopwatch.h"
|
|
#include "XBase.h"
|
|
#include <memory>
|
|
|
|
//
|
|
// CServerProxy
|
|
//
|
|
|
|
CServerProxy::CServerProxy(IClient* client,
|
|
IInputStream* adoptedInput, IOutputStream* adoptedOutput) :
|
|
m_client(client),
|
|
m_input(adoptedInput),
|
|
m_output(adoptedOutput),
|
|
m_seqNum(0)
|
|
{
|
|
assert(m_client != NULL);
|
|
assert(m_input != NULL);
|
|
assert(m_output != NULL);
|
|
}
|
|
|
|
CServerProxy::~CServerProxy()
|
|
{
|
|
delete m_input;
|
|
delete m_output;
|
|
}
|
|
|
|
bool
|
|
CServerProxy::mainLoop()
|
|
{
|
|
bool failedToConnect = false;
|
|
try {
|
|
// no compressed mouse motion yet
|
|
m_compressMouse = false;
|
|
|
|
// not ignoring mouse motions
|
|
m_ignoreMouse = false;
|
|
|
|
// reset sequence number
|
|
m_seqNum = 0;
|
|
|
|
// handle messages from server
|
|
CStopwatch heartbeat;
|
|
for (;;) {
|
|
// if no input is pending then flush compressed mouse motion
|
|
if (getInputStream()->getSize() == 0) {
|
|
flushCompressedMouse();
|
|
}
|
|
|
|
// wait for a message
|
|
log((CLOG_DEBUG2 "waiting for message"));
|
|
UInt8 code[4];
|
|
UInt32 n = getInputStream()->read(code, 4, kHeartRate);
|
|
|
|
// check if server hungup
|
|
if (n == 0) {
|
|
log((CLOG_NOTE "server disconnected"));
|
|
break;
|
|
}
|
|
|
|
// check for time out
|
|
if (n == (UInt32)-1 ||
|
|
(kHeartRate >= 0.0 && heartbeat.getTime() > kHeartRate)) {
|
|
// send heartbeat
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::writef(getOutputStream(), kMsgCNoop);
|
|
heartbeat.reset();
|
|
if (n == (UInt32)-1) {
|
|
// no message to process
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// verify we got an entire code
|
|
if (n != 4) {
|
|
// client sent an incomplete message
|
|
log((CLOG_ERR "incomplete message from server"));
|
|
break;
|
|
}
|
|
|
|
// parse message
|
|
log((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3]));
|
|
if (memcmp(code, kMsgDMouseMove, 4) == 0) {
|
|
mouseMove();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
|
|
mouseWheel();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
|
|
keyDown();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
|
|
keyUp();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
|
|
mouseDown();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
|
|
mouseUp();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
|
|
keyRepeat();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgCNoop, 4) == 0) {
|
|
// accept and discard no-op
|
|
}
|
|
|
|
else if (memcmp(code, kMsgCEnter, 4) == 0) {
|
|
enter();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgCLeave, 4) == 0) {
|
|
leave();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgCClipboard, 4) == 0) {
|
|
grabClipboard();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
|
|
screensaver();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgQInfo, 4) == 0) {
|
|
queryInfo();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
|
|
infoAcknowledgment();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgDClipboard, 4) == 0) {
|
|
setClipboard();
|
|
}
|
|
|
|
else if (memcmp(code, kMsgCClose, 4) == 0) {
|
|
// server wants us to hangup
|
|
log((CLOG_DEBUG1 "recv close"));
|
|
break;
|
|
}
|
|
|
|
else if (memcmp(code, kMsgEIncompatible, 4) == 0) {
|
|
SInt32 major, minor;
|
|
CProtocolUtil::readf(getInputStream(),
|
|
kMsgEIncompatible + 4, &major, &minor);
|
|
log((CLOG_ERR "server has incompatible version %d.%d", major, minor));
|
|
failedToConnect = true;
|
|
break;
|
|
}
|
|
|
|
else if (memcmp(code, kMsgEBusy, 4) == 0) {
|
|
log((CLOG_ERR "server already has a connected client with name \"%s\"", getName().c_str()));
|
|
failedToConnect = true;
|
|
break;
|
|
}
|
|
|
|
else if (memcmp(code, kMsgEUnknown, 4) == 0) {
|
|
log((CLOG_ERR "server refused client with name \"%s\"", getName().c_str()));
|
|
failedToConnect = true;
|
|
break;
|
|
}
|
|
|
|
else if (memcmp(code, kMsgEBad, 4) == 0) {
|
|
log((CLOG_ERR "server disconnected due to a protocol error"));
|
|
failedToConnect = true;
|
|
break;
|
|
}
|
|
|
|
else {
|
|
// unknown message
|
|
log((CLOG_ERR "unknown message from server"));
|
|
failedToConnect = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (XBase& e) {
|
|
log((CLOG_ERR "error: %s", e.what()));
|
|
}
|
|
catch (...) {
|
|
throw;
|
|
}
|
|
|
|
return !failedToConnect;
|
|
}
|
|
|
|
IClient*
|
|
CServerProxy::getClient() const
|
|
{
|
|
return m_client;
|
|
}
|
|
|
|
CString
|
|
CServerProxy::getName() const
|
|
{
|
|
return m_client->getName();
|
|
}
|
|
|
|
IInputStream*
|
|
CServerProxy::getInputStream() const
|
|
{
|
|
return m_input;
|
|
}
|
|
|
|
IOutputStream*
|
|
CServerProxy::getOutputStream() const
|
|
{
|
|
return m_output;
|
|
}
|
|
|
|
void
|
|
CServerProxy::onError()
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
void
|
|
CServerProxy::onInfoChanged(const CClientInfo& info)
|
|
{
|
|
// ignore mouse motion until we receive acknowledgment of our info
|
|
// change message.
|
|
CLock lock(&m_mutex);
|
|
m_ignoreMouse = true;
|
|
|
|
// send info update
|
|
sendInfo(info);
|
|
}
|
|
|
|
bool
|
|
CServerProxy::onGrabClipboard(ClipboardID id)
|
|
{
|
|
log((CLOG_DEBUG1 "sending clipboard %d changed", id));
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, m_seqNum);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
CServerProxy::onClipboardChanged(ClipboardID id, const CString& data)
|
|
{
|
|
CLock lock(&m_mutex);
|
|
log((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size()));
|
|
CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, m_seqNum, &data);
|
|
}
|
|
|
|
void
|
|
CServerProxy::flushCompressedMouse()
|
|
{
|
|
bool send = false;
|
|
SInt32 x = 0, y = 0;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
if (m_compressMouse) {
|
|
m_compressMouse = false;
|
|
x = m_xMouse;
|
|
y = m_yMouse;
|
|
send = true;
|
|
}
|
|
}
|
|
|
|
if (send) {
|
|
getClient()->mouseMove(x, y);
|
|
}
|
|
}
|
|
|
|
void
|
|
CServerProxy::sendInfo(const CClientInfo& info)
|
|
{
|
|
// note -- m_mutex should be locked on entry
|
|
log((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my));
|
|
CProtocolUtil::writef(getOutputStream(), kMsgDInfo,
|
|
info.m_x, info.m_y,
|
|
info.m_w, info.m_h,
|
|
info.m_zoneSize,
|
|
info.m_mx, info.m_my);
|
|
}
|
|
|
|
void
|
|
CServerProxy::enter()
|
|
{
|
|
// parse
|
|
SInt16 x, y;
|
|
UInt16 mask;
|
|
UInt32 seqNum;
|
|
CProtocolUtil::readf(getInputStream(),
|
|
kMsgCEnter + 4, &x, &y, &seqNum, &mask);
|
|
log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask));
|
|
|
|
// discard old compressed mouse motion, if any
|
|
{
|
|
CLock lock(&m_mutex);
|
|
m_compressMouse = false;
|
|
m_seqNum = seqNum;
|
|
}
|
|
|
|
// forward
|
|
getClient()->enter(x, y, seqNum, static_cast<KeyModifierMask>(mask), false);
|
|
}
|
|
|
|
void
|
|
CServerProxy::leave()
|
|
{
|
|
// parse
|
|
log((CLOG_DEBUG1 "recv leave"));
|
|
|
|
// send last mouse motion
|
|
flushCompressedMouse();
|
|
|
|
// forward
|
|
getClient()->leave();
|
|
}
|
|
|
|
void
|
|
CServerProxy::setClipboard()
|
|
{
|
|
// parse
|
|
ClipboardID id;
|
|
UInt32 seqNum;
|
|
CString data;
|
|
CProtocolUtil::readf(getInputStream(),
|
|
kMsgDClipboard + 4, &id, &seqNum, &data);
|
|
log((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size()));
|
|
|
|
// validate
|
|
if (id >= kClipboardEnd) {
|
|
return;
|
|
}
|
|
|
|
// forward
|
|
getClient()->setClipboard(id, data);
|
|
}
|
|
|
|
void
|
|
CServerProxy::grabClipboard()
|
|
{
|
|
// parse
|
|
ClipboardID id;
|
|
UInt32 seqNum;
|
|
CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum);
|
|
log((CLOG_DEBUG "recv grab clipboard %d", id));
|
|
|
|
// validate
|
|
if (id >= kClipboardEnd) {
|
|
return;
|
|
}
|
|
|
|
// forward
|
|
getClient()->grabClipboard(id);
|
|
}
|
|
|
|
void
|
|
CServerProxy::keyDown()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
// parse
|
|
UInt16 id, mask;
|
|
CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, &id, &mask);
|
|
log((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask));
|
|
|
|
// forward
|
|
getClient()->keyDown(static_cast<KeyID>(id),
|
|
static_cast<KeyModifierMask>(mask));
|
|
}
|
|
|
|
void
|
|
CServerProxy::keyRepeat()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
// parse
|
|
UInt16 id, mask, count;
|
|
CProtocolUtil::readf(getInputStream(),
|
|
kMsgDKeyRepeat + 4, &id, &mask, &count);
|
|
log((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count));
|
|
|
|
// forward
|
|
getClient()->keyRepeat(static_cast<KeyID>(id),
|
|
static_cast<KeyModifierMask>(mask),
|
|
count);
|
|
}
|
|
|
|
void
|
|
CServerProxy::keyUp()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
// parse
|
|
UInt16 id, mask;
|
|
CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask);
|
|
log((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask));
|
|
|
|
// forward
|
|
getClient()->keyUp(static_cast<KeyID>(id),
|
|
static_cast<KeyModifierMask>(mask));
|
|
}
|
|
|
|
void
|
|
CServerProxy::mouseDown()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
// parse
|
|
SInt8 id;
|
|
CProtocolUtil::readf(getInputStream(), kMsgDMouseDown + 4, &id);
|
|
log((CLOG_DEBUG1 "recv mouse down id=%d", id));
|
|
|
|
// forward
|
|
getClient()->mouseDown(static_cast<ButtonID>(id));
|
|
}
|
|
|
|
void
|
|
CServerProxy::mouseUp()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
// parse
|
|
SInt8 id;
|
|
CProtocolUtil::readf(getInputStream(), kMsgDMouseUp + 4, &id);
|
|
log((CLOG_DEBUG1 "recv mouse up id=%d", id));
|
|
|
|
// forward
|
|
getClient()->mouseUp(static_cast<ButtonID>(id));
|
|
}
|
|
|
|
void
|
|
CServerProxy::mouseMove()
|
|
{
|
|
// parse
|
|
bool ignore;
|
|
SInt16 x, y;
|
|
CProtocolUtil::readf(getInputStream(), kMsgDMouseMove + 4, &x, &y);
|
|
|
|
{
|
|
// note if we should ignore the move
|
|
CLock lock(&m_mutex);
|
|
ignore = m_ignoreMouse;
|
|
|
|
// compress mouse motion events if more input follows
|
|
if (!ignore && !m_compressMouse && getInputStream()->getSize() > 0) {
|
|
m_compressMouse = true;
|
|
}
|
|
|
|
// if compressing then ignore the motion but record it
|
|
if (m_compressMouse) {
|
|
ignore = true;
|
|
m_xMouse = x;
|
|
m_yMouse = y;
|
|
}
|
|
}
|
|
log((CLOG_DEBUG2 "recv mouse move %d,%d", x, y));
|
|
|
|
// forward
|
|
if (!ignore) {
|
|
getClient()->mouseMove(x, y);
|
|
}
|
|
}
|
|
|
|
void
|
|
CServerProxy::mouseWheel()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
// parse
|
|
SInt16 delta;
|
|
CProtocolUtil::readf(getInputStream(), kMsgDMouseWheel + 4, &delta);
|
|
log((CLOG_DEBUG2 "recv mouse wheel %+d", delta));
|
|
|
|
// forward
|
|
getClient()->mouseWheel(delta);
|
|
}
|
|
|
|
void
|
|
CServerProxy::screensaver()
|
|
{
|
|
// parse
|
|
SInt8 on;
|
|
CProtocolUtil::readf(getInputStream(), kMsgCScreenSaver + 4, &on);
|
|
log((CLOG_DEBUG1 "recv screen saver on=%d", on));
|
|
|
|
// forward
|
|
getClient()->screensaver(on != 0);
|
|
}
|
|
|
|
void
|
|
CServerProxy::queryInfo()
|
|
{
|
|
// get current info
|
|
CClientInfo info;
|
|
getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h);
|
|
getClient()->getCursorPos(info.m_mx, info.m_my);
|
|
info.m_zoneSize = getClient()->getJumpZoneSize();
|
|
|
|
// send it
|
|
CLock lock(&m_mutex);
|
|
sendInfo(info);
|
|
}
|
|
|
|
void
|
|
CServerProxy::infoAcknowledgment()
|
|
{
|
|
// parse
|
|
log((CLOG_DEBUG1 "recv info acknowledgment"));
|
|
|
|
// now allow mouse motion
|
|
CLock lock(&m_mutex);
|
|
m_ignoreMouse = false;
|
|
}
|