#include "CClient.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" #include "ISecondaryScreen.h" #include "ProtocolTypes.h" #include "CThread.h" #include "CTimerThread.h" #include "XSynergy.h" #include "TMethodJob.h" #include "CLog.h" #include #include // 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) { // 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(this, &CClient::runSession)); // handle events log((CLOG_DEBUG "starting event handling")); m_screen->run(); // clean up log((CLOG_DEBUG "stopping client")); thread->cancel(); thread->wait(); delete thread; closeSecondaryScreen(); } catch (XBase& e) { log((CLOG_ERR "client error: %s\n", 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; } } #include "CTCPSocket.h" // FIXME void CClient::runSession(void*) { log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); std::auto_ptr socket; std::auto_ptr input; std::auto_ptr 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_DEBUG "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_DEBUG "wait for hello")); SInt32 major, minor; CProtocolUtil::readf(input.get(), "Synergy%2i%2i", &major, &minor); // check versions log((CLOG_DEBUG "got hello version %d.%d", major, minor)); if (major < kMajorVersion || (major == kMajorVersion && minor < kMinorVersion)) { throw XIncompatibleClient(major, minor); } // say hello back log((CLOG_DEBUG "say hello version %d.%d", kMajorVersion, kMinorVersion)); CProtocolUtil::writef(output.get(), "Synergy%2i%2i%s", kMajorVersion, kMinorVersion, m_name.size(), m_name.data()); // record streams in a more useful place 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_DEBUG "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_DEBUG "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, kMsgQClipboard, 4) == 0) { onQueryClipboard(); } 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); // open screen log((CLOG_DEBUG "creating secondary screen")); #if defined(CONFIG_PLATFORM_WIN32) m_screen = new CMSWindowsSecondaryScreen; #elif defined(CONFIG_PLATFORM_UNIX) m_screen = new CXWindowsSecondaryScreen; #endif log((CLOG_DEBUG "opening secondary screen")); m_screen->open(this); } void CClient::closeSecondaryScreen() { assert(m_screen != NULL); // close the secondary screen try { log((CLOG_DEBUG "closing secondary screen")); m_screen->close(); } catch (...) { // ignore } // clean up log((CLOG_DEBUG "destroying secondary screen")); delete m_screen; m_screen = NULL; } void CClient::onEnter() { SInt32 x, y; CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y); m_screen->enter(x, y); } void CClient::onLeave() { m_screen->leave(); } void CClient::onGrabClipboard() { // FIXME } void CClient::onScreenSaver() { SInt32 on; 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_DEBUG "sending info size=%d,%d zone=%d", w, h, zoneSize)); CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); } void CClient::onQueryClipboard() { // FIXME } void CClient::onSetClipboard() { // FIXME } void CClient::onKeyDown() { SInt32 id, mask; CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); m_screen->keyDown(static_cast(id), static_cast(mask)); } void CClient::onKeyRepeat() { SInt32 id, mask, count; CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); m_screen->keyRepeat(static_cast(id), static_cast(mask), count); } void CClient::onKeyUp() { SInt32 id, mask; CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); m_screen->keyUp(static_cast(id), static_cast(mask)); } void CClient::onMouseDown() { SInt32 id; CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); m_screen->mouseDown(static_cast(id)); } void CClient::onMouseUp() { SInt32 id; CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); m_screen->mouseUp(static_cast(id)); } void CClient::onMouseMove() { SInt32 x, y; CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); m_screen->mouseMove(x, y); } void CClient::onMouseWheel() { SInt32 delta; CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); m_screen->mouseWheel(delta); }