• Main Page
  • Classes
  • Files
  • File List

CServer.cpp

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

Generated on Sat May 25 2013 00:00:04 for Synergy by  doxygen 1.7.1