CServer.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 "CServer.h"
00016 #include "CClientProxy.h"
00017 #include "CClientProxyUnknown.h"
00018 #include "CPrimaryClient.h"
00019 #include "IPlatformScreen.h"
00020 #include "OptionTypes.h"
00021 #include "ProtocolTypes.h"
00022 #include "XScreen.h"
00023 #include "XSynergy.h"
00024 #include "IDataSocket.h"
00025 #include "IListenSocket.h"
00026 #include "XSocket.h"
00027 #include "IEventQueue.h"
00028 #include "CLog.h"
00029 #include "TMethodEventJob.h"
00030 #include "CArch.h"
00031 #include <cstring>
00032 #include <cstdlib>
00033 
00034 //
00035 // CServer
00036 //
00037 
00038 CEvent::Type            CServer::s_errorEvent         = CEvent::kUnknown;
00039 CEvent::Type            CServer::s_connectedEvent     = CEvent::kUnknown;
00040 CEvent::Type            CServer::s_disconnectedEvent  = CEvent::kUnknown;
00041 CEvent::Type            CServer::s_switchToScreen     = CEvent::kUnknown;
00042 CEvent::Type            CServer::s_switchInDirection  = CEvent::kUnknown;
00043 CEvent::Type            CServer::s_keyboardBroadcast  = CEvent::kUnknown;
00044 CEvent::Type            CServer::s_lockCursorToScreen = CEvent::kUnknown;
00045 
00046 CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) :
00047     m_primaryClient(primaryClient),
00048     m_active(primaryClient),
00049     m_seqNum(0),
00050     m_xDelta(0),
00051     m_yDelta(0),
00052     m_xDelta2(0),
00053     m_yDelta2(0),
00054     m_config(),
00055     m_inputFilter(m_config.getInputFilter()),
00056     m_activeSaver(NULL),
00057     m_switchDir(kNoDirection),
00058     m_switchScreen(NULL),
00059     m_switchWaitDelay(0.0),
00060     m_switchWaitTimer(NULL),
00061     m_switchTwoTapDelay(0.0),
00062     m_switchTwoTapEngaged(false),
00063     m_switchTwoTapArmed(false),
00064     m_switchTwoTapZone(3),
00065     m_relativeMoves(false),
00066     m_keyboardBroadcasting(false),
00067     m_lockedToScreen(false)
00068 {
00069     // must have a primary client and it must have a canonical name
00070     assert(m_primaryClient != NULL);
00071     assert(config.isScreen(primaryClient->getName()));
00072 
00073     CString primaryName = getName(primaryClient);
00074 
00075     // clear clipboards
00076     for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
00077         CClipboardInfo& clipboard   = m_clipboards[id];
00078         clipboard.m_clipboardOwner  = primaryName;
00079         clipboard.m_clipboardSeqNum = m_seqNum;
00080         if (clipboard.m_clipboard.open(0)) {
00081             clipboard.m_clipboard.empty();
00082             clipboard.m_clipboard.close();
00083         }
00084         clipboard.m_clipboardData   = clipboard.m_clipboard.marshall();
00085     }
00086 
00087     // install event handlers
00088     EVENTQUEUE->adoptHandler(CEvent::kTimer, this,
00089                             new TMethodEventJob<CServer>(this,
00090                                 &CServer::handleSwitchWaitTimeout));
00091     EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyDownEvent(),
00092                             m_inputFilter,
00093                             new TMethodEventJob<CServer>(this,
00094                                 &CServer::handleKeyDownEvent));
00095     EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyUpEvent(),
00096                             m_inputFilter,
00097                             new TMethodEventJob<CServer>(this,
00098                                 &CServer::handleKeyUpEvent));
00099     EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyRepeatEvent(),
00100                             m_inputFilter,
00101                             new TMethodEventJob<CServer>(this,
00102                                 &CServer::handleKeyRepeatEvent));
00103     EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonDownEvent(),
00104                             m_inputFilter,
00105                             new TMethodEventJob<CServer>(this,
00106                                 &CServer::handleButtonDownEvent));
00107     EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonUpEvent(),
00108                             m_inputFilter,
00109                             new TMethodEventJob<CServer>(this,
00110                                 &CServer::handleButtonUpEvent));
00111     EVENTQUEUE->adoptHandler(IPlatformScreen::getMotionOnPrimaryEvent(),
00112                             m_primaryClient->getEventTarget(),
00113                             new TMethodEventJob<CServer>(this,
00114                                 &CServer::handleMotionPrimaryEvent));
00115     EVENTQUEUE->adoptHandler(IPlatformScreen::getMotionOnSecondaryEvent(),
00116                             m_primaryClient->getEventTarget(),
00117                             new TMethodEventJob<CServer>(this,
00118                                 &CServer::handleMotionSecondaryEvent));
00119     EVENTQUEUE->adoptHandler(IPlatformScreen::getWheelEvent(),
00120                             m_primaryClient->getEventTarget(),
00121                             new TMethodEventJob<CServer>(this,
00122                                 &CServer::handleWheelEvent));
00123     EVENTQUEUE->adoptHandler(IPlatformScreen::getScreensaverActivatedEvent(),
00124                             m_primaryClient->getEventTarget(),
00125                             new TMethodEventJob<CServer>(this,
00126                                 &CServer::handleScreensaverActivatedEvent));
00127     EVENTQUEUE->adoptHandler(IPlatformScreen::getScreensaverDeactivatedEvent(),
00128                             m_primaryClient->getEventTarget(),
00129                             new TMethodEventJob<CServer>(this,
00130                                 &CServer::handleScreensaverDeactivatedEvent));
00131     EVENTQUEUE->adoptHandler(getSwitchToScreenEvent(),
00132                             m_inputFilter,
00133                             new TMethodEventJob<CServer>(this,
00134                                 &CServer::handleSwitchToScreenEvent));
00135     EVENTQUEUE->adoptHandler(getSwitchInDirectionEvent(),
00136                             m_inputFilter,
00137                             new TMethodEventJob<CServer>(this,
00138                                 &CServer::handleSwitchInDirectionEvent));
00139     EVENTQUEUE->adoptHandler(getKeyboardBroadcastEvent(),
00140                             m_inputFilter,
00141                             new TMethodEventJob<CServer>(this,
00142                                 &CServer::handleKeyboardBroadcastEvent));
00143     EVENTQUEUE->adoptHandler(getLockCursorToScreenEvent(),
00144                             m_inputFilter,
00145                             new TMethodEventJob<CServer>(this,
00146                                 &CServer::handleLockCursorToScreenEvent));
00147     EVENTQUEUE->adoptHandler(IPlatformScreen::getFakeInputBeginEvent(),
00148                             m_inputFilter,
00149                             new TMethodEventJob<CServer>(this,
00150                                 &CServer::handleFakeInputBeginEvent));
00151     EVENTQUEUE->adoptHandler(IPlatformScreen::getFakeInputEndEvent(),
00152                             m_inputFilter,
00153                             new TMethodEventJob<CServer>(this,
00154                                 &CServer::handleFakeInputEndEvent));
00155 
00156     // add connection
00157     addClient(m_primaryClient);
00158 
00159     // set initial configuration
00160     setConfig(config);
00161 
00162     // enable primary client
00163     m_primaryClient->enable();
00164     m_inputFilter->setPrimaryClient(m_primaryClient);
00165 }
00166 
00167 CServer::~CServer()
00168 {
00169     // remove event handlers and timers
00170     EVENTQUEUE->removeHandler(IPlatformScreen::getKeyDownEvent(),
00171                             m_inputFilter);
00172     EVENTQUEUE->removeHandler(IPlatformScreen::getKeyUpEvent(),
00173                             m_inputFilter);
00174     EVENTQUEUE->removeHandler(IPlatformScreen::getKeyRepeatEvent(),
00175                             m_inputFilter);
00176     EVENTQUEUE->removeHandler(IPlatformScreen::getButtonDownEvent(),
00177                             m_inputFilter);
00178     EVENTQUEUE->removeHandler(IPlatformScreen::getButtonUpEvent(),
00179                             m_inputFilter);
00180     EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnPrimaryEvent(),
00181                             m_primaryClient->getEventTarget());
00182     EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnSecondaryEvent(),
00183                             m_primaryClient->getEventTarget());
00184     EVENTQUEUE->removeHandler(IPlatformScreen::getWheelEvent(),
00185                             m_primaryClient->getEventTarget());
00186     EVENTQUEUE->removeHandler(IPlatformScreen::getScreensaverActivatedEvent(),
00187                             m_primaryClient->getEventTarget());
00188     EVENTQUEUE->removeHandler(IPlatformScreen::getScreensaverDeactivatedEvent(),
00189                             m_primaryClient->getEventTarget());
00190     EVENTQUEUE->removeHandler(IPlatformScreen::getFakeInputBeginEvent(),
00191                             m_inputFilter);
00192     EVENTQUEUE->removeHandler(IPlatformScreen::getFakeInputEndEvent(),
00193                             m_inputFilter);
00194     EVENTQUEUE->removeHandler(CEvent::kTimer, this);
00195     stopSwitch();
00196 
00197     // force immediate disconnection of secondary clients
00198     disconnect();
00199     for (COldClients::iterator index = m_oldClients.begin();
00200                             index != m_oldClients.begin(); ++index) {
00201         CBaseClientProxy* client = index->first;
00202         EVENTQUEUE->deleteTimer(index->second);
00203         EVENTQUEUE->removeHandler(CEvent::kTimer, client);
00204         EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client);
00205         delete client;
00206     }
00207 
00208     // remove input filter
00209     m_inputFilter->setPrimaryClient(NULL);
00210 
00211     // disable and disconnect primary client
00212     m_primaryClient->disable();
00213     removeClient(m_primaryClient);
00214 }
00215 
00216 bool
00217 CServer::setConfig(const CConfig& config)
00218 {
00219     // refuse configuration if it doesn't include the primary screen
00220     if (!config.isScreen(m_primaryClient->getName())) {
00221         return false;
00222     }
00223 
00224     // close clients that are connected but being dropped from the
00225     // configuration.
00226     closeClients(config);
00227 
00228     // cut over
00229     m_config = config;
00230     processOptions();
00231 
00232     // add ScrollLock as a hotkey to lock to the screen.  this was a
00233     // built-in feature in earlier releases and is now supported via
00234     // the user configurable hotkey mechanism.  if the user has already
00235     // registered ScrollLock for something else then that will win but
00236     // we will unfortunately generate a warning.  if the user has
00237     // configured a CLockCursorToScreenAction then we don't add
00238     // ScrollLock as a hotkey.
00239     if (!m_config.hasLockToScreenAction()) {
00240         IPlatformScreen::CKeyInfo* key =
00241             IPlatformScreen::CKeyInfo::alloc(kKeyScrollLock, 0, 0, 0);
00242         CInputFilter::CRule rule(new CInputFilter::CKeystrokeCondition(key));
00243         rule.adoptAction(new CInputFilter::CLockCursorToScreenAction, true);
00244         m_inputFilter->addFilterRule(rule);
00245     }
00246 
00247     // tell primary screen about reconfiguration
00248     m_primaryClient->reconfigure(getActivePrimarySides());
00249 
00250     // tell all (connected) clients about current options
00251     for (CClientList::const_iterator index = m_clients.begin();
00252                                 index != m_clients.end(); ++index) {
00253         CBaseClientProxy* client = index->second;
00254         sendOptions(client);
00255     }
00256 
00257     return true;
00258 }
00259 
00260 void
00261 CServer::adoptClient(CBaseClientProxy* client)
00262 {
00263     assert(client != NULL);
00264 
00265     // watch for client disconnection
00266     EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client,
00267                             new TMethodEventJob<CServer>(this,
00268                                 &CServer::handleClientDisconnected, client));
00269 
00270     // name must be in our configuration
00271     if (!m_config.isScreen(client->getName())) {
00272         LOG((CLOG_WARN "a client with name \"%s\" is not in the map", client->getName().c_str()));
00273         closeClient(client, kMsgEUnknown);
00274         return;
00275     }
00276 
00277     // add client to client list
00278     if (!addClient(client)) {
00279         // can only have one screen with a given name at any given time
00280         LOG((CLOG_WARN "a client with name \"%s\" is already connected", getName(client).c_str()));
00281         closeClient(client, kMsgEBusy);
00282         return;
00283     }
00284     LOG((CLOG_NOTE "client \"%s\" has connected", getName(client).c_str()));
00285 
00286     // send configuration options to client
00287     sendOptions(client);
00288 
00289     // activate screen saver on new client if active on the primary screen
00290     if (m_activeSaver != NULL) {
00291         client->screensaver(true);
00292     }
00293 
00294     // send notification
00295     CServer::CScreenConnectedInfo* info =
00296         CServer::CScreenConnectedInfo::alloc(getName(client));
00297     EVENTQUEUE->addEvent(CEvent(CServer::getConnectedEvent(),
00298                                 m_primaryClient->getEventTarget(), info));
00299 }
00300 
00301 void
00302 CServer::disconnect()
00303 {
00304     // close all secondary clients
00305     if (m_clients.size() > 1 || !m_oldClients.empty()) {
00306         CConfig emptyConfig;
00307         closeClients(emptyConfig);
00308     }
00309     else {
00310         EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this));
00311     }
00312 }
00313 
00314 UInt32
00315 CServer::getNumClients() const
00316 {
00317     return m_clients.size();
00318 }
00319 
00320 void
00321 CServer::getClients(std::vector<CString>& list) const
00322 {
00323     list.clear();
00324     for (CClientList::const_iterator index = m_clients.begin();
00325                             index != m_clients.end(); ++index) {
00326         list.push_back(index->first);
00327     }
00328 }
00329 
00330 CEvent::Type
00331 CServer::getErrorEvent()
00332 {
00333     return CEvent::registerTypeOnce(s_errorEvent,
00334                             "CServer::error");
00335 }
00336 
00337 CEvent::Type
00338 CServer::getConnectedEvent()
00339 {
00340     return CEvent::registerTypeOnce(s_connectedEvent,
00341                             "CServer::connected");
00342 }
00343 
00344 CEvent::Type
00345 CServer::getDisconnectedEvent()
00346 {
00347     return CEvent::registerTypeOnce(s_disconnectedEvent,
00348                             "CServer::disconnected");
00349 }
00350 
00351 CEvent::Type
00352 CServer::getSwitchToScreenEvent()
00353 {
00354     return CEvent::registerTypeOnce(s_switchToScreen,
00355                             "CServer::switchToScreen");
00356 }
00357 
00358 CEvent::Type
00359 CServer::getSwitchInDirectionEvent()
00360 {
00361     return CEvent::registerTypeOnce(s_switchInDirection,
00362                             "CServer::switchInDirection");
00363 }
00364 
00365 CEvent::Type
00366 CServer::getKeyboardBroadcastEvent()
00367 {
00368     return CEvent::registerTypeOnce(s_keyboardBroadcast,
00369                             "CServer:keyboardBroadcast");
00370 }
00371 
00372 CEvent::Type
00373 CServer::getLockCursorToScreenEvent()
00374 {
00375     return CEvent::registerTypeOnce(s_lockCursorToScreen,
00376                             "CServer::lockCursorToScreen");
00377 }
00378 
00379 CString
00380 CServer::getName(const CBaseClientProxy* client) const
00381 {
00382     CString name = m_config.getCanonicalName(client->getName());
00383     if (name.empty()) {
00384         name = client->getName();
00385     }
00386     return name;
00387 }
00388 
00389 UInt32
00390 CServer::getActivePrimarySides() const
00391 {
00392     UInt32 sides = 0;
00393     if (!isLockedToScreenServer()) {
00394         if (hasAnyNeighbor(m_primaryClient, kLeft)) {
00395             sides |= kLeftMask;
00396         }
00397         if (hasAnyNeighbor(m_primaryClient, kRight)) {
00398             sides |= kRightMask;
00399         }
00400         if (hasAnyNeighbor(m_primaryClient, kTop)) {
00401             sides |= kTopMask;
00402         }
00403         if (hasAnyNeighbor(m_primaryClient, kBottom)) {
00404             sides |= kBottomMask;
00405         }
00406     }
00407     return sides;
00408 }
00409 
00410 bool
00411 CServer::isLockedToScreenServer() const
00412 {
00413     // locked if scroll-lock is toggled on
00414     return m_lockedToScreen;
00415 }
00416 
00417 bool
00418 CServer::isLockedToScreen() const
00419 {
00420     // locked if we say we're locked
00421     if (isLockedToScreenServer()) {
00422         LOG((CLOG_DEBUG "locked to screen"));
00423         return true;
00424     }
00425 
00426     // locked if primary says we're locked
00427     if (m_primaryClient->isLockedToScreen()) {
00428         return true;
00429     }
00430 
00431     // not locked
00432     return false;
00433 }
00434 
00435 SInt32
00436 CServer::getJumpZoneSize(CBaseClientProxy* client) const
00437 {
00438     if (client == m_primaryClient) {
00439         return m_primaryClient->getJumpZoneSize();
00440     }
00441     else {
00442         return 0;
00443     }
00444 }
00445 
00446 void
00447 CServer::switchScreen(CBaseClientProxy* dst,
00448                 SInt32 x, SInt32 y, bool forScreensaver)
00449 {
00450     assert(dst != NULL);
00451 #ifndef NDEBUG
00452     {
00453         SInt32 dx, dy, dw, dh;
00454         dst->getShape(dx, dy, dw, dh);
00455         assert(x >= dx && y >= dy && x < dx + dw && y < dy + dh);
00456     }
00457 #endif
00458     assert(m_active != NULL);
00459 
00460     LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", getName(m_active).c_str(), getName(dst).c_str(), x, y));
00461 
00462     // stop waiting to switch
00463     stopSwitch();
00464 
00465     // record new position
00466     m_x       = x;
00467     m_y       = y;
00468     m_xDelta  = 0;
00469     m_yDelta  = 0;
00470     m_xDelta2 = 0;
00471     m_yDelta2 = 0;
00472 
00473     // wrapping means leaving the active screen and entering it again.
00474     // since that's a waste of time we skip that and just warp the
00475     // mouse.
00476     if (m_active != dst) {
00477         // leave active screen
00478         if (!m_active->leave()) {
00479             // cannot leave screen
00480             LOG((CLOG_WARN "can't leave screen"));
00481             return;
00482         }
00483 
00484         // update the primary client's clipboards if we're leaving the
00485         // primary screen.
00486         if (m_active == m_primaryClient) {
00487             for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
00488                 CClipboardInfo& clipboard = m_clipboards[id];
00489                 if (clipboard.m_clipboardOwner == getName(m_primaryClient)) {
00490                     onClipboardChanged(m_primaryClient,
00491                         id, clipboard.m_clipboardSeqNum);
00492                 }
00493             }
00494         }
00495 
00496         // cut over
00497         m_active = dst;
00498 
00499         // increment enter sequence number
00500         ++m_seqNum;
00501 
00502         // enter new screen
00503         m_active->enter(x, y, m_seqNum,
00504                                 m_primaryClient->getToggleMask(),
00505                                 forScreensaver);
00506 
00507         // send the clipboard data to new active screen
00508         for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
00509             m_active->setClipboard(id, &m_clipboards[id].m_clipboard);
00510         }
00511     }
00512     else {
00513         m_active->mouseMove(x, y);
00514     }
00515 }
00516 
00517 void
00518 CServer::jumpToScreen(CBaseClientProxy* newScreen)
00519 {
00520     assert(newScreen != NULL);
00521 
00522     // record the current cursor position on the active screen
00523     m_active->setJumpCursorPos(m_x, m_y);
00524 
00525     // get the last cursor position on the target screen
00526     SInt32 x, y;
00527     newScreen->getJumpCursorPos(x, y);
00528     
00529     switchScreen(newScreen, x, y, false);
00530 }
00531 
00532 float
00533 CServer::mapToFraction(CBaseClientProxy* client,
00534                 EDirection dir, SInt32 x, SInt32 y) const
00535 {
00536     SInt32 sx, sy, sw, sh;
00537     client->getShape(sx, sy, sw, sh);
00538     switch (dir) {
00539     case kLeft:
00540     case kRight:
00541         return static_cast<float>(y - sy + 0.5f) / static_cast<float>(sh);
00542 
00543     case kTop:
00544     case kBottom:
00545         return static_cast<float>(x - sx + 0.5f) / static_cast<float>(sw);
00546 
00547     case kNoDirection:
00548         assert(0 && "bad direction");
00549         break;
00550     }
00551     return 0.0f;
00552 }
00553 
00554 void
00555 CServer::mapToPixel(CBaseClientProxy* client,
00556                 EDirection dir, float f, SInt32& x, SInt32& y) const
00557 {
00558     SInt32 sx, sy, sw, sh;
00559     client->getShape(sx, sy, sw, sh);
00560     switch (dir) {
00561     case kLeft:
00562     case kRight:
00563         y = static_cast<SInt32>(f * sh) + sy;
00564         break;
00565 
00566     case kTop:
00567     case kBottom:
00568         x = static_cast<SInt32>(f * sw) + sx;
00569         break;
00570 
00571     case kNoDirection:
00572         assert(0 && "bad direction");
00573         break;
00574     }
00575 }
00576 
00577 bool
00578 CServer::hasAnyNeighbor(CBaseClientProxy* client, EDirection dir) const
00579 {
00580     assert(client != NULL);
00581 
00582     return m_config.hasNeighbor(getName(client), dir);
00583 }
00584 
00585 CBaseClientProxy*
00586 CServer::getNeighbor(CBaseClientProxy* src,
00587                 EDirection dir, SInt32& x, SInt32& y) const
00588 {
00589     // note -- must be locked on entry
00590 
00591     assert(src != NULL);
00592 
00593     // get source screen name
00594     CString srcName = getName(src);
00595     assert(!srcName.empty());
00596     LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str()));
00597 
00598     // convert position to fraction
00599     float t = mapToFraction(src, dir, x, y);
00600 
00601     // search for the closest neighbor that exists in direction dir
00602     float tTmp;
00603     for (;;) {
00604         CString dstName(m_config.getNeighbor(srcName, dir, t, &tTmp));
00605 
00606         // if nothing in that direction then return NULL. if the
00607         // destination is the source then we can make no more
00608         // progress in this direction.  since we haven't found a
00609         // connected neighbor we return NULL.
00610         if (dstName.empty()) {
00611             LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str()));
00612             return NULL;
00613         }
00614 
00615         // look up neighbor cell.  if the screen is connected and
00616         // ready then we can stop.
00617         CClientList::const_iterator index = m_clients.find(dstName);
00618         if (index != m_clients.end()) {
00619             LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\" at %f", dstName.c_str(), CConfig::dirName(dir), srcName.c_str(), t));
00620             mapToPixel(index->second, dir, tTmp, x, y);
00621             return index->second;
00622         }
00623 
00624         // skip over unconnected screen
00625         LOG((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str()));
00626         srcName = dstName;
00627 
00628         // use position on skipped screen
00629         t = tTmp;
00630     }
00631 }
00632 
00633 CBaseClientProxy*
00634 CServer::mapToNeighbor(CBaseClientProxy* src,
00635                 EDirection srcSide, SInt32& x, SInt32& y) const
00636 {
00637     // note -- must be locked on entry
00638 
00639     assert(src != NULL);
00640 
00641     // get the first neighbor
00642     CBaseClientProxy* dst = getNeighbor(src, srcSide, x, y);
00643     if (dst == NULL) {
00644         return NULL;
00645     }
00646 
00647     // get the source screen's size
00648     SInt32 dx, dy, dw, dh;
00649     CBaseClientProxy* lastGoodScreen = src;
00650     lastGoodScreen->getShape(dx, dy, dw, dh);
00651 
00652     // find destination screen, adjusting x or y (but not both).  the
00653     // searches are done in a sort of canonical screen space where
00654     // the upper-left corner is 0,0 for each screen.  we adjust from
00655     // actual to canonical position on entry to and from canonical to
00656     // actual on exit from the search.
00657     switch (srcSide) {
00658     case kLeft:
00659         x -= dx;
00660         while (dst != NULL) {
00661             lastGoodScreen = dst;
00662             lastGoodScreen->getShape(dx, dy, dw, dh);
00663             x += dw;
00664             if (x >= 0) {
00665                 break;
00666             }
00667             LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str()));
00668             dst = getNeighbor(lastGoodScreen, srcSide, x, y);
00669         }
00670         assert(lastGoodScreen != NULL);
00671         x += dx;
00672         break;
00673 
00674     case kRight:
00675         x -= dx;
00676         while (dst != NULL) {
00677             x -= dw;
00678             lastGoodScreen = dst;
00679             lastGoodScreen->getShape(dx, dy, dw, dh);
00680             if (x < dw) {
00681                 break;
00682             }
00683             LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str()));
00684             dst = getNeighbor(lastGoodScreen, srcSide, x, y);
00685         }
00686         assert(lastGoodScreen != NULL);
00687         x += dx;
00688         break;
00689 
00690     case kTop:
00691         y -= dy;
00692         while (dst != NULL) {
00693             lastGoodScreen = dst;
00694             lastGoodScreen->getShape(dx, dy, dw, dh);
00695             y += dh;
00696             if (y >= 0) {
00697                 break;
00698             }
00699             LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str()));
00700             dst = getNeighbor(lastGoodScreen, srcSide, x, y);
00701         }
00702         assert(lastGoodScreen != NULL);
00703         y += dy;
00704         break;
00705 
00706     case kBottom:
00707         y -= dy;
00708         while (dst != NULL) {
00709             y -= dh;
00710             lastGoodScreen = dst;
00711             lastGoodScreen->getShape(dx, dy, dw, dh);
00712             if (y < dh) {
00713                 break;
00714             }
00715             LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str()));
00716             dst = getNeighbor(lastGoodScreen, srcSide, x, y);
00717         }
00718         assert(lastGoodScreen != NULL);
00719         y += dy;
00720         break;
00721 
00722     case kNoDirection:
00723         assert(0 && "bad direction");
00724         return NULL;
00725     }
00726 
00727     // save destination screen
00728     assert(lastGoodScreen != NULL);
00729     dst = lastGoodScreen;
00730 
00731     // if entering primary screen then be sure to move in far enough
00732     // to avoid the jump zone.  if entering a side that doesn't have
00733     // a neighbor (i.e. an asymmetrical side) then we don't need to
00734     // move inwards because that side can't provoke a jump.
00735     avoidJumpZone(dst, srcSide, x, y);
00736 
00737     return dst;
00738 }
00739 
00740 void
00741 CServer::avoidJumpZone(CBaseClientProxy* dst,
00742                 EDirection dir, SInt32& x, SInt32& y) const
00743 {
00744     // we only need to avoid jump zones on the primary screen
00745     if (dst != m_primaryClient) {
00746         return;
00747     }
00748 
00749     const CString dstName(getName(dst));
00750     SInt32 dx, dy, dw, dh;
00751     dst->getShape(dx, dy, dw, dh);
00752     float t = mapToFraction(dst, dir, x, y);
00753     SInt32 z = getJumpZoneSize(dst);
00754 
00755     // move in far enough to avoid the jump zone.  if entering a side
00756     // that doesn't have a neighbor (i.e. an asymmetrical side) then we
00757     // don't need to move inwards because that side can't provoke a jump.
00758     switch (dir) {
00759     case kLeft:
00760         if (!m_config.getNeighbor(dstName, kRight, t, NULL).empty() &&
00761             x > dx + dw - 1 - z)
00762             x = dx + dw - 1 - z;
00763         break;
00764 
00765     case kRight:
00766         if (!m_config.getNeighbor(dstName, kLeft, t, NULL).empty() &&
00767             x < dx + z)
00768             x = dx + z;
00769         break;
00770 
00771     case kTop:
00772         if (!m_config.getNeighbor(dstName, kBottom, t, NULL).empty() &&
00773             y > dy + dh - 1 - z)
00774             y = dy + dh - 1 - z;
00775         break;
00776 
00777     case kBottom:
00778         if (!m_config.getNeighbor(dstName, kTop, t, NULL).empty() &&
00779             y < dy + z)
00780             y = dy + z;
00781         break;
00782 
00783     case kNoDirection:
00784         assert(0 && "bad direction");
00785     }
00786 }
00787 
00788 bool
00789 CServer::isSwitchOkay(CBaseClientProxy* newScreen,
00790                 EDirection dir, SInt32 x, SInt32 y,
00791                 SInt32 xActive, SInt32 yActive)
00792 {
00793     LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", getName(m_active).c_str(), CConfig::dirName(dir)));
00794 
00795     // is there a neighbor?
00796     if (newScreen == NULL) {
00797         // there's no neighbor.  we don't want to switch and we don't
00798         // want to try to switch later.
00799         LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir)));
00800         stopSwitch();
00801         return false;
00802     }
00803 
00804     // should we switch or not?
00805     bool preventSwitch = false;
00806     bool allowSwitch   = false;
00807 
00808     // note if the switch direction has changed.  save the new
00809     // direction and screen if so.
00810     bool isNewDirection  = (dir != m_switchDir);
00811     if (isNewDirection || m_switchScreen == NULL) {
00812         m_switchDir    = dir;
00813         m_switchScreen = newScreen;
00814     }
00815 
00816     // is this a double tap and do we care?
00817     if (!allowSwitch && m_switchTwoTapDelay > 0.0) {
00818         if (isNewDirection ||
00819             !isSwitchTwoTapStarted() || !shouldSwitchTwoTap()) {
00820             // tapping a different or new edge or second tap not
00821             // fast enough.  prepare for second tap.
00822             preventSwitch = true;
00823             startSwitchTwoTap();
00824         }
00825         else {
00826             // got second tap
00827             allowSwitch = true;
00828         }
00829     }
00830 
00831     // if waiting before a switch then prepare to switch later
00832     if (!allowSwitch && m_switchWaitDelay > 0.0) {
00833         if (isNewDirection || !isSwitchWaitStarted()) {
00834             startSwitchWait(x, y);
00835         }
00836         preventSwitch = true;
00837     }
00838 
00839     // are we in a locked corner?  first check if screen has the option set
00840     // and, if not, check the global options.
00841     const CConfig::CScreenOptions* options =
00842                         m_config.getOptions(getName(m_active));
00843     if (options == NULL || options->count(kOptionScreenSwitchCorners) == 0) {
00844         options = m_config.getOptions("");
00845     }
00846     if (options != NULL && options->count(kOptionScreenSwitchCorners) > 0) {
00847         // get corner mask and size
00848         CConfig::CScreenOptions::const_iterator i =
00849             options->find(kOptionScreenSwitchCorners);
00850         UInt32 corners = static_cast<UInt32>(i->second);
00851         i = options->find(kOptionScreenSwitchCornerSize);
00852         SInt32 size = 0;
00853         if (i != options->end()) {
00854             size = i->second;
00855         }
00856 
00857         // see if we're in a locked corner
00858         if ((getCorner(m_active, xActive, yActive, size) & corners) != 0) {
00859             // yep, no switching
00860             LOG((CLOG_DEBUG1 "locked in corner"));
00861             preventSwitch = true;
00862             stopSwitch();
00863         }
00864     }
00865 
00866     // ignore if mouse is locked to screen and don't try to switch later
00867     if (!preventSwitch && isLockedToScreen()) {
00868         LOG((CLOG_DEBUG1 "locked to screen"));
00869         preventSwitch = true;
00870         stopSwitch();
00871     }
00872 
00873     return !preventSwitch;
00874 }
00875 
00876 void
00877 CServer::noSwitch(SInt32 x, SInt32 y)
00878 {
00879     armSwitchTwoTap(x, y);
00880     stopSwitchWait();
00881 }
00882 
00883 void
00884 CServer::stopSwitch()
00885 {
00886     if (m_switchScreen != NULL) {
00887         m_switchScreen = NULL;
00888         m_switchDir    = kNoDirection;
00889         stopSwitchTwoTap();
00890         stopSwitchWait();
00891     }
00892 }
00893 
00894 void
00895 CServer::startSwitchTwoTap()
00896 {
00897     m_switchTwoTapEngaged = true;
00898     m_switchTwoTapArmed   = false;
00899     m_switchTwoTapTimer.reset();
00900     LOG((CLOG_DEBUG1 "waiting for second tap"));
00901 }
00902 
00903 void
00904 CServer::armSwitchTwoTap(SInt32 x, SInt32 y)
00905 {
00906     if (m_switchTwoTapEngaged) {
00907         if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) {
00908             // second tap took too long.  disengage.
00909             stopSwitchTwoTap();
00910         }
00911         else if (!m_switchTwoTapArmed) {
00912             // still time for a double tap.  see if we left the tap
00913             // zone and, if so, arm the two tap.
00914             SInt32 ax, ay, aw, ah;
00915             m_active->getShape(ax, ay, aw, ah);
00916             SInt32 tapZone = m_primaryClient->getJumpZoneSize();
00917             if (tapZone < m_switchTwoTapZone) {
00918                 tapZone = m_switchTwoTapZone;
00919             }
00920             if (x >= ax + tapZone && x < ax + aw - tapZone &&
00921                 y >= ay + tapZone && y < ay + ah - tapZone) {
00922                 // win32 can generate bogus mouse events that appear to
00923                 // move in the opposite direction that the mouse actually
00924                 // moved.  try to ignore that crap here.
00925                 switch (m_switchDir) {
00926                 case kLeft:
00927                     m_switchTwoTapArmed = (m_xDelta > 0 && m_xDelta2 > 0);
00928                     break;
00929 
00930                 case kRight:
00931                     m_switchTwoTapArmed = (m_xDelta < 0 && m_xDelta2 < 0);
00932                     break;
00933 
00934                 case kTop:
00935                     m_switchTwoTapArmed = (m_yDelta > 0 && m_yDelta2 > 0);
00936                     break;
00937 
00938                 case kBottom:
00939                     m_switchTwoTapArmed = (m_yDelta < 0 && m_yDelta2 < 0);
00940                     break;
00941 
00942                 default:
00943                     break;
00944                 }
00945             }
00946         }
00947     }
00948 }
00949 
00950 void
00951 CServer::stopSwitchTwoTap()
00952 {
00953     m_switchTwoTapEngaged = false;
00954     m_switchTwoTapArmed   = false;
00955 }
00956 
00957 bool
00958 CServer::isSwitchTwoTapStarted() const
00959 {
00960     return m_switchTwoTapEngaged;
00961 }
00962 
00963 bool
00964 CServer::shouldSwitchTwoTap() const
00965 {
00966     // this is the second tap if two-tap is armed and this tap
00967     // came fast enough
00968     return (m_switchTwoTapArmed &&
00969             m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay);
00970 }
00971 
00972 void
00973 CServer::startSwitchWait(SInt32 x, SInt32 y)
00974 {
00975     stopSwitchWait();
00976     m_switchWaitX     = x;
00977     m_switchWaitY     = y;
00978     m_switchWaitTimer = EVENTQUEUE->newOneShotTimer(m_switchWaitDelay, this);
00979     LOG((CLOG_DEBUG1 "waiting to switch"));
00980 }
00981 
00982 void
00983 CServer::stopSwitchWait()
00984 {
00985     if (m_switchWaitTimer != NULL) {
00986         EVENTQUEUE->deleteTimer(m_switchWaitTimer);
00987         m_switchWaitTimer = NULL;
00988     }
00989 }
00990 
00991 bool
00992 CServer::isSwitchWaitStarted() const
00993 {
00994     return (m_switchWaitTimer != NULL);
00995 }
00996 
00997 UInt32
00998 CServer::getCorner(CBaseClientProxy* client,
00999                 SInt32 x, SInt32 y, SInt32 size) const
01000 {
01001     assert(client != NULL);
01002 
01003     // get client screen shape
01004     SInt32 ax, ay, aw, ah;
01005     client->getShape(ax, ay, aw, ah);
01006 
01007     // check for x,y on the left or right
01008     SInt32 xSide;
01009     if (x <= ax) {
01010         xSide = -1;
01011     }
01012     else if (x >= ax + aw - 1) {
01013         xSide = 1;
01014     }
01015     else {
01016         xSide = 0;
01017     }
01018 
01019     // check for x,y on the top or bottom
01020     SInt32 ySide;
01021     if (y <= ay) {
01022         ySide = -1;
01023     }
01024     else if (y >= ay + ah - 1) {
01025         ySide = 1;
01026     }
01027     else {
01028         ySide = 0;
01029     }
01030 
01031     // if against the left or right then check if y is within size
01032     if (xSide != 0) {
01033         if (y < ay + size) {
01034             return (xSide < 0) ? kTopLeftMask : kTopRightMask;
01035         }
01036         else if (y >= ay + ah - size) {
01037             return (xSide < 0) ? kBottomLeftMask : kBottomRightMask;
01038         }
01039     }
01040 
01041     // if against the left or right then check if y is within size
01042     if (ySide != 0) {
01043         if (x < ax + size) {
01044             return (ySide < 0) ? kTopLeftMask : kBottomLeftMask;
01045         }
01046         else if (x >= ax + aw - size) {
01047             return (ySide < 0) ? kTopRightMask : kBottomRightMask;
01048         }
01049     }
01050 
01051     return kNoCornerMask;
01052 }
01053 
01054 void
01055 CServer::stopRelativeMoves()
01056 {
01057     if (m_relativeMoves && m_active != m_primaryClient) {
01058         // warp to the center of the active client so we know where we are
01059         SInt32 ax, ay, aw, ah;
01060         m_active->getShape(ax, ay, aw, ah);
01061         m_x       = ax + (aw >> 1);
01062         m_y       = ay + (ah >> 1);
01063         m_xDelta  = 0;
01064         m_yDelta  = 0;
01065         m_xDelta2 = 0;
01066         m_yDelta2 = 0;
01067         LOG((CLOG_DEBUG2 "synchronize move on %s by %d,%d", getName(m_active).c_str(), m_x, m_y));
01068         m_active->mouseMove(m_x, m_y);
01069     }
01070 }
01071 
01072 void
01073 CServer::sendOptions(CBaseClientProxy* client) const
01074 {
01075     COptionsList optionsList;
01076 
01077     // look up options for client
01078     const CConfig::CScreenOptions* options =
01079                         m_config.getOptions(getName(client));
01080     if (options != NULL) {
01081         // convert options to a more convenient form for sending
01082         optionsList.reserve(2 * options->size());
01083         for (CConfig::CScreenOptions::const_iterator index = options->begin();
01084                                     index != options->end(); ++index) {
01085             optionsList.push_back(index->first);
01086             optionsList.push_back(static_cast<UInt32>(index->second));
01087         }
01088     }
01089 
01090     // look up global options
01091     options = m_config.getOptions("");
01092     if (options != NULL) {
01093         // convert options to a more convenient form for sending
01094         optionsList.reserve(optionsList.size() + 2 * options->size());
01095         for (CConfig::CScreenOptions::const_iterator index = options->begin();
01096                                     index != options->end(); ++index) {
01097             optionsList.push_back(index->first);
01098             optionsList.push_back(static_cast<UInt32>(index->second));
01099         }
01100     }
01101 
01102     // send the options
01103     client->resetOptions();
01104     client->setOptions(optionsList);
01105 }
01106 
01107 void
01108 CServer::processOptions()
01109 {
01110     const CConfig::CScreenOptions* options = m_config.getOptions("");
01111     if (options == NULL) {
01112         return;
01113     }
01114 
01115     bool newRelativeMoves = m_relativeMoves;
01116     for (CConfig::CScreenOptions::const_iterator index = options->begin();
01117                                 index != options->end(); ++index) {
01118         const OptionID id       = index->first;
01119         const OptionValue value = index->second;
01120         if (id == kOptionScreenSwitchDelay) {
01121             m_switchWaitDelay = 1.0e-3 * static_cast<double>(value);
01122             if (m_switchWaitDelay < 0.0) {
01123                 m_switchWaitDelay = 0.0;
01124             }
01125             stopSwitchWait();
01126         }
01127         else if (id == kOptionScreenSwitchTwoTap) {
01128             m_switchTwoTapDelay = 1.0e-3 * static_cast<double>(value);
01129             if (m_switchTwoTapDelay < 0.0) {
01130                 m_switchTwoTapDelay = 0.0;
01131             }
01132             stopSwitchTwoTap();
01133         }
01134         else if (id == kOptionRelativeMouseMoves) {
01135             newRelativeMoves = (value != 0);
01136         }
01137     }
01138 
01139     if (m_relativeMoves && !newRelativeMoves) {
01140         stopRelativeMoves();
01141     }
01142     m_relativeMoves = newRelativeMoves;
01143 }
01144 
01145 void
01146 CServer::handleShapeChanged(const CEvent&, void* vclient)
01147 {
01148     // ignore events from unknown clients
01149     CBaseClientProxy* client = reinterpret_cast<CBaseClientProxy*>(vclient);
01150     if (m_clientSet.count(client) == 0) {
01151         return;
01152     }
01153 
01154     LOG((CLOG_INFO "screen \"%s\" shape changed", getName(client).c_str()));
01155 
01156     // update jump coordinate
01157     SInt32 x, y;
01158     client->getCursorPos(x, y);
01159     client->setJumpCursorPos(x, y);
01160 
01161     // update the mouse coordinates
01162     if (client == m_active) {
01163         m_x = x;
01164         m_y = y;
01165     }
01166 
01167     // handle resolution change to primary screen
01168     if (client == m_primaryClient) {
01169         if (client == m_active) {
01170             onMouseMovePrimary(m_x, m_y);
01171         }
01172         else {
01173             onMouseMoveSecondary(0, 0);
01174         }
01175     }
01176 }
01177 
01178 void
01179 CServer::handleClipboardGrabbed(const CEvent& event, void* vclient)
01180 {
01181     // ignore events from unknown clients
01182     CBaseClientProxy* grabber = reinterpret_cast<CBaseClientProxy*>(vclient);
01183     if (m_clientSet.count(grabber) == 0) {
01184         return;
01185     }
01186     const IScreen::CClipboardInfo* info =
01187         reinterpret_cast<const IScreen::CClipboardInfo*>(event.getData());
01188 
01189     // ignore grab if sequence number is old.  always allow primary
01190     // screen to grab.
01191     CClipboardInfo& clipboard = m_clipboards[info->m_id];
01192     if (grabber != m_primaryClient &&
01193         info->m_sequenceNumber < clipboard.m_clipboardSeqNum) {
01194         LOG((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", getName(grabber).c_str(), info->m_id));
01195         return;
01196     }
01197 
01198     // mark screen as owning clipboard
01199     LOG((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", getName(grabber).c_str(), info->m_id, clipboard.m_clipboardOwner.c_str()));
01200     clipboard.m_clipboardOwner  = getName(grabber);
01201     clipboard.m_clipboardSeqNum = info->m_sequenceNumber;
01202 
01203     // clear the clipboard data (since it's not known at this point)
01204     if (clipboard.m_clipboard.open(0)) {
01205         clipboard.m_clipboard.empty();
01206         clipboard.m_clipboard.close();
01207     }
01208     clipboard.m_clipboardData = clipboard.m_clipboard.marshall();
01209 
01210     // tell all other screens to take ownership of clipboard.  tell the
01211     // grabber that it's clipboard isn't dirty.
01212     for (CClientList::iterator index = m_clients.begin();
01213                                 index != m_clients.end(); ++index) {
01214         CBaseClientProxy* client = index->second;
01215         if (client == grabber) {
01216             client->setClipboardDirty(info->m_id, false);
01217         }
01218         else {
01219             client->grabClipboard(info->m_id);
01220         }
01221     }
01222 }
01223 
01224 void
01225 CServer::handleClipboardChanged(const CEvent& event, void* vclient)
01226 {
01227     // ignore events from unknown clients
01228     CBaseClientProxy* sender = reinterpret_cast<CBaseClientProxy*>(vclient);
01229     if (m_clientSet.count(sender) == 0) {
01230         return;
01231     }
01232     const IScreen::CClipboardInfo* info =
01233         reinterpret_cast<const IScreen::CClipboardInfo*>(event.getData());
01234     onClipboardChanged(sender, info->m_id, info->m_sequenceNumber);
01235 }
01236 
01237 void
01238 CServer::handleKeyDownEvent(const CEvent& event, void*)
01239 {
01240     IPlatformScreen::CKeyInfo* info =
01241         reinterpret_cast<IPlatformScreen::CKeyInfo*>(event.getData());
01242     onKeyDown(info->m_key, info->m_mask, info->m_button, info->m_screens);
01243 }
01244 
01245 void
01246 CServer::handleKeyUpEvent(const CEvent& event, void*)
01247 {
01248     IPlatformScreen::CKeyInfo* info =
01249          reinterpret_cast<IPlatformScreen::CKeyInfo*>(event.getData());
01250     onKeyUp(info->m_key, info->m_mask, info->m_button, info->m_screens);
01251 }
01252 
01253 void
01254 CServer::handleKeyRepeatEvent(const CEvent& event, void*)
01255 {
01256     IPlatformScreen::CKeyInfo* info =
01257         reinterpret_cast<IPlatformScreen::CKeyInfo*>(event.getData());
01258     onKeyRepeat(info->m_key, info->m_mask, info->m_count, info->m_button);
01259 }
01260 
01261 void
01262 CServer::handleButtonDownEvent(const CEvent& event, void*)
01263 {
01264     IPlatformScreen::CButtonInfo* info =
01265         reinterpret_cast<IPlatformScreen::CButtonInfo*>(event.getData());
01266     onMouseDown(info->m_button);
01267 }
01268 
01269 void
01270 CServer::handleButtonUpEvent(const CEvent& event, void*)
01271 {
01272     IPlatformScreen::CButtonInfo* info =
01273         reinterpret_cast<IPlatformScreen::CButtonInfo*>(event.getData());
01274     onMouseUp(info->m_button);
01275 }
01276 
01277 void
01278 CServer::handleMotionPrimaryEvent(const CEvent& event, void*)
01279 {
01280     IPlatformScreen::CMotionInfo* info =
01281         reinterpret_cast<IPlatformScreen::CMotionInfo*>(event.getData());
01282     onMouseMovePrimary(info->m_x, info->m_y);
01283 }
01284 
01285 void
01286 CServer::handleMotionSecondaryEvent(const CEvent& event, void*)
01287 {
01288     IPlatformScreen::CMotionInfo* info =
01289         reinterpret_cast<IPlatformScreen::CMotionInfo*>(event.getData());
01290     onMouseMoveSecondary(info->m_x, info->m_y);
01291 }
01292 
01293 void
01294 CServer::handleWheelEvent(const CEvent& event, void*)
01295 {
01296     IPlatformScreen::CWheelInfo* info =
01297         reinterpret_cast<IPlatformScreen::CWheelInfo*>(event.getData());
01298     onMouseWheel(info->m_xDelta, info->m_yDelta);
01299 }
01300 
01301 void
01302 CServer::handleScreensaverActivatedEvent(const CEvent&, void*)
01303 {
01304     onScreensaver(true);
01305 }
01306 
01307 void
01308 CServer::handleScreensaverDeactivatedEvent(const CEvent&, void*)
01309 {
01310     onScreensaver(false);
01311 }
01312 
01313 void
01314 CServer::handleSwitchWaitTimeout(const CEvent&, void*)
01315 {
01316     // ignore if mouse is locked to screen
01317     if (isLockedToScreen()) {
01318         LOG((CLOG_DEBUG1 "locked to screen"));
01319         stopSwitch();
01320         return;
01321     }
01322 
01323     // switch screen
01324     switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false);
01325 }
01326 
01327 void
01328 CServer::handleClientDisconnected(const CEvent&, void* vclient)
01329 {
01330     // client has disconnected.  it might be an old client or an
01331     // active client.  we don't care so just handle it both ways.
01332     CBaseClientProxy* client = reinterpret_cast<CBaseClientProxy*>(vclient);
01333     removeActiveClient(client);
01334     removeOldClient(client);
01335     delete client;
01336 }
01337 
01338 void
01339 CServer::handleClientCloseTimeout(const CEvent&, void* vclient)
01340 {
01341     // client took too long to disconnect.  just dump it.
01342     CBaseClientProxy* client = reinterpret_cast<CBaseClientProxy*>(vclient);
01343     LOG((CLOG_NOTE "forced disconnection of client \"%s\"", getName(client).c_str()));
01344     removeOldClient(client);
01345     delete client;
01346 }
01347 
01348 void
01349 CServer::handleSwitchToScreenEvent(const CEvent& event, void*)
01350 {
01351     CSwitchToScreenInfo* info = 
01352         reinterpret_cast<CSwitchToScreenInfo*>(event.getData());
01353 
01354     CClientList::const_iterator index = m_clients.find(info->m_screen);
01355     if (index == m_clients.end()) {
01356         LOG((CLOG_DEBUG1 "screen \"%s\" not active", info->m_screen));
01357     }
01358     else {
01359         jumpToScreen(index->second);
01360     }
01361 }
01362 
01363 void
01364 CServer::handleSwitchInDirectionEvent(const CEvent& event, void*)
01365 {
01366     CSwitchInDirectionInfo* info = 
01367         reinterpret_cast<CSwitchInDirectionInfo*>(event.getData());
01368 
01369     // jump to screen in chosen direction from center of this screen
01370     SInt32 x = m_x, y = m_y;
01371     CBaseClientProxy* newScreen =
01372         getNeighbor(m_active, info->m_direction, x, y);
01373     if (newScreen == NULL) {
01374         LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(info->m_direction)));
01375     }
01376     else {
01377         jumpToScreen(newScreen);
01378     }
01379 }
01380 
01381 void
01382 CServer::handleKeyboardBroadcastEvent(const CEvent& event, void*)
01383 {
01384     CKeyboardBroadcastInfo* info = (CKeyboardBroadcastInfo*)event.getData();
01385 
01386     // choose new state
01387     bool newState;
01388     switch (info->m_state) {
01389     case CKeyboardBroadcastInfo::kOff:
01390         newState = false;
01391         break;
01392 
01393     default:
01394     case CKeyboardBroadcastInfo::kOn:
01395         newState = true;
01396         break;
01397 
01398     case CKeyboardBroadcastInfo::kToggle:
01399         newState = !m_keyboardBroadcasting;
01400         break;
01401     }
01402 
01403     // enter new state
01404     if (newState != m_keyboardBroadcasting ||
01405         info->m_screens != m_keyboardBroadcastingScreens) {
01406         m_keyboardBroadcasting        = newState;
01407         m_keyboardBroadcastingScreens = info->m_screens;
01408         LOG((CLOG_DEBUG "keyboard broadcasting %s: %s", m_keyboardBroadcasting ? "on" : "off", m_keyboardBroadcastingScreens.c_str()));
01409     }
01410 }
01411 
01412 void
01413 CServer::handleLockCursorToScreenEvent(const CEvent& event, void*)
01414 {
01415     CLockCursorToScreenInfo* info = (CLockCursorToScreenInfo*)event.getData();
01416 
01417     // choose new state
01418     bool newState;
01419     switch (info->m_state) {
01420     case CLockCursorToScreenInfo::kOff:
01421         newState = false;
01422         break;
01423 
01424     default:
01425     case CLockCursorToScreenInfo::kOn:
01426         newState = true;
01427         break;
01428 
01429     case CLockCursorToScreenInfo::kToggle:
01430         newState = !m_lockedToScreen;
01431         break;
01432     }
01433 
01434     // enter new state
01435     if (newState != m_lockedToScreen) {
01436         m_lockedToScreen = newState;
01437         LOG((CLOG_DEBUG "cursor %s current screen", m_lockedToScreen ? "locked to" : "unlocked from"));
01438 
01439         m_primaryClient->reconfigure(getActivePrimarySides());
01440         if (!isLockedToScreenServer()) {
01441             stopRelativeMoves();
01442         }
01443     }
01444 }
01445 
01446 void
01447 CServer::handleFakeInputBeginEvent(const CEvent&, void*)
01448 {
01449     m_primaryClient->fakeInputBegin();
01450 }
01451 
01452 void
01453 CServer::handleFakeInputEndEvent(const CEvent&, void*)
01454 {
01455     m_primaryClient->fakeInputEnd();
01456 }
01457 
01458 void
01459 CServer::onClipboardChanged(CBaseClientProxy* sender,
01460                 ClipboardID id, UInt32 seqNum)
01461 {
01462     CClipboardInfo& clipboard = m_clipboards[id];
01463 
01464     // ignore update if sequence number is old
01465     if (seqNum < clipboard.m_clipboardSeqNum) {
01466         LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", getName(sender).c_str(), id));
01467         return;
01468     }
01469 
01470     // should be the expected client
01471     assert(sender == m_clients.find(clipboard.m_clipboardOwner)->second);
01472 
01473     // get data
01474     sender->getClipboard(id, &clipboard.m_clipboard);
01475 
01476     // ignore if data hasn't changed
01477     CString data = clipboard.m_clipboard.marshall();
01478     if (data == clipboard.m_clipboardData) {
01479         LOG((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id));
01480         return;
01481     }
01482 
01483     // got new data
01484     LOG((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id));
01485     clipboard.m_clipboardData = data;
01486 
01487     // tell all clients except the sender that the clipboard is dirty
01488     for (CClientList::const_iterator index = m_clients.begin();
01489                                 index != m_clients.end(); ++index) {
01490         CBaseClientProxy* client = index->second;
01491         client->setClipboardDirty(id, client != sender);
01492     }
01493 
01494     // send the new clipboard to the active screen
01495     m_active->setClipboard(id, &clipboard.m_clipboard);
01496 }
01497 
01498 void
01499 CServer::onScreensaver(bool activated)
01500 {
01501     LOG((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated"));
01502 
01503     if (activated) {
01504         // save current screen and position
01505         m_activeSaver = m_active;
01506         m_xSaver      = m_x;
01507         m_ySaver      = m_y;
01508 
01509         // jump to primary screen
01510         if (m_active != m_primaryClient) {
01511             switchScreen(m_primaryClient, 0, 0, true);
01512         }
01513     }
01514     else {
01515         // jump back to previous screen and position.  we must check
01516         // that the position is still valid since the screen may have
01517         // changed resolutions while the screen saver was running.
01518         if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) {
01519             // check position
01520             CBaseClientProxy* screen = m_activeSaver;
01521             SInt32 x, y, w, h;
01522             screen->getShape(x, y, w, h);
01523             SInt32 zoneSize = getJumpZoneSize(screen);
01524             if (m_xSaver < x + zoneSize) {
01525                 m_xSaver = x + zoneSize;
01526             }
01527             else if (m_xSaver >= x + w - zoneSize) {
01528                 m_xSaver = x + w - zoneSize - 1;
01529             }
01530             if (m_ySaver < y + zoneSize) {
01531                 m_ySaver = y + zoneSize;
01532             }
01533             else if (m_ySaver >= y + h - zoneSize) {
01534                 m_ySaver = y + h - zoneSize - 1;
01535             }
01536 
01537             // jump
01538             switchScreen(screen, m_xSaver, m_ySaver, false);
01539         }
01540 
01541         // reset state
01542         m_activeSaver = NULL;
01543     }
01544 
01545     // send message to all clients
01546     for (CClientList::const_iterator index = m_clients.begin();
01547                                 index != m_clients.end(); ++index) {
01548         CBaseClientProxy* client = index->second;
01549         client->screensaver(activated);
01550     }
01551 }
01552 
01553 void
01554 CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button,
01555                 const char* screens)
01556 {
01557     LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button));
01558     assert(m_active != NULL);
01559 
01560     // relay
01561     if (!m_keyboardBroadcasting ||
01562             (screens && IKeyState::CKeyInfo::isDefault(screens))) {
01563         m_active->keyDown(id, mask, button);
01564     }
01565     else {
01566         if (!screens && m_keyboardBroadcasting) {
01567             screens = m_keyboardBroadcastingScreens.c_str();
01568             if (IKeyState::CKeyInfo::isDefault(screens)) {
01569                 screens = "*";
01570             }
01571         }
01572         for (CClientList::const_iterator index = m_clients.begin();
01573                                 index != m_clients.end(); ++index) {
01574             if (IKeyState::CKeyInfo::contains(screens, index->first)) {
01575                 index->second->keyDown(id, mask, button);
01576             }
01577         }
01578     }
01579 }
01580 
01581 void
01582 CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button,
01583                 const char* screens)
01584 {
01585     LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button));
01586     assert(m_active != NULL);
01587 
01588     // relay
01589     if (!m_keyboardBroadcasting ||
01590             (screens && IKeyState::CKeyInfo::isDefault(screens))) {
01591         m_active->keyUp(id, mask, button);
01592     }
01593     else {
01594         if (!screens && m_keyboardBroadcasting) {
01595             screens = m_keyboardBroadcastingScreens.c_str();
01596             if (IKeyState::CKeyInfo::isDefault(screens)) {
01597                 screens = "*";
01598             }
01599         }
01600         for (CClientList::const_iterator index = m_clients.begin();
01601                                 index != m_clients.end(); ++index) {
01602             if (IKeyState::CKeyInfo::contains(screens, index->first)) {
01603                 index->second->keyUp(id, mask, button);
01604             }
01605         }
01606     }
01607 }
01608 
01609 void
01610 CServer::onKeyRepeat(KeyID id, KeyModifierMask mask,
01611                 SInt32 count, KeyButton button)
01612 {
01613     LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button));
01614     assert(m_active != NULL);
01615 
01616     // relay
01617     m_active->keyRepeat(id, mask, count, button);
01618 }
01619 
01620 void
01621 CServer::onMouseDown(ButtonID id)
01622 {
01623     LOG((CLOG_DEBUG1 "onMouseDown id=%d", id));
01624     assert(m_active != NULL);
01625 
01626     // relay
01627     m_active->mouseDown(id);
01628 }
01629 
01630 void
01631 CServer::onMouseUp(ButtonID id)
01632 {
01633     LOG((CLOG_DEBUG1 "onMouseUp id=%d", id));
01634     assert(m_active != NULL);
01635 
01636     // relay
01637     m_active->mouseUp(id);
01638 }
01639 
01640 bool
01641 CServer::onMouseMovePrimary(SInt32 x, SInt32 y)
01642 {
01643     LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y));
01644 
01645     // mouse move on primary (server's) screen
01646     if (m_active != m_primaryClient) {
01647         // stale event -- we're actually on a secondary screen
01648         return false;
01649     }
01650 
01651     // save last delta
01652     m_xDelta2 = m_xDelta;
01653     m_yDelta2 = m_yDelta;
01654 
01655     // save current delta
01656     m_xDelta  = x - m_x;
01657     m_yDelta  = y - m_y;
01658 
01659     // save position
01660     m_x       = x;
01661     m_y       = y;
01662 
01663     // get screen shape
01664     SInt32 ax, ay, aw, ah;
01665     m_active->getShape(ax, ay, aw, ah);
01666     SInt32 zoneSize = getJumpZoneSize(m_active);
01667 
01668     // clamp position to screen
01669     SInt32 xc = x, yc = y;
01670     if (xc < ax + zoneSize) {
01671         xc = ax;
01672     }
01673     else if (xc >= ax + aw - zoneSize) {
01674         xc = ax + aw - 1;
01675     }
01676     if (yc < ay + zoneSize) {
01677         yc = ay;
01678     }
01679     else if (yc >= ay + ah - zoneSize) {
01680         yc = ay + ah - 1;
01681     }
01682 
01683     // see if we should change screens
01684     EDirection dir;
01685     if (x < ax + zoneSize) {
01686         x  -= zoneSize;
01687         dir = kLeft;
01688     }
01689     else if (x >= ax + aw - zoneSize) {
01690         x  += zoneSize;
01691         dir = kRight;
01692     }
01693     else if (y < ay + zoneSize) {
01694         y  -= zoneSize;
01695         dir = kTop;
01696     }
01697     else if (y >= ay + ah - zoneSize) {
01698         y  += zoneSize;
01699         dir = kBottom;
01700     }
01701     else {
01702         // still on local screen
01703         noSwitch(x, y);
01704         return false;
01705     }
01706 
01707     // get jump destination
01708     CBaseClientProxy* newScreen = mapToNeighbor(m_active, dir, x, y);
01709 
01710     // should we switch or not?
01711     if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) {
01712         // switch screen
01713         switchScreen(newScreen, x, y, false);
01714         return true;
01715     }
01716     else {
01717         return false;
01718     }
01719 }
01720 
01721 void
01722 CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
01723 {
01724     LOG((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy));
01725 
01726     // mouse move on secondary (client's) screen
01727     assert(m_active != NULL);
01728     if (m_active == m_primaryClient) {
01729         // stale event -- we're actually on the primary screen
01730         return;
01731     }
01732 
01733     // if doing relative motion on secondary screens and we're locked
01734     // to the screen (which activates relative moves) then send a
01735     // relative mouse motion.  when we're doing this we pretend as if
01736     // the mouse isn't actually moving because we're expecting some
01737     // program on the secondary screen to warp the mouse on us, so we
01738     // have no idea where it really is.
01739     if (m_relativeMoves && isLockedToScreenServer()) {
01740         LOG((CLOG_DEBUG2 "relative move on %s by %d,%d", getName(m_active).c_str(), dx, dy));
01741         m_active->mouseRelativeMove(dx, dy);
01742         return;
01743     }
01744 
01745     // save old position
01746     const SInt32 xOld = m_x;
01747     const SInt32 yOld = m_y;
01748 
01749     // save last delta
01750     m_xDelta2 = m_xDelta;
01751     m_yDelta2 = m_yDelta;
01752 
01753     // save current delta
01754     m_xDelta  = dx;
01755     m_yDelta  = dy;
01756 
01757     // accumulate motion
01758     m_x      += dx;
01759     m_y      += dy;
01760 
01761     // get screen shape
01762     SInt32 ax, ay, aw, ah;
01763     m_active->getShape(ax, ay, aw, ah);
01764 
01765     // find direction of neighbor and get the neighbor
01766     bool jump = true;
01767     CBaseClientProxy* newScreen;
01768     do {
01769         // clamp position to screen
01770         SInt32 xc = m_x, yc = m_y;
01771         if (xc < ax) {
01772             xc = ax;
01773         }
01774         else if (xc >= ax + aw) {
01775             xc = ax + aw - 1;
01776         }
01777         if (yc < ay) {
01778             yc = ay;
01779         }
01780         else if (yc >= ay + ah) {
01781             yc = ay + ah - 1;
01782         }
01783 
01784         EDirection dir;
01785         if (m_x < ax) {
01786             dir = kLeft;
01787         }
01788         else if (m_x > ax + aw - 1) {
01789             dir = kRight;
01790         }
01791         else if (m_y < ay) {
01792             dir = kTop;
01793         }
01794         else if (m_y > ay + ah - 1) {
01795             dir = kBottom;
01796         }
01797         else {
01798             // we haven't left the screen
01799             newScreen = m_active;
01800             jump      = false;
01801 
01802             // if waiting and mouse is not on the border we're waiting
01803             // on then stop waiting.  also if it's not on the border
01804             // then arm the double tap.
01805             if (m_switchScreen != NULL) {
01806                 bool clearWait;
01807                 SInt32 zoneSize = m_primaryClient->getJumpZoneSize();
01808                 switch (m_switchDir) {
01809                 case kLeft:
01810                     clearWait = (m_x >= ax + zoneSize);
01811                     break;
01812 
01813                 case kRight:
01814                     clearWait = (m_x <= ax + aw - 1 - zoneSize);
01815                     break;
01816 
01817                 case kTop:
01818                     clearWait = (m_y >= ay + zoneSize);
01819                     break;
01820 
01821                 case kBottom:
01822                     clearWait = (m_y <= ay + ah - 1 + zoneSize);
01823                     break;
01824 
01825                 default:
01826                     clearWait = false;
01827                     break;
01828                 }
01829                 if (clearWait) {
01830                     // still on local screen
01831                     noSwitch(m_x, m_y);
01832                 }
01833             }
01834 
01835             // skip rest of block
01836             break;
01837         }
01838 
01839         // try to switch screen.  get the neighbor.
01840         newScreen = mapToNeighbor(m_active, dir, m_x, m_y);
01841 
01842         // see if we should switch
01843         if (!isSwitchOkay(newScreen, dir, m_x, m_y, xc, yc)) {
01844             newScreen = m_active;
01845             jump      = false;
01846         }
01847     } while (false);
01848 
01849     if (jump) {
01850         // switch screens
01851         switchScreen(newScreen, m_x, m_y, false);
01852     }
01853     else {
01854         // same screen.  clamp mouse to edge.
01855         m_x = xOld + dx;
01856         m_y = yOld + dy;
01857         if (m_x < ax) {
01858             m_x = ax;
01859             LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", getName(m_active).c_str()));
01860         }
01861         else if (m_x > ax + aw - 1) {
01862             m_x = ax + aw - 1;
01863             LOG((CLOG_DEBUG2 "clamp to right of \"%s\"", getName(m_active).c_str()));
01864         }
01865         if (m_y < ay) {
01866             m_y = ay;
01867             LOG((CLOG_DEBUG2 "clamp to top of \"%s\"", getName(m_active).c_str()));
01868         }
01869         else if (m_y > ay + ah - 1) {
01870             m_y = ay + ah - 1;
01871             LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", getName(m_active).c_str()));
01872         }
01873 
01874         // warp cursor if it moved.
01875         if (m_x != xOld || m_y != yOld) {
01876             LOG((CLOG_DEBUG2 "move on %s to %d,%d", getName(m_active).c_str(), m_x, m_y));
01877             m_active->mouseMove(m_x, m_y);
01878         }
01879     }
01880 }
01881 
01882 void
01883 CServer::onMouseWheel(SInt32 xDelta, SInt32 yDelta)
01884 {
01885     LOG((CLOG_DEBUG1 "onMouseWheel %+d,%+d", xDelta, yDelta));
01886     assert(m_active != NULL);
01887 
01888     // relay
01889     m_active->mouseWheel(xDelta, yDelta);
01890 }
01891 
01892 bool
01893 CServer::addClient(CBaseClientProxy* client)
01894 {
01895     CString name = getName(client);
01896     if (m_clients.count(name) != 0) {
01897         return false;
01898     }
01899 
01900     // add event handlers
01901     EVENTQUEUE->adoptHandler(IScreen::getShapeChangedEvent(),
01902                             client->getEventTarget(),
01903                             new TMethodEventJob<CServer>(this,
01904                                 &CServer::handleShapeChanged, client));
01905     EVENTQUEUE->adoptHandler(IScreen::getClipboardGrabbedEvent(),
01906                             client->getEventTarget(),
01907                             new TMethodEventJob<CServer>(this,
01908                                 &CServer::handleClipboardGrabbed, client));
01909     EVENTQUEUE->adoptHandler(CClientProxy::getClipboardChangedEvent(),
01910                             client->getEventTarget(),
01911                             new TMethodEventJob<CServer>(this,
01912                                 &CServer::handleClipboardChanged, client));
01913 
01914     // add to list
01915     m_clientSet.insert(client);
01916     m_clients.insert(std::make_pair(name, client));
01917 
01918     // initialize client data
01919     SInt32 x, y;
01920     client->getCursorPos(x, y);
01921     client->setJumpCursorPos(x, y);
01922 
01923     // tell primary client about the active sides
01924     m_primaryClient->reconfigure(getActivePrimarySides());
01925 
01926     return true;
01927 }
01928 
01929 bool
01930 CServer::removeClient(CBaseClientProxy* client)
01931 {
01932     // return false if not in list
01933     CClientSet::iterator i = m_clientSet.find(client);
01934     if (i == m_clientSet.end()) {
01935         return false;
01936     }
01937 
01938     // remove event handlers
01939     EVENTQUEUE->removeHandler(IScreen::getShapeChangedEvent(),
01940                             client->getEventTarget());
01941     EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(),
01942                             client->getEventTarget());
01943     EVENTQUEUE->removeHandler(CClientProxy::getClipboardChangedEvent(),
01944                             client->getEventTarget());
01945 
01946     // remove from list
01947     m_clients.erase(getName(client));
01948     m_clientSet.erase(i);
01949 
01950     return true;
01951 }
01952 
01953 void
01954 CServer::closeClient(CBaseClientProxy* client, const char* msg)
01955 {
01956     assert(client != m_primaryClient);
01957     assert(msg != NULL);
01958 
01959     // send message to client.  this message should cause the client
01960     // to disconnect.  we add this client to the closed client list
01961     // and install a timer to remove the client if it doesn't respond
01962     // quickly enough.  we also remove the client from the active
01963     // client list since we're not going to listen to it anymore.
01964     // note that this method also works on clients that are not in
01965     // the m_clients list.  adoptClient() may call us with such a
01966     // client.
01967     LOG((CLOG_NOTE "disconnecting client \"%s\"", getName(client).c_str()));
01968 
01969     // send message
01970     // FIXME -- avoid type cast (kinda hard, though)
01971     ((CClientProxy*)client)->close(msg);
01972 
01973     // install timer.  wait timeout seconds for client to close.
01974     double timeout = 5.0;
01975     CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL);
01976     EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
01977                             new TMethodEventJob<CServer>(this,
01978                                 &CServer::handleClientCloseTimeout, client));
01979 
01980     // move client to closing list
01981     removeClient(client);
01982     m_oldClients.insert(std::make_pair(client, timer));
01983 
01984     // if this client is the active screen then we have to
01985     // jump off of it
01986     forceLeaveClient(client);
01987 }
01988 
01989 void
01990 CServer::closeClients(const CConfig& config)
01991 {
01992     // collect the clients that are connected but are being dropped
01993     // from the configuration (or who's canonical name is changing).
01994     typedef std::set<CBaseClientProxy*> CRemovedClients;
01995     CRemovedClients removed;
01996     for (CClientList::iterator index = m_clients.begin();
01997                                 index != m_clients.end(); ++index) {
01998         if (!config.isCanonicalName(index->first)) {
01999             removed.insert(index->second);
02000         }
02001     }
02002 
02003     // don't close the primary client
02004     removed.erase(m_primaryClient);
02005 
02006     // now close them.  we collect the list then close in two steps
02007     // because closeClient() modifies the collection we iterate over.
02008     for (CRemovedClients::iterator index = removed.begin();
02009                                 index != removed.end(); ++index) {
02010         closeClient(*index, kMsgCClose);
02011     }
02012 }
02013 
02014 void
02015 CServer::removeActiveClient(CBaseClientProxy* client)
02016 {
02017     if (removeClient(client)) {
02018         forceLeaveClient(client);
02019         EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client);
02020         if (m_clients.size() == 1 && m_oldClients.empty()) {
02021             EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this));
02022         }
02023     }
02024 }
02025 
02026 void
02027 CServer::removeOldClient(CBaseClientProxy* client)
02028 {
02029     COldClients::iterator i = m_oldClients.find(client);
02030     if (i != m_oldClients.end()) {
02031         EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client);
02032         EVENTQUEUE->removeHandler(CEvent::kTimer, i->second);
02033         EVENTQUEUE->deleteTimer(i->second);
02034         m_oldClients.erase(i);
02035         if (m_clients.size() == 1 && m_oldClients.empty()) {
02036             EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this));
02037         }
02038     }
02039 }
02040 
02041 void
02042 CServer::forceLeaveClient(CBaseClientProxy* client)
02043 {
02044     CBaseClientProxy* active =
02045         (m_activeSaver != NULL) ? m_activeSaver : m_active;
02046     if (active == client) {
02047         // record new position (center of primary screen)
02048         m_primaryClient->getCursorCenter(m_x, m_y);
02049 
02050         // stop waiting to switch to this client
02051         if (active == m_switchScreen) {
02052             stopSwitch();
02053         }
02054 
02055         // don't notify active screen since it has probably already
02056         // disconnected.
02057         LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", getName(active).c_str(), getName(m_primaryClient).c_str(), m_x, m_y));
02058 
02059         // cut over
02060         m_active = m_primaryClient;
02061 
02062         // enter new screen (unless we already have because of the
02063         // screen saver)
02064         if (m_activeSaver == NULL) {
02065             m_primaryClient->enter(m_x, m_y, m_seqNum,
02066                                 m_primaryClient->getToggleMask(), false);
02067         }
02068     }
02069 
02070     // if this screen had the cursor when the screen saver activated
02071     // then we can't switch back to it when the screen saver
02072     // deactivates.
02073     if (m_activeSaver == client) {
02074         m_activeSaver = NULL;
02075     }
02076 
02077     // tell primary client about the active sides
02078     m_primaryClient->reconfigure(getActivePrimarySides());
02079 }
02080 
02081 
02082 //
02083 // CServer::CClipboardInfo
02084 //
02085 
02086 CServer::CClipboardInfo::CClipboardInfo() :
02087     m_clipboard(),
02088     m_clipboardData(),
02089     m_clipboardOwner(),
02090     m_clipboardSeqNum(0)
02091 {
02092     // do nothing
02093 }
02094 
02095 
02096 //
02097 // CServer::CLockCursorToScreenInfo
02098 //
02099 
02100 CServer::CLockCursorToScreenInfo*
02101 CServer::CLockCursorToScreenInfo::alloc(State state)
02102 {
02103     CLockCursorToScreenInfo* info =
02104         (CLockCursorToScreenInfo*)malloc(sizeof(CLockCursorToScreenInfo));
02105     info->m_state = state;
02106     return info;
02107 }
02108 
02109 
02110 //
02111 // CServer::CSwitchToScreenInfo
02112 //
02113 
02114 CServer::CSwitchToScreenInfo*
02115 CServer::CSwitchToScreenInfo::alloc(const CString& screen)
02116 {
02117     CSwitchToScreenInfo* info =
02118         (CSwitchToScreenInfo*)malloc(sizeof(CSwitchToScreenInfo) +
02119                                 screen.size());
02120     strcpy(info->m_screen, screen.c_str());
02121     return info;
02122 }
02123 
02124 
02125 //
02126 // CServer::CSwitchInDirectionInfo
02127 //
02128 
02129 CServer::CSwitchInDirectionInfo*
02130 CServer::CSwitchInDirectionInfo::alloc(EDirection direction)
02131 {
02132     CSwitchInDirectionInfo* info =
02133         (CSwitchInDirectionInfo*)malloc(sizeof(CSwitchInDirectionInfo));
02134     info->m_direction = direction;
02135     return info;
02136 }
02137 
02138 
02139 //
02140 // CServer::CScreenConnectedInfo
02141 //
02142 
02143 CServer::CScreenConnectedInfo*
02144 CServer::CScreenConnectedInfo::alloc(const CString& screen)
02145 {
02146     CScreenConnectedInfo* info =
02147         (CScreenConnectedInfo*)malloc(sizeof(CScreenConnectedInfo) +
02148                                 screen.size());
02149     strcpy(info->m_screen, screen.c_str());
02150     return info;
02151 }
02152 
02153 
02154 //
02155 // CServer::CKeyboardBroadcastInfo
02156 //
02157 
02158 CServer::CKeyboardBroadcastInfo*
02159 CServer::CKeyboardBroadcastInfo::alloc(State state)
02160 {
02161     CKeyboardBroadcastInfo* info =
02162         (CKeyboardBroadcastInfo*)malloc(sizeof(CKeyboardBroadcastInfo));
02163     info->m_state      = state;
02164     info->m_screens[0] = '\0';
02165     return info;
02166 }
02167 
02168 CServer::CKeyboardBroadcastInfo*
02169 CServer::CKeyboardBroadcastInfo::alloc(State state, const CString& screens)
02170 {
02171     CKeyboardBroadcastInfo* info =
02172         (CKeyboardBroadcastInfo*)malloc(sizeof(CKeyboardBroadcastInfo) +
02173                                 screens.size());
02174     info->m_state = state;
02175     strcpy(info->m_screens, screens.c_str());
02176     return info;
02177 }

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