mirror of
https://github.com/debauchee/barrier.git
synced 2024-12-25 12:06:26 +03:00
ed8ed72f26
init() it while it's already running. fixed an uninitialized pointer bug in CServer and some cleanup-on-error code in CMSWindowsPrimaryScreen. also added timeout to read() on IInputStream and a heartbeat sent by clients so the server can disconnect clients that are dead but never reset the TCP connection. previously the server would keep these dead clients around forever and if the user was locked on the client screen for some reason then the server would have to be rebooted (or the server would have to be killed via a remote login).
799 lines
17 KiB
C++
799 lines
17 KiB
C++
#include "CClient.h"
|
|
#include "CClipboard.h"
|
|
#include "CInputPacketStream.h"
|
|
#include "COutputPacketStream.h"
|
|
#include "CProtocolUtil.h"
|
|
#include "ISecondaryScreen.h"
|
|
#include "ProtocolTypes.h"
|
|
#include "XScreen.h"
|
|
#include "XSynergy.h"
|
|
#include "XSocket.h"
|
|
#include "CLock.h"
|
|
#include "CThread.h"
|
|
#include "CTimerThread.h"
|
|
#include "XThread.h"
|
|
#include "CLog.h"
|
|
#include "CStopwatch.h"
|
|
#include "TMethodJob.h"
|
|
#include <memory>
|
|
|
|
// hack to work around operator=() bug in STL in g++ prior to v3
|
|
#if defined(__GNUC__) && (__GNUC__ < 3)
|
|
#define assign(_dst, _src, _type) _dst.reset(_src)
|
|
#else
|
|
#define assign(_dst, _src, _type) _dst = std::auto_ptr<_type >(_src)
|
|
#endif
|
|
|
|
|
|
//
|
|
// CClient
|
|
//
|
|
|
|
CClient::CClient(const CString& clientName) :
|
|
m_name(clientName),
|
|
m_input(NULL),
|
|
m_output(NULL),
|
|
m_screen(NULL),
|
|
m_camp(false),
|
|
m_active(false),
|
|
m_seqNum(0),
|
|
m_ignoreMove(false)
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
CClient::~CClient()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
void
|
|
CClient::camp(bool on)
|
|
{
|
|
m_camp = on;
|
|
}
|
|
|
|
bool
|
|
CClient::open()
|
|
{
|
|
// open the screen
|
|
try {
|
|
log((CLOG_INFO "opening screen"));
|
|
openSecondaryScreen();
|
|
return true;
|
|
}
|
|
catch (XScreenOpenFailure&) {
|
|
// can't open screen yet. wait a few seconds to retry.
|
|
CThread::sleep(3.0);
|
|
log((CLOG_INFO "failed to open screen"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool
|
|
CClient::run(const CNetworkAddress& serverAddress)
|
|
{
|
|
// check preconditions
|
|
{
|
|
CLock lock(&m_mutex);
|
|
assert(m_screen != NULL);
|
|
}
|
|
|
|
CThread* thread = NULL;
|
|
try {
|
|
log((CLOG_NOTE "starting client"));
|
|
|
|
// start server interactions
|
|
m_serverAddress = &serverAddress;
|
|
thread = new CThread(new TMethodJob<CClient>(this, &CClient::runSession));
|
|
|
|
// handle events
|
|
log((CLOG_DEBUG "starting event handling"));
|
|
m_screen->run();
|
|
|
|
// clean up
|
|
log((CLOG_NOTE "stopping client"));
|
|
thread->cancel();
|
|
void* result = thread->getResult();
|
|
delete thread;
|
|
closeSecondaryScreen();
|
|
return (result != NULL);
|
|
}
|
|
catch (XBase& e) {
|
|
log((CLOG_ERR "client error: %s", e.what()));
|
|
|
|
// clean up
|
|
log((CLOG_NOTE "stopping client"));
|
|
if (thread != NULL) {
|
|
thread->cancel();
|
|
thread->wait();
|
|
delete thread;
|
|
}
|
|
closeSecondaryScreen();
|
|
return true;
|
|
}
|
|
catch (XThread&) {
|
|
// clean up
|
|
log((CLOG_NOTE "stopping client"));
|
|
if (thread != NULL) {
|
|
thread->cancel();
|
|
thread->wait();
|
|
delete thread;
|
|
}
|
|
closeSecondaryScreen();
|
|
throw;
|
|
}
|
|
catch (...) {
|
|
log((CLOG_DEBUG "unknown client error"));
|
|
|
|
// clean up
|
|
log((CLOG_NOTE "stopping client"));
|
|
if (thread != NULL) {
|
|
thread->cancel();
|
|
thread->wait();
|
|
delete thread;
|
|
}
|
|
closeSecondaryScreen();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void
|
|
CClient::quit()
|
|
{
|
|
m_screen->stop();
|
|
}
|
|
|
|
void
|
|
CClient::onClipboardChanged(ClipboardID id)
|
|
{
|
|
log((CLOG_DEBUG "sending clipboard %d changed", id));
|
|
CLock lock(&m_mutex);
|
|
if (m_output != NULL) {
|
|
// m_output can be NULL if the screen calls this method
|
|
// before we've gotten around to connecting to the server.
|
|
CProtocolUtil::writef(m_output, kMsgCClipboard, id, m_seqNum);
|
|
}
|
|
|
|
// we now own the clipboard and it has not been sent to the server
|
|
m_ownClipboard[id] = true;
|
|
m_timeClipboard[id] = 0;
|
|
|
|
// if we're not the active screen then send the clipboard now,
|
|
// otherwise we'll wait until we leave.
|
|
if (!m_active) {
|
|
// get clipboard
|
|
CClipboard clipboard;
|
|
m_screen->getClipboard(id, &clipboard);
|
|
|
|
// save new time
|
|
m_timeClipboard[id] = clipboard.getTime();
|
|
|
|
// marshall the data
|
|
CString data = clipboard.marshall();
|
|
|
|
// send data
|
|
log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size()));
|
|
if (m_output != NULL) {
|
|
// FIXME -- will we send the clipboard when we connect?
|
|
CProtocolUtil::writef(m_output, kMsgDClipboard, id, m_seqNum, &data);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CClient::onResolutionChanged()
|
|
{
|
|
log((CLOG_DEBUG "resolution changed"));
|
|
|
|
CLock lock(&m_mutex);
|
|
|
|
// start ignoring mouse movement until we get an acknowledgment
|
|
m_ignoreMove = true;
|
|
|
|
// send notification of resolution change
|
|
onQueryInfoNoLock();
|
|
}
|
|
|
|
#include "CTCPSocket.h" // FIXME
|
|
void
|
|
CClient::runSession(void*)
|
|
{
|
|
log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str()));
|
|
|
|
std::auto_ptr<IDataSocket> socket;
|
|
std::auto_ptr<IInputStream> input;
|
|
std::auto_ptr<IOutputStream> output;
|
|
try {
|
|
for (;;) {
|
|
try {
|
|
// allow connect this much time to succeed
|
|
// FIXME -- timeout in member
|
|
CTimerThread timer(m_camp ? -1.0 : 30.0);
|
|
|
|
// create socket and attempt to connect to server
|
|
log((CLOG_DEBUG1 "connecting to server"));
|
|
assign(socket, new CTCPSocket(), IDataSocket); // FIXME -- use factory
|
|
socket->connect(*m_serverAddress);
|
|
log((CLOG_INFO "connected to server"));
|
|
break;
|
|
}
|
|
catch (XSocketConnect&) {
|
|
// failed to connect. if not camping then rethrow.
|
|
if (!m_camp) {
|
|
throw;
|
|
}
|
|
|
|
// we're camping. wait a bit before retrying
|
|
CThread::sleep(5.0);
|
|
}
|
|
}
|
|
|
|
// get the input and output streams
|
|
IInputStream* srcInput = socket->getInputStream();
|
|
IOutputStream* srcOutput = socket->getOutputStream();
|
|
|
|
// attach the encryption layer
|
|
bool own = false;
|
|
/* FIXME -- implement ISecurityFactory
|
|
if (m_securityFactory != NULL) {
|
|
input.reset(m_securityFactory->createInputFilter(srcInput, own));
|
|
output.reset(m_securityFactory->createOutputFilter(srcOutput, own));
|
|
srcInput = input.get();
|
|
srcOutput = output.get();
|
|
own = true;
|
|
}
|
|
*/
|
|
|
|
// give handshake some time
|
|
CTimerThread timer(30.0);
|
|
|
|
// attach the packetizing filters
|
|
assign(input, new CInputPacketStream(srcInput, own), IInputStream);
|
|
assign(output, new COutputPacketStream(srcOutput, own), IOutputStream);
|
|
|
|
// wait for hello from server
|
|
log((CLOG_DEBUG1 "wait for hello"));
|
|
SInt16 major, minor;
|
|
CProtocolUtil::readf(input.get(), "Synergy%2i%2i", &major, &minor);
|
|
|
|
// check versions
|
|
log((CLOG_DEBUG1 "got hello version %d.%d", major, minor));
|
|
if (major < kProtocolMajorVersion ||
|
|
(major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) {
|
|
throw XIncompatibleClient(major, minor);
|
|
}
|
|
|
|
// say hello back
|
|
log((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion));
|
|
CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s",
|
|
kProtocolMajorVersion,
|
|
kProtocolMinorVersion, &m_name);
|
|
|
|
// record streams in a more useful place
|
|
CLock lock(&m_mutex);
|
|
m_input = input.get();
|
|
m_output = output.get();
|
|
}
|
|
catch (XIncompatibleClient& e) {
|
|
log((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor()));
|
|
m_screen->stop();
|
|
CThread::exit(NULL);
|
|
}
|
|
catch (XThread&) {
|
|
log((CLOG_ERR "connection timed out"));
|
|
m_screen->stop();
|
|
throw;
|
|
}
|
|
catch (XBase& e) {
|
|
log((CLOG_ERR "connection failed: %s", e.what()));
|
|
m_screen->stop();
|
|
CThread::exit(NULL);
|
|
}
|
|
catch (...) {
|
|
log((CLOG_ERR "connection failed: <unknown error>"));
|
|
m_screen->stop();
|
|
CThread::exit(NULL);
|
|
}
|
|
|
|
bool fail = false;
|
|
try {
|
|
// no compressed mouse motion yet
|
|
m_compressMouse = false;
|
|
|
|
// handle messages from server
|
|
CStopwatch heartbeat;
|
|
for (;;) {
|
|
// if no input is pending then flush compressed mouse motion
|
|
if (input->getSize() == 0) {
|
|
flushCompressedMouse();
|
|
}
|
|
|
|
// wait for a message
|
|
log((CLOG_DEBUG2 "waiting for message"));
|
|
UInt8 code[4];
|
|
UInt32 n = input->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 || heartbeat.getTime() > kHeartRate) {
|
|
// send heartbeat
|
|
CProtocolUtil::writef(m_output, 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) {
|
|
onMouseMove();
|
|
}
|
|
else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
|
|
onMouseWheel();
|
|
}
|
|
else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
|
|
onKeyDown();
|
|
}
|
|
else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
|
|
onKeyUp();
|
|
}
|
|
else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
|
|
onMouseDown();
|
|
}
|
|
else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
|
|
onMouseUp();
|
|
}
|
|
else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
|
|
onKeyRepeat();
|
|
}
|
|
else if (memcmp(code, kMsgCNoop, 4) == 0) {
|
|
// accept and discard no-op
|
|
continue;
|
|
}
|
|
else if (memcmp(code, kMsgCEnter, 4) == 0) {
|
|
onEnter();
|
|
}
|
|
else if (memcmp(code, kMsgCLeave, 4) == 0) {
|
|
onLeave();
|
|
}
|
|
else if (memcmp(code, kMsgCClipboard, 4) == 0) {
|
|
onGrabClipboard();
|
|
}
|
|
else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
|
|
onScreenSaver();
|
|
}
|
|
else if (memcmp(code, kMsgQInfo, 4) == 0) {
|
|
onQueryInfo();
|
|
}
|
|
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
|
|
onInfoAcknowledgment();
|
|
}
|
|
else if (memcmp(code, kMsgDClipboard, 4) == 0) {
|
|
onSetClipboard();
|
|
}
|
|
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) {
|
|
onErrorIncompatible();
|
|
fail = true;
|
|
break;
|
|
}
|
|
else if (memcmp(code, kMsgEBusy, 4) == 0) {
|
|
onErrorBusy();
|
|
fail = true;
|
|
break;
|
|
}
|
|
else if (memcmp(code, kMsgEUnknown, 4) == 0) {
|
|
onErrorUnknown();
|
|
fail = true;
|
|
break;
|
|
}
|
|
else if (memcmp(code, kMsgEBad, 4) == 0) {
|
|
onErrorBad();
|
|
fail = true;
|
|
break;
|
|
}
|
|
else {
|
|
// unknown message
|
|
log((CLOG_ERR "unknown message from server"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (XBase& e) {
|
|
log((CLOG_ERR "error: %s", e.what()));
|
|
m_screen->stop();
|
|
CThread::exit(reinterpret_cast<void*>(1));
|
|
}
|
|
|
|
// done with socket
|
|
log((CLOG_DEBUG "disconnecting from server"));
|
|
socket->close();
|
|
|
|
// exit event loop
|
|
m_screen->stop();
|
|
|
|
CThread::exit(fail ? NULL : reinterpret_cast<void*>(1));
|
|
}
|
|
|
|
// FIXME -- use factory to create screen
|
|
#if WINDOWS_LIKE
|
|
#include "CMSWindowsSecondaryScreen.h"
|
|
#elif UNIX_LIKE
|
|
#include "CXWindowsSecondaryScreen.h"
|
|
#endif
|
|
void
|
|
CClient::openSecondaryScreen()
|
|
{
|
|
assert(m_screen == NULL);
|
|
|
|
// not active
|
|
m_active = false;
|
|
|
|
// reset last sequence number
|
|
m_seqNum = 0;
|
|
|
|
// reset clipboard state
|
|
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
|
m_ownClipboard[id] = false;
|
|
m_timeClipboard[id] = 0;
|
|
}
|
|
|
|
// open screen
|
|
log((CLOG_DEBUG1 "creating secondary screen"));
|
|
#if WINDOWS_LIKE
|
|
m_screen = new CMSWindowsSecondaryScreen;
|
|
#elif UNIX_LIKE
|
|
m_screen = new CXWindowsSecondaryScreen;
|
|
#endif
|
|
log((CLOG_DEBUG1 "opening secondary screen"));
|
|
m_screen->open(this);
|
|
}
|
|
|
|
void
|
|
CClient::closeSecondaryScreen()
|
|
{
|
|
assert(m_screen != NULL);
|
|
|
|
// close the secondary screen
|
|
try {
|
|
log((CLOG_DEBUG1 "closing secondary screen"));
|
|
m_screen->close();
|
|
}
|
|
catch (...) {
|
|
// ignore
|
|
}
|
|
|
|
// clean up
|
|
log((CLOG_DEBUG1 "destroying secondary screen"));
|
|
delete m_screen;
|
|
m_screen = NULL;
|
|
}
|
|
|
|
void
|
|
CClient::flushCompressedMouse()
|
|
{
|
|
if (m_compressMouse) {
|
|
m_compressMouse = false;
|
|
m_screen->mouseMove(m_xMouse, m_yMouse);
|
|
}
|
|
}
|
|
|
|
void
|
|
CClient::onEnter()
|
|
{
|
|
SInt16 x, y;
|
|
UInt16 mask;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum, &mask);
|
|
m_active = true;
|
|
}
|
|
log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, m_seqNum, mask));
|
|
|
|
// discard old compressed mouse motion, if any
|
|
m_compressMouse = false;
|
|
|
|
// tell screen we're entering
|
|
m_screen->enter(x, y, static_cast<KeyModifierMask>(mask));
|
|
}
|
|
|
|
void
|
|
CClient::onLeave()
|
|
{
|
|
log((CLOG_DEBUG1 "recv leave"));
|
|
|
|
// send last mouse motion
|
|
flushCompressedMouse();
|
|
|
|
// tell screen we're leaving
|
|
m_screen->leave();
|
|
|
|
// no longer the active screen
|
|
CLock lock(&m_mutex);
|
|
m_active = false;
|
|
|
|
// send clipboards that we own and that have changed
|
|
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
|
if (m_ownClipboard[id]) {
|
|
// get clipboard data. set the clipboard time to the last
|
|
// clipboard time before getting the data from the screen
|
|
// as the screen may detect an unchanged clipboard and
|
|
// avoid copying the data.
|
|
CClipboard clipboard;
|
|
if (clipboard.open(m_timeClipboard[id]))
|
|
clipboard.close();
|
|
m_screen->getClipboard(id, &clipboard);
|
|
|
|
// check time
|
|
if (m_timeClipboard[id] == 0 ||
|
|
clipboard.getTime() != m_timeClipboard[id]) {
|
|
// save new time
|
|
m_timeClipboard[id] = clipboard.getTime();
|
|
|
|
// marshall the data
|
|
CString data = clipboard.marshall();
|
|
|
|
// save and send data if different
|
|
if (data != m_dataClipboard[id]) {
|
|
log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size()));
|
|
m_dataClipboard[id] = data;
|
|
CProtocolUtil::writef(m_output,
|
|
kMsgDClipboard, id, m_seqNum, &data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CClient::onGrabClipboard()
|
|
{
|
|
ClipboardID id;
|
|
UInt32 seqNum;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id, &seqNum);
|
|
log((CLOG_DEBUG "recv grab clipboard %d", id));
|
|
|
|
// validate
|
|
if (id >= kClipboardEnd) {
|
|
return;
|
|
}
|
|
|
|
// we no longer own the clipboard
|
|
m_ownClipboard[id] = false;
|
|
}
|
|
m_screen->grabClipboard(id);
|
|
}
|
|
|
|
void
|
|
CClient::onScreenSaver()
|
|
{
|
|
SInt8 on;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on);
|
|
}
|
|
log((CLOG_DEBUG1 "recv screen saver on=%d", on));
|
|
m_screen->screenSaver(on != 0);
|
|
}
|
|
|
|
void
|
|
CClient::onQueryInfo()
|
|
{
|
|
CLock lock(&m_mutex);
|
|
onQueryInfoNoLock();
|
|
}
|
|
|
|
void
|
|
CClient::onQueryInfoNoLock()
|
|
{
|
|
SInt32 mx, my, x, y, w, h;
|
|
m_screen->getMousePos(mx, my);
|
|
m_screen->getShape(x, y, w, h);
|
|
SInt32 zoneSize = m_screen->getJumpZoneSize();
|
|
|
|
log((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", x, y, w, h, zoneSize, mx, my));
|
|
CProtocolUtil::writef(m_output, kMsgDInfo, x, y, w, h, zoneSize, mx, my);
|
|
}
|
|
|
|
void
|
|
CClient::onInfoAcknowledgment()
|
|
{
|
|
log((CLOG_DEBUG1 "recv info acknowledgment"));
|
|
CLock lock(&m_mutex);
|
|
m_ignoreMove = false;
|
|
}
|
|
|
|
void
|
|
CClient::onSetClipboard()
|
|
{
|
|
ClipboardID id;
|
|
CString data;
|
|
{
|
|
// parse message
|
|
UInt32 seqNum;
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDClipboard + 4, &id, &seqNum, &data);
|
|
}
|
|
log((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size()));
|
|
|
|
// validate
|
|
if (id >= kClipboardEnd) {
|
|
return;
|
|
}
|
|
|
|
// unmarshall
|
|
CClipboard clipboard;
|
|
clipboard.unmarshall(data, 0);
|
|
|
|
// set screen's clipboard
|
|
m_screen->setClipboard(id, &clipboard);
|
|
}
|
|
|
|
void
|
|
CClient::onKeyDown()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
UInt16 id, mask;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask);
|
|
}
|
|
log((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask));
|
|
m_screen->keyDown(static_cast<KeyID>(id),
|
|
static_cast<KeyModifierMask>(mask));
|
|
}
|
|
|
|
void
|
|
CClient::onKeyRepeat()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
UInt16 id, mask, count;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count);
|
|
}
|
|
log((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count));
|
|
m_screen->keyRepeat(static_cast<KeyID>(id),
|
|
static_cast<KeyModifierMask>(mask),
|
|
count);
|
|
}
|
|
|
|
void
|
|
CClient::onKeyUp()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
UInt16 id, mask;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask);
|
|
}
|
|
log((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask));
|
|
m_screen->keyUp(static_cast<KeyID>(id),
|
|
static_cast<KeyModifierMask>(mask));
|
|
}
|
|
|
|
void
|
|
CClient::onMouseDown()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
SInt8 id;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id);
|
|
}
|
|
log((CLOG_DEBUG1 "recv mouse down id=%d", id));
|
|
m_screen->mouseDown(static_cast<ButtonID>(id));
|
|
}
|
|
|
|
void
|
|
CClient::onMouseUp()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
SInt8 id;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id);
|
|
}
|
|
log((CLOG_DEBUG1 "recv mouse up id=%d", id));
|
|
m_screen->mouseUp(static_cast<ButtonID>(id));
|
|
}
|
|
|
|
void
|
|
CClient::onMouseMove()
|
|
{
|
|
bool ignore;
|
|
SInt16 x, y;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y);
|
|
ignore = m_ignoreMove;
|
|
|
|
// compress mouse motion events if more input follows
|
|
if (!ignore && !m_compressMouse && m_input->getSize() > 0) {
|
|
m_compressMouse = true;
|
|
}
|
|
if (m_compressMouse) {
|
|
ignore = true;
|
|
m_xMouse = x;
|
|
m_yMouse = y;
|
|
}
|
|
}
|
|
log((CLOG_DEBUG2 "recv mouse move %d,%d", x, y));
|
|
if (!ignore) {
|
|
m_screen->mouseMove(x, y);
|
|
}
|
|
}
|
|
|
|
void
|
|
CClient::onMouseWheel()
|
|
{
|
|
// get mouse up to date
|
|
flushCompressedMouse();
|
|
|
|
SInt16 delta;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta);
|
|
}
|
|
log((CLOG_DEBUG2 "recv mouse wheel %+d", delta));
|
|
m_screen->mouseWheel(delta);
|
|
}
|
|
|
|
void
|
|
CClient::onErrorIncompatible()
|
|
{
|
|
SInt32 major, minor;
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgEIncompatible + 4, &major, &minor);
|
|
log((CLOG_ERR "server has incompatible version %d.%d", major, minor));
|
|
}
|
|
|
|
void
|
|
CClient::onErrorBusy()
|
|
{
|
|
log((CLOG_ERR "server already has a connected client with name \"%s\"", m_name.c_str()));
|
|
}
|
|
|
|
void
|
|
CClient::onErrorUnknown()
|
|
{
|
|
log((CLOG_ERR "server refused client with name \"%s\"", m_name.c_str()));
|
|
}
|
|
|
|
void
|
|
CClient::onErrorBad()
|
|
{
|
|
log((CLOG_ERR "server disconnected due to a protocol error"));
|
|
}
|