mirror of
https://github.com/debauchee/barrier.git
synced 2026-02-08 21:03:54 +08:00
sending a sequence number with enter messages. screens use that sequence number in clipboard grab and data messages. the server uses the sequence number to order messages across clients. also changed secondary screens to send clipboard updates on leaving (or when grab occurs when not active) instead of on a query from the server. primary effectively does the same. the query message has been removed.
515 lines
12 KiB
C++
515 lines
12 KiB
C++
#include "CClient.h"
|
|
#include "CInputPacketStream.h"
|
|
#include "COutputPacketStream.h"
|
|
#include "CProtocolUtil.h"
|
|
#include "CClipboard.h"
|
|
#include "ISecondaryScreen.h"
|
|
#include "ProtocolTypes.h"
|
|
#include "CLock.h"
|
|
#include "CThread.h"
|
|
#include "CTimerThread.h"
|
|
#include "XSynergy.h"
|
|
#include "TMethodJob.h"
|
|
#include "CLog.h"
|
|
#include <assert.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_active(false),
|
|
m_seqNum(0)
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
CClient::~CClient()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
void CClient::run(const CNetworkAddress& serverAddress)
|
|
{
|
|
CThread* thread;
|
|
try {
|
|
log((CLOG_NOTE "starting client"));
|
|
|
|
// connect to secondary screen
|
|
openSecondaryScreen();
|
|
|
|
// 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();
|
|
thread->wait();
|
|
delete thread;
|
|
closeSecondaryScreen();
|
|
}
|
|
catch (XBase& e) {
|
|
log((CLOG_ERR "client error: %s", e.what()));
|
|
|
|
// clean up
|
|
thread->cancel();
|
|
thread->wait();
|
|
delete thread;
|
|
closeSecondaryScreen();
|
|
}
|
|
catch (...) {
|
|
log((CLOG_DEBUG "unknown client error"));
|
|
|
|
// clean up
|
|
thread->cancel();
|
|
thread->wait();
|
|
delete thread;
|
|
closeSecondaryScreen();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
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 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()));
|
|
CProtocolUtil::writef(m_output, kMsgDClipboard, id, m_seqNum, &data);
|
|
}
|
|
}
|
|
|
|
#include "CTCPSocket.h" // FIXME
|
|
void CClient::runSession(void*)
|
|
{
|
|
log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str()));
|
|
|
|
std::auto_ptr<ISocket> socket;
|
|
std::auto_ptr<IInputStream> input;
|
|
std::auto_ptr<IOutputStream> output;
|
|
try {
|
|
// allow connect this much time to succeed
|
|
CTimerThread timer(30.0); // FIXME -- timeout in member
|
|
|
|
// create socket and attempt to connect to server
|
|
log((CLOG_DEBUG1 "connecting to server"));
|
|
assign(socket, new CTCPSocket(), ISocket); // FIXME -- use factory
|
|
socket->connect(*m_serverAddress);
|
|
log((CLOG_INFO "connected to server"));
|
|
|
|
// 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;
|
|
}
|
|
*/
|
|
|
|
// 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 < kMajorVersion ||
|
|
(major == kMajorVersion && minor < kMinorVersion)) {
|
|
throw XIncompatibleClient(major, minor);
|
|
}
|
|
|
|
// say hello back
|
|
log((CLOG_DEBUG1 "say hello version %d.%d", kMajorVersion, kMinorVersion));
|
|
CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s",
|
|
kMajorVersion, kMinorVersion, &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();
|
|
return;
|
|
}
|
|
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();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// handle messages from server
|
|
for (;;) {
|
|
// wait for reply
|
|
log((CLOG_DEBUG1 "waiting for message"));
|
|
UInt8 code[4];
|
|
UInt32 n = input->read(code, 4);
|
|
|
|
// verify we got an entire code
|
|
if (n == 0) {
|
|
log((CLOG_NOTE "server disconnected"));
|
|
// server hungup
|
|
break;
|
|
}
|
|
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, 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, kMsgDClipboard, 4) == 0) {
|
|
onSetClipboard();
|
|
}
|
|
else if (memcmp(code, kMsgCClose, 4) == 0) {
|
|
// server wants us to hangup
|
|
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();
|
|
return;
|
|
}
|
|
|
|
// done with socket
|
|
log((CLOG_DEBUG "disconnecting from server"));
|
|
socket->close();
|
|
|
|
// exit event loop
|
|
m_screen->stop();
|
|
}
|
|
|
|
// FIXME -- use factory to create screen
|
|
#if defined(CONFIG_PLATFORM_WIN32)
|
|
#include "CMSWindowsSecondaryScreen.h"
|
|
#elif defined(CONFIG_PLATFORM_UNIX)
|
|
#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 defined(CONFIG_PLATFORM_WIN32)
|
|
m_screen = new CMSWindowsSecondaryScreen;
|
|
#elif defined(CONFIG_PLATFORM_UNIX)
|
|
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::onEnter()
|
|
{
|
|
SInt16 x, y;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum);
|
|
m_active = true;
|
|
}
|
|
m_screen->enter(x, y);
|
|
}
|
|
|
|
void CClient::onLeave()
|
|
{
|
|
// 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();
|
|
|
|
// send data
|
|
log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size()));
|
|
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);
|
|
|
|
// 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);
|
|
}
|
|
// FIXME
|
|
}
|
|
|
|
void CClient::onQueryInfo()
|
|
{
|
|
SInt32 w, h;
|
|
m_screen->getSize(&w, &h);
|
|
SInt32 zoneSize = m_screen->getJumpZoneSize();
|
|
|
|
log((CLOG_DEBUG1 "sending info size=%d,%d zone=%d", w, h, zoneSize));
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize);
|
|
}
|
|
|
|
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 "received clipboard %d size=%d", id, data.size()));
|
|
|
|
// unmarshall
|
|
CClipboard clipboard;
|
|
clipboard.unmarshall(data, 0);
|
|
|
|
// set screen's clipboard
|
|
m_screen->setClipboard(id, &clipboard);
|
|
}
|
|
|
|
void CClient::onKeyDown()
|
|
{
|
|
SInt16 id, mask;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask);
|
|
}
|
|
m_screen->keyDown(static_cast<KeyID>(id),
|
|
static_cast<KeyModifierMask>(mask));
|
|
}
|
|
|
|
void CClient::onKeyRepeat()
|
|
{
|
|
SInt16 id, mask, count;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count);
|
|
}
|
|
m_screen->keyRepeat(static_cast<KeyID>(id),
|
|
static_cast<KeyModifierMask>(mask),
|
|
count);
|
|
}
|
|
|
|
void CClient::onKeyUp()
|
|
{
|
|
SInt16 id, mask;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask);
|
|
}
|
|
m_screen->keyUp(static_cast<KeyID>(id),
|
|
static_cast<KeyModifierMask>(mask));
|
|
}
|
|
|
|
void CClient::onMouseDown()
|
|
{
|
|
SInt8 id;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id);
|
|
}
|
|
m_screen->mouseDown(static_cast<ButtonID>(id));
|
|
}
|
|
|
|
void CClient::onMouseUp()
|
|
{
|
|
SInt8 id;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id);
|
|
}
|
|
m_screen->mouseUp(static_cast<ButtonID>(id));
|
|
}
|
|
|
|
void CClient::onMouseMove()
|
|
{
|
|
SInt16 x, y;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y);
|
|
}
|
|
m_screen->mouseMove(x, y);
|
|
}
|
|
|
|
void CClient::onMouseWheel()
|
|
{
|
|
SInt16 delta;
|
|
{
|
|
CLock lock(&m_mutex);
|
|
CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta);
|
|
}
|
|
m_screen->mouseWheel(delta);
|
|
}
|