Files
barrier/client/CClient.cpp
crs c5f6b34d85 checkpoint. changed protocol to better handle clipboards. now
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.
2002-04-29 13:31:44 +00:00

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);
}