CClientProxy1_0.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 "CClientProxy1_0.h"
00016 #include "CProtocolUtil.h"
00017 #include "XSynergy.h"
00018 #include "IStream.h"
00019 #include "CLog.h"
00020 #include "IEventQueue.h"
00021 #include "TMethodEventJob.h"
00022 #include <cstring>
00023 
00024 //
00025 // CClientProxy1_0
00026 //
00027 
00028 CClientProxy1_0::CClientProxy1_0(const CString& name, IStream* stream) :
00029     CClientProxy(name, stream),
00030     m_heartbeatTimer(NULL),
00031     m_parser(&CClientProxy1_0::parseHandshakeMessage)
00032 {
00033     // install event handlers
00034     EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(),
00035                             stream->getEventTarget(),
00036                             new TMethodEventJob<CClientProxy1_0>(this,
00037                                 &CClientProxy1_0::handleData, NULL));
00038     EVENTQUEUE->adoptHandler(IStream::getOutputErrorEvent(),
00039                             stream->getEventTarget(),
00040                             new TMethodEventJob<CClientProxy1_0>(this,
00041                                 &CClientProxy1_0::handleWriteError, NULL));
00042     EVENTQUEUE->adoptHandler(IStream::getInputShutdownEvent(),
00043                             stream->getEventTarget(),
00044                             new TMethodEventJob<CClientProxy1_0>(this,
00045                                 &CClientProxy1_0::handleDisconnect, NULL));
00046     EVENTQUEUE->adoptHandler(IStream::getOutputShutdownEvent(),
00047                             stream->getEventTarget(),
00048                             new TMethodEventJob<CClientProxy1_0>(this,
00049                                 &CClientProxy1_0::handleWriteError, NULL));
00050     EVENTQUEUE->adoptHandler(CEvent::kTimer, this,
00051                             new TMethodEventJob<CClientProxy1_0>(this,
00052                                 &CClientProxy1_0::handleFlatline, NULL));
00053 
00054     setHeartbeatRate(kHeartRate, kHeartRate * kHeartBeatsUntilDeath);
00055 
00056     LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str()));
00057     CProtocolUtil::writef(getStream(), kMsgQInfo);
00058 }
00059 
00060 CClientProxy1_0::~CClientProxy1_0()
00061 {
00062     removeHandlers();
00063 }
00064 
00065 void
00066 CClientProxy1_0::disconnect()
00067 {
00068     removeHandlers();
00069     getStream()->close();
00070     EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), getEventTarget()));
00071 }
00072 
00073 void
00074 CClientProxy1_0::removeHandlers()
00075 {
00076     // uninstall event handlers
00077     EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(),
00078                             getStream()->getEventTarget());
00079     EVENTQUEUE->removeHandler(IStream::getOutputErrorEvent(),
00080                             getStream()->getEventTarget());
00081     EVENTQUEUE->removeHandler(IStream::getInputShutdownEvent(),
00082                             getStream()->getEventTarget());
00083     EVENTQUEUE->removeHandler(IStream::getOutputShutdownEvent(),
00084                             getStream()->getEventTarget());
00085     EVENTQUEUE->removeHandler(CEvent::kTimer, this);
00086 
00087     // remove timer
00088     removeHeartbeatTimer();
00089 }
00090 
00091 void
00092 CClientProxy1_0::addHeartbeatTimer()
00093 {
00094     if (m_heartbeatAlarm > 0.0) {
00095         m_heartbeatTimer = EVENTQUEUE->newOneShotTimer(m_heartbeatAlarm, this);
00096     }
00097 }
00098 
00099 void
00100 CClientProxy1_0::removeHeartbeatTimer()
00101 {
00102     if (m_heartbeatTimer != NULL) {
00103         EVENTQUEUE->deleteTimer(m_heartbeatTimer);
00104         m_heartbeatTimer = NULL;
00105     }
00106 }
00107 
00108 void
00109 CClientProxy1_0::resetHeartbeatTimer()
00110 {
00111     // reset the alarm
00112     removeHeartbeatTimer();
00113     addHeartbeatTimer();
00114 }
00115 
00116 void
00117 CClientProxy1_0::resetHeartbeatRate()
00118 {
00119     setHeartbeatRate(kHeartRate, kHeartRate * kHeartBeatsUntilDeath);
00120 }
00121 
00122 void
00123 CClientProxy1_0::setHeartbeatRate(double, double alarm)
00124 {
00125     m_heartbeatAlarm = alarm;
00126 }
00127 
00128 void
00129 CClientProxy1_0::handleData(const CEvent&, void*)
00130 {
00131     // handle messages until there are no more.  first read message code.
00132     UInt8 code[4];
00133     UInt32 n = getStream()->read(code, 4);
00134     while (n != 0) {
00135         // verify we got an entire code
00136         if (n != 4) {
00137             LOG((CLOG_ERR "incomplete message from \"%s\": %d bytes", getName().c_str(), n));
00138             disconnect();
00139             return;
00140         }
00141 
00142         // parse message
00143         LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3]));
00144         if (!(this->*m_parser)(code)) {
00145             LOG((CLOG_ERR "invalid message from client \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3]));
00146             disconnect();
00147             return;
00148         }
00149 
00150         // next message
00151         n = getStream()->read(code, 4);
00152     }
00153 
00154     // restart heartbeat timer
00155     resetHeartbeatTimer();
00156 }
00157 
00158 bool
00159 CClientProxy1_0::parseHandshakeMessage(const UInt8* code)
00160 {
00161     if (memcmp(code, kMsgCNoop, 4) == 0) {
00162         // discard no-ops
00163         LOG((CLOG_DEBUG2 "no-op from", getName().c_str()));
00164         return true;
00165     }
00166     else if (memcmp(code, kMsgDInfo, 4) == 0) {
00167         // future messages get parsed by parseMessage
00168         m_parser = &CClientProxy1_0::parseMessage;
00169         if (recvInfo()) {
00170             EVENTQUEUE->addEvent(CEvent(getReadyEvent(), getEventTarget()));
00171             addHeartbeatTimer();
00172             return true;
00173         }
00174     }
00175     return false;
00176 }
00177 
00178 bool
00179 CClientProxy1_0::parseMessage(const UInt8* code)
00180 {
00181     if (memcmp(code, kMsgDInfo, 4) == 0) {
00182         if (recvInfo()) {
00183             EVENTQUEUE->addEvent(
00184                             CEvent(getShapeChangedEvent(), getEventTarget()));
00185             return true;
00186         }
00187         return false;
00188     }
00189     else if (memcmp(code, kMsgCNoop, 4) == 0) {
00190         // discard no-ops
00191         LOG((CLOG_DEBUG2 "no-op from", getName().c_str()));
00192         return true;
00193     }
00194     else if (memcmp(code, kMsgCClipboard, 4) == 0) {
00195         return recvGrabClipboard();
00196     }
00197     else if (memcmp(code, kMsgDClipboard, 4) == 0) {
00198         return recvClipboard();
00199     }
00200     return false;
00201 }
00202 
00203 void
00204 CClientProxy1_0::handleDisconnect(const CEvent&, void*)
00205 {
00206     LOG((CLOG_NOTE "client \"%s\" has disconnected", getName().c_str()));
00207     disconnect();
00208 }
00209 
00210 void
00211 CClientProxy1_0::handleWriteError(const CEvent&, void*)
00212 {
00213     LOG((CLOG_WARN "error writing to client \"%s\"", getName().c_str()));
00214     disconnect();
00215 }
00216 
00217 void
00218 CClientProxy1_0::handleFlatline(const CEvent&, void*)
00219 {
00220     // didn't get a heartbeat fast enough.  assume client is dead.
00221     LOG((CLOG_NOTE "client \"%s\" is dead", getName().c_str()));
00222     disconnect();
00223 }
00224 
00225 bool
00226 CClientProxy1_0::getClipboard(ClipboardID id, IClipboard* clipboard) const
00227 {
00228     CClipboard::copy(clipboard, &m_clipboard[id].m_clipboard);
00229     return true;
00230 }
00231 
00232 void
00233 CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
00234 {
00235     x = m_info.m_x;
00236     y = m_info.m_y;
00237     w = m_info.m_w;
00238     h = m_info.m_h;
00239 }
00240 
00241 void
00242 CClientProxy1_0::getCursorPos(SInt32& x, SInt32& y) const
00243 {
00244     // note -- this returns the cursor pos from when we last got client info
00245     x = m_info.m_mx;
00246     y = m_info.m_my;
00247 }
00248 
00249 void
00250 CClientProxy1_0::enter(SInt32 xAbs, SInt32 yAbs,
00251                 UInt32 seqNum, KeyModifierMask mask, bool)
00252 {
00253     LOG((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getName().c_str(), xAbs, yAbs, seqNum, mask));
00254     CProtocolUtil::writef(getStream(), kMsgCEnter,
00255                                 xAbs, yAbs, seqNum, mask);
00256 }
00257 
00258 bool
00259 CClientProxy1_0::leave()
00260 {
00261     LOG((CLOG_DEBUG1 "send leave to \"%s\"", getName().c_str()));
00262     CProtocolUtil::writef(getStream(), kMsgCLeave);
00263 
00264     // we can never prevent the user from leaving
00265     return true;
00266 }
00267 
00268 void
00269 CClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard)
00270 {
00271     // ignore if this clipboard is already clean
00272     if (m_clipboard[id].m_dirty) {
00273         // this clipboard is now clean
00274         m_clipboard[id].m_dirty = false;
00275         CClipboard::copy(&m_clipboard[id].m_clipboard, clipboard);
00276 
00277         CString data = m_clipboard[id].m_clipboard.marshall();
00278         LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size()));
00279         CProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data);
00280     }
00281 }
00282 
00283 void
00284 CClientProxy1_0::grabClipboard(ClipboardID id)
00285 {
00286     LOG((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getName().c_str()));
00287     CProtocolUtil::writef(getStream(), kMsgCClipboard, id, 0);
00288 
00289     // this clipboard is now dirty
00290     m_clipboard[id].m_dirty = true;
00291 }
00292 
00293 void
00294 CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty)
00295 {
00296     m_clipboard[id].m_dirty = dirty;
00297 }
00298 
00299 void
00300 CClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask, KeyButton)
00301 {
00302     LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask));
00303     CProtocolUtil::writef(getStream(), kMsgDKeyDown1_0, key, mask);
00304 }
00305 
00306 void
00307 CClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask,
00308                 SInt32 count, KeyButton)
00309 {
00310     LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count));
00311     CProtocolUtil::writef(getStream(), kMsgDKeyRepeat1_0, key, mask, count);
00312 }
00313 
00314 void
00315 CClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask, KeyButton)
00316 {
00317     LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask));
00318     CProtocolUtil::writef(getStream(), kMsgDKeyUp1_0, key, mask);
00319 }
00320 
00321 void
00322 CClientProxy1_0::mouseDown(ButtonID button)
00323 {
00324     LOG((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getName().c_str(), button));
00325     CProtocolUtil::writef(getStream(), kMsgDMouseDown, button);
00326 }
00327 
00328 void
00329 CClientProxy1_0::mouseUp(ButtonID button)
00330 {
00331     LOG((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getName().c_str(), button));
00332     CProtocolUtil::writef(getStream(), kMsgDMouseUp, button);
00333 }
00334 
00335 void
00336 CClientProxy1_0::mouseMove(SInt32 xAbs, SInt32 yAbs)
00337 {
00338     LOG((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getName().c_str(), xAbs, yAbs));
00339     CProtocolUtil::writef(getStream(), kMsgDMouseMove, xAbs, yAbs);
00340 }
00341 
00342 void
00343 CClientProxy1_0::mouseRelativeMove(SInt32, SInt32)
00344 {
00345     // ignore -- not supported in protocol 1.0
00346 }
00347 
00348 void
00349 CClientProxy1_0::mouseWheel(SInt32, SInt32 yDelta)
00350 {
00351     // clients prior to 1.3 only support the y axis
00352     LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), yDelta));
00353     CProtocolUtil::writef(getStream(), kMsgDMouseWheel1_0, yDelta);
00354 }
00355 
00356 void
00357 CClientProxy1_0::screensaver(bool on)
00358 {
00359     LOG((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getName().c_str(), on ? 1 : 0));
00360     CProtocolUtil::writef(getStream(), kMsgCScreenSaver, on ? 1 : 0);
00361 }
00362 
00363 void
00364 CClientProxy1_0::resetOptions()
00365 {
00366     LOG((CLOG_DEBUG1 "send reset options to \"%s\"", getName().c_str()));
00367     CProtocolUtil::writef(getStream(), kMsgCResetOptions);
00368 
00369     // reset heart rate and death
00370     resetHeartbeatRate();
00371     removeHeartbeatTimer();
00372     addHeartbeatTimer();
00373 }
00374 
00375 void
00376 CClientProxy1_0::setOptions(const COptionsList& options)
00377 {
00378     LOG((CLOG_DEBUG1 "send set options to \"%s\" size=%d", getName().c_str(), options.size()));
00379     CProtocolUtil::writef(getStream(), kMsgDSetOptions, &options);
00380 
00381     // check options
00382     for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
00383         if (options[i] == kOptionHeartbeat) {
00384             double rate = 1.0e-3 * static_cast<double>(options[i + 1]);
00385             if (rate <= 0.0) {
00386                 rate = -1.0;
00387             }
00388             setHeartbeatRate(rate, rate * kHeartBeatsUntilDeath);
00389             removeHeartbeatTimer();
00390             addHeartbeatTimer();
00391         }
00392     }
00393 }
00394 
00395 bool
00396 CClientProxy1_0::recvInfo()
00397 {
00398     // parse the message
00399     SInt16 x, y, w, h, dummy1, mx, my;
00400     if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4,
00401                             &x, &y, &w, &h, &dummy1, &mx, &my)) {
00402         return false;
00403     }
00404     LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d at %d,%d", getName().c_str(), x, y, w, h, mx, my));
00405 
00406     // validate
00407     if (w <= 0 || h <= 0) {
00408         return false;
00409     }
00410     if (mx < x || mx >= x + w || my < y || my >= y + h) {
00411         mx = x + w / 2;
00412         my = y + h / 2;
00413     }
00414 
00415     // save
00416     m_info.m_x  = x;
00417     m_info.m_y  = y;
00418     m_info.m_w  = w;
00419     m_info.m_h  = h;
00420     m_info.m_mx = mx;
00421     m_info.m_my = my;
00422 
00423     // acknowledge receipt
00424     LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str()));
00425     CProtocolUtil::writef(getStream(), kMsgCInfoAck);
00426     return true;
00427 }
00428 
00429 bool
00430 CClientProxy1_0::recvClipboard()
00431 {
00432     // parse message
00433     ClipboardID id;
00434     UInt32 seqNum;
00435     CString data;
00436     if (!CProtocolUtil::readf(getStream(),
00437                             kMsgDClipboard + 4, &id, &seqNum, &data)) {
00438         return false;
00439     }
00440     LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, data.size()));
00441 
00442     // validate
00443     if (id >= kClipboardEnd) {
00444         return false;
00445     }
00446 
00447     // save clipboard
00448     m_clipboard[id].m_clipboard.unmarshall(data, 0);
00449     m_clipboard[id].m_sequenceNumber = seqNum;
00450 
00451     // notify
00452     CClipboardInfo* info   = new CClipboardInfo;
00453     info->m_id             = id;
00454     info->m_sequenceNumber = seqNum;
00455     EVENTQUEUE->addEvent(CEvent(getClipboardChangedEvent(),
00456                             getEventTarget(), info));
00457 
00458     return true;
00459 }
00460 
00461 bool
00462 CClientProxy1_0::recvGrabClipboard()
00463 {
00464     // parse message
00465     ClipboardID id;
00466     UInt32 seqNum;
00467     if (!CProtocolUtil::readf(getStream(), kMsgCClipboard + 4, &id, &seqNum)) {
00468         return false;
00469     }
00470     LOG((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getName().c_str(), id, seqNum));
00471 
00472     // validate
00473     if (id >= kClipboardEnd) {
00474         return false;
00475     }
00476 
00477     // notify
00478     CClipboardInfo* info   = new CClipboardInfo;
00479     info->m_id             = id;
00480     info->m_sequenceNumber = seqNum;
00481     EVENTQUEUE->addEvent(CEvent(getClipboardGrabbedEvent(),
00482                             getEventTarget(), info));
00483 
00484     return true;
00485 }
00486 
00487 
00488 //
00489 // CClientProxy1_0::CClientClipboard
00490 //
00491 
00492 CClientProxy1_0::CClientClipboard::CClientClipboard() :
00493     m_clipboard(),
00494     m_sequenceNumber(0),
00495     m_dirty(true)
00496 {
00497     // do nothing
00498 }

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