CClient.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2002 Chris Schoeneman
00004  * 
00005  * This package is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License
00007  * found in the file COPYING that should have accompanied this file.
00008  * 
00009  * This package is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
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 // CClient
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     // register suspend/resume event handlers
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         // resolve the server hostname.  do this every time we connect
00100         // in case we couldn't resolve the address earlier or the address
00101         // has changed (which can happen frequently if this is a laptop
00102         // being shuttled between various networks).  patch by Brent
00103         // Priddy.
00104         m_serverAddress.resolve();
00105         
00106         // m_serverAddress will be null if the hostname address is not reolved
00107         if (m_serverAddress.getAddress() != NULL) {
00108           // to help users troubleshoot, show server host name (issue: 60)
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         // create the socket
00116         IDataSocket* socket = m_socketFactory->create();
00117 
00118         // filter socket messages, including a packetizing filter
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         // connect
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     // send clipboards that we own and that have changed
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     // note -- m_mutex must be locked on entry
00353     assert(m_screen != NULL);
00354     assert(m_server != NULL);
00355 
00356     // get clipboard data.  set the clipboard time to the last
00357     // clipboard time before getting the data from the screen
00358     // as the screen may detect an unchanged clipboard and
00359     // avoid copying the data.
00360     CClipboard clipboard;
00361     if (clipboard.open(m_timeClipboard[id])) {
00362         clipboard.close();
00363     }
00364     m_screen->getClipboard(id, &clipboard);
00365 
00366     // check time
00367     if (m_timeClipboard[id] == 0 ||
00368         clipboard.getTime() != m_timeClipboard[id]) {
00369         // save new time
00370         m_timeClipboard[id] = clipboard.getTime();
00371 
00372         // marshall the data
00373         CString data = clipboard.marshall();
00374 
00375         // save and send data if different or not yet sent
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     // reset clipboard state
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     // grab ownership
00601     m_server->onGrabClipboard(info->m_id);
00602 
00603     // we now own the clipboard and it has not been sent to the server
00604     m_ownClipboard[info->m_id]  = true;
00605     m_sentClipboard[info->m_id] = false;
00606     m_timeClipboard[info->m_id] = 0;
00607 
00608     // if we're not the active screen then send the clipboard now,
00609     // otherwise we'll wait until we leave.
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     // check versions
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     // say hello back
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     // now connected but waiting to complete handshake
00643     setupScreen();
00644     cleanupTimer();
00645 
00646     // make sure we process any remaining messages later.  we won't
00647     // receive another event for already pending messages so we fake
00648     // one.
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 }

Generated on Fri Nov 6 00:18:44 2009 for synergy-plus by  doxygen 1.4.7