00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "CClient.h"
00016 #include "CServerProxy.h"
00017 #include "CScreen.h"
00018 #include "CClipboard.h"
00019 #include "CPacketStreamFilter.h"
00020 #include "CProtocolUtil.h"
00021 #include "ProtocolTypes.h"
00022 #include "XSynergy.h"
00023 #include "IDataSocket.h"
00024 #include "ISocketFactory.h"
00025 #include "IStreamFilterFactory.h"
00026 #include "CLog.h"
00027 #include "IEventQueue.h"
00028 #include "TMethodEventJob.h"
00029 #include <cstring>
00030 #include <cstdlib>
00031 #include "CArch.h"
00032
00033
00034
00035
00036
00037 CEvent::Type CClient::s_connectedEvent = CEvent::kUnknown;
00038 CEvent::Type CClient::s_connectionFailedEvent = CEvent::kUnknown;
00039 CEvent::Type CClient::s_disconnectedEvent = CEvent::kUnknown;
00040
00041 CClient::CClient(const CString& name, const CNetworkAddress& address,
00042 ISocketFactory* socketFactory,
00043 IStreamFilterFactory* streamFilterFactory,
00044 CScreen* screen) :
00045 m_name(name),
00046 m_serverAddress(address),
00047 m_socketFactory(socketFactory),
00048 m_streamFilterFactory(streamFilterFactory),
00049 m_screen(screen),
00050 m_stream(NULL),
00051 m_timer(NULL),
00052 m_server(NULL),
00053 m_ready(false),
00054 m_active(false),
00055 m_suspended(false),
00056 m_connectOnResume(false)
00057 {
00058 assert(m_socketFactory != NULL);
00059 assert(m_screen != NULL);
00060
00061
00062 EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(),
00063 getEventTarget(),
00064 new TMethodEventJob<CClient>(this,
00065 &CClient::handleSuspend));
00066 EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(),
00067 getEventTarget(),
00068 new TMethodEventJob<CClient>(this,
00069 &CClient::handleResume));
00070 }
00071
00072 CClient::~CClient()
00073 {
00074 EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(),
00075 getEventTarget());
00076 EVENTQUEUE->removeHandler(IScreen::getResumeEvent(),
00077 getEventTarget());
00078
00079 cleanupTimer();
00080 cleanupScreen();
00081 cleanupConnecting();
00082 cleanupConnection();
00083 delete m_socketFactory;
00084 delete m_streamFilterFactory;
00085 }
00086
00087 void
00088 CClient::connect()
00089 {
00090 if (m_stream != NULL) {
00091 return;
00092 }
00093 if (m_suspended) {
00094 m_connectOnResume = true;
00095 return;
00096 }
00097
00098 try {
00099
00100
00101
00102
00103
00104 m_serverAddress.resolve();
00105
00106
00107 if (m_serverAddress.getAddress() != NULL) {
00108
00109 LOG((CLOG_NOTE "connecting to '%s': %s:%i",
00110 m_serverAddress.getHostname().c_str(),
00111 ARCH->addrToString(m_serverAddress.getAddress()).c_str(),
00112 m_serverAddress.getPort()));
00113 }
00114
00115
00116 IDataSocket* socket = m_socketFactory->create();
00117
00118
00119 m_stream = socket;
00120 if (m_streamFilterFactory != NULL) {
00121 m_stream = m_streamFilterFactory->create(m_stream, true);
00122 }
00123 m_stream = new CPacketStreamFilter(m_stream, true);
00124
00125
00126 LOG((CLOG_DEBUG1 "connecting to server"));
00127 setupConnecting();
00128 setupTimer();
00129 socket->connect(m_serverAddress);
00130 }
00131 catch (XBase& e) {
00132 cleanupTimer();
00133 cleanupConnecting();
00134 delete m_stream;
00135 m_stream = NULL;
00136 LOG((CLOG_DEBUG1 "connection failed"));
00137 sendConnectionFailedEvent(e.what());
00138 return;
00139 }
00140 }
00141
00142 void
00143 CClient::disconnect(const char* msg)
00144 {
00145 m_connectOnResume = false;
00146 cleanupTimer();
00147 cleanupScreen();
00148 cleanupConnecting();
00149 cleanupConnection();
00150 if (msg != NULL) {
00151 sendConnectionFailedEvent(msg);
00152 }
00153 else {
00154 sendEvent(getDisconnectedEvent(), NULL);
00155 }
00156 }
00157
00158 void
00159 CClient::handshakeComplete()
00160 {
00161 m_ready = true;
00162 m_screen->enable();
00163 sendEvent(getConnectedEvent(), NULL);
00164 }
00165
00166 bool
00167 CClient::isConnected() const
00168 {
00169 return (m_server != NULL);
00170 }
00171
00172 bool
00173 CClient::isConnecting() const
00174 {
00175 return (m_timer != NULL);
00176 }
00177
00178 CNetworkAddress
00179 CClient::getServerAddress() const
00180 {
00181 return m_serverAddress;
00182 }
00183
00184 CEvent::Type
00185 CClient::getConnectedEvent()
00186 {
00187 return CEvent::registerTypeOnce(s_connectedEvent,
00188 "CClient::connected");
00189 }
00190
00191 CEvent::Type
00192 CClient::getConnectionFailedEvent()
00193 {
00194 return CEvent::registerTypeOnce(s_connectionFailedEvent,
00195 "CClient::failed");
00196 }
00197
00198 CEvent::Type
00199 CClient::getDisconnectedEvent()
00200 {
00201 return CEvent::registerTypeOnce(s_disconnectedEvent,
00202 "CClient::disconnected");
00203 }
00204
00205 void*
00206 CClient::getEventTarget() const
00207 {
00208 return m_screen->getEventTarget();
00209 }
00210
00211 bool
00212 CClient::getClipboard(ClipboardID id, IClipboard* clipboard) const
00213 {
00214 return m_screen->getClipboard(id, clipboard);
00215 }
00216
00217 void
00218 CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
00219 {
00220 m_screen->getShape(x, y, w, h);
00221 }
00222
00223 void
00224 CClient::getCursorPos(SInt32& x, SInt32& y) const
00225 {
00226 m_screen->getCursorPos(x, y);
00227 }
00228
00229 void
00230 CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool)
00231 {
00232 m_active = true;
00233 m_screen->mouseMove(xAbs, yAbs);
00234 m_screen->enter(mask);
00235 }
00236
00237 bool
00238 CClient::leave()
00239 {
00240 m_screen->leave();
00241
00242 m_active = false;
00243
00244
00245 for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
00246 if (m_ownClipboard[id]) {
00247 sendClipboard(id);
00248 }
00249 }
00250
00251 return true;
00252 }
00253
00254 void
00255 CClient::setClipboard(ClipboardID id, const IClipboard* clipboard)
00256 {
00257 m_screen->setClipboard(id, clipboard);
00258 m_ownClipboard[id] = false;
00259 m_sentClipboard[id] = false;
00260 }
00261
00262 void
00263 CClient::grabClipboard(ClipboardID id)
00264 {
00265 m_screen->grabClipboard(id);
00266 m_ownClipboard[id] = false;
00267 m_sentClipboard[id] = false;
00268 }
00269
00270 void
00271 CClient::setClipboardDirty(ClipboardID, bool)
00272 {
00273 assert(0 && "shouldn't be called");
00274 }
00275
00276 void
00277 CClient::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
00278 {
00279 m_screen->keyDown(id, mask, button);
00280 }
00281
00282 void
00283 CClient::keyRepeat(KeyID id, KeyModifierMask mask,
00284 SInt32 count, KeyButton button)
00285 {
00286 m_screen->keyRepeat(id, mask, count, button);
00287 }
00288
00289 void
00290 CClient::keyUp(KeyID id, KeyModifierMask mask, KeyButton button)
00291 {
00292 m_screen->keyUp(id, mask, button);
00293 }
00294
00295 void
00296 CClient::mouseDown(ButtonID id)
00297 {
00298 m_screen->mouseDown(id);
00299 }
00300
00301 void
00302 CClient::mouseUp(ButtonID id)
00303 {
00304 m_screen->mouseUp(id);
00305 }
00306
00307 void
00308 CClient::mouseMove(SInt32 x, SInt32 y)
00309 {
00310 m_screen->mouseMove(x, y);
00311 }
00312
00313 void
00314 CClient::mouseRelativeMove(SInt32 dx, SInt32 dy)
00315 {
00316 m_screen->mouseRelativeMove(dx, dy);
00317 }
00318
00319 void
00320 CClient::mouseWheel(SInt32 xDelta, SInt32 yDelta)
00321 {
00322 m_screen->mouseWheel(xDelta, yDelta);
00323 }
00324
00325 void
00326 CClient::screensaver(bool activate)
00327 {
00328 m_screen->screensaver(activate);
00329 }
00330
00331 void
00332 CClient::resetOptions()
00333 {
00334 m_screen->resetOptions();
00335 }
00336
00337 void
00338 CClient::setOptions(const COptionsList& options)
00339 {
00340 m_screen->setOptions(options);
00341 }
00342
00343 CString
00344 CClient::getName() const
00345 {
00346 return m_name;
00347 }
00348
00349 void
00350 CClient::sendClipboard(ClipboardID id)
00351 {
00352
00353 assert(m_screen != NULL);
00354 assert(m_server != NULL);
00355
00356
00357
00358
00359
00360 CClipboard clipboard;
00361 if (clipboard.open(m_timeClipboard[id])) {
00362 clipboard.close();
00363 }
00364 m_screen->getClipboard(id, &clipboard);
00365
00366
00367 if (m_timeClipboard[id] == 0 ||
00368 clipboard.getTime() != m_timeClipboard[id]) {
00369
00370 m_timeClipboard[id] = clipboard.getTime();
00371
00372
00373 CString data = clipboard.marshall();
00374
00375
00376 if (!m_sentClipboard[id] || data != m_dataClipboard[id]) {
00377 m_sentClipboard[id] = true;
00378 m_dataClipboard[id] = data;
00379 m_server->onClipboardChanged(id, &clipboard);
00380 }
00381 }
00382 }
00383
00384 void
00385 CClient::sendEvent(CEvent::Type type, void* data)
00386 {
00387 EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data));
00388 }
00389
00390 void
00391 CClient::sendConnectionFailedEvent(const char* msg)
00392 {
00393 CFailInfo* info = (CFailInfo*)malloc(sizeof(CFailInfo) + strlen(msg));
00394 info->m_retry = true;
00395 strcpy(info->m_what, msg);
00396 sendEvent(getConnectionFailedEvent(), info);
00397 }
00398
00399 void
00400 CClient::setupConnecting()
00401 {
00402 assert(m_stream != NULL);
00403
00404 EVENTQUEUE->adoptHandler(IDataSocket::getConnectedEvent(),
00405 m_stream->getEventTarget(),
00406 new TMethodEventJob<CClient>(this,
00407 &CClient::handleConnected));
00408 EVENTQUEUE->adoptHandler(IDataSocket::getConnectionFailedEvent(),
00409 m_stream->getEventTarget(),
00410 new TMethodEventJob<CClient>(this,
00411 &CClient::handleConnectionFailed));
00412 }
00413
00414 void
00415 CClient::setupConnection()
00416 {
00417 assert(m_stream != NULL);
00418
00419 EVENTQUEUE->adoptHandler(ISocket::getDisconnectedEvent(),
00420 m_stream->getEventTarget(),
00421 new TMethodEventJob<CClient>(this,
00422 &CClient::handleDisconnected));
00423 EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(),
00424 m_stream->getEventTarget(),
00425 new TMethodEventJob<CClient>(this,
00426 &CClient::handleHello));
00427 EVENTQUEUE->adoptHandler(IStream::getOutputErrorEvent(),
00428 m_stream->getEventTarget(),
00429 new TMethodEventJob<CClient>(this,
00430 &CClient::handleOutputError));
00431 EVENTQUEUE->adoptHandler(IStream::getInputShutdownEvent(),
00432 m_stream->getEventTarget(),
00433 new TMethodEventJob<CClient>(this,
00434 &CClient::handleDisconnected));
00435 EVENTQUEUE->adoptHandler(IStream::getOutputShutdownEvent(),
00436 m_stream->getEventTarget(),
00437 new TMethodEventJob<CClient>(this,
00438 &CClient::handleDisconnected));
00439 }
00440
00441 void
00442 CClient::setupScreen()
00443 {
00444 assert(m_server == NULL);
00445
00446 m_ready = false;
00447 m_server = new CServerProxy(this, m_stream);
00448 EVENTQUEUE->adoptHandler(IScreen::getShapeChangedEvent(),
00449 getEventTarget(),
00450 new TMethodEventJob<CClient>(this,
00451 &CClient::handleShapeChanged));
00452 EVENTQUEUE->adoptHandler(IScreen::getClipboardGrabbedEvent(),
00453 getEventTarget(),
00454 new TMethodEventJob<CClient>(this,
00455 &CClient::handleClipboardGrabbed));
00456 }
00457
00458 void
00459 CClient::setupTimer()
00460 {
00461 assert(m_timer == NULL);
00462
00463 m_timer = EVENTQUEUE->newOneShotTimer(15.0, NULL);
00464 EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer,
00465 new TMethodEventJob<CClient>(this,
00466 &CClient::handleConnectTimeout));
00467 }
00468
00469 void
00470 CClient::cleanupConnecting()
00471 {
00472 if (m_stream != NULL) {
00473 EVENTQUEUE->removeHandler(IDataSocket::getConnectedEvent(),
00474 m_stream->getEventTarget());
00475 EVENTQUEUE->removeHandler(IDataSocket::getConnectionFailedEvent(),
00476 m_stream->getEventTarget());
00477 }
00478 }
00479
00480 void
00481 CClient::cleanupConnection()
00482 {
00483 if (m_stream != NULL) {
00484 EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(),
00485 m_stream->getEventTarget());
00486 EVENTQUEUE->removeHandler(IStream::getOutputErrorEvent(),
00487 m_stream->getEventTarget());
00488 EVENTQUEUE->removeHandler(IStream::getInputShutdownEvent(),
00489 m_stream->getEventTarget());
00490 EVENTQUEUE->removeHandler(IStream::getOutputShutdownEvent(),
00491 m_stream->getEventTarget());
00492 EVENTQUEUE->removeHandler(ISocket::getDisconnectedEvent(),
00493 m_stream->getEventTarget());
00494 delete m_stream;
00495 m_stream = NULL;
00496 }
00497 }
00498
00499 void
00500 CClient::cleanupScreen()
00501 {
00502 if (m_server != NULL) {
00503 if (m_ready) {
00504 m_screen->disable();
00505 m_ready = false;
00506 }
00507 EVENTQUEUE->removeHandler(IScreen::getShapeChangedEvent(),
00508 getEventTarget());
00509 EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(),
00510 getEventTarget());
00511 delete m_server;
00512 m_server = NULL;
00513 }
00514 }
00515
00516 void
00517 CClient::cleanupTimer()
00518 {
00519 if (m_timer != NULL) {
00520 EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer);
00521 EVENTQUEUE->deleteTimer(m_timer);
00522 m_timer = NULL;
00523 }
00524 }
00525
00526 void
00527 CClient::handleConnected(const CEvent&, void*)
00528 {
00529 LOG((CLOG_DEBUG1 "connected; wait for hello"));
00530 cleanupConnecting();
00531 setupConnection();
00532
00533
00534 for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
00535 m_ownClipboard[id] = false;
00536 m_sentClipboard[id] = false;
00537 m_timeClipboard[id] = 0;
00538 }
00539 }
00540
00541 void
00542 CClient::handleConnectionFailed(const CEvent& event, void*)
00543 {
00544 IDataSocket::CConnectionFailedInfo* info =
00545 reinterpret_cast<IDataSocket::CConnectionFailedInfo*>(event.getData());
00546
00547 cleanupTimer();
00548 cleanupConnecting();
00549 delete m_stream;
00550 m_stream = NULL;
00551 LOG((CLOG_DEBUG1 "connection failed"));
00552 sendConnectionFailedEvent(info->m_what);
00553 }
00554
00555 void
00556 CClient::handleConnectTimeout(const CEvent&, void*)
00557 {
00558 cleanupTimer();
00559 cleanupConnecting();
00560 cleanupConnection();
00561 delete m_stream;
00562 m_stream = NULL;
00563 LOG((CLOG_DEBUG1 "connection timed out"));
00564 sendConnectionFailedEvent("Timed out");
00565 }
00566
00567 void
00568 CClient::handleOutputError(const CEvent&, void*)
00569 {
00570 cleanupTimer();
00571 cleanupScreen();
00572 cleanupConnection();
00573 LOG((CLOG_WARN "error sending to server"));
00574 sendEvent(getDisconnectedEvent(), NULL);
00575 }
00576
00577 void
00578 CClient::handleDisconnected(const CEvent&, void*)
00579 {
00580 cleanupTimer();
00581 cleanupScreen();
00582 cleanupConnection();
00583 LOG((CLOG_DEBUG1 "disconnected"));
00584 sendEvent(getDisconnectedEvent(), NULL);
00585 }
00586
00587 void
00588 CClient::handleShapeChanged(const CEvent&, void*)
00589 {
00590 LOG((CLOG_DEBUG "resolution changed"));
00591 m_server->onInfoChanged();
00592 }
00593
00594 void
00595 CClient::handleClipboardGrabbed(const CEvent& event, void*)
00596 {
00597 const IScreen::CClipboardInfo* info =
00598 reinterpret_cast<const IScreen::CClipboardInfo*>(event.getData());
00599
00600
00601 m_server->onGrabClipboard(info->m_id);
00602
00603
00604 m_ownClipboard[info->m_id] = true;
00605 m_sentClipboard[info->m_id] = false;
00606 m_timeClipboard[info->m_id] = 0;
00607
00608
00609
00610 if (!m_active) {
00611 sendClipboard(info->m_id);
00612 }
00613 }
00614
00615 void
00616 CClient::handleHello(const CEvent&, void*)
00617 {
00618 SInt16 major, minor;
00619 if (!CProtocolUtil::readf(m_stream, kMsgHello, &major, &minor)) {
00620 sendConnectionFailedEvent("Protocol error from server");
00621 cleanupTimer();
00622 cleanupConnection();
00623 return;
00624 }
00625
00626
00627 LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor));
00628 if (major < kProtocolMajorVersion ||
00629 (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) {
00630 sendConnectionFailedEvent(XIncompatibleClient(major, minor).what());
00631 cleanupTimer();
00632 cleanupConnection();
00633 return;
00634 }
00635
00636
00637 LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion));
00638 CProtocolUtil::writef(m_stream, kMsgHelloBack,
00639 kProtocolMajorVersion,
00640 kProtocolMinorVersion, &m_name);
00641
00642
00643 setupScreen();
00644 cleanupTimer();
00645
00646
00647
00648
00649 if (m_stream->isReady()) {
00650 EVENTQUEUE->addEvent(CEvent(IStream::getInputReadyEvent(),
00651 m_stream->getEventTarget()));
00652 }
00653 }
00654
00655 void
00656 CClient::handleSuspend(const CEvent&, void*)
00657 {
00658 LOG((CLOG_INFO "suspend"));
00659 m_suspended = true;
00660 bool wasConnected = isConnected();
00661 disconnect(NULL);
00662 m_connectOnResume = wasConnected;
00663 }
00664
00665 void
00666 CClient::handleResume(const CEvent&, void*)
00667 {
00668 LOG((CLOG_INFO "resume"));
00669 m_suspended = false;
00670 if (m_connectOnResume) {
00671 m_connectOnResume = false;
00672 connect();
00673 }
00674 }