New Ticket     Wiki     Browse Source     Timeline     Roadmap     Ticket Reports     Search

Ticket #13797: synergy-1.3.1-broadcast.patch

File synergy-1.3.1-broadcast.patch, 151.2 KB (added by anton@…, 4 years ago)

Broadcast patch for Synergy 1.3.1

  • lib/platform/COSXScreen.cpp

    diff -uNr synergy-1.3.1/lib/platform/COSXScreen.cpp synergy-1.3.1-broadcast/lib/platform/COSXScreen.cpp
    old new  
    120120                                                        &inputBounds, &m_userInputWindow); 
    121121 
    122122                        SetWindowAlpha(m_userInputWindow, 0); 
     123 
     124                        // Install global event handlers, so we can broadcast. 
     125                        InstallMyEventHandlers(); 
    123126                } 
    124127                 
    125128                // install display manager notification handler 
     
    233236        delete m_screensaver; 
    234237} 
    235238 
     239static OSStatus MonitorHandler( EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon ) 
     240{ 
     241        // nothing 
     242        return noErr; 
     243} 
     244 
     245void 
     246COSXScreen::InstallMyEventHandlers(void) 
     247{ 
     248        EventTypeSpec   kEvents[] = 
     249                { 
     250                        { kEventClassKeyboard, kEventRawKeyDown }, 
     251                        { kEventClassKeyboard, kEventRawKeyUp }, 
     252                        { kEventClassKeyboard, kEventRawKeyRepeat }, 
     253                        { kEventClassKeyboard, kEventRawKeyModifiersChanged } 
     254                }; 
     255 
     256        InstallEventHandler( GetEventMonitorTarget(), MonitorHandler, GetEventTypeCount( kEvents ), 
     257                                                 kEvents, 0, NULL ); 
     258         
     259} 
     260 
    236261void* 
    237262COSXScreen::getEventTarget() const 
    238263{ 
     
    803828 
    804829        UInt32 eventClass = GetEventClass(*carbonEvent); 
    805830 
     831        LOG((CLOG_DEBUG1 "OSX system event.")); 
     832 
    806833        switch (eventClass) { 
    807834        case kEventClassMouse: 
    808835                switch (GetEventKind(*carbonEvent)) { 
  • lib/platform/COSXScreen.h

    diff -uNr synergy-1.3.1/lib/platform/COSXScreen.h synergy-1.3.1-broadcast/lib/platform/COSXScreen.h
    old new  
    2525#include <mach/mach_init.h> 
    2626#include <IOKit/pwr_mgt/IOPMLib.h> 
    2727#include <IOKit/IOMessage.h> 
     28#include <ApplicationServices/ApplicationServices.h> 
    2829 
    2930template <class T> 
    3031class CCondVar; 
     
    147148        void                            handlePowerChangeRequest(natural_t messageType, 
    148149                                                         void* messageArgument); 
    149150 
     151        void                            InstallMyEventHandlers(void); 
     152 
    150153        static CEvent::Type     getConfirmSleepEvent(); 
    151154        void                            handleConfirmSleep(const CEvent& event, void*); 
    152155         
     
    227230        EventHandlerRef                 m_switchEventHandlerRef; 
    228231 
    229232        // sleep / wakeup 
    230         CMutex*                                 m_pmMutex; 
    231         CThread*                                m_pmWatchThread; 
    232     CCondVar<bool>*                     m_pmThreadReady; 
     233        CMutex*                         m_pmMutex; 
     234        CThread*                        m_pmWatchThread; 
     235        CCondVar<bool>*                 m_pmThreadReady; 
    233236        CFRunLoopRef                    m_pmRunloop; 
    234237        io_connect_t                    m_pmRootPort; 
    235238 
  • lib/server/CPrimaryClient.cpp

    diff -uNr synergy-1.3.1/lib/server/CPrimaryClient.cpp synergy-1.3.1-broadcast/lib/server/CPrimaryClient.cpp
    old new  
    181181void 
    182182CPrimaryClient::keyDown(KeyID key, KeyModifierMask mask, KeyButton button) 
    183183{ 
     184        LOG((CLOG_DEBUG1 "KeyDownMain id=%d mask=0x%04x button=0x%04x", key, mask, button)); 
     185 
    184186        if (m_fakeInputCount > 0) { 
    185187// XXX -- don't forward keystrokes to primary screen for now 
    186188                (void)key; 
     
    191193} 
    192194 
    193195void 
    194 CPrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton) 
     196CPrimaryClient::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button) 
    195197{ 
    196198        // ignore 
     199        LOG((CLOG_DEBUG1 "KeyRepeatMain id=%d mask=0x%04x count=%d button=0x%04x", key, mask, count, button)); 
    197200} 
    198201 
    199202void 
    200203CPrimaryClient::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) 
    201204{ 
     205        LOG((CLOG_DEBUG1 "KeyUpMain id=%d mask=0x%04x button=0x%04x", key, mask, button)); 
    202206        if (m_fakeInputCount > 0) { 
    203207// XXX -- don't forward keystrokes to primary screen for now 
    204208                (void)key; 
  • lib/server/CServer.cpp

    diff -uNr synergy-1.3.1/lib/server/CServer.cpp synergy-1.3.1-broadcast/lib/server/CServer.cpp
    old new  
    6161        m_switchTwoTapArmed(false), 
    6262        m_switchTwoTapZone(3), 
    6363        m_relativeMoves(false), 
    64         m_lockedToScreen(false) 
     64        m_lockedToScreen(false), 
     65        m_broadcast(false) 
    6566{ 
    6667        // must have a primary client and it must have a canonical name 
    6768        assert(m_primaryClient != NULL); 
     
    13881389 
    13891390        // enter new state 
    13901391        if (newState != m_lockedToScreen) { 
     1392                SInt32 x, y; 
    13911393                m_lockedToScreen = newState; 
     1394 
    13921395                LOG((CLOG_DEBUG "cursor %s current screen", m_lockedToScreen ? "locked to" : "unlocked from")); 
    13931396 
    13941397                m_primaryClient->reconfigure(getActivePrimarySides()); 
    13951398                if (!isLockedToScreenServer()) { 
    1396                         stopRelativeMoves(); 
     1399                        //stopRelativeMoves(); 
    13971400                } 
    13981401        } 
    13991402} 
     
    15141517 
    15151518        // relay 
    15161519        if (IKeyState::CKeyInfo::isDefault(screens)) { 
    1517                 m_active->keyDown(id, mask, button); 
     1520                if (m_active != m_primaryClient) 
     1521                { 
     1522                        m_active->keyDown(id, mask, button); 
     1523                } 
     1524                else 
     1525                {  
     1526                        if (id == 96 && mask == 0 && button == 0x0033) 
     1527                        { 
     1528                                if (m_broadcast) 
     1529                                { 
     1530                                        m_broadcast = false; 
     1531                                } 
     1532                                else 
     1533                                { 
     1534                                        m_broadcast = true; 
     1535                                } 
     1536                        } 
     1537                        if (m_broadcast) 
     1538                        { 
     1539                                for (CClientList::const_iterator index = m_clients.begin(); 
     1540                                                 index != m_clients.end(); ++index) { 
     1541                                        index->second->keyDown(id, mask, button); 
     1542                                } 
     1543                        } 
     1544                } 
    15181545        } 
    15191546        else { 
    15201547                for (CClientList::const_iterator index = m_clients.begin(); 
     
    15351562 
    15361563        // relay 
    15371564        if (IKeyState::CKeyInfo::isDefault(screens)) { 
    1538                 m_active->keyUp(id, mask, button); 
     1565                if (m_active != m_primaryClient) 
     1566                { 
     1567                        m_active->keyUp(id, mask, button); 
     1568                } 
     1569                else if (m_broadcast) 
     1570                { 
     1571                        for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { 
     1572                                index->second->keyUp(id, mask, button); 
     1573                        } 
     1574                } 
    15391575        } 
    15401576        else { 
    15411577                for (CClientList::const_iterator index = m_clients.begin(); 
     
    15551591        assert(m_active != NULL); 
    15561592 
    15571593        // relay 
    1558         m_active->keyRepeat(id, mask, count, button); 
     1594        if (m_active != m_primaryClient) 
     1595        { 
     1596                m_active->keyRepeat(id, mask, count, button); 
     1597        } 
     1598        else if (m_broadcast) 
     1599        { 
     1600                for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { 
     1601                        index->second->keyRepeat(id, mask, count, button); 
     1602                } 
     1603        } 
    15591604} 
    15601605 
    15611606void 
  • lib/server/CServer.cpp.hack

    diff -uNr synergy-1.3.1/lib/server/CServer.cpp.hack synergy-1.3.1-broadcast/lib/server/CServer.cpp.hack
    old new  
     1/* 
     2 * synergy -- mouse and keyboard sharing utility 
     3 * Copyright (C) 2002 Chris Schoeneman 
     4 *  
     5 * This package is free software; you can redistribute it and/or 
     6 * modify it under the terms of the GNU General Public License 
     7 * found in the file COPYING that should have accompanied this file. 
     8 *  
     9 * This package is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 */ 
     14 
     15#include "CServer.h" 
     16#include "CClientProxy.h" 
     17#include "CClientProxyUnknown.h" 
     18#include "CPrimaryClient.h" 
     19#include "IPlatformScreen.h" 
     20#include "OptionTypes.h" 
     21#include "ProtocolTypes.h" 
     22#include "XScreen.h" 
     23#include "XSynergy.h" 
     24#include "IDataSocket.h" 
     25#include "IListenSocket.h" 
     26#include "XSocket.h" 
     27#include "IEventQueue.h" 
     28#include "CLog.h" 
     29#include "TMethodEventJob.h" 
     30#include "CArch.h" 
     31#include <string.h> 
     32 
     33// 
     34// CServer 
     35// 
     36 
     37CEvent::Type                    CServer::s_errorEvent         = CEvent::kUnknown; 
     38CEvent::Type                    CServer::s_connectedEvent     = CEvent::kUnknown; 
     39CEvent::Type                    CServer::s_disconnectedEvent  = CEvent::kUnknown; 
     40CEvent::Type                    CServer::s_switchToScreen     = CEvent::kUnknown; 
     41CEvent::Type                    CServer::s_switchInDirection  = CEvent::kUnknown; 
     42CEvent::Type                    CServer::s_lockCursorToScreen = CEvent::kUnknown; 
     43 
     44CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : 
     45        m_primaryClient(primaryClient), 
     46        m_active(primaryClient), 
     47        m_seqNum(0), 
     48        m_xDelta(0), 
     49        m_yDelta(0), 
     50        m_xDelta2(0), 
     51        m_yDelta2(0), 
     52        m_config(), 
     53        m_inputFilter(m_config.getInputFilter()), 
     54        m_activeSaver(NULL), 
     55        m_switchDir(kNoDirection), 
     56        m_switchScreen(NULL), 
     57        m_switchWaitDelay(0.0), 
     58        m_switchWaitTimer(NULL), 
     59        m_switchTwoTapDelay(0.0), 
     60        m_switchTwoTapEngaged(false), 
     61        m_switchTwoTapArmed(false), 
     62        m_switchTwoTapZone(3), 
     63        m_relativeMoves(false), 
     64        m_lockedToScreen(false), 
     65        m_broadcast(false), 
     66        m_broadcastOnID(NULL), 
     67        m_broadcastOffID(NULL) 
     68{ 
     69        const char *soundOnFile = "/System/Library/Sounds/Blow.aiff"; 
     70        FSRef soundOnFileRef; 
     71        const char *soundOffFile = "/System/Library/Sounds/Submarine.aiff"; 
     72        FSRef soundOffFileRef; 
     73        OSStatus soundErr; 
     74 
     75        soundErr = FSPathMakeRef(soundOnFile, &soundOnFileRef, NULL); 
     76        if (noErr == soundErr) 
     77        { 
     78                soundErr = SystemSoundGetActionID(&soundOnFileRef, &m_broadcastOnID); 
     79        } 
     80 
     81        soundErr = FSPathMakeRef(soundOffFile, &soundOffFileRef, NULL); 
     82        if (noErr == soundErr) 
     83        { 
     84                soundErr = SystemSoundGetActionID(&soundOffFileRef, &m_broadcastOffID); 
     85        } 
     86 
     87        // must have a primary client and it must have a canonical name 
     88        assert(m_primaryClient != NULL); 
     89        assert(config.isScreen(primaryClient->getName())); 
     90 
     91        CString primaryName = getName(primaryClient); 
     92 
     93        // clear clipboards 
     94        for (ClipboardID id = 0; id < kClipboardEnd; ++id) { 
     95                CClipboardInfo& clipboard   = m_clipboards[id]; 
     96                clipboard.m_clipboardOwner  = primaryName; 
     97                clipboard.m_clipboardSeqNum = m_seqNum; 
     98                if (clipboard.m_clipboard.open(0)) { 
     99                        clipboard.m_clipboard.empty(); 
     100                        clipboard.m_clipboard.close(); 
     101                } 
     102                clipboard.m_clipboardData   = clipboard.m_clipboard.marshall(); 
     103        } 
     104 
     105        // install event handlers 
     106        EVENTQUEUE->adoptHandler(CEvent::kTimer, this, 
     107                                                        new TMethodEventJob<CServer>(this, 
     108                                                                &CServer::handleSwitchWaitTimeout)); 
     109        EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyDownEvent(), 
     110                                                        m_inputFilter, 
     111                                                        new TMethodEventJob<CServer>(this, 
     112                                                                &CServer::handleKeyDownEvent)); 
     113        EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyUpEvent(), 
     114                                                        m_inputFilter, 
     115                                                        new TMethodEventJob<CServer>(this, 
     116                                                                &CServer::handleKeyUpEvent)); 
     117        EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyRepeatEvent(), 
     118                                                        m_inputFilter, 
     119                                                        new TMethodEventJob<CServer>(this, 
     120                                                                &CServer::handleKeyRepeatEvent)); 
     121        EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonDownEvent(), 
     122                                                        m_inputFilter, 
     123                                                        new TMethodEventJob<CServer>(this, 
     124                                                                &CServer::handleButtonDownEvent)); 
     125        EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonUpEvent(), 
     126                                                        m_inputFilter, 
     127                                                        new TMethodEventJob<CServer>(this, 
     128                                                                &CServer::handleButtonUpEvent)); 
     129        EVENTQUEUE->adoptHandler(IPlatformScreen::getMotionOnPrimaryEvent(), 
     130                                                        m_primaryClient->getEventTarget(), 
     131                                                        new TMethodEventJob<CServer>(this, 
     132                                                                &CServer::handleMotionPrimaryEvent)); 
     133        EVENTQUEUE->adoptHandler(IPlatformScreen::getMotionOnSecondaryEvent(), 
     134                                                        m_primaryClient->getEventTarget(), 
     135                                                        new TMethodEventJob<CServer>(this, 
     136                                                                &CServer::handleMotionSecondaryEvent)); 
     137        EVENTQUEUE->adoptHandler(IPlatformScreen::getWheelEvent(), 
     138                                                        m_primaryClient->getEventTarget(), 
     139                                                        new TMethodEventJob<CServer>(this, 
     140                                                                &CServer::handleWheelEvent)); 
     141        EVENTQUEUE->adoptHandler(IPlatformScreen::getScreensaverActivatedEvent(), 
     142                                                        m_primaryClient->getEventTarget(), 
     143                                                        new TMethodEventJob<CServer>(this, 
     144                                                                &CServer::handleScreensaverActivatedEvent)); 
     145        EVENTQUEUE->adoptHandler(IPlatformScreen::getScreensaverDeactivatedEvent(), 
     146                                                        m_primaryClient->getEventTarget(), 
     147                                                        new TMethodEventJob<CServer>(this, 
     148                                                                &CServer::handleScreensaverDeactivatedEvent)); 
     149        EVENTQUEUE->adoptHandler(getSwitchToScreenEvent(), 
     150                                                        m_inputFilter, 
     151                                                        new TMethodEventJob<CServer>(this, 
     152                                                                &CServer::handleSwitchToScreenEvent)); 
     153        EVENTQUEUE->adoptHandler(getSwitchInDirectionEvent(), 
     154                                                        m_inputFilter, 
     155                                                        new TMethodEventJob<CServer>(this, 
     156                                                                &CServer::handleSwitchInDirectionEvent)); 
     157        EVENTQUEUE->adoptHandler(getLockCursorToScreenEvent(), 
     158                                                        m_inputFilter, 
     159                                                        new TMethodEventJob<CServer>(this, 
     160                                                                &CServer::handleLockCursorToScreenEvent)); 
     161        EVENTQUEUE->adoptHandler(IPlatformScreen::getFakeInputBeginEvent(), 
     162                                                        m_inputFilter, 
     163                                                        new TMethodEventJob<CServer>(this, 
     164                                                                &CServer::handleFakeInputBeginEvent)); 
     165        EVENTQUEUE->adoptHandler(IPlatformScreen::getFakeInputEndEvent(), 
     166                                                        m_inputFilter, 
     167                                                        new TMethodEventJob<CServer>(this, 
     168                                                                &CServer::handleFakeInputEndEvent)); 
     169 
     170        // add connection 
     171        addClient(m_primaryClient); 
     172 
     173        // set initial configuration 
     174        setConfig(config); 
     175 
     176        // enable primary client 
     177        m_primaryClient->enable(); 
     178        m_inputFilter->setPrimaryClient(m_primaryClient); 
     179} 
     180 
     181CServer::~CServer() 
     182{ 
     183        if (m_broadcastOnId) 
     184        { 
     185                SystemSoundRemoveActionID(m_broadcastOnID); 
     186        } 
     187        if (m_broadcastOffId) 
     188        { 
     189                SystemSoundRemoveActionID(m_broadcastOffID); 
     190        } 
     191 
     192        // remove event handlers and timers 
     193        EVENTQUEUE->removeHandler(IPlatformScreen::getKeyDownEvent(), 
     194                                                        m_inputFilter); 
     195        EVENTQUEUE->removeHandler(IPlatformScreen::getKeyUpEvent(), 
     196                                                        m_inputFilter); 
     197        EVENTQUEUE->removeHandler(IPlatformScreen::getKeyRepeatEvent(), 
     198                                                        m_inputFilter); 
     199        EVENTQUEUE->removeHandler(IPlatformScreen::getButtonDownEvent(), 
     200                                                        m_inputFilter); 
     201        EVENTQUEUE->removeHandler(IPlatformScreen::getButtonUpEvent(), 
     202                                                        m_inputFilter); 
     203        EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnPrimaryEvent(), 
     204                                                        m_primaryClient->getEventTarget()); 
     205        EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnSecondaryEvent(), 
     206                                                        m_primaryClient->getEventTarget()); 
     207        EVENTQUEUE->removeHandler(IPlatformScreen::getWheelEvent(), 
     208                                                        m_primaryClient->getEventTarget()); 
     209        EVENTQUEUE->removeHandler(IPlatformScreen::getScreensaverActivatedEvent(), 
     210                                                        m_primaryClient->getEventTarget()); 
     211        EVENTQUEUE->removeHandler(IPlatformScreen::getScreensaverDeactivatedEvent(), 
     212                                                        m_primaryClient->getEventTarget()); 
     213        EVENTQUEUE->removeHandler(IPlatformScreen::getFakeInputBeginEvent(), 
     214                                                        m_inputFilter); 
     215        EVENTQUEUE->removeHandler(IPlatformScreen::getFakeInputEndEvent(), 
     216                                                        m_inputFilter); 
     217        EVENTQUEUE->removeHandler(CEvent::kTimer, this); 
     218        stopSwitch(); 
     219 
     220        // force immediate disconnection of secondary clients 
     221        disconnect(); 
     222        for (COldClients::iterator index = m_oldClients.begin(); 
     223                                                        index != m_oldClients.begin(); ++index) { 
     224                CBaseClientProxy* client = index->first; 
     225                EVENTQUEUE->deleteTimer(index->second); 
     226                EVENTQUEUE->removeHandler(CEvent::kTimer, client); 
     227                EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); 
     228                delete client; 
     229        } 
     230 
     231        // remove input filter 
     232        m_inputFilter->setPrimaryClient(NULL); 
     233 
     234        // disable and disconnect primary client 
     235        m_primaryClient->disable(); 
     236        removeClient(m_primaryClient); 
     237} 
     238 
     239bool 
     240CServer::setConfig(const CConfig& config) 
     241{ 
     242        // refuse configuration if it doesn't include the primary screen 
     243        if (!config.isScreen(m_primaryClient->getName())) { 
     244                return false; 
     245        } 
     246 
     247        // close clients that are connected but being dropped from the 
     248        // configuration. 
     249        closeClients(config); 
     250 
     251        // cut over 
     252        m_config = config; 
     253        processOptions(); 
     254 
     255        // add ScrollLock as a hotkey to lock to the screen.  this was a 
     256        // built-in feature in earlier releases and is now supported via 
     257        // the user configurable hotkey mechanism.  if the user has already 
     258        // registered ScrollLock for something else then that will win but 
     259        // we will unfortunately generate a warning.  if the user has 
     260        // configured a CLockCursorToScreenAction then we don't add 
     261        // ScrollLock as a hotkey. 
     262        if (!m_config.hasLockToScreenAction()) { 
     263                IPlatformScreen::CKeyInfo* key = 
     264                        IPlatformScreen::CKeyInfo::alloc(kKeyScrollLock, 0, 0, 0); 
     265                CInputFilter::CRule rule(new CInputFilter::CKeystrokeCondition(key)); 
     266                rule.adoptAction(new CInputFilter::CLockCursorToScreenAction, true); 
     267                m_inputFilter->addFilterRule(rule); 
     268        } 
     269 
     270        // tell primary screen about reconfiguration 
     271        m_primaryClient->reconfigure(getActivePrimarySides()); 
     272 
     273        // tell all (connected) clients about current options 
     274        for (CClientList::const_iterator index = m_clients.begin(); 
     275                                                                index != m_clients.end(); ++index) { 
     276                CBaseClientProxy* client = index->second; 
     277                sendOptions(client); 
     278        } 
     279 
     280        return true; 
     281} 
     282 
     283void 
     284CServer::adoptClient(CBaseClientProxy* client) 
     285{ 
     286        assert(client != NULL); 
     287 
     288        // watch for client disconnection 
     289        EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client, 
     290                                                        new TMethodEventJob<CServer>(this, 
     291                                                                &CServer::handleClientDisconnected, client)); 
     292 
     293        // name must be in our configuration 
     294        if (!m_config.isScreen(client->getName())) { 
     295                LOG((CLOG_WARN "a client with name \"%s\" is not in the map", client->getName().c_str())); 
     296                closeClient(client, kMsgEUnknown); 
     297                return; 
     298        } 
     299 
     300        // add client to client list 
     301        if (!addClient(client)) { 
     302                // can only have one screen with a given name at any given time 
     303                LOG((CLOG_WARN "a client with name \"%s\" is already connected", getName(client).c_str())); 
     304                closeClient(client, kMsgEBusy); 
     305                return; 
     306        } 
     307        LOG((CLOG_NOTE "client \"%s\" has connected", getName(client).c_str())); 
     308 
     309        // send configuration options to client 
     310        sendOptions(client); 
     311 
     312        // activate screen saver on new client if active on the primary screen 
     313        if (m_activeSaver != NULL) { 
     314                client->screensaver(true); 
     315        } 
     316 
     317        // send notification 
     318        CServer::CScreenConnectedInfo* info = 
     319                CServer::CScreenConnectedInfo::alloc(getName(client)); 
     320        EVENTQUEUE->addEvent(CEvent(CServer::getConnectedEvent(), 
     321                                                                m_primaryClient->getEventTarget(), info)); 
     322} 
     323 
     324void 
     325CServer::disconnect() 
     326{ 
     327        // close all secondary clients 
     328        if (m_clients.size() > 1 || !m_oldClients.empty()) { 
     329                CConfig emptyConfig; 
     330                closeClients(emptyConfig); 
     331        } 
     332        else { 
     333                EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this)); 
     334        } 
     335} 
     336 
     337UInt32 
     338CServer::getNumClients() const 
     339{ 
     340        return m_clients.size(); 
     341} 
     342 
     343void 
     344CServer::getClients(std::vector<CString>& list) const 
     345{ 
     346        list.clear(); 
     347        for (CClientList::const_iterator index = m_clients.begin(); 
     348                                                        index != m_clients.end(); ++index) { 
     349                list.push_back(index->first); 
     350        } 
     351} 
     352 
     353CEvent::Type 
     354CServer::getErrorEvent() 
     355{ 
     356        return CEvent::registerTypeOnce(s_errorEvent, 
     357                                                        "CServer::error"); 
     358} 
     359 
     360CEvent::Type 
     361CServer::getConnectedEvent() 
     362{ 
     363        return CEvent::registerTypeOnce(s_connectedEvent, 
     364                                                        "CServer::connected"); 
     365} 
     366 
     367CEvent::Type 
     368CServer::getDisconnectedEvent() 
     369{ 
     370        return CEvent::registerTypeOnce(s_disconnectedEvent, 
     371                                                        "CServer::disconnected"); 
     372} 
     373 
     374CEvent::Type 
     375CServer::getSwitchToScreenEvent() 
     376{ 
     377        return CEvent::registerTypeOnce(s_switchToScreen, 
     378                                                        "CServer::switchToScreen"); 
     379} 
     380 
     381CEvent::Type 
     382CServer::getSwitchInDirectionEvent() 
     383{ 
     384        return CEvent::registerTypeOnce(s_switchInDirection, 
     385                                                        "CServer::switchInDirection"); 
     386} 
     387 
     388CEvent::Type 
     389CServer::getLockCursorToScreenEvent() 
     390{ 
     391        return CEvent::registerTypeOnce(s_lockCursorToScreen, 
     392                                                        "CServer::lockCursorToScreen"); 
     393} 
     394 
     395CString 
     396CServer::getName(const CBaseClientProxy* client) const 
     397{ 
     398        CString name = m_config.getCanonicalName(client->getName()); 
     399        if (name.empty()) { 
     400                name = client->getName(); 
     401        } 
     402        return name; 
     403} 
     404 
     405UInt32 
     406CServer::getActivePrimarySides() const 
     407{ 
     408        UInt32 sides = 0; 
     409        if (!isLockedToScreenServer()) { 
     410                if (hasAnyNeighbor(m_primaryClient, kLeft)) { 
     411                        sides |= kLeftMask; 
     412                } 
     413                if (hasAnyNeighbor(m_primaryClient, kRight)) { 
     414                        sides |= kRightMask; 
     415                } 
     416                if (hasAnyNeighbor(m_primaryClient, kTop)) { 
     417                        sides |= kTopMask; 
     418                } 
     419                if (hasAnyNeighbor(m_primaryClient, kBottom)) { 
     420                        sides |= kBottomMask; 
     421                } 
     422        } 
     423        return sides; 
     424} 
     425 
     426bool 
     427CServer::isLockedToScreenServer() const 
     428{ 
     429        // locked if scroll-lock is toggled on 
     430        return m_lockedToScreen; 
     431} 
     432 
     433bool 
     434CServer::isLockedToScreen() const 
     435{ 
     436        // locked if we say we're locked 
     437        if (isLockedToScreenServer()) { 
     438                LOG((CLOG_DEBUG "locked to screen")); 
     439                return true; 
     440        } 
     441 
     442        // locked if primary says we're locked 
     443        if (m_primaryClient->isLockedToScreen()) { 
     444                return true; 
     445        } 
     446 
     447        // not locked 
     448        return false; 
     449} 
     450 
     451SInt32 
     452CServer::getJumpZoneSize(CBaseClientProxy* client) const 
     453{ 
     454        if (client == m_primaryClient) { 
     455                return m_primaryClient->getJumpZoneSize(); 
     456        } 
     457        else { 
     458                return 0; 
     459        } 
     460} 
     461 
     462void 
     463CServer::switchScreen(CBaseClientProxy* dst, 
     464                                SInt32 x, SInt32 y, bool forScreensaver) 
     465{ 
     466        assert(dst != NULL); 
     467#ifndef NDEBUG 
     468        { 
     469                SInt32 dx, dy, dw, dh; 
     470                dst->getShape(dx, dy, dw, dh); 
     471                assert(x >= dx && y >= dy && x < dx + dw && y < dy + dh); 
     472        } 
     473#endif 
     474        assert(m_active != NULL); 
     475 
     476        LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", getName(m_active).c_str(), getName(dst).c_str(), x, y)); 
     477 
     478        // stop waiting to switch 
     479        stopSwitch(); 
     480 
     481        // record new position 
     482        m_x       = x; 
     483        m_y       = y; 
     484        m_xDelta  = 0; 
     485        m_yDelta  = 0; 
     486        m_xDelta2 = 0; 
     487        m_yDelta2 = 0; 
     488 
     489        // wrapping means leaving the active screen and entering it again. 
     490        // since that's a waste of time we skip that and just warp the 
     491        // mouse. 
     492        if (m_active != dst) { 
     493                // leave active screen 
     494                if (!m_active->leave()) { 
     495                        // cannot leave screen 
     496                        LOG((CLOG_WARN "can't leave screen")); 
     497                        return; 
     498                } 
     499 
     500                // update the primary client's clipboards if we're leaving the 
     501                // primary screen. 
     502                if (m_active == m_primaryClient) { 
     503                        for (ClipboardID id = 0; id < kClipboardEnd; ++id) { 
     504                                CClipboardInfo& clipboard = m_clipboards[id]; 
     505                                if (clipboard.m_clipboardOwner == getName(m_primaryClient)) { 
     506                                        onClipboardChanged(m_primaryClient, 
     507                                                id, clipboard.m_clipboardSeqNum); 
     508                                } 
     509                        } 
     510                } 
     511 
     512                // cut over 
     513                m_active = dst; 
     514 
     515                // increment enter sequence number 
     516                ++m_seqNum; 
     517 
     518                // enter new screen 
     519                m_active->enter(x, y, m_seqNum, 
     520                                                                m_primaryClient->getToggleMask(), 
     521                                                                forScreensaver); 
     522 
     523                // send the clipboard data to new active screen 
     524                for (ClipboardID id = 0; id < kClipboardEnd; ++id) { 
     525                        m_active->setClipboard(id, &m_clipboards[id].m_clipboard); 
     526                } 
     527        } 
     528        else { 
     529                m_active->mouseMove(x, y); 
     530        } 
     531} 
     532 
     533void 
     534CServer::jumpToScreen(CBaseClientProxy* newScreen) 
     535{ 
     536        assert(newScreen != NULL); 
     537 
     538        // record the current cursor position on the active screen 
     539        m_active->setJumpCursorPos(m_x, m_y); 
     540 
     541        // get the last cursor position on the target screen 
     542        SInt32 x, y; 
     543        newScreen->getJumpCursorPos(x, y); 
     544         
     545        switchScreen(newScreen, x, y, false); 
     546} 
     547 
     548float 
     549CServer::mapToFraction(CBaseClientProxy* client, 
     550                                EDirection dir, SInt32 x, SInt32 y) const 
     551{ 
     552        SInt32 sx, sy, sw, sh; 
     553        client->getShape(sx, sy, sw, sh); 
     554        switch (dir) { 
     555        case kLeft: 
     556        case kRight: 
     557                return static_cast<float>(y - sy + 0.5f) / static_cast<float>(sh); 
     558 
     559        case kTop: 
     560        case kBottom: 
     561                return static_cast<float>(x - sx + 0.5f) / static_cast<float>(sw); 
     562 
     563        case kNoDirection: 
     564                assert(0 && "bad direction"); 
     565                break; 
     566        } 
     567        return 0.0f; 
     568} 
     569 
     570void 
     571CServer::mapToPixel(CBaseClientProxy* client, 
     572                                EDirection dir, float f, SInt32& x, SInt32& y) const 
     573{ 
     574        SInt32 sx, sy, sw, sh; 
     575        client->getShape(sx, sy, sw, sh); 
     576        switch (dir) { 
     577        case kLeft: 
     578        case kRight: 
     579                y = static_cast<SInt32>(f * sh) + sy; 
     580                break; 
     581 
     582        case kTop: 
     583        case kBottom: 
     584                x = static_cast<SInt32>(f * sw) + sx; 
     585                break; 
     586 
     587        case kNoDirection: 
     588                assert(0 && "bad direction"); 
     589                break; 
     590        } 
     591} 
     592 
     593bool 
     594CServer::hasAnyNeighbor(CBaseClientProxy* client, EDirection dir) const 
     595{ 
     596        assert(client != NULL); 
     597 
     598        return m_config.hasNeighbor(getName(client), dir); 
     599} 
     600 
     601CBaseClientProxy* 
     602CServer::getNeighbor(CBaseClientProxy* src, 
     603                                EDirection dir, SInt32& x, SInt32& y) const 
     604{ 
     605        // note -- must be locked on entry 
     606 
     607        assert(src != NULL); 
     608 
     609        // get source screen name 
     610        CString srcName = getName(src); 
     611        assert(!srcName.empty()); 
     612        LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); 
     613 
     614        // convert position to fraction 
     615        float t = mapToFraction(src, dir, x, y); 
     616 
     617        // search for the closest neighbor that exists in direction dir 
     618        float tTmp; 
     619        for (;;) { 
     620                CString dstName(m_config.getNeighbor(srcName, dir, t, &tTmp)); 
     621 
     622                // if nothing in that direction then return NULL. if the 
     623                // destination is the source then we can make no more 
     624                // progress in this direction.  since we haven't found a 
     625                // connected neighbor we return NULL. 
     626                if (dstName.empty()) { 
     627                        LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); 
     628                        return NULL; 
     629                } 
     630 
     631                // look up neighbor cell.  if the screen is connected and 
     632                // ready then we can stop. 
     633                CClientList::const_iterator index = m_clients.find(dstName); 
     634                if (index != m_clients.end()) { 
     635                        LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\" at %f", dstName.c_str(), CConfig::dirName(dir), srcName.c_str(), t)); 
     636                        mapToPixel(index->second, dir, tTmp, x, y); 
     637                        return index->second; 
     638                } 
     639 
     640                // skip over unconnected screen 
     641                LOG((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); 
     642                srcName = dstName; 
     643 
     644                // use position on skipped screen 
     645                t = tTmp; 
     646        } 
     647} 
     648 
     649CBaseClientProxy* 
     650CServer::mapToNeighbor(CBaseClientProxy* src, 
     651                                EDirection srcSide, SInt32& x, SInt32& y) const 
     652{ 
     653        // note -- must be locked on entry 
     654 
     655        assert(src != NULL); 
     656 
     657        // get the first neighbor 
     658        CBaseClientProxy* dst = getNeighbor(src, srcSide, x, y); 
     659        if (dst == NULL) { 
     660                return NULL; 
     661        } 
     662 
     663        // get the source screen's size 
     664        SInt32 dx, dy, dw, dh; 
     665        CBaseClientProxy* lastGoodScreen = src; 
     666        lastGoodScreen->getShape(dx, dy, dw, dh); 
     667 
     668        // find destination screen, adjusting x or y (but not both).  the 
     669        // searches are done in a sort of canonical screen space where 
     670        // the upper-left corner is 0,0 for each screen.  we adjust from 
     671        // actual to canonical position on entry to and from canonical to 
     672        // actual on exit from the search. 
     673        switch (srcSide) { 
     674        case kLeft: 
     675                x -= dx; 
     676                while (dst != NULL) { 
     677                        lastGoodScreen = dst; 
     678                        lastGoodScreen->getShape(dx, dy, dw, dh); 
     679                        x += dw; 
     680                        if (x >= 0) { 
     681                                break; 
     682                        } 
     683                        LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); 
     684                        dst = getNeighbor(lastGoodScreen, srcSide, x, y); 
     685                } 
     686                assert(lastGoodScreen != NULL); 
     687                x += dx; 
     688                break; 
     689 
     690        case kRight: 
     691                x -= dx; 
     692                while (dst != NULL) { 
     693                        x -= dw; 
     694                        lastGoodScreen = dst; 
     695                        lastGoodScreen->getShape(dx, dy, dw, dh); 
     696                        if (x < dw) { 
     697                                break; 
     698                        } 
     699                        LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); 
     700                        dst = getNeighbor(lastGoodScreen, srcSide, x, y); 
     701                } 
     702                assert(lastGoodScreen != NULL); 
     703                x += dx; 
     704                break; 
     705 
     706        case kTop: 
     707                y -= dy; 
     708                while (dst != NULL) { 
     709                        lastGoodScreen = dst; 
     710                        lastGoodScreen->getShape(dx, dy, dw, dh); 
     711                        y += dh; 
     712                        if (y >= 0) { 
     713                                break; 
     714                        } 
     715                        LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); 
     716                        dst = getNeighbor(lastGoodScreen, srcSide, x, y); 
     717                } 
     718                assert(lastGoodScreen != NULL); 
     719                y += dy; 
     720                break; 
     721 
     722        case kBottom: 
     723                y -= dy; 
     724                while (dst != NULL) { 
     725                        y -= dh; 
     726                        lastGoodScreen = dst; 
     727                        lastGoodScreen->getShape(dx, dy, dw, dh); 
     728                        if (y < dh) { 
     729                                break; 
     730                        } 
     731                        LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); 
     732                        dst = getNeighbor(lastGoodScreen, srcSide, x, y); 
     733                } 
     734                assert(lastGoodScreen != NULL); 
     735                y += dy; 
     736                break; 
     737 
     738        case kNoDirection: 
     739                assert(0 && "bad direction"); 
     740                return NULL; 
     741        } 
     742 
     743        // save destination screen 
     744        assert(lastGoodScreen != NULL); 
     745        dst = lastGoodScreen; 
     746 
     747        // if entering primary screen then be sure to move in far enough 
     748        // to avoid the jump zone.  if entering a side that doesn't have 
     749        // a neighbor (i.e. an asymmetrical side) then we don't need to 
     750        // move inwards because that side can't provoke a jump. 
     751        avoidJumpZone(dst, srcSide, x, y); 
     752 
     753        return dst; 
     754} 
     755 
     756void 
     757CServer::avoidJumpZone(CBaseClientProxy* dst, 
     758                                EDirection dir, SInt32& x, SInt32& y) const 
     759{ 
     760        // we only need to avoid jump zones on the primary screen 
     761        if (dst != m_primaryClient) { 
     762                return; 
     763        } 
     764 
     765        const CString dstName(getName(dst)); 
     766        SInt32 dx, dy, dw, dh; 
     767        dst->getShape(dx, dy, dw, dh); 
     768        float t = mapToFraction(dst, dir, x, y); 
     769        SInt32 z = getJumpZoneSize(dst); 
     770 
     771        // move in far enough to avoid the jump zone.  if entering a side 
     772        // that doesn't have a neighbor (i.e. an asymmetrical side) then we 
     773        // don't need to move inwards because that side can't provoke a jump. 
     774        switch (dir) { 
     775        case kLeft: 
     776                if (!m_config.getNeighbor(dstName, kRight, t, NULL).empty() && 
     777                        x > dx + dw - 1 - z) 
     778                        x = dx + dw - 1 - z; 
     779                break; 
     780 
     781        case kRight: 
     782                if (!m_config.getNeighbor(dstName, kLeft, t, NULL).empty() && 
     783                        x < dx + z) 
     784                        x = dx + z; 
     785                break; 
     786 
     787        case kTop: 
     788                if (!m_config.getNeighbor(dstName, kBottom, t, NULL).empty() && 
     789                        y > dy + dh - 1 - z) 
     790                        y = dy + dh - 1 - z; 
     791                break; 
     792 
     793        case kBottom: 
     794                if (!m_config.getNeighbor(dstName, kTop, t, NULL).empty() && 
     795                        y < dy + z) 
     796                        y = dy + z; 
     797                break; 
     798 
     799        case kNoDirection: 
     800                assert(0 && "bad direction"); 
     801        } 
     802} 
     803 
     804bool 
     805CServer::isSwitchOkay(CBaseClientProxy* newScreen, 
     806                                EDirection dir, SInt32 x, SInt32 y, 
     807                                SInt32 xActive, SInt32 yActive) 
     808{ 
     809        LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", getName(m_active).c_str(), CConfig::dirName(dir))); 
     810 
     811        // is there a neighbor? 
     812        if (newScreen == NULL) { 
     813                // there's no neighbor.  we don't want to switch and we don't 
     814                // want to try to switch later. 
     815                LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir))); 
     816                stopSwitch(); 
     817                return false; 
     818        } 
     819 
     820        // should we switch or not? 
     821        bool preventSwitch = false; 
     822        bool allowSwitch   = false; 
     823 
     824        // note if the switch direction has changed.  save the new 
     825        // direction and screen if so. 
     826        bool isNewDirection  = (dir != m_switchDir); 
     827        if (isNewDirection || m_switchScreen == NULL) { 
     828                m_switchDir    = dir; 
     829                m_switchScreen = newScreen; 
     830        } 
     831 
     832        // is this a double tap and do we care? 
     833        if (!allowSwitch && m_switchTwoTapDelay > 0.0) { 
     834                if (isNewDirection || 
     835                        !isSwitchTwoTapStarted() || !shouldSwitchTwoTap()) { 
     836                        // tapping a different or new edge or second tap not 
     837                        // fast enough.  prepare for second tap. 
     838                        preventSwitch = true; 
     839                        startSwitchTwoTap(); 
     840                } 
     841                else { 
     842                        // got second tap 
     843                        allowSwitch = true; 
     844                } 
     845        } 
     846 
     847        // if waiting before a switch then prepare to switch later 
     848        if (!allowSwitch && m_switchWaitDelay > 0.0) { 
     849                if (isNewDirection || !isSwitchWaitStarted()) { 
     850                        startSwitchWait(x, y); 
     851                } 
     852                preventSwitch = true; 
     853        } 
     854 
     855        // are we in a locked corner?  first check if screen has the option set 
     856        // and, if not, check the global options. 
     857        const CConfig::CScreenOptions* options = 
     858                                                m_config.getOptions(getName(m_active)); 
     859        if (options == NULL || options->count(kOptionScreenSwitchCorners) == 0) { 
     860                options = m_config.getOptions(""); 
     861        } 
     862        if (options != NULL && options->count(kOptionScreenSwitchCorners) > 0) { 
     863                // get corner mask and size 
     864                CConfig::CScreenOptions::const_iterator i = 
     865                        options->find(kOptionScreenSwitchCorners); 
     866                UInt32 corners = static_cast<UInt32>(i->second); 
     867                i = options->find(kOptionScreenSwitchCornerSize); 
     868                SInt32 size = 0; 
     869                if (i != options->end()) { 
     870                        size = i->second; 
     871                } 
     872 
     873                // see if we're in a locked corner 
     874                if ((getCorner(m_active, xActive, yActive, size) & corners) != 0) { 
     875                        // yep, no switching 
     876                        LOG((CLOG_DEBUG1 "locked in corner")); 
     877                        preventSwitch = true; 
     878                        stopSwitch(); 
     879                } 
     880        } 
     881 
     882        // ignore if mouse is locked to screen and don't try to switch later 
     883        if (!preventSwitch && isLockedToScreen()) { 
     884                LOG((CLOG_DEBUG1 "locked to screen")); 
     885                preventSwitch = true; 
     886                stopSwitch(); 
     887        } 
     888 
     889        return !preventSwitch; 
     890} 
     891 
     892void 
     893CServer::noSwitch(SInt32 x, SInt32 y) 
     894{ 
     895        armSwitchTwoTap(x, y); 
     896        stopSwitchWait(); 
     897} 
     898 
     899void 
     900CServer::stopSwitch() 
     901{ 
     902        if (m_switchScreen != NULL) { 
     903                m_switchScreen = NULL; 
     904                m_switchDir    = kNoDirection; 
     905                stopSwitchTwoTap(); 
     906                stopSwitchWait(); 
     907        } 
     908} 
     909 
     910void 
     911CServer::startSwitchTwoTap() 
     912{ 
     913        m_switchTwoTapEngaged = true; 
     914        m_switchTwoTapArmed   = false; 
     915        m_switchTwoTapTimer.reset(); 
     916        LOG((CLOG_DEBUG1 "waiting for second tap")); 
     917} 
     918 
     919void 
     920CServer::armSwitchTwoTap(SInt32 x, SInt32 y) 
     921{ 
     922        if (m_switchTwoTapEngaged) { 
     923                if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) { 
     924                        // second tap took too long.  disengage. 
     925                        stopSwitchTwoTap(); 
     926                } 
     927                else if (!m_switchTwoTapArmed) { 
     928                        // still time for a double tap.  see if we left the tap 
     929                        // zone and, if so, arm the two tap. 
     930                        SInt32 ax, ay, aw, ah; 
     931                        m_active->getShape(ax, ay, aw, ah); 
     932                        SInt32 tapZone = m_primaryClient->getJumpZoneSize(); 
     933                        if (tapZone < m_switchTwoTapZone) { 
     934                                tapZone = m_switchTwoTapZone; 
     935                        } 
     936                        if (x >= ax + tapZone && x < ax + aw - tapZone && 
     937                                y >= ay + tapZone && y < ay + ah - tapZone) { 
     938                                // win32 can generate bogus mouse events that appear to 
     939                                // move in the opposite direction that the mouse actually 
     940                                // moved.  try to ignore that crap here. 
     941                                switch (m_switchDir) { 
     942                                case kLeft: 
     943                                        m_switchTwoTapArmed = (m_xDelta > 0 && m_xDelta2 > 0); 
     944                                        break; 
     945 
     946                                case kRight: 
     947                                        m_switchTwoTapArmed = (m_xDelta < 0 && m_xDelta2 < 0); 
     948                                        break; 
     949 
     950                                case kTop: 
     951                                        m_switchTwoTapArmed = (m_yDelta > 0 && m_yDelta2 > 0); 
     952                                        break; 
     953 
     954                                case kBottom: 
     955                                        m_switchTwoTapArmed = (m_yDelta < 0 && m_yDelta2 < 0); 
     956                                        break; 
     957 
     958                                default: 
     959                                        break; 
     960                                } 
     961                        } 
     962                } 
     963        } 
     964} 
     965 
     966void 
     967CServer::stopSwitchTwoTap() 
     968{ 
     969        m_switchTwoTapEngaged = false; 
     970        m_switchTwoTapArmed   = false; 
     971} 
     972 
     973bool 
     974CServer::isSwitchTwoTapStarted() const 
     975{ 
     976        return m_switchTwoTapEngaged; 
     977} 
     978 
     979bool 
     980CServer::shouldSwitchTwoTap() const 
     981{ 
     982        // this is the second tap if two-tap is armed and this tap 
     983        // came fast enough 
     984        return (m_switchTwoTapArmed && 
     985                        m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay); 
     986} 
     987 
     988void 
     989CServer::startSwitchWait(SInt32 x, SInt32 y) 
     990{ 
     991        stopSwitchWait(); 
     992        m_switchWaitX     = x; 
     993        m_switchWaitY     = y; 
     994        m_switchWaitTimer = EVENTQUEUE->newOneShotTimer(m_switchWaitDelay, this); 
     995        LOG((CLOG_DEBUG1 "waiting to switch")); 
     996} 
     997 
     998void 
     999CServer::stopSwitchWait() 
     1000{ 
     1001        if (m_switchWaitTimer != NULL) { 
     1002                EVENTQUEUE->deleteTimer(m_switchWaitTimer); 
     1003                m_switchWaitTimer = NULL; 
     1004        } 
     1005} 
     1006 
     1007bool 
     1008CServer::isSwitchWaitStarted() const 
     1009{ 
     1010        return (m_switchWaitTimer != NULL); 
     1011} 
     1012 
     1013UInt32 
     1014CServer::getCorner(CBaseClientProxy* client, 
     1015                                SInt32 x, SInt32 y, SInt32 size) const 
     1016{ 
     1017        assert(client != NULL); 
     1018 
     1019        // get client screen shape 
     1020        SInt32 ax, ay, aw, ah; 
     1021        client->getShape(ax, ay, aw, ah); 
     1022 
     1023        // check for x,y on the left or right 
     1024        SInt32 xSide; 
     1025        if (x <= ax) { 
     1026                xSide = -1; 
     1027        } 
     1028        else if (x >= ax + aw - 1) { 
     1029                xSide = 1; 
     1030        } 
     1031        else { 
     1032                xSide = 0; 
     1033        } 
     1034 
     1035        // check for x,y on the top or bottom 
     1036        SInt32 ySide; 
     1037        if (y <= ay) { 
     1038                ySide = -1; 
     1039        } 
     1040        else if (y >= ay + ah - 1) { 
     1041                ySide = 1; 
     1042        } 
     1043        else { 
     1044                ySide = 0; 
     1045        } 
     1046 
     1047        // if against the left or right then check if y is within size 
     1048        if (xSide != 0) { 
     1049                if (y < ay + size) { 
     1050                        return (xSide < 0) ? kTopLeftMask : kTopRightMask; 
     1051                } 
     1052                else if (y >= ay + ah - size) { 
     1053                        return (xSide < 0) ? kBottomLeftMask : kBottomRightMask; 
     1054                } 
     1055        } 
     1056 
     1057        // if against the left or right then check if y is within size 
     1058        if (ySide != 0) { 
     1059                if (x < ax + size) { 
     1060                        return (ySide < 0) ? kTopLeftMask : kBottomLeftMask; 
     1061                } 
     1062                else if (x >= ax + aw - size) { 
     1063                        return (ySide < 0) ? kTopRightMask : kBottomRightMask; 
     1064                } 
     1065        } 
     1066 
     1067        return kNoCornerMask; 
     1068} 
     1069 
     1070void 
     1071CServer::stopRelativeMoves() 
     1072{ 
     1073        if (m_relativeMoves && m_active != m_primaryClient) { 
     1074                // warp to the center of the active client so we know where we are 
     1075                SInt32 ax, ay, aw, ah; 
     1076                m_active->getShape(ax, ay, aw, ah); 
     1077                m_x       = ax + (aw >> 1); 
     1078                m_y       = ay + (ah >> 1); 
     1079                m_xDelta  = 0; 
     1080                m_yDelta  = 0; 
     1081                m_xDelta2 = 0; 
     1082                m_yDelta2 = 0; 
     1083                LOG((CLOG_DEBUG2 "synchronize move on %s by %d,%d", getName(m_active).c_str(), m_x, m_y)); 
     1084                m_active->mouseMove(m_x, m_y); 
     1085        } 
     1086} 
     1087 
     1088void 
     1089CServer::sendOptions(CBaseClientProxy* client) const 
     1090{ 
     1091        COptionsList optionsList; 
     1092 
     1093        // look up options for client 
     1094        const CConfig::CScreenOptions* options = 
     1095                                                m_config.getOptions(getName(client)); 
     1096        if (options != NULL) { 
     1097                // convert options to a more convenient form for sending 
     1098                optionsList.reserve(2 * options->size()); 
     1099                for (CConfig::CScreenOptions::const_iterator index = options->begin(); 
     1100                                                                        index != options->end(); ++index) { 
     1101                        optionsList.push_back(index->first); 
     1102                        optionsList.push_back(static_cast<UInt32>(index->second)); 
     1103                } 
     1104        } 
     1105 
     1106        // look up global options 
     1107        options = m_config.getOptions(""); 
     1108        if (options != NULL) { 
     1109                // convert options to a more convenient form for sending 
     1110                optionsList.reserve(optionsList.size() + 2 * options->size()); 
     1111                for (CConfig::CScreenOptions::const_iterator index = options->begin(); 
     1112                                                                        index != options->end(); ++index) { 
     1113                        optionsList.push_back(index->first); 
     1114                        optionsList.push_back(static_cast<UInt32>(index->second)); 
     1115                } 
     1116        } 
     1117 
     1118        // send the options 
     1119        client->resetOptions(); 
     1120        client->setOptions(optionsList); 
     1121} 
     1122 
     1123void 
     1124CServer::processOptions() 
     1125{ 
     1126        const CConfig::CScreenOptions* options = m_config.getOptions(""); 
     1127        if (options == NULL) { 
     1128                return; 
     1129        } 
     1130 
     1131        bool newRelativeMoves = m_relativeMoves; 
     1132        for (CConfig::CScreenOptions::const_iterator index = options->begin(); 
     1133                                                                index != options->end(); ++index) { 
     1134                const OptionID id       = index->first; 
     1135                const OptionValue value = index->second; 
     1136                if (id == kOptionScreenSwitchDelay) { 
     1137                        m_switchWaitDelay = 1.0e-3 * static_cast<double>(value); 
     1138                        if (m_switchWaitDelay < 0.0) { 
     1139                                m_switchWaitDelay = 0.0; 
     1140                        } 
     1141                        stopSwitchWait(); 
     1142                } 
     1143                else if (id == kOptionScreenSwitchTwoTap) { 
     1144                        m_switchTwoTapDelay = 1.0e-3 * static_cast<double>(value); 
     1145                        if (m_switchTwoTapDelay < 0.0) { 
     1146                                m_switchTwoTapDelay = 0.0; 
     1147                        } 
     1148                        stopSwitchTwoTap(); 
     1149                } 
     1150                else if (id == kOptionRelativeMouseMoves) { 
     1151                        newRelativeMoves = (value != 0); 
     1152                } 
     1153        } 
     1154 
     1155        if (m_relativeMoves && !newRelativeMoves) { 
     1156                stopRelativeMoves(); 
     1157        } 
     1158        m_relativeMoves = newRelativeMoves; 
     1159} 
     1160 
     1161void 
     1162CServer::handleShapeChanged(const CEvent&, void* vclient) 
     1163{ 
     1164        // ignore events from unknown clients 
     1165        CBaseClientProxy* client = reinterpret_cast<CBaseClientProxy*>(vclient); 
     1166        if (m_clientSet.count(client) == 0) { 
     1167                return; 
     1168        } 
     1169 
     1170        LOG((CLOG_INFO "screen \"%s\" shape changed", getName(client).c_str())); 
     1171 
     1172        // update jump coordinate 
     1173        SInt32 x, y; 
     1174        client->getCursorPos(x, y); 
     1175        client->setJumpCursorPos(x, y); 
     1176 
     1177        // update the mouse coordinates 
     1178        if (client == m_active) { 
     1179                m_x = x; 
     1180                m_y = y; 
     1181        } 
     1182 
     1183        // handle resolution change to primary screen 
     1184        if (client == m_primaryClient) { 
     1185                if (client == m_active) { 
     1186                        onMouseMovePrimary(m_x, m_y); 
     1187                } 
     1188                else { 
     1189                        onMouseMoveSecondary(0, 0); 
     1190                } 
     1191        } 
     1192} 
     1193 
     1194void 
     1195CServer::handleClipboardGrabbed(const CEvent& event, void* vclient) 
     1196{ 
     1197        // ignore events from unknown clients 
     1198        CBaseClientProxy* grabber = reinterpret_cast<CBaseClientProxy*>(vclient); 
     1199        if (m_clientSet.count(grabber) == 0) { 
     1200                return; 
     1201        } 
     1202        const IScreen::CClipboardInfo* info = 
     1203                reinterpret_cast<const IScreen::CClipboardInfo*>(event.getData()); 
     1204 
     1205        // ignore grab if sequence number is old.  always allow primary 
     1206        // screen to grab. 
     1207        CClipboardInfo& clipboard = m_clipboards[info->m_id]; 
     1208        if (grabber != m_primaryClient && 
     1209                info->m_sequenceNumber < clipboard.m_clipboardSeqNum) { 
     1210                LOG((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", getName(grabber).c_str(), info->m_id)); 
     1211                return; 
     1212        } 
     1213 
     1214        // mark screen as owning clipboard 
     1215        LOG((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", getName(grabber).c_str(), info->m_id, clipboard.m_clipboardOwner.c_str())); 
     1216        clipboard.m_clipboardOwner  = getName(grabber); 
     1217        clipboard.m_clipboardSeqNum = info->m_sequenceNumber; 
     1218 
     1219        // clear the clipboard data (since it's not known at this point) 
     1220        if (clipboard.m_clipboard.open(0)) { 
     1221                clipboard.m_clipboard.empty(); 
     1222                clipboard.m_clipboard.close(); 
     1223        } 
     1224        clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); 
     1225 
     1226        // tell all other screens to take ownership of clipboard.  tell the 
     1227        // grabber that it's clipboard isn't dirty. 
     1228        for (CClientList::iterator index = m_clients.begin(); 
     1229                                                                index != m_clients.end(); ++index) { 
     1230                CBaseClientProxy* client = index->second; 
     1231                if (client == grabber) { 
     1232                        client->setClipboardDirty(info->m_id, false); 
     1233                } 
     1234                else { 
     1235                        client->grabClipboard(info->m_id); 
     1236                } 
     1237        } 
     1238} 
     1239 
     1240void 
     1241CServer::handleClipboardChanged(const CEvent& event, void* vclient) 
     1242{ 
     1243        // ignore events from unknown clients 
     1244        CBaseClientProxy* sender = reinterpret_cast<CBaseClientProxy*>(vclient); 
     1245        if (m_clientSet.count(sender) == 0) { 
     1246                return; 
     1247        } 
     1248        const IScreen::CClipboardInfo* info = 
     1249                reinterpret_cast<const IScreen::CClipboardInfo*>(event.getData()); 
     1250        onClipboardChanged(sender, info->m_id, info->m_sequenceNumber); 
     1251} 
     1252 
     1253void 
     1254CServer::handleKeyDownEvent(const CEvent& event, void*) 
     1255{ 
     1256        IPlatformScreen::CKeyInfo* info = 
     1257                reinterpret_cast<IPlatformScreen::CKeyInfo*>(event.getData()); 
     1258        onKeyDown(info->m_key, info->m_mask, info->m_button, info->m_screens); 
     1259} 
     1260 
     1261void 
     1262CServer::handleKeyUpEvent(const CEvent& event, void*) 
     1263{ 
     1264        IPlatformScreen::CKeyInfo* info = 
     1265                 reinterpret_cast<IPlatformScreen::CKeyInfo*>(event.getData()); 
     1266        onKeyUp(info->m_key, info->m_mask, info->m_button, info->m_screens); 
     1267} 
     1268 
     1269void 
     1270CServer::handleKeyRepeatEvent(const CEvent& event, void*) 
     1271{ 
     1272        IPlatformScreen::CKeyInfo* info = 
     1273                reinterpret_cast<IPlatformScreen::CKeyInfo*>(event.getData()); 
     1274        onKeyRepeat(info->m_key, info->m_mask, info->m_count, info->m_button); 
     1275} 
     1276 
     1277void 
     1278CServer::handleButtonDownEvent(const CEvent& event, void*) 
     1279{ 
     1280        IPlatformScreen::CButtonInfo* info = 
     1281                reinterpret_cast<IPlatformScreen::CButtonInfo*>(event.getData()); 
     1282        onMouseDown(info->m_button); 
     1283} 
     1284 
     1285void 
     1286CServer::handleButtonUpEvent(const CEvent& event, void*) 
     1287{ 
     1288        IPlatformScreen::CButtonInfo* info = 
     1289                reinterpret_cast<IPlatformScreen::CButtonInfo*>(event.getData()); 
     1290        onMouseUp(info->m_button); 
     1291} 
     1292 
     1293void 
     1294CServer::handleMotionPrimaryEvent(const CEvent& event, void*) 
     1295{ 
     1296        IPlatformScreen::CMotionInfo* info = 
     1297                reinterpret_cast<IPlatformScreen::CMotionInfo*>(event.getData()); 
     1298        onMouseMovePrimary(info->m_x, info->m_y); 
     1299} 
     1300 
     1301void 
     1302CServer::handleMotionSecondaryEvent(const CEvent& event, void*) 
     1303{ 
     1304        IPlatformScreen::CMotionInfo* info = 
     1305                reinterpret_cast<IPlatformScreen::CMotionInfo*>(event.getData()); 
     1306        onMouseMoveSecondary(info->m_x, info->m_y); 
     1307} 
     1308 
     1309void 
     1310CServer::handleWheelEvent(const CEvent& event, void*) 
     1311{ 
     1312        IPlatformScreen::CWheelInfo* info = 
     1313                reinterpret_cast<IPlatformScreen::CWheelInfo*>(event.getData()); 
     1314        onMouseWheel(info->m_xDelta, info->m_yDelta); 
     1315} 
     1316 
     1317void 
     1318CServer::handleScreensaverActivatedEvent(const CEvent&, void*) 
     1319{ 
     1320        onScreensaver(true); 
     1321} 
     1322 
     1323void 
     1324CServer::handleScreensaverDeactivatedEvent(const CEvent&, void*) 
     1325{ 
     1326        onScreensaver(false); 
     1327} 
     1328 
     1329void 
     1330CServer::handleSwitchWaitTimeout(const CEvent&, void*) 
     1331{ 
     1332        // ignore if mouse is locked to screen 
     1333        if (isLockedToScreen()) { 
     1334                LOG((CLOG_DEBUG1 "locked to screen")); 
     1335                stopSwitch(); 
     1336                return; 
     1337        } 
     1338 
     1339        // switch screen 
     1340        switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false); 
     1341} 
     1342 
     1343void 
     1344CServer::handleClientDisconnected(const CEvent&, void* vclient) 
     1345{ 
     1346        // client has disconnected.  it might be an old client or an 
     1347        // active client.  we don't care so just handle it both ways. 
     1348        CBaseClientProxy* client = reinterpret_cast<CBaseClientProxy*>(vclient); 
     1349        removeActiveClient(client); 
     1350        removeOldClient(client); 
     1351        delete client; 
     1352} 
     1353 
     1354void 
     1355CServer::handleClientCloseTimeout(const CEvent&, void* vclient) 
     1356{ 
     1357        // client took too long to disconnect.  just dump it. 
     1358        CBaseClientProxy* client = reinterpret_cast<CBaseClientProxy*>(vclient); 
     1359        LOG((CLOG_NOTE "forced disconnection of client \"%s\"", getName(client).c_str())); 
     1360        removeOldClient(client); 
     1361        delete client; 
     1362} 
     1363 
     1364void 
     1365CServer::handleSwitchToScreenEvent(const CEvent& event, void*) 
     1366{ 
     1367        CSwitchToScreenInfo* info =  
     1368                reinterpret_cast<CSwitchToScreenInfo*>(event.getData()); 
     1369 
     1370        CClientList::const_iterator index = m_clients.find(info->m_screen); 
     1371        if (index == m_clients.end()) { 
     1372                LOG((CLOG_DEBUG1 "screen \"%s\" not active", info->m_screen)); 
     1373        } 
     1374        else { 
     1375                jumpToScreen(index->second); 
     1376        } 
     1377} 
     1378 
     1379void 
     1380CServer::handleSwitchInDirectionEvent(const CEvent& event, void*) 
     1381{ 
     1382        CSwitchInDirectionInfo* info =  
     1383                reinterpret_cast<CSwitchInDirectionInfo*>(event.getData()); 
     1384 
     1385        // jump to screen in chosen direction from center of this screen 
     1386        SInt32 x = m_x, y = m_y; 
     1387        CBaseClientProxy* newScreen = 
     1388                getNeighbor(m_active, info->m_direction, x, y); 
     1389        if (newScreen == NULL) { 
     1390                LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(info->m_direction))); 
     1391        } 
     1392        else { 
     1393                jumpToScreen(newScreen); 
     1394        } 
     1395} 
     1396 
     1397void 
     1398CServer::handleLockCursorToScreenEvent(const CEvent& event, void*) 
     1399{ 
     1400        CLockCursorToScreenInfo* info = (CLockCursorToScreenInfo*)event.getData(); 
     1401 
     1402        // choose new state 
     1403        bool newState; 
     1404        switch (info->m_state) { 
     1405        case CLockCursorToScreenInfo::kOff: 
     1406                newState = false; 
     1407                break; 
     1408 
     1409        default: 
     1410        case CLockCursorToScreenInfo::kOn: 
     1411                newState = true; 
     1412                break; 
     1413 
     1414        case CLockCursorToScreenInfo::kToggle: 
     1415                newState = !m_lockedToScreen; 
     1416                break; 
     1417        } 
     1418 
     1419        // enter new state 
     1420        if (newState != m_lockedToScreen) { 
     1421                SInt32 x, y; 
     1422                m_lockedToScreen = newState; 
     1423 
     1424                LOG((CLOG_DEBUG "cursor %s current screen", m_lockedToScreen ? "locked to" : "unlocked from")); 
     1425 
     1426                m_primaryClient->reconfigure(getActivePrimarySides()); 
     1427                if (!isLockedToScreenServer()) { 
     1428                        //stopRelativeMoves(); 
     1429                } 
     1430        } 
     1431} 
     1432 
     1433void 
     1434CServer::handleFakeInputBeginEvent(const CEvent&, void*) 
     1435{ 
     1436        m_primaryClient->fakeInputBegin(); 
     1437} 
     1438 
     1439void 
     1440CServer::handleFakeInputEndEvent(const CEvent&, void*) 
     1441{ 
     1442        m_primaryClient->fakeInputEnd(); 
     1443} 
     1444 
     1445void 
     1446CServer::onClipboardChanged(CBaseClientProxy* sender, 
     1447                                ClipboardID id, UInt32 seqNum) 
     1448{ 
     1449        CClipboardInfo& clipboard = m_clipboards[id]; 
     1450 
     1451        // ignore update if sequence number is old 
     1452        if (seqNum < clipboard.m_clipboardSeqNum) { 
     1453                LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", getName(sender).c_str(), id)); 
     1454                return; 
     1455        } 
     1456 
     1457        // should be the expected client 
     1458        assert(sender == m_clients.find(clipboard.m_clipboardOwner)->second); 
     1459 
     1460        // get data 
     1461        sender->getClipboard(id, &clipboard.m_clipboard); 
     1462 
     1463        // ignore if data hasn't changed 
     1464        CString data = clipboard.m_clipboard.marshall(); 
     1465        if (data == clipboard.m_clipboardData) { 
     1466                LOG((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id)); 
     1467                return; 
     1468        } 
     1469 
     1470        // got new data 
     1471        LOG((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); 
     1472        clipboard.m_clipboardData = data; 
     1473 
     1474        // tell all clients except the sender that the clipboard is dirty 
     1475        for (CClientList::const_iterator index = m_clients.begin(); 
     1476                                                                index != m_clients.end(); ++index) { 
     1477                CBaseClientProxy* client = index->second; 
     1478                client->setClipboardDirty(id, client != sender); 
     1479        } 
     1480 
     1481        // send the new clipboard to the active screen 
     1482        m_active->setClipboard(id, &clipboard.m_clipboard); 
     1483} 
     1484 
     1485void 
     1486CServer::onScreensaver(bool activated) 
     1487{ 
     1488        LOG((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); 
     1489 
     1490        if (activated) { 
     1491                // save current screen and position 
     1492                m_activeSaver = m_active; 
     1493                m_xSaver      = m_x; 
     1494                m_ySaver      = m_y; 
     1495 
     1496                // jump to primary screen 
     1497                if (m_active != m_primaryClient) { 
     1498                        switchScreen(m_primaryClient, 0, 0, true); 
     1499                } 
     1500        } 
     1501        else { 
     1502                // jump back to previous screen and position.  we must check 
     1503                // that the position is still valid since the screen may have 
     1504                // changed resolutions while the screen saver was running. 
     1505                if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { 
     1506                        // check position 
     1507                        CBaseClientProxy* screen = m_activeSaver; 
     1508                        SInt32 x, y, w, h; 
     1509                        screen->getShape(x, y, w, h); 
     1510                        SInt32 zoneSize = getJumpZoneSize(screen); 
     1511                        if (m_xSaver < x + zoneSize) { 
     1512                                m_xSaver = x + zoneSize; 
     1513                        } 
     1514                        else if (m_xSaver >= x + w - zoneSize) { 
     1515                                m_xSaver = x + w - zoneSize - 1; 
     1516                        } 
     1517                        if (m_ySaver < y + zoneSize) { 
     1518                                m_ySaver = y + zoneSize; 
     1519                        } 
     1520                        else if (m_ySaver >= y + h - zoneSize) { 
     1521                                m_ySaver = y + h - zoneSize - 1; 
     1522                        } 
     1523 
     1524                        // jump 
     1525                        switchScreen(screen, m_xSaver, m_ySaver, false); 
     1526                } 
     1527 
     1528                // reset state 
     1529                m_activeSaver = NULL; 
     1530        } 
     1531 
     1532        // send message to all clients 
     1533        for (CClientList::const_iterator index = m_clients.begin(); 
     1534                                                                index != m_clients.end(); ++index) { 
     1535                CBaseClientProxy* client = index->second; 
     1536                client->screensaver(activated); 
     1537        } 
     1538} 
     1539 
     1540void 
     1541CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button, 
     1542                                const char* screens) 
     1543{ 
     1544        LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); 
     1545        assert(m_active != NULL); 
     1546 
     1547        // relay 
     1548        if (IKeyState::CKeyInfo::isDefault(screens)) { 
     1549                if (m_active != m_primaryClient) 
     1550                { 
     1551                        m_active->keyDown(id, mask, button); 
     1552                } 
     1553                else 
     1554                {  
     1555                        if (id == 96 && mask == 0 && button == 0x0033) 
     1556                        { 
     1557                                if (m_broadcast) 
     1558                                { 
     1559                                        m_broadcast = false; 
     1560                                        AlertSoundPlayCustomSound(m_broadcastOffID); 
     1561                                } 
     1562                                else 
     1563                                { 
     1564                                        m_broadcast = true; 
     1565                                        AlertSoundPlayCustomSound(m_broadcastOnID); 
     1566                                } 
     1567                        } 
     1568                        if (m_broadcast) 
     1569                        { 
     1570                                for (CClientList::const_iterator index = m_clients.begin(); 
     1571                                                 index != m_clients.end(); ++index) { 
     1572                                        index->second->keyDown(id, mask, button); 
     1573                                } 
     1574                        } 
     1575                } 
     1576        } 
     1577        else { 
     1578                for (CClientList::const_iterator index = m_clients.begin(); 
     1579                                                                index != m_clients.end(); ++index) { 
     1580                        if (IKeyState::CKeyInfo::contains(screens, index->first)) { 
     1581                                index->second->keyDown(id, mask, button); 
     1582                        } 
     1583                } 
     1584        } 
     1585} 
     1586 
     1587void 
     1588CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button, 
     1589                                const char* screens) 
     1590{ 
     1591        LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); 
     1592        assert(m_active != NULL); 
     1593 
     1594        // relay 
     1595        if (IKeyState::CKeyInfo::isDefault(screens)) { 
     1596                if (m_active != m_primaryClient) 
     1597                { 
     1598                        m_active->keyUp(id, mask, button); 
     1599                } 
     1600                else if (m_broadcast) 
     1601                { 
     1602                        for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { 
     1603                                index->second->keyUp(id, mask, button); 
     1604                        } 
     1605                } 
     1606        } 
     1607        else { 
     1608                for (CClientList::const_iterator index = m_clients.begin(); 
     1609                                                                index != m_clients.end(); ++index) { 
     1610                        if (IKeyState::CKeyInfo::contains(screens, index->first)) { 
     1611                                index->second->keyUp(id, mask, button); 
     1612                        } 
     1613                } 
     1614        } 
     1615} 
     1616 
     1617void 
     1618CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, 
     1619                                SInt32 count, KeyButton button) 
     1620{ 
     1621        LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); 
     1622        assert(m_active != NULL); 
     1623 
     1624        // relay 
     1625        if (m_active != m_primaryClient) 
     1626        { 
     1627                m_active->keyRepeat(id, mask, count, button); 
     1628        } 
     1629        else if (m_broadcast) 
     1630        { 
     1631                for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { 
     1632                        index->second->keyRepeat(id, mask, count, button); 
     1633                } 
     1634        } 
     1635} 
     1636 
     1637void 
     1638CServer::onMouseDown(ButtonID id) 
     1639{ 
     1640        LOG((CLOG_DEBUG1 "onMouseDown id=%d", id)); 
     1641        assert(m_active != NULL); 
     1642 
     1643        // relay 
     1644        m_active->mouseDown(id); 
     1645} 
     1646 
     1647void 
     1648CServer::onMouseUp(ButtonID id) 
     1649{ 
     1650        LOG((CLOG_DEBUG1 "onMouseUp id=%d", id)); 
     1651        assert(m_active != NULL); 
     1652 
     1653        // relay 
     1654        m_active->mouseUp(id); 
     1655} 
     1656 
     1657bool 
     1658CServer::onMouseMovePrimary(SInt32 x, SInt32 y) 
     1659{ 
     1660        LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); 
     1661 
     1662        // mouse move on primary (server's) screen 
     1663        if (m_active != m_primaryClient) { 
     1664                // stale event -- we're actually on a secondary screen 
     1665                return false; 
     1666        } 
     1667 
     1668        // save last delta 
     1669        m_xDelta2 = m_xDelta; 
     1670        m_yDelta2 = m_yDelta; 
     1671 
     1672        // save current delta 
     1673        m_xDelta  = x - m_x; 
     1674        m_yDelta  = y - m_y; 
     1675 
     1676        // save position 
     1677        m_x       = x; 
     1678        m_y       = y; 
     1679 
     1680        // get screen shape 
     1681        SInt32 ax, ay, aw, ah; 
     1682        m_active->getShape(ax, ay, aw, ah); 
     1683        SInt32 zoneSize = getJumpZoneSize(m_active); 
     1684 
     1685        // clamp position to screen 
     1686        SInt32 xc = x, yc = y; 
     1687        if (xc < ax + zoneSize) { 
     1688                xc = ax; 
     1689        } 
     1690        else if (xc >= ax + aw - zoneSize) { 
     1691                xc = ax + aw - 1; 
     1692        } 
     1693        if (yc < ay + zoneSize) { 
     1694                yc = ay; 
     1695        } 
     1696        else if (yc >= ay + ah - zoneSize) { 
     1697                yc = ay + ah - 1; 
     1698        } 
     1699 
     1700        // see if we should change screens 
     1701        EDirection dir; 
     1702        if (x < ax + zoneSize) { 
     1703                x  -= zoneSize; 
     1704                dir = kLeft; 
     1705        } 
     1706        else if (x >= ax + aw - zoneSize) { 
     1707                x  += zoneSize; 
     1708                dir = kRight; 
     1709        } 
     1710        else if (y < ay + zoneSize) { 
     1711                y  -= zoneSize; 
     1712                dir = kTop; 
     1713        } 
     1714        else if (y >= ay + ah - zoneSize) { 
     1715                y  += zoneSize; 
     1716                dir = kBottom; 
     1717        } 
     1718        else { 
     1719                // still on local screen 
     1720                noSwitch(x, y); 
     1721                return false; 
     1722        } 
     1723 
     1724        // get jump destination 
     1725        CBaseClientProxy* newScreen = mapToNeighbor(m_active, dir, x, y); 
     1726 
     1727        // should we switch or not? 
     1728        if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) { 
     1729                // switch screen 
     1730                switchScreen(newScreen, x, y, false); 
     1731                return true; 
     1732        } 
     1733        else { 
     1734                return false; 
     1735        } 
     1736} 
     1737 
     1738void 
     1739CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) 
     1740{ 
     1741        LOG((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); 
     1742 
     1743        // mouse move on secondary (client's) screen 
     1744        assert(m_active != NULL); 
     1745        if (m_active == m_primaryClient) { 
     1746                // stale event -- we're actually on the primary screen 
     1747                return; 
     1748        } 
     1749 
     1750        // if doing relative motion on secondary screens and we're locked 
     1751        // to the screen (which activates relative moves) then send a 
     1752        // relative mouse motion.  when we're doing this we pretend as if 
     1753        // the mouse isn't actually moving because we're expecting some 
     1754        // program on the secondary screen to warp the mouse on us, so we 
     1755        // have no idea where it really is. 
     1756        if (m_relativeMoves && isLockedToScreenServer()) { 
     1757                LOG((CLOG_DEBUG2 "relative move on %s by %d,%d", getName(m_active).c_str(), dx, dy)); 
     1758                m_active->mouseRelativeMove(dx, dy); 
     1759                return; 
     1760        } 
     1761 
     1762        // save old position 
     1763        const SInt32 xOld = m_x; 
     1764        const SInt32 yOld = m_y; 
     1765 
     1766        // save last delta 
     1767        m_xDelta2 = m_xDelta; 
     1768        m_yDelta2 = m_yDelta; 
     1769 
     1770        // save current delta 
     1771        m_xDelta  = dx; 
     1772        m_yDelta  = dy; 
     1773 
     1774        // accumulate motion 
     1775        m_x      += dx; 
     1776        m_y      += dy; 
     1777 
     1778        // get screen shape 
     1779        SInt32 ax, ay, aw, ah; 
     1780        m_active->getShape(ax, ay, aw, ah); 
     1781 
     1782        // find direction of neighbor and get the neighbor 
     1783        bool jump = true; 
     1784        CBaseClientProxy* newScreen; 
     1785        do { 
     1786                // clamp position to screen 
     1787                SInt32 xc = m_x, yc = m_y; 
     1788                if (xc < ax) { 
     1789                        xc = ax; 
     1790                } 
     1791                else if (xc >= ax + aw) { 
     1792                        xc = ax + aw - 1; 
     1793                } 
     1794                if (yc < ay) { 
     1795                        yc = ay; 
     1796                } 
     1797                else if (yc >= ay + ah) { 
     1798                        yc = ay + ah - 1; 
     1799                } 
     1800 
     1801                EDirection dir; 
     1802                if (m_x < ax) { 
     1803                        dir = kLeft; 
     1804                } 
     1805                else if (m_x > ax + aw - 1) { 
     1806                        dir = kRight; 
     1807                } 
     1808                else if (m_y < ay) { 
     1809                        dir = kTop; 
     1810                } 
     1811                else if (m_y > ay + ah - 1) { 
     1812                        dir = kBottom; 
     1813                } 
     1814                else { 
     1815                        // we haven't left the screen 
     1816                        newScreen = m_active; 
     1817                        jump      = false; 
     1818 
     1819                        // if waiting and mouse is not on the border we're waiting 
     1820                        // on then stop waiting.  also if it's not on the border 
     1821                        // then arm the double tap. 
     1822                        if (m_switchScreen != NULL) { 
     1823                                bool clearWait; 
     1824                                SInt32 zoneSize = m_primaryClient->getJumpZoneSize(); 
     1825                                switch (m_switchDir) { 
     1826                                case kLeft: 
     1827                                        clearWait = (m_x >= ax + zoneSize); 
     1828                                        break; 
     1829 
     1830                                case kRight: 
     1831                                        clearWait = (m_x <= ax + aw - 1 - zoneSize); 
     1832                                        break; 
     1833 
     1834                                case kTop: 
     1835                                        clearWait = (m_y >= ay + zoneSize); 
     1836                                        break; 
     1837 
     1838                                case kBottom: 
     1839                                        clearWait = (m_y <= ay + ah - 1 + zoneSize); 
     1840                                        break; 
     1841 
     1842                                default: 
     1843                                        clearWait = false; 
     1844                                        break; 
     1845                                } 
     1846                                if (clearWait) { 
     1847                                        // still on local screen 
     1848                                        noSwitch(m_x, m_y); 
     1849                                } 
     1850                        } 
     1851 
     1852                        // skip rest of block 
     1853                        break; 
     1854                } 
     1855 
     1856                // try to switch screen.  get the neighbor. 
     1857                newScreen = mapToNeighbor(m_active, dir, m_x, m_y); 
     1858 
     1859                // see if we should switch 
     1860                if (!isSwitchOkay(newScreen, dir, m_x, m_y, xc, yc)) { 
     1861                        newScreen = m_active; 
     1862                        jump      = false; 
     1863                } 
     1864        } while (false); 
     1865 
     1866        if (jump) { 
     1867                // switch screens 
     1868                switchScreen(newScreen, m_x, m_y, false); 
     1869        } 
     1870        else { 
     1871                // same screen.  clamp mouse to edge. 
     1872                m_x = xOld + dx; 
     1873                m_y = yOld + dy; 
     1874                if (m_x < ax) { 
     1875                        m_x = ax; 
     1876                        LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", getName(m_active).c_str())); 
     1877                } 
     1878                else if (m_x > ax + aw - 1) { 
     1879                        m_x = ax + aw - 1; 
     1880                        LOG((CLOG_DEBUG2 "clamp to right of \"%s\"", getName(m_active).c_str())); 
     1881                } 
     1882                if (m_y < ay) { 
     1883                        m_y = ay; 
     1884                        LOG((CLOG_DEBUG2 "clamp to top of \"%s\"", getName(m_active).c_str())); 
     1885                } 
     1886                else if (m_y > ay + ah - 1) { 
     1887                        m_y = ay + ah - 1; 
     1888                        LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", getName(m_active).c_str())); 
     1889                } 
     1890 
     1891                // warp cursor if it moved. 
     1892                if (m_x != xOld || m_y != yOld) { 
     1893                        LOG((CLOG_DEBUG2 "move on %s to %d,%d", getName(m_active).c_str(), m_x, m_y)); 
     1894                        m_active->mouseMove(m_x, m_y); 
     1895                } 
     1896        } 
     1897} 
     1898 
     1899void 
     1900CServer::onMouseWheel(SInt32 xDelta, SInt32 yDelta) 
     1901{ 
     1902        LOG((CLOG_DEBUG1 "onMouseWheel %+d,%+d", xDelta, yDelta)); 
     1903        assert(m_active != NULL); 
     1904 
     1905        // relay 
     1906        m_active->mouseWheel(xDelta, yDelta); 
     1907} 
     1908 
     1909bool 
     1910CServer::addClient(CBaseClientProxy* client) 
     1911{ 
     1912        CString name = getName(client); 
     1913        if (m_clients.count(name) != 0) { 
     1914                return false; 
     1915        } 
     1916 
     1917        // add event handlers 
     1918        EVENTQUEUE->adoptHandler(IScreen::getShapeChangedEvent(), 
     1919                                                        client->getEventTarget(), 
     1920                                                        new TMethodEventJob<CServer>(this, 
     1921                                                                &CServer::handleShapeChanged, client)); 
     1922        EVENTQUEUE->adoptHandler(IScreen::getClipboardGrabbedEvent(), 
     1923                                                        client->getEventTarget(), 
     1924                                                        new TMethodEventJob<CServer>(this, 
     1925                                                                &CServer::handleClipboardGrabbed, client)); 
     1926        EVENTQUEUE->adoptHandler(CClientProxy::getClipboardChangedEvent(), 
     1927                                                        client->getEventTarget(), 
     1928                                                        new TMethodEventJob<CServer>(this, 
     1929                                                                &CServer::handleClipboardChanged, client)); 
     1930 
     1931        // add to list 
     1932        m_clientSet.insert(client); 
     1933        m_clients.insert(std::make_pair(name, client)); 
     1934 
     1935        // initialize client data 
     1936        SInt32 x, y; 
     1937        client->getCursorPos(x, y); 
     1938        client->setJumpCursorPos(x, y); 
     1939 
     1940        // tell primary client about the active sides 
     1941        m_primaryClient->reconfigure(getActivePrimarySides()); 
     1942 
     1943        return true; 
     1944} 
     1945 
     1946bool 
     1947CServer::removeClient(CBaseClientProxy* client) 
     1948{ 
     1949        // return false if not in list 
     1950        CClientSet::iterator i = m_clientSet.find(client); 
     1951        if (i == m_clientSet.end()) { 
     1952                return false; 
     1953        } 
     1954 
     1955        // remove event handlers 
     1956        EVENTQUEUE->removeHandler(IScreen::getShapeChangedEvent(), 
     1957                                                        client->getEventTarget()); 
     1958        EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(), 
     1959                                                        client->getEventTarget()); 
     1960        EVENTQUEUE->removeHandler(CClientProxy::getClipboardChangedEvent(), 
     1961                                                        client->getEventTarget()); 
     1962 
     1963        // remove from list 
     1964        m_clients.erase(getName(client)); 
     1965        m_clientSet.erase(i); 
     1966 
     1967        return true; 
     1968} 
     1969 
     1970void 
     1971CServer::closeClient(CBaseClientProxy* client, const char* msg) 
     1972{ 
     1973        assert(client != m_primaryClient); 
     1974        assert(msg != NULL); 
     1975 
     1976        // send message to client.  this message should cause the client 
     1977        // to disconnect.  we add this client to the closed client list 
     1978        // and install a timer to remove the client if it doesn't respond 
     1979        // quickly enough.  we also remove the client from the active 
     1980        // client list since we're not going to listen to it anymore. 
     1981        // note that this method also works on clients that are not in 
     1982        // the m_clients list.  adoptClient() may call us with such a 
     1983        // client. 
     1984        LOG((CLOG_NOTE "disconnecting client \"%s\"", getName(client).c_str())); 
     1985 
     1986        // send message 
     1987        // FIXME -- avoid type cast (kinda hard, though) 
     1988        ((CClientProxy*)client)->close(msg); 
     1989 
     1990        // install timer.  wait timeout seconds for client to close. 
     1991        double timeout = 5.0; 
     1992        CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL); 
     1993        EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, 
     1994                                                        new TMethodEventJob<CServer>(this, 
     1995                                                                &CServer::handleClientCloseTimeout, client)); 
     1996 
     1997        // move client to closing list 
     1998        removeClient(client); 
     1999        m_oldClients.insert(std::make_pair(client, timer)); 
     2000 
     2001        // if this client is the active screen then we have to 
     2002        // jump off of it 
     2003        forceLeaveClient(client); 
     2004} 
     2005 
     2006void 
     2007CServer::closeClients(const CConfig& config) 
     2008{ 
     2009        // collect the clients that are connected but are being dropped 
     2010        // from the configuration (or who's canonical name is changing). 
     2011        typedef std::set<CBaseClientProxy*> CRemovedClients; 
     2012        CRemovedClients removed; 
     2013        for (CClientList::iterator index = m_clients.begin(); 
     2014                                                                index != m_clients.end(); ++index) { 
     2015                if (!config.isCanonicalName(index->first)) { 
     2016                        removed.insert(index->second); 
     2017                } 
     2018        } 
     2019 
     2020        // don't close the primary client 
     2021        removed.erase(m_primaryClient); 
     2022 
     2023        // now close them.  we collect the list then close in two steps 
     2024        // because closeClient() modifies the collection we iterate over. 
     2025        for (CRemovedClients::iterator index = removed.begin(); 
     2026                                                                index != removed.end(); ++index) { 
     2027                closeClient(*index, kMsgCClose); 
     2028        } 
     2029} 
     2030 
     2031void 
     2032CServer::removeActiveClient(CBaseClientProxy* client) 
     2033{ 
     2034        if (removeClient(client)) { 
     2035                forceLeaveClient(client); 
     2036                EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); 
     2037                if (m_clients.size() == 1 && m_oldClients.empty()) { 
     2038                        EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this)); 
     2039                } 
     2040        } 
     2041} 
     2042 
     2043void 
     2044CServer::removeOldClient(CBaseClientProxy* client) 
     2045{ 
     2046        COldClients::iterator i = m_oldClients.find(client); 
     2047        if (i != m_oldClients.end()) { 
     2048                EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); 
     2049                EVENTQUEUE->removeHandler(CEvent::kTimer, i->second); 
     2050                EVENTQUEUE->deleteTimer(i->second); 
     2051                m_oldClients.erase(i); 
     2052                if (m_clients.size() == 1 && m_oldClients.empty()) { 
     2053                        EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this)); 
     2054                } 
     2055        } 
     2056} 
     2057 
     2058void 
     2059CServer::forceLeaveClient(CBaseClientProxy* client) 
     2060{ 
     2061        CBaseClientProxy* active = 
     2062                (m_activeSaver != NULL) ? m_activeSaver : m_active; 
     2063        if (active == client) { 
     2064                // record new position (center of primary screen) 
     2065                m_primaryClient->getCursorCenter(m_x, m_y); 
     2066 
     2067                // stop waiting to switch to this client 
     2068                if (active == m_switchScreen) { 
     2069                        stopSwitch(); 
     2070                } 
     2071 
     2072                // don't notify active screen since it has probably already 
     2073                // disconnected. 
     2074                LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", getName(active).c_str(), getName(m_primaryClient).c_str(), m_x, m_y)); 
     2075 
     2076                // cut over 
     2077                m_active = m_primaryClient; 
     2078 
     2079                // enter new screen (unless we already have because of the 
     2080                // screen saver) 
     2081                if (m_activeSaver == NULL) { 
     2082                        m_primaryClient->enter(m_x, m_y, m_seqNum, 
     2083                                                                m_primaryClient->getToggleMask(), false); 
     2084                } 
     2085        } 
     2086 
     2087        // if this screen had the cursor when the screen saver activated 
     2088        // then we can't switch back to it when the screen saver 
     2089        // deactivates. 
     2090        if (m_activeSaver == client) { 
     2091                m_activeSaver = NULL; 
     2092        } 
     2093 
     2094        // tell primary client about the active sides 
     2095        m_primaryClient->reconfigure(getActivePrimarySides()); 
     2096} 
     2097 
     2098 
     2099// 
     2100// CServer::CClipboardInfo 
     2101// 
     2102 
     2103CServer::CClipboardInfo::CClipboardInfo() : 
     2104        m_clipboard(), 
     2105        m_clipboardData(), 
     2106        m_clipboardOwner(), 
     2107        m_clipboardSeqNum(0) 
     2108{ 
     2109        // do nothing 
     2110} 
     2111 
     2112 
     2113// 
     2114// CServer::CLockCursorToScreenInfo 
     2115// 
     2116 
     2117CServer::CLockCursorToScreenInfo* 
     2118CServer::CLockCursorToScreenInfo::alloc(State state) 
     2119{ 
     2120        CLockCursorToScreenInfo* info = 
     2121                (CLockCursorToScreenInfo*)malloc(sizeof(CLockCursorToScreenInfo)); 
     2122        info->m_state = state; 
     2123        return info; 
     2124} 
     2125 
     2126 
     2127// 
     2128// CServer::CSwitchToScreenInfo 
     2129// 
     2130 
     2131CServer::CSwitchToScreenInfo* 
     2132CServer::CSwitchToScreenInfo::alloc(const CString& screen) 
     2133{ 
     2134        CSwitchToScreenInfo* info = 
     2135                (CSwitchToScreenInfo*)malloc(sizeof(CSwitchToScreenInfo) + 
     2136                                                                screen.size()); 
     2137        strcpy(info->m_screen, screen.c_str()); 
     2138        return info; 
     2139} 
     2140 
     2141 
     2142// 
     2143// CServer::CSwitchInDirectionInfo 
     2144// 
     2145 
     2146CServer::CSwitchInDirectionInfo* 
     2147CServer::CSwitchInDirectionInfo::alloc(EDirection direction) 
     2148{ 
     2149        CSwitchInDirectionInfo* info = 
     2150                (CSwitchInDirectionInfo*)malloc(sizeof(CSwitchInDirectionInfo)); 
     2151        info->m_direction = direction; 
     2152        return info; 
     2153} 
     2154 
     2155 
     2156// 
     2157// CServer::CScreenConnectedInfo 
     2158// 
     2159 
     2160CServer::CScreenConnectedInfo* 
     2161CServer::CScreenConnectedInfo::alloc(const CString& screen) 
     2162{ 
     2163        CScreenConnectedInfo* info = 
     2164                (CScreenConnectedInfo*)malloc(sizeof(CScreenConnectedInfo) + 
     2165                                                                screen.size()); 
     2166        strcpy(info->m_screen, screen.c_str()); 
     2167        return info; 
     2168} 
  • lib/server/CServer.cpp.ok

    diff -uNr synergy-1.3.1/lib/server/CServer.cpp.ok synergy-1.3.1-broadcast/lib/server/CServer.cpp.ok
    old new  
     1/* 
     2 * synergy -- mouse and keyboard sharing utility 
     3 * Copyright (C) 2002 Chris Schoeneman 
     4 *  
     5 * This package is free software; you can redistribute it and/or 
     6 * modify it under the terms of the GNU General Public License 
     7 * found in the file COPYING that should have accompanied this file. 
     8 *  
     9 * This package is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 */ 
     14 
     15#include "CServer.h" 
     16#include "CClientProxy.h" 
     17#include "CClientProxyUnknown.h" 
     18#include "CPrimaryClient.h" 
     19#include "IPlatformScreen.h" 
     20#include "OptionTypes.h" 
     21#include "ProtocolTypes.h" 
     22#include "XScreen.h" 
     23#include "XSynergy.h" 
     24#include "IDataSocket.h" 
     25#include "IListenSocket.h" 
     26#include "XSocket.h" 
     27#include "IEventQueue.h" 
     28#include "CLog.h" 
     29#include "TMethodEventJob.h" 
     30#include "CArch.h" 
     31#include <string.h> 
     32 
     33// 
     34// CServer 
     35// 
     36 
     37CEvent::Type                    CServer::s_errorEvent         = CEvent::kUnknown; 
     38CEvent::Type                    CServer::s_connectedEvent     = CEvent::kUnknown; 
     39CEvent::Type                    CServer::s_disconnectedEvent  = CEvent::kUnknown; 
     40CEvent::Type                    CServer::s_switchToScreen     = CEvent::kUnknown; 
     41CEvent::Type                    CServer::s_switchInDirection  = CEvent::kUnknown; 
     42CEvent::Type                    CServer::s_lockCursorToScreen = CEvent::kUnknown; 
     43 
     44CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : 
     45        m_primaryClient(primaryClient), 
     46        m_active(primaryClient), 
     47        m_seqNum(0), 
     48        m_xDelta(0), 
     49        m_yDelta(0), 
     50        m_xDelta2(0), 
     51        m_yDelta2(0), 
     52        m_config(), 
     53        m_inputFilter(m_config.getInputFilter()), 
     54        m_activeSaver(NULL), 
     55        m_switchDir(kNoDirection), 
     56        m_switchScreen(NULL), 
     57        m_switchWaitDelay(0.0), 
     58        m_switchWaitTimer(NULL), 
     59        m_switchTwoTapDelay(0.0), 
     60        m_switchTwoTapEngaged(false), 
     61        m_switchTwoTapArmed(false), 
     62        m_switchTwoTapZone(3), 
     63        m_relativeMoves(false), 
     64        m_lockedToScreen(false), 
     65        m_broadcast(false) 
     66{ 
     67        // must have a primary client and it must have a canonical name 
     68        assert(m_primaryClient != NULL); 
     69        assert(config.isScreen(primaryClient->getName())); 
     70 
     71        CString primaryName = getName(primaryClient); 
     72 
     73        // clear clipboards 
     74        for (ClipboardID id = 0; id < kClipboardEnd; ++id) { 
     75                CClipboardInfo& clipboard   = m_clipboards[id]; 
     76                clipboard.m_clipboardOwner  = primaryName; 
     77                clipboard.m_clipboardSeqNum = m_seqNum; 
     78                if (clipboard.m_clipboard.open(0)) { 
     79                        clipboard.m_clipboard.empty(); 
     80                        clipboard.m_clipboard.close(); 
     81                } 
     82                clipboard.m_clipboardData   = clipboard.m_clipboard.marshall(); 
     83        } 
     84 
     85        // install event handlers 
     86        EVENTQUEUE->adoptHandler(CEvent::kTimer, this, 
     87                                                        new TMethodEventJob<CServer>(this, 
     88                                                                &CServer::handleSwitchWaitTimeout)); 
     89        EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyDownEvent(), 
     90                                                        m_inputFilter, 
     91                                                        new TMethodEventJob<CServer>(this, 
     92                                                                &CServer::handleKeyDownEvent)); 
     93        EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyUpEvent(), 
     94                                                        m_inputFilter, 
     95                                                        new TMethodEventJob<CServer>(this, 
     96                                                                &CServer::handleKeyUpEvent)); 
     97        EVENTQUEUE->adoptHandler(IPlatformScreen::getKeyRepeatEvent(), 
     98                                                        m_inputFilter, 
     99                                                        new TMethodEventJob<CServer>(this, 
     100                                                                &CServer::handleKeyRepeatEvent)); 
     101        EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonDownEvent(), 
     102                                                        m_inputFilter, 
     103                                                        new TMethodEventJob<CServer>(this, 
     104                                                                &CServer::handleButtonDownEvent)); 
     105        EVENTQUEUE->adoptHandler(IPlatformScreen::getButtonUpEvent(), 
     106                                                        m_inputFilter, 
     107                                                        new TMethodEventJob<CServer>(this, 
     108                                                                &CServer::handleButtonUpEvent)); 
     109        EVENTQUEUE->adoptHandler(IPlatformScreen::getMotionOnPrimaryEvent(), 
     110                                                        m_primaryClient->getEventTarget(), 
     111                                                        new TMethodEventJob<CServer>(this, 
     112                                                                &CServer::handleMotionPrimaryEvent)); 
     113        EVENTQUEUE->adoptHandler(IPlatformScreen::getMotionOnSecondaryEvent(), 
     114                                                        m_primaryClient->getEventTarget(), 
     115                                                        new TMethodEventJob<CServer>(this, 
     116                                                                &CServer::handleMotionSecondaryEvent)); 
     117        EVENTQUEUE->adoptHandler(IPlatformScreen::getWheelEvent(), 
     118                                                        m_primaryClient->getEventTarget(), 
     119                                                        new TMethodEventJob<CServer>(this, 
     120                                                                &CServer::handleWheelEvent)); 
     121        EVENTQUEUE->adoptHandler(IPlatformScreen::getScreensaverActivatedEvent(), 
     122                                                        m_primaryClient->getEventTarget(), 
     123                                                        new TMethodEventJob<CServer>(this, 
     124                                                                &CServer::handleScreensaverActivatedEvent)); 
     125        EVENTQUEUE->adoptHandler(IPlatformScreen::getScreensaverDeactivatedEvent(), 
     126                                                        m_primaryClient->getEventTarget(), 
     127                                                        new TMethodEventJob<CServer>(this, 
     128                                                                &CServer::handleScreensaverDeactivatedEvent)); 
     129        EVENTQUEUE->adoptHandler(getSwitchToScreenEvent(), 
     130                                                        m_inputFilter, 
     131                                                        new TMethodEventJob<CServer>(this, 
     132                                                                &CServer::handleSwitchToScreenEvent)); 
     133        EVENTQUEUE->adoptHandler(getSwitchInDirectionEvent(), 
     134                                                        m_inputFilter, 
     135                                                        new TMethodEventJob<CServer>(this, 
     136                                                                &CServer::handleSwitchInDirectionEvent)); 
     137        EVENTQUEUE->adoptHandler(getLockCursorToScreenEvent(), 
     138                                                        m_inputFilter, 
     139                                                        new TMethodEventJob<CServer>(this, 
     140                                                                &CServer::handleLockCursorToScreenEvent)); 
     141        EVENTQUEUE->adoptHandler(IPlatformScreen::getFakeInputBeginEvent(), 
     142                                                        m_inputFilter, 
     143                                                        new TMethodEventJob<CServer>(this, 
     144                                                                &CServer::handleFakeInputBeginEvent)); 
     145        EVENTQUEUE->adoptHandler(IPlatformScreen::getFakeInputEndEvent(), 
     146                                                        m_inputFilter, 
     147                                                        new TMethodEventJob<CServer>(this, 
     148                                                                &CServer::handleFakeInputEndEvent)); 
     149 
     150        // add connection 
     151        addClient(m_primaryClient); 
     152 
     153        // set initial configuration 
     154        setConfig(config); 
     155 
     156        // enable primary client 
     157        m_primaryClient->enable(); 
     158        m_inputFilter->setPrimaryClient(m_primaryClient); 
     159} 
     160 
     161CServer::~CServer() 
     162{ 
     163        // remove event handlers and timers 
     164        EVENTQUEUE->removeHandler(IPlatformScreen::getKeyDownEvent(), 
     165                                                        m_inputFilter); 
     166        EVENTQUEUE->removeHandler(IPlatformScreen::getKeyUpEvent(), 
     167                                                        m_inputFilter); 
     168        EVENTQUEUE->removeHandler(IPlatformScreen::getKeyRepeatEvent(), 
     169                                                        m_inputFilter); 
     170        EVENTQUEUE->removeHandler(IPlatformScreen::getButtonDownEvent(), 
     171                                                        m_inputFilter); 
     172        EVENTQUEUE->removeHandler(IPlatformScreen::getButtonUpEvent(), 
     173                                                        m_inputFilter); 
     174        EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnPrimaryEvent(), 
     175                                                        m_primaryClient->getEventTarget()); 
     176        EVENTQUEUE->removeHandler(IPlatformScreen::getMotionOnSecondaryEvent(), 
     177                                                        m_primaryClient->getEventTarget()); 
     178        EVENTQUEUE->removeHandler(IPlatformScreen::getWheelEvent(), 
     179                                                        m_primaryClient->getEventTarget()); 
     180        EVENTQUEUE->removeHandler(IPlatformScreen::getScreensaverActivatedEvent(), 
     181                                                        m_primaryClient->getEventTarget()); 
     182        EVENTQUEUE->removeHandler(IPlatformScreen::getScreensaverDeactivatedEvent(), 
     183                                                        m_primaryClient->getEventTarget()); 
     184        EVENTQUEUE->removeHandler(IPlatformScreen::getFakeInputBeginEvent(), 
     185                                                        m_inputFilter); 
     186        EVENTQUEUE->removeHandler(IPlatformScreen::getFakeInputEndEvent(), 
     187                                                        m_inputFilter); 
     188        EVENTQUEUE->removeHandler(CEvent::kTimer, this); 
     189        stopSwitch(); 
     190 
     191        // force immediate disconnection of secondary clients 
     192        disconnect(); 
     193        for (COldClients::iterator index = m_oldClients.begin(); 
     194                                                        index != m_oldClients.begin(); ++index) { 
     195                CBaseClientProxy* client = index->first; 
     196                EVENTQUEUE->deleteTimer(index->second); 
     197                EVENTQUEUE->removeHandler(CEvent::kTimer, client); 
     198                EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); 
     199                delete client; 
     200        } 
     201 
     202        // remove input filter 
     203        m_inputFilter->setPrimaryClient(NULL); 
     204 
     205        // disable and disconnect primary client 
     206        m_primaryClient->disable(); 
     207        removeClient(m_primaryClient); 
     208} 
     209 
     210bool 
     211CServer::setConfig(const CConfig& config) 
     212{ 
     213        // refuse configuration if it doesn't include the primary screen 
     214        if (!config.isScreen(m_primaryClient->getName())) { 
     215                return false; 
     216        } 
     217 
     218        // close clients that are connected but being dropped from the 
     219        // configuration. 
     220        closeClients(config); 
     221 
     222        // cut over 
     223        m_config = config; 
     224        processOptions(); 
     225 
     226        // add ScrollLock as a hotkey to lock to the screen.  this was a 
     227        // built-in feature in earlier releases and is now supported via 
     228        // the user configurable hotkey mechanism.  if the user has already 
     229        // registered ScrollLock for something else then that will win but 
     230        // we will unfortunately generate a warning.  if the user has 
     231        // configured a CLockCursorToScreenAction then we don't add 
     232        // ScrollLock as a hotkey. 
     233        if (!m_config.hasLockToScreenAction()) { 
     234                IPlatformScreen::CKeyInfo* key = 
     235                        IPlatformScreen::CKeyInfo::alloc(kKeyScrollLock, 0, 0, 0); 
     236                CInputFilter::CRule rule(new CInputFilter::CKeystrokeCondition(key)); 
     237                rule.adoptAction(new CInputFilter::CLockCursorToScreenAction, true); 
     238                m_inputFilter->addFilterRule(rule); 
     239        } 
     240 
     241        // tell primary screen about reconfiguration 
     242        m_primaryClient->reconfigure(getActivePrimarySides()); 
     243 
     244        // tell all (connected) clients about current options 
     245        for (CClientList::const_iterator index = m_clients.begin(); 
     246                                                                index != m_clients.end(); ++index) { 
     247                CBaseClientProxy* client = index->second; 
     248                sendOptions(client); 
     249        } 
     250 
     251        return true; 
     252} 
     253 
     254void 
     255CServer::adoptClient(CBaseClientProxy* client) 
     256{ 
     257        assert(client != NULL); 
     258 
     259        // watch for client disconnection 
     260        EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client, 
     261                                                        new TMethodEventJob<CServer>(this, 
     262                                                                &CServer::handleClientDisconnected, client)); 
     263 
     264        // name must be in our configuration 
     265        if (!m_config.isScreen(client->getName())) { 
     266                LOG((CLOG_WARN "a client with name \"%s\" is not in the map", client->getName().c_str())); 
     267                closeClient(client, kMsgEUnknown); 
     268                return; 
     269        } 
     270 
     271        // add client to client list 
     272        if (!addClient(client)) { 
     273                // can only have one screen with a given name at any given time 
     274                LOG((CLOG_WARN "a client with name \"%s\" is already connected", getName(client).c_str())); 
     275                closeClient(client, kMsgEBusy); 
     276                return; 
     277        } 
     278        LOG((CLOG_NOTE "client \"%s\" has connected", getName(client).c_str())); 
     279 
     280        // send configuration options to client 
     281        sendOptions(client); 
     282 
     283        // activate screen saver on new client if active on the primary screen 
     284        if (m_activeSaver != NULL) { 
     285                client->screensaver(true); 
     286        } 
     287 
     288        // send notification 
     289        CServer::CScreenConnectedInfo* info = 
     290                CServer::CScreenConnectedInfo::alloc(getName(client)); 
     291        EVENTQUEUE->addEvent(CEvent(CServer::getConnectedEvent(), 
     292                                                                m_primaryClient->getEventTarget(), info)); 
     293} 
     294 
     295void 
     296CServer::disconnect() 
     297{ 
     298        // close all secondary clients 
     299        if (m_clients.size() > 1 || !m_oldClients.empty()) { 
     300                CConfig emptyConfig; 
     301                closeClients(emptyConfig); 
     302        } 
     303        else { 
     304                EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this)); 
     305        } 
     306} 
     307 
     308UInt32 
     309CServer::getNumClients() const 
     310{ 
     311        return m_clients.size(); 
     312} 
     313 
     314void 
     315CServer::getClients(std::vector<CString>& list) const 
     316{ 
     317        list.clear(); 
     318        for (CClientList::const_iterator index = m_clients.begin(); 
     319                                                        index != m_clients.end(); ++index) { 
     320                list.push_back(index->first); 
     321        } 
     322} 
     323 
     324CEvent::Type 
     325CServer::getErrorEvent() 
     326{ 
     327        return CEvent::registerTypeOnce(s_errorEvent, 
     328                                                        "CServer::error"); 
     329} 
     330 
     331CEvent::Type 
     332CServer::getConnectedEvent() 
     333{ 
     334        return CEvent::registerTypeOnce(s_connectedEvent, 
     335                                                        "CServer::connected"); 
     336} 
     337 
     338CEvent::Type 
     339CServer::getDisconnectedEvent() 
     340{ 
     341        return CEvent::registerTypeOnce(s_disconnectedEvent, 
     342                                                        "CServer::disconnected"); 
     343} 
     344 
     345CEvent::Type 
     346CServer::getSwitchToScreenEvent() 
     347{ 
     348        return CEvent::registerTypeOnce(s_switchToScreen, 
     349                                                        "CServer::switchToScreen"); 
     350} 
     351 
     352CEvent::Type 
     353CServer::getSwitchInDirectionEvent() 
     354{ 
     355        return CEvent::registerTypeOnce(s_switchInDirection, 
     356                                                        "CServer::switchInDirection"); 
     357} 
     358 
     359CEvent::Type 
     360CServer::getLockCursorToScreenEvent() 
     361{ 
     362        return CEvent::registerTypeOnce(s_lockCursorToScreen, 
     363                                                        "CServer::lockCursorToScreen"); 
     364} 
     365 
     366CString 
     367CServer::getName(const CBaseClientProxy* client) const 
     368{ 
     369        CString name = m_config.getCanonicalName(client->getName()); 
     370        if (name.empty()) { 
     371                name = client->getName(); 
     372        } 
     373        return name; 
     374} 
     375 
     376UInt32 
     377CServer::getActivePrimarySides() const 
     378{ 
     379        UInt32 sides = 0; 
     380        if (!isLockedToScreenServer()) { 
     381                if (hasAnyNeighbor(m_primaryClient, kLeft)) { 
     382                        sides |= kLeftMask; 
     383                } 
     384                if (hasAnyNeighbor(m_primaryClient, kRight)) { 
     385                        sides |= kRightMask; 
     386                } 
     387                if (hasAnyNeighbor(m_primaryClient, kTop)) { 
     388                        sides |= kTopMask; 
     389                } 
     390                if (hasAnyNeighbor(m_primaryClient, kBottom)) { 
     391                        sides |= kBottomMask; 
     392                } 
     393        } 
     394        return sides; 
     395} 
     396 
     397bool 
     398CServer::isLockedToScreenServer() const 
     399{ 
     400        // locked if scroll-lock is toggled on 
     401        return m_lockedToScreen; 
     402} 
     403 
     404bool 
     405CServer::isLockedToScreen() const 
     406{ 
     407        // locked if we say we're locked 
     408        if (isLockedToScreenServer()) { 
     409                LOG((CLOG_DEBUG "locked to screen")); 
     410                return true; 
     411        } 
     412 
     413        // locked if primary says we're locked 
     414        if (m_primaryClient->isLockedToScreen()) { 
     415                return true; 
     416        } 
     417 
     418        // not locked 
     419        return false; 
     420} 
     421 
     422SInt32 
     423CServer::getJumpZoneSize(CBaseClientProxy* client) const 
     424{ 
     425        if (client == m_primaryClient) { 
     426                return m_primaryClient->getJumpZoneSize(); 
     427        } 
     428        else { 
     429                return 0; 
     430        } 
     431} 
     432 
     433void 
     434CServer::switchScreen(CBaseClientProxy* dst, 
     435                                SInt32 x, SInt32 y, bool forScreensaver) 
     436{ 
     437        assert(dst != NULL); 
     438#ifndef NDEBUG 
     439        { 
     440                SInt32 dx, dy, dw, dh; 
     441                dst->getShape(dx, dy, dw, dh); 
     442                assert(x >= dx && y >= dy && x < dx + dw && y < dy + dh); 
     443        } 
     444#endif 
     445        assert(m_active != NULL); 
     446 
     447        LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", getName(m_active).c_str(), getName(dst).c_str(), x, y)); 
     448 
     449        // stop waiting to switch 
     450        stopSwitch(); 
     451 
     452        // record new position 
     453        m_x       = x; 
     454        m_y       = y; 
     455        m_xDelta  = 0; 
     456        m_yDelta  = 0; 
     457        m_xDelta2 = 0; 
     458        m_yDelta2 = 0; 
     459 
     460        // wrapping means leaving the active screen and entering it again. 
     461        // since that's a waste of time we skip that and just warp the 
     462        // mouse. 
     463        if (m_active != dst) { 
     464                // leave active screen 
     465                if (!m_active->leave()) { 
     466                        // cannot leave screen 
     467                        LOG((CLOG_WARN "can't leave screen")); 
     468                        return; 
     469                } 
     470 
     471                // update the primary client's clipboards if we're leaving the 
     472                // primary screen. 
     473                if (m_active == m_primaryClient) { 
     474                        for (ClipboardID id = 0; id < kClipboardEnd; ++id) { 
     475                                CClipboardInfo& clipboard = m_clipboards[id]; 
     476                                if (clipboard.m_clipboardOwner == getName(m_primaryClient)) { 
     477                                        onClipboardChanged(m_primaryClient, 
     478                                                id, clipboard.m_clipboardSeqNum); 
     479                                } 
     480                        } 
     481                } 
     482 
     483                // cut over 
     484                m_active = dst; 
     485 
     486                // increment enter sequence number 
     487                ++m_seqNum; 
     488 
     489                // enter new screen 
     490                m_active->enter(x, y, m_seqNum, 
     491                                                                m_primaryClient->getToggleMask(), 
     492                                                                forScreensaver); 
     493 
     494                // send the clipboard data to new active screen 
     495                for (ClipboardID id = 0; id < kClipboardEnd; ++id) { 
     496                        m_active->setClipboard(id, &m_clipboards[id].m_clipboard); 
     497                } 
     498        } 
     499        else { 
     500                m_active->mouseMove(x, y); 
     501        } 
     502} 
     503 
     504void 
     505CServer::jumpToScreen(CBaseClientProxy* newScreen) 
     506{ 
     507        assert(newScreen != NULL); 
     508 
     509        // record the current cursor position on the active screen 
     510        m_active->setJumpCursorPos(m_x, m_y); 
     511 
     512        // get the last cursor position on the target screen 
     513        SInt32 x, y; 
     514        newScreen->getJumpCursorPos(x, y); 
     515         
     516        switchScreen(newScreen, x, y, false); 
     517} 
     518 
     519float 
     520CServer::mapToFraction(CBaseClientProxy* client, 
     521                                EDirection dir, SInt32 x, SInt32 y) const 
     522{ 
     523        SInt32 sx, sy, sw, sh; 
     524        client->getShape(sx, sy, sw, sh); 
     525        switch (dir) { 
     526        case kLeft: 
     527        case kRight: 
     528                return static_cast<float>(y - sy + 0.5f) / static_cast<float>(sh); 
     529 
     530        case kTop: 
     531        case kBottom: 
     532                return static_cast<float>(x - sx + 0.5f) / static_cast<float>(sw); 
     533 
     534        case kNoDirection: 
     535                assert(0 && "bad direction"); 
     536                break; 
     537        } 
     538        return 0.0f; 
     539} 
     540 
     541void 
     542CServer::mapToPixel(CBaseClientProxy* client, 
     543                                EDirection dir, float f, SInt32& x, SInt32& y) const 
     544{ 
     545        SInt32 sx, sy, sw, sh; 
     546        client->getShape(sx, sy, sw, sh); 
     547        switch (dir) { 
     548        case kLeft: 
     549        case kRight: 
     550                y = static_cast<SInt32>(f * sh) + sy; 
     551                break; 
     552 
     553        case kTop: 
     554        case kBottom: 
     555                x = static_cast<SInt32>(f * sw) + sx; 
     556                break; 
     557 
     558        case kNoDirection: 
     559                assert(0 && "bad direction"); 
     560                break; 
     561        } 
     562} 
     563 
     564bool 
     565CServer::hasAnyNeighbor(CBaseClientProxy* client, EDirection dir) const 
     566{ 
     567        assert(client != NULL); 
     568 
     569        return m_config.hasNeighbor(getName(client), dir); 
     570} 
     571 
     572CBaseClientProxy* 
     573CServer::getNeighbor(CBaseClientProxy* src, 
     574                                EDirection dir, SInt32& x, SInt32& y) const 
     575{ 
     576        // note -- must be locked on entry 
     577 
     578        assert(src != NULL); 
     579 
     580        // get source screen name 
     581        CString srcName = getName(src); 
     582        assert(!srcName.empty()); 
     583        LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); 
     584 
     585        // convert position to fraction 
     586        float t = mapToFraction(src, dir, x, y); 
     587 
     588        // search for the closest neighbor that exists in direction dir 
     589        float tTmp; 
     590        for (;;) { 
     591                CString dstName(m_config.getNeighbor(srcName, dir, t, &tTmp)); 
     592 
     593                // if nothing in that direction then return NULL. if the 
     594                // destination is the source then we can make no more 
     595                // progress in this direction.  since we haven't found a 
     596                // connected neighbor we return NULL. 
     597                if (dstName.empty()) { 
     598                        LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str())); 
     599                        return NULL; 
     600                } 
     601 
     602                // look up neighbor cell.  if the screen is connected and 
     603                // ready then we can stop. 
     604                CClientList::const_iterator index = m_clients.find(dstName); 
     605                if (index != m_clients.end()) { 
     606                        LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\" at %f", dstName.c_str(), CConfig::dirName(dir), srcName.c_str(), t)); 
     607                        mapToPixel(index->second, dir, tTmp, x, y); 
     608                        return index->second; 
     609                } 
     610 
     611                // skip over unconnected screen 
     612                LOG((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str())); 
     613                srcName = dstName; 
     614 
     615                // use position on skipped screen 
     616                t = tTmp; 
     617        } 
     618} 
     619 
     620CBaseClientProxy* 
     621CServer::mapToNeighbor(CBaseClientProxy* src, 
     622                                EDirection srcSide, SInt32& x, SInt32& y) const 
     623{ 
     624        // note -- must be locked on entry 
     625 
     626        assert(src != NULL); 
     627 
     628        // get the first neighbor 
     629        CBaseClientProxy* dst = getNeighbor(src, srcSide, x, y); 
     630        if (dst == NULL) { 
     631                return NULL; 
     632        } 
     633 
     634        // get the source screen's size 
     635        SInt32 dx, dy, dw, dh; 
     636        CBaseClientProxy* lastGoodScreen = src; 
     637        lastGoodScreen->getShape(dx, dy, dw, dh); 
     638 
     639        // find destination screen, adjusting x or y (but not both).  the 
     640        // searches are done in a sort of canonical screen space where 
     641        // the upper-left corner is 0,0 for each screen.  we adjust from 
     642        // actual to canonical position on entry to and from canonical to 
     643        // actual on exit from the search. 
     644        switch (srcSide) { 
     645        case kLeft: 
     646                x -= dx; 
     647                while (dst != NULL) { 
     648                        lastGoodScreen = dst; 
     649                        lastGoodScreen->getShape(dx, dy, dw, dh); 
     650                        x += dw; 
     651                        if (x >= 0) { 
     652                                break; 
     653                        } 
     654                        LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); 
     655                        dst = getNeighbor(lastGoodScreen, srcSide, x, y); 
     656                } 
     657                assert(lastGoodScreen != NULL); 
     658                x += dx; 
     659                break; 
     660 
     661        case kRight: 
     662                x -= dx; 
     663                while (dst != NULL) { 
     664                        x -= dw; 
     665                        lastGoodScreen = dst; 
     666                        lastGoodScreen->getShape(dx, dy, dw, dh); 
     667                        if (x < dw) { 
     668                                break; 
     669                        } 
     670                        LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); 
     671                        dst = getNeighbor(lastGoodScreen, srcSide, x, y); 
     672                } 
     673                assert(lastGoodScreen != NULL); 
     674                x += dx; 
     675                break; 
     676 
     677        case kTop: 
     678                y -= dy; 
     679                while (dst != NULL) { 
     680                        lastGoodScreen = dst; 
     681                        lastGoodScreen->getShape(dx, dy, dw, dh); 
     682                        y += dh; 
     683                        if (y >= 0) { 
     684                                break; 
     685                        } 
     686                        LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); 
     687                        dst = getNeighbor(lastGoodScreen, srcSide, x, y); 
     688                } 
     689                assert(lastGoodScreen != NULL); 
     690                y += dy; 
     691                break; 
     692 
     693        case kBottom: 
     694                y -= dy; 
     695                while (dst != NULL) { 
     696                        y -= dh; 
     697                        lastGoodScreen = dst; 
     698                        lastGoodScreen->getShape(dx, dy, dw, dh); 
     699                        if (y < dh) { 
     700                                break; 
     701                        } 
     702                        LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str())); 
     703                        dst = getNeighbor(lastGoodScreen, srcSide, x, y); 
     704                } 
     705                assert(lastGoodScreen != NULL); 
     706                y += dy; 
     707                break; 
     708 
     709        case kNoDirection: 
     710                assert(0 && "bad direction"); 
     711                return NULL; 
     712        } 
     713 
     714        // save destination screen 
     715        assert(lastGoodScreen != NULL); 
     716        dst = lastGoodScreen; 
     717 
     718        // if entering primary screen then be sure to move in far enough 
     719        // to avoid the jump zone.  if entering a side that doesn't have 
     720        // a neighbor (i.e. an asymmetrical side) then we don't need to 
     721        // move inwards because that side can't provoke a jump. 
     722        avoidJumpZone(dst, srcSide, x, y); 
     723 
     724        return dst; 
     725} 
     726 
     727void 
     728CServer::avoidJumpZone(CBaseClientProxy* dst, 
     729                                EDirection dir, SInt32& x, SInt32& y) const 
     730{ 
     731        // we only need to avoid jump zones on the primary screen 
     732        if (dst != m_primaryClient) { 
     733                return; 
     734        } 
     735 
     736        const CString dstName(getName(dst)); 
     737        SInt32 dx, dy, dw, dh; 
     738        dst->getShape(dx, dy, dw, dh); 
     739        float t = mapToFraction(dst, dir, x, y); 
     740        SInt32 z = getJumpZoneSize(dst); 
     741 
     742        // move in far enough to avoid the jump zone.  if entering a side 
     743        // that doesn't have a neighbor (i.e. an asymmetrical side) then we 
     744        // don't need to move inwards because that side can't provoke a jump. 
     745        switch (dir) { 
     746        case kLeft: 
     747                if (!m_config.getNeighbor(dstName, kRight, t, NULL).empty() && 
     748                        x > dx + dw - 1 - z) 
     749                        x = dx + dw - 1 - z; 
     750                break; 
     751 
     752        case kRight: 
     753                if (!m_config.getNeighbor(dstName, kLeft, t, NULL).empty() && 
     754                        x < dx + z) 
     755                        x = dx + z; 
     756                break; 
     757 
     758        case kTop: 
     759                if (!m_config.getNeighbor(dstName, kBottom, t, NULL).empty() && 
     760                        y > dy + dh - 1 - z) 
     761                        y = dy + dh - 1 - z; 
     762                break; 
     763 
     764        case kBottom: 
     765                if (!m_config.getNeighbor(dstName, kTop, t, NULL).empty() && 
     766                        y < dy + z) 
     767                        y = dy + z; 
     768                break; 
     769 
     770        case kNoDirection: 
     771                assert(0 && "bad direction"); 
     772        } 
     773} 
     774 
     775bool 
     776CServer::isSwitchOkay(CBaseClientProxy* newScreen, 
     777                                EDirection dir, SInt32 x, SInt32 y, 
     778                                SInt32 xActive, SInt32 yActive) 
     779{ 
     780        LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", getName(m_active).c_str(), CConfig::dirName(dir))); 
     781 
     782        // is there a neighbor? 
     783        if (newScreen == NULL) { 
     784                // there's no neighbor.  we don't want to switch and we don't 
     785                // want to try to switch later. 
     786                LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir))); 
     787                stopSwitch(); 
     788                return false; 
     789        } 
     790 
     791        // should we switch or not? 
     792        bool preventSwitch = false; 
     793        bool allowSwitch   = false; 
     794 
     795        // note if the switch direction has changed.  save the new 
     796        // direction and screen if so. 
     797        bool isNewDirection  = (dir != m_switchDir); 
     798        if (isNewDirection || m_switchScreen == NULL) { 
     799                m_switchDir    = dir; 
     800                m_switchScreen = newScreen; 
     801        } 
     802 
     803        // is this a double tap and do we care? 
     804        if (!allowSwitch && m_switchTwoTapDelay > 0.0) { 
     805                if (isNewDirection || 
     806                        !isSwitchTwoTapStarted() || !shouldSwitchTwoTap()) { 
     807                        // tapping a different or new edge or second tap not 
     808                        // fast enough.  prepare for second tap. 
     809                        preventSwitch = true; 
     810                        startSwitchTwoTap(); 
     811                } 
     812                else { 
     813                        // got second tap 
     814                        allowSwitch = true; 
     815                } 
     816        } 
     817 
     818        // if waiting before a switch then prepare to switch later 
     819        if (!allowSwitch && m_switchWaitDelay > 0.0) { 
     820                if (isNewDirection || !isSwitchWaitStarted()) { 
     821                        startSwitchWait(x, y); 
     822                } 
     823                preventSwitch = true; 
     824        } 
     825 
     826        // are we in a locked corner?  first check if screen has the option set 
     827        // and, if not, check the global options. 
     828        const CConfig::CScreenOptions* options = 
     829                                                m_config.getOptions(getName(m_active)); 
     830        if (options == NULL || options->count(kOptionScreenSwitchCorners) == 0) { 
     831                options = m_config.getOptions(""); 
     832        } 
     833        if (options != NULL && options->count(kOptionScreenSwitchCorners) > 0) { 
     834                // get corner mask and size 
     835                CConfig::CScreenOptions::const_iterator i = 
     836                        options->find(kOptionScreenSwitchCorners); 
     837                UInt32 corners = static_cast<UInt32>(i->second); 
     838                i = options->find(kOptionScreenSwitchCornerSize); 
     839                SInt32 size = 0; 
     840                if (i != options->end()) { 
     841                        size = i->second; 
     842                } 
     843 
     844                // see if we're in a locked corner 
     845                if ((getCorner(m_active, xActive, yActive, size) & corners) != 0) { 
     846                        // yep, no switching 
     847                        LOG((CLOG_DEBUG1 "locked in corner")); 
     848                        preventSwitch = true; 
     849                        stopSwitch(); 
     850                } 
     851        } 
     852 
     853        // ignore if mouse is locked to screen and don't try to switch later 
     854        if (!preventSwitch && isLockedToScreen()) { 
     855                LOG((CLOG_DEBUG1 "locked to screen")); 
     856                preventSwitch = true; 
     857                stopSwitch(); 
     858        } 
     859 
     860        return !preventSwitch; 
     861} 
     862 
     863void 
     864CServer::noSwitch(SInt32 x, SInt32 y) 
     865{ 
     866        armSwitchTwoTap(x, y); 
     867        stopSwitchWait(); 
     868} 
     869 
     870void 
     871CServer::stopSwitch() 
     872{ 
     873        if (m_switchScreen != NULL) { 
     874                m_switchScreen = NULL; 
     875                m_switchDir    = kNoDirection; 
     876                stopSwitchTwoTap(); 
     877                stopSwitchWait(); 
     878        } 
     879} 
     880 
     881void 
     882CServer::startSwitchTwoTap() 
     883{ 
     884        m_switchTwoTapEngaged = true; 
     885        m_switchTwoTapArmed   = false; 
     886        m_switchTwoTapTimer.reset(); 
     887        LOG((CLOG_DEBUG1 "waiting for second tap")); 
     888} 
     889 
     890void 
     891CServer::armSwitchTwoTap(SInt32 x, SInt32 y) 
     892{ 
     893        if (m_switchTwoTapEngaged) { 
     894                if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) { 
     895                        // second tap took too long.  disengage. 
     896                        stopSwitchTwoTap(); 
     897                } 
     898                else if (!m_switchTwoTapArmed) { 
     899                        // still time for a double tap.  see if we left the tap 
     900                        // zone and, if so, arm the two tap. 
     901                        SInt32 ax, ay, aw, ah; 
     902                        m_active->getShape(ax, ay, aw, ah); 
     903                        SInt32 tapZone = m_primaryClient->getJumpZoneSize(); 
     904                        if (tapZone < m_switchTwoTapZone) { 
     905                                tapZone = m_switchTwoTapZone; 
     906                        } 
     907                        if (x >= ax + tapZone && x < ax + aw - tapZone && 
     908                                y >= ay + tapZone && y < ay + ah - tapZone) { 
     909                                // win32 can generate bogus mouse events that appear to 
     910                                // move in the opposite direction that the mouse actually 
     911                                // moved.  try to ignore that crap here. 
     912                                switch (m_switchDir) { 
     913                                case kLeft: 
     914                                        m_switchTwoTapArmed = (m_xDelta > 0 && m_xDelta2 > 0); 
     915                                        break; 
     916 
     917                                case kRight: 
     918                                        m_switchTwoTapArmed = (m_xDelta < 0 && m_xDelta2 < 0); 
     919                                        break; 
     920 
     921                                case kTop: 
     922                                        m_switchTwoTapArmed = (m_yDelta > 0 && m_yDelta2 > 0); 
     923                                        break; 
     924 
     925                                case kBottom: 
     926                                        m_switchTwoTapArmed = (m_yDelta < 0 && m_yDelta2 < 0); 
     927                                        break; 
     928 
     929                                default: 
     930                                        break; 
     931                                } 
     932                        } 
     933                } 
     934        } 
     935} 
     936 
     937void 
     938CServer::stopSwitchTwoTap() 
     939{ 
     940        m_switchTwoTapEngaged = false; 
     941        m_switchTwoTapArmed   = false; 
     942} 
     943 
     944bool 
     945CServer::isSwitchTwoTapStarted() const 
     946{ 
     947        return m_switchTwoTapEngaged; 
     948} 
     949 
     950bool 
     951CServer::shouldSwitchTwoTap() const 
     952{ 
     953        // this is the second tap if two-tap is armed and this tap 
     954        // came fast enough 
     955        return (m_switchTwoTapArmed && 
     956                        m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay); 
     957} 
     958 
     959void 
     960CServer::startSwitchWait(SInt32 x, SInt32 y) 
     961{ 
     962        stopSwitchWait(); 
     963        m_switchWaitX     = x; 
     964        m_switchWaitY     = y; 
     965        m_switchWaitTimer = EVENTQUEUE->newOneShotTimer(m_switchWaitDelay, this); 
     966        LOG((CLOG_DEBUG1 "waiting to switch")); 
     967} 
     968 
     969void 
     970CServer::stopSwitchWait() 
     971{ 
     972        if (m_switchWaitTimer != NULL) { 
     973                EVENTQUEUE->deleteTimer(m_switchWaitTimer); 
     974                m_switchWaitTimer = NULL; 
     975        } 
     976} 
     977 
     978bool 
     979CServer::isSwitchWaitStarted() const 
     980{ 
     981        return (m_switchWaitTimer != NULL); 
     982} 
     983 
     984UInt32 
     985CServer::getCorner(CBaseClientProxy* client, 
     986                                SInt32 x, SInt32 y, SInt32 size) const 
     987{ 
     988        assert(client != NULL); 
     989 
     990        // get client screen shape 
     991        SInt32 ax, ay, aw, ah; 
     992        client->getShape(ax, ay, aw, ah); 
     993 
     994        // check for x,y on the left or right 
     995        SInt32 xSide; 
     996        if (x <= ax) { 
     997                xSide = -1; 
     998        } 
     999        else if (x >= ax + aw - 1) { 
     1000                xSide = 1; 
     1001        } 
     1002        else { 
     1003                xSide = 0; 
     1004        } 
     1005 
     1006        // check for x,y on the top or bottom 
     1007        SInt32 ySide; 
     1008        if (y <= ay) { 
     1009                ySide = -1; 
     1010        } 
     1011        else if (y >= ay + ah - 1) { 
     1012                ySide = 1; 
     1013        } 
     1014        else { 
     1015                ySide = 0; 
     1016        } 
     1017 
     1018        // if against the left or right then check if y is within size 
     1019        if (xSide != 0) { 
     1020                if (y < ay + size) { 
     1021                        return (xSide < 0) ? kTopLeftMask : kTopRightMask; 
     1022                } 
     1023                else if (y >= ay + ah - size) { 
     1024                        return (xSide < 0) ? kBottomLeftMask : kBottomRightMask; 
     1025                } 
     1026        } 
     1027 
     1028        // if against the left or right then check if y is within size 
     1029        if (ySide != 0) { 
     1030                if (x < ax + size) { 
     1031                        return (ySide < 0) ? kTopLeftMask : kBottomLeftMask; 
     1032                } 
     1033                else if (x >= ax + aw - size) { 
     1034                        return (ySide < 0) ? kTopRightMask : kBottomRightMask; 
     1035                } 
     1036        } 
     1037 
     1038        return kNoCornerMask; 
     1039} 
     1040 
     1041void 
     1042CServer::stopRelativeMoves() 
     1043{ 
     1044        if (m_relativeMoves && m_active != m_primaryClient) { 
     1045                // warp to the center of the active client so we know where we are 
     1046                SInt32 ax, ay, aw, ah; 
     1047                m_active->getShape(ax, ay, aw, ah); 
     1048                m_x       = ax + (aw >> 1); 
     1049                m_y       = ay + (ah >> 1); 
     1050                m_xDelta  = 0; 
     1051                m_yDelta  = 0; 
     1052                m_xDelta2 = 0; 
     1053                m_yDelta2 = 0; 
     1054                LOG((CLOG_DEBUG2 "synchronize move on %s by %d,%d", getName(m_active).c_str(), m_x, m_y)); 
     1055                m_active->mouseMove(m_x, m_y); 
     1056        } 
     1057} 
     1058 
     1059void 
     1060CServer::sendOptions(CBaseClientProxy* client) const 
     1061{ 
     1062        COptionsList optionsList; 
     1063 
     1064        // look up options for client 
     1065        const CConfig::CScreenOptions* options = 
     1066                                                m_config.getOptions(getName(client)); 
     1067        if (options != NULL) { 
     1068                // convert options to a more convenient form for sending 
     1069                optionsList.reserve(2 * options->size()); 
     1070                for (CConfig::CScreenOptions::const_iterator index = options->begin(); 
     1071                                                                        index != options->end(); ++index) { 
     1072                        optionsList.push_back(index->first); 
     1073                        optionsList.push_back(static_cast<UInt32>(index->second)); 
     1074                } 
     1075        } 
     1076 
     1077        // look up global options 
     1078        options = m_config.getOptions(""); 
     1079        if (options != NULL) { 
     1080                // convert options to a more convenient form for sending 
     1081                optionsList.reserve(optionsList.size() + 2 * options->size()); 
     1082                for (CConfig::CScreenOptions::const_iterator index = options->begin(); 
     1083                                                                        index != options->end(); ++index) { 
     1084                        optionsList.push_back(index->first); 
     1085                        optionsList.push_back(static_cast<UInt32>(index->second)); 
     1086                } 
     1087        } 
     1088 
     1089        // send the options 
     1090        client->resetOptions(); 
     1091        client->setOptions(optionsList); 
     1092} 
     1093 
     1094void 
     1095CServer::processOptions() 
     1096{ 
     1097        const CConfig::CScreenOptions* options = m_config.getOptions(""); 
     1098        if (options == NULL) { 
     1099                return; 
     1100        } 
     1101 
     1102        bool newRelativeMoves = m_relativeMoves; 
     1103        for (CConfig::CScreenOptions::const_iterator index = options->begin(); 
     1104                                                                index != options->end(); ++index) { 
     1105                const OptionID id       = index->first; 
     1106                const OptionValue value = index->second; 
     1107                if (id == kOptionScreenSwitchDelay) { 
     1108                        m_switchWaitDelay = 1.0e-3 * static_cast<double>(value); 
     1109                        if (m_switchWaitDelay < 0.0) { 
     1110                                m_switchWaitDelay = 0.0; 
     1111                        } 
     1112                        stopSwitchWait(); 
     1113                } 
     1114                else if (id == kOptionScreenSwitchTwoTap) { 
     1115                        m_switchTwoTapDelay = 1.0e-3 * static_cast<double>(value); 
     1116                        if (m_switchTwoTapDelay < 0.0) { 
     1117                                m_switchTwoTapDelay = 0.0; 
     1118                        } 
     1119                        stopSwitchTwoTap(); 
     1120                } 
     1121                else if (id == kOptionRelativeMouseMoves) { 
     1122                        newRelativeMoves = (value != 0); 
     1123                } 
     1124        } 
     1125 
     1126        if (m_relativeMoves && !newRelativeMoves) { 
     1127                stopRelativeMoves(); 
     1128        } 
     1129        m_relativeMoves = newRelativeMoves; 
     1130} 
     1131 
     1132void 
     1133CServer::handleShapeChanged(const CEvent&, void* vclient) 
     1134{ 
     1135        // ignore events from unknown clients 
     1136        CBaseClientProxy* client = reinterpret_cast<CBaseClientProxy*>(vclient); 
     1137        if (m_clientSet.count(client) == 0) { 
     1138                return; 
     1139        } 
     1140 
     1141        LOG((CLOG_INFO "screen \"%s\" shape changed", getName(client).c_str())); 
     1142 
     1143        // update jump coordinate 
     1144        SInt32 x, y; 
     1145        client->getCursorPos(x, y); 
     1146        client->setJumpCursorPos(x, y); 
     1147 
     1148        // update the mouse coordinates 
     1149        if (client == m_active) { 
     1150                m_x = x; 
     1151                m_y = y; 
     1152        } 
     1153 
     1154        // handle resolution change to primary screen 
     1155        if (client == m_primaryClient) { 
     1156                if (client == m_active) { 
     1157                        onMouseMovePrimary(m_x, m_y); 
     1158                } 
     1159                else { 
     1160                        onMouseMoveSecondary(0, 0); 
     1161                } 
     1162        } 
     1163} 
     1164 
     1165void 
     1166CServer::handleClipboardGrabbed(const CEvent& event, void* vclient) 
     1167{ 
     1168        // ignore events from unknown clients 
     1169        CBaseClientProxy* grabber = reinterpret_cast<CBaseClientProxy*>(vclient); 
     1170        if (m_clientSet.count(grabber) == 0) { 
     1171                return; 
     1172        } 
     1173        const IScreen::CClipboardInfo* info = 
     1174                reinterpret_cast<const IScreen::CClipboardInfo*>(event.getData()); 
     1175 
     1176        // ignore grab if sequence number is old.  always allow primary 
     1177        // screen to grab. 
     1178        CClipboardInfo& clipboard = m_clipboards[info->m_id]; 
     1179        if (grabber != m_primaryClient && 
     1180                info->m_sequenceNumber < clipboard.m_clipboardSeqNum) { 
     1181                LOG((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", getName(grabber).c_str(), info->m_id)); 
     1182                return; 
     1183        } 
     1184 
     1185        // mark screen as owning clipboard 
     1186        LOG((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", getName(grabber).c_str(), info->m_id, clipboard.m_clipboardOwner.c_str())); 
     1187        clipboard.m_clipboardOwner  = getName(grabber); 
     1188        clipboard.m_clipboardSeqNum = info->m_sequenceNumber; 
     1189 
     1190        // clear the clipboard data (since it's not known at this point) 
     1191        if (clipboard.m_clipboard.open(0)) { 
     1192                clipboard.m_clipboard.empty(); 
     1193                clipboard.m_clipboard.close(); 
     1194        } 
     1195        clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); 
     1196 
     1197        // tell all other screens to take ownership of clipboard.  tell the 
     1198        // grabber that it's clipboard isn't dirty. 
     1199        for (CClientList::iterator index = m_clients.begin(); 
     1200                                                                index != m_clients.end(); ++index) { 
     1201                CBaseClientProxy* client = index->second; 
     1202                if (client == grabber) { 
     1203                        client->setClipboardDirty(info->m_id, false); 
     1204                } 
     1205                else { 
     1206                        client->grabClipboard(info->m_id); 
     1207                } 
     1208        } 
     1209} 
     1210 
     1211void 
     1212CServer::handleClipboardChanged(const CEvent& event, void* vclient) 
     1213{ 
     1214        // ignore events from unknown clients 
     1215        CBaseClientProxy* sender = reinterpret_cast<CBaseClientProxy*>(vclient); 
     1216        if (m_clientSet.count(sender) == 0) { 
     1217                return; 
     1218        } 
     1219        const IScreen::CClipboardInfo* info = 
     1220                reinterpret_cast<const IScreen::CClipboardInfo*>(event.getData()); 
     1221        onClipboardChanged(sender, info->m_id, info->m_sequenceNumber); 
     1222} 
     1223 
     1224void 
     1225CServer::handleKeyDownEvent(const CEvent& event, void*) 
     1226{ 
     1227        IPlatformScreen::CKeyInfo* info = 
     1228                reinterpret_cast<IPlatformScreen::CKeyInfo*>(event.getData()); 
     1229        onKeyDown(info->m_key, info->m_mask, info->m_button, info->m_screens); 
     1230} 
     1231 
     1232void 
     1233CServer::handleKeyUpEvent(const CEvent& event, void*) 
     1234{ 
     1235        IPlatformScreen::CKeyInfo* info = 
     1236                 reinterpret_cast<IPlatformScreen::CKeyInfo*>(event.getData()); 
     1237        onKeyUp(info->m_key, info->m_mask, info->m_button, info->m_screens); 
     1238} 
     1239 
     1240void 
     1241CServer::handleKeyRepeatEvent(const CEvent& event, void*) 
     1242{ 
     1243        IPlatformScreen::CKeyInfo* info = 
     1244                reinterpret_cast<IPlatformScreen::CKeyInfo*>(event.getData()); 
     1245        onKeyRepeat(info->m_key, info->m_mask, info->m_count, info->m_button); 
     1246} 
     1247 
     1248void 
     1249CServer::handleButtonDownEvent(const CEvent& event, void*) 
     1250{ 
     1251        IPlatformScreen::CButtonInfo* info = 
     1252                reinterpret_cast<IPlatformScreen::CButtonInfo*>(event.getData()); 
     1253        onMouseDown(info->m_button); 
     1254} 
     1255 
     1256void 
     1257CServer::handleButtonUpEvent(const CEvent& event, void*) 
     1258{ 
     1259        IPlatformScreen::CButtonInfo* info = 
     1260                reinterpret_cast<IPlatformScreen::CButtonInfo*>(event.getData()); 
     1261        onMouseUp(info->m_button); 
     1262} 
     1263 
     1264void 
     1265CServer::handleMotionPrimaryEvent(const CEvent& event, void*) 
     1266{ 
     1267        IPlatformScreen::CMotionInfo* info = 
     1268                reinterpret_cast<IPlatformScreen::CMotionInfo*>(event.getData()); 
     1269        onMouseMovePrimary(info->m_x, info->m_y); 
     1270} 
     1271 
     1272void 
     1273CServer::handleMotionSecondaryEvent(const CEvent& event, void*) 
     1274{ 
     1275        IPlatformScreen::CMotionInfo* info = 
     1276                reinterpret_cast<IPlatformScreen::CMotionInfo*>(event.getData()); 
     1277        onMouseMoveSecondary(info->m_x, info->m_y); 
     1278} 
     1279 
     1280void 
     1281CServer::handleWheelEvent(const CEvent& event, void*) 
     1282{ 
     1283        IPlatformScreen::CWheelInfo* info = 
     1284                reinterpret_cast<IPlatformScreen::CWheelInfo*>(event.getData()); 
     1285        onMouseWheel(info->m_xDelta, info->m_yDelta); 
     1286} 
     1287 
     1288void 
     1289CServer::handleScreensaverActivatedEvent(const CEvent&, void*) 
     1290{ 
     1291        onScreensaver(true); 
     1292} 
     1293 
     1294void 
     1295CServer::handleScreensaverDeactivatedEvent(const CEvent&, void*) 
     1296{ 
     1297        onScreensaver(false); 
     1298} 
     1299 
     1300void 
     1301CServer::handleSwitchWaitTimeout(const CEvent&, void*) 
     1302{ 
     1303        // ignore if mouse is locked to screen 
     1304        if (isLockedToScreen()) { 
     1305                LOG((CLOG_DEBUG1 "locked to screen")); 
     1306                stopSwitch(); 
     1307                return; 
     1308        } 
     1309 
     1310        // switch screen 
     1311        switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false); 
     1312} 
     1313 
     1314void 
     1315CServer::handleClientDisconnected(const CEvent&, void* vclient) 
     1316{ 
     1317        // client has disconnected.  it might be an old client or an 
     1318        // active client.  we don't care so just handle it both ways. 
     1319        CBaseClientProxy* client = reinterpret_cast<CBaseClientProxy*>(vclient); 
     1320        removeActiveClient(client); 
     1321        removeOldClient(client); 
     1322        delete client; 
     1323} 
     1324 
     1325void 
     1326CServer::handleClientCloseTimeout(const CEvent&, void* vclient) 
     1327{ 
     1328        // client took too long to disconnect.  just dump it. 
     1329        CBaseClientProxy* client = reinterpret_cast<CBaseClientProxy*>(vclient); 
     1330        LOG((CLOG_NOTE "forced disconnection of client \"%s\"", getName(client).c_str())); 
     1331        removeOldClient(client); 
     1332        delete client; 
     1333} 
     1334 
     1335void 
     1336CServer::handleSwitchToScreenEvent(const CEvent& event, void*) 
     1337{ 
     1338        CSwitchToScreenInfo* info =  
     1339                reinterpret_cast<CSwitchToScreenInfo*>(event.getData()); 
     1340 
     1341        CClientList::const_iterator index = m_clients.find(info->m_screen); 
     1342        if (index == m_clients.end()) { 
     1343                LOG((CLOG_DEBUG1 "screen \"%s\" not active", info->m_screen)); 
     1344        } 
     1345        else { 
     1346                jumpToScreen(index->second); 
     1347        } 
     1348} 
     1349 
     1350void 
     1351CServer::handleSwitchInDirectionEvent(const CEvent& event, void*) 
     1352{ 
     1353        CSwitchInDirectionInfo* info =  
     1354                reinterpret_cast<CSwitchInDirectionInfo*>(event.getData()); 
     1355 
     1356        // jump to screen in chosen direction from center of this screen 
     1357        SInt32 x = m_x, y = m_y; 
     1358        CBaseClientProxy* newScreen = 
     1359                getNeighbor(m_active, info->m_direction, x, y); 
     1360        if (newScreen == NULL) { 
     1361                LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(info->m_direction))); 
     1362        } 
     1363        else { 
     1364                jumpToScreen(newScreen); 
     1365        } 
     1366} 
     1367 
     1368void 
     1369CServer::handleLockCursorToScreenEvent(const CEvent& event, void*) 
     1370{ 
     1371        CLockCursorToScreenInfo* info = (CLockCursorToScreenInfo*)event.getData(); 
     1372 
     1373        // choose new state 
     1374        bool newState; 
     1375        switch (info->m_state) { 
     1376        case CLockCursorToScreenInfo::kOff: 
     1377                newState = false; 
     1378                break; 
     1379 
     1380        default: 
     1381        case CLockCursorToScreenInfo::kOn: 
     1382                newState = true; 
     1383                break; 
     1384 
     1385        case CLockCursorToScreenInfo::kToggle: 
     1386                newState = !m_lockedToScreen; 
     1387                break; 
     1388        } 
     1389 
     1390        // enter new state 
     1391        if (newState != m_lockedToScreen) { 
     1392                SInt32 x, y; 
     1393                m_lockedToScreen = newState; 
     1394 
     1395                LOG((CLOG_DEBUG "cursor %s current screen", m_lockedToScreen ? "locked to" : "unlocked from")); 
     1396 
     1397                m_primaryClient->reconfigure(getActivePrimarySides()); 
     1398                if (!isLockedToScreenServer()) { 
     1399                        //stopRelativeMoves(); 
     1400                } 
     1401        } 
     1402} 
     1403 
     1404void 
     1405CServer::handleFakeInputBeginEvent(const CEvent&, void*) 
     1406{ 
     1407        m_primaryClient->fakeInputBegin(); 
     1408} 
     1409 
     1410void 
     1411CServer::handleFakeInputEndEvent(const CEvent&, void*) 
     1412{ 
     1413        m_primaryClient->fakeInputEnd(); 
     1414} 
     1415 
     1416void 
     1417CServer::onClipboardChanged(CBaseClientProxy* sender, 
     1418                                ClipboardID id, UInt32 seqNum) 
     1419{ 
     1420        CClipboardInfo& clipboard = m_clipboards[id]; 
     1421 
     1422        // ignore update if sequence number is old 
     1423        if (seqNum < clipboard.m_clipboardSeqNum) { 
     1424                LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", getName(sender).c_str(), id)); 
     1425                return; 
     1426        } 
     1427 
     1428        // should be the expected client 
     1429        assert(sender == m_clients.find(clipboard.m_clipboardOwner)->second); 
     1430 
     1431        // get data 
     1432        sender->getClipboard(id, &clipboard.m_clipboard); 
     1433 
     1434        // ignore if data hasn't changed 
     1435        CString data = clipboard.m_clipboard.marshall(); 
     1436        if (data == clipboard.m_clipboardData) { 
     1437                LOG((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id)); 
     1438                return; 
     1439        } 
     1440 
     1441        // got new data 
     1442        LOG((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); 
     1443        clipboard.m_clipboardData = data; 
     1444 
     1445        // tell all clients except the sender that the clipboard is dirty 
     1446        for (CClientList::const_iterator index = m_clients.begin(); 
     1447                                                                index != m_clients.end(); ++index) { 
     1448                CBaseClientProxy* client = index->second; 
     1449                client->setClipboardDirty(id, client != sender); 
     1450        } 
     1451 
     1452        // send the new clipboard to the active screen 
     1453        m_active->setClipboard(id, &clipboard.m_clipboard); 
     1454} 
     1455 
     1456void 
     1457CServer::onScreensaver(bool activated) 
     1458{ 
     1459        LOG((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); 
     1460 
     1461        if (activated) { 
     1462                // save current screen and position 
     1463                m_activeSaver = m_active; 
     1464                m_xSaver      = m_x; 
     1465                m_ySaver      = m_y; 
     1466 
     1467                // jump to primary screen 
     1468                if (m_active != m_primaryClient) { 
     1469                        switchScreen(m_primaryClient, 0, 0, true); 
     1470                } 
     1471        } 
     1472        else { 
     1473                // jump back to previous screen and position.  we must check 
     1474                // that the position is still valid since the screen may have 
     1475                // changed resolutions while the screen saver was running. 
     1476                if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { 
     1477                        // check position 
     1478                        CBaseClientProxy* screen = m_activeSaver; 
     1479                        SInt32 x, y, w, h; 
     1480                        screen->getShape(x, y, w, h); 
     1481                        SInt32 zoneSize = getJumpZoneSize(screen); 
     1482                        if (m_xSaver < x + zoneSize) { 
     1483                                m_xSaver = x + zoneSize; 
     1484                        } 
     1485                        else if (m_xSaver >= x + w - zoneSize) { 
     1486                                m_xSaver = x + w - zoneSize - 1; 
     1487                        } 
     1488                        if (m_ySaver < y + zoneSize) { 
     1489                                m_ySaver = y + zoneSize; 
     1490                        } 
     1491                        else if (m_ySaver >= y + h - zoneSize) { 
     1492                                m_ySaver = y + h - zoneSize - 1; 
     1493                        } 
     1494 
     1495                        // jump 
     1496                        switchScreen(screen, m_xSaver, m_ySaver, false); 
     1497                } 
     1498 
     1499                // reset state 
     1500                m_activeSaver = NULL; 
     1501        } 
     1502 
     1503        // send message to all clients 
     1504        for (CClientList::const_iterator index = m_clients.begin(); 
     1505                                                                index != m_clients.end(); ++index) { 
     1506                CBaseClientProxy* client = index->second; 
     1507                client->screensaver(activated); 
     1508        } 
     1509} 
     1510 
     1511void 
     1512CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button, 
     1513                                const char* screens) 
     1514{ 
     1515        LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button)); 
     1516        assert(m_active != NULL); 
     1517 
     1518        // relay 
     1519        if (IKeyState::CKeyInfo::isDefault(screens)) { 
     1520                if (m_active != m_primaryClient) 
     1521                { 
     1522                        m_active->keyDown(id, mask, button); 
     1523                } 
     1524                else 
     1525                {  
     1526                        if (id == 96 && mask == 0 && button == 0x0033) 
     1527                        { 
     1528                                if (m_broadcast) 
     1529                                { 
     1530                                        m_broadcast = false; 
     1531                                } 
     1532                                else 
     1533                                { 
     1534                                        m_broadcast = true; 
     1535                                } 
     1536                        } 
     1537                        if (m_broadcast) 
     1538                        { 
     1539                                for (CClientList::const_iterator index = m_clients.begin(); 
     1540                                                 index != m_clients.end(); ++index) { 
     1541                                        index->second->keyDown(id, mask, button); 
     1542                                } 
     1543                        } 
     1544                } 
     1545        } 
     1546        else { 
     1547                for (CClientList::const_iterator index = m_clients.begin(); 
     1548                                                                index != m_clients.end(); ++index) { 
     1549                        if (IKeyState::CKeyInfo::contains(screens, index->first)) { 
     1550                                index->second->keyDown(id, mask, button); 
     1551                        } 
     1552                } 
     1553        } 
     1554} 
     1555 
     1556void 
     1557CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button, 
     1558                                const char* screens) 
     1559{ 
     1560        LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button)); 
     1561        assert(m_active != NULL); 
     1562 
     1563        // relay 
     1564        if (IKeyState::CKeyInfo::isDefault(screens)) { 
     1565                if (m_active != m_primaryClient) 
     1566                { 
     1567                        m_active->keyUp(id, mask, button); 
     1568                } 
     1569                else if (m_broadcast) 
     1570                { 
     1571                        for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { 
     1572                                index->second->keyUp(id, mask, button); 
     1573                        } 
     1574                } 
     1575        } 
     1576        else { 
     1577                for (CClientList::const_iterator index = m_clients.begin(); 
     1578                                                                index != m_clients.end(); ++index) { 
     1579                        if (IKeyState::CKeyInfo::contains(screens, index->first)) { 
     1580                                index->second->keyUp(id, mask, button); 
     1581                        } 
     1582                } 
     1583        } 
     1584} 
     1585 
     1586void 
     1587CServer::onKeyRepeat(KeyID id, KeyModifierMask mask, 
     1588                                SInt32 count, KeyButton button) 
     1589{ 
     1590        LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button)); 
     1591        assert(m_active != NULL); 
     1592 
     1593        // relay 
     1594        if (m_active != m_primaryClient) 
     1595        { 
     1596                m_active->keyRepeat(id, mask, count, button); 
     1597        } 
     1598        else if (m_broadcast) 
     1599        { 
     1600                for (CClientList::const_iterator index = m_clients.begin(); index != m_clients.end(); ++index) { 
     1601                        index->second->keyRepeat(id, mask, count, button); 
     1602                } 
     1603        } 
     1604} 
     1605 
     1606void 
     1607CServer::onMouseDown(ButtonID id) 
     1608{ 
     1609        LOG((CLOG_DEBUG1 "onMouseDown id=%d", id)); 
     1610        assert(m_active != NULL); 
     1611 
     1612        // relay 
     1613        m_active->mouseDown(id); 
     1614} 
     1615 
     1616void 
     1617CServer::onMouseUp(ButtonID id) 
     1618{ 
     1619        LOG((CLOG_DEBUG1 "onMouseUp id=%d", id)); 
     1620        assert(m_active != NULL); 
     1621 
     1622        // relay 
     1623        m_active->mouseUp(id); 
     1624} 
     1625 
     1626bool 
     1627CServer::onMouseMovePrimary(SInt32 x, SInt32 y) 
     1628{ 
     1629        LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); 
     1630 
     1631        // mouse move on primary (server's) screen 
     1632        if (m_active != m_primaryClient) { 
     1633                // stale event -- we're actually on a secondary screen 
     1634                return false; 
     1635        } 
     1636 
     1637        // save last delta 
     1638        m_xDelta2 = m_xDelta; 
     1639        m_yDelta2 = m_yDelta; 
     1640 
     1641        // save current delta 
     1642        m_xDelta  = x - m_x; 
     1643        m_yDelta  = y - m_y; 
     1644 
     1645        // save position 
     1646        m_x       = x; 
     1647        m_y       = y; 
     1648 
     1649        // get screen shape 
     1650        SInt32 ax, ay, aw, ah; 
     1651        m_active->getShape(ax, ay, aw, ah); 
     1652        SInt32 zoneSize = getJumpZoneSize(m_active); 
     1653 
     1654        // clamp position to screen 
     1655        SInt32 xc = x, yc = y; 
     1656        if (xc < ax + zoneSize) { 
     1657                xc = ax; 
     1658        } 
     1659        else if (xc >= ax + aw - zoneSize) { 
     1660                xc = ax + aw - 1; 
     1661        } 
     1662        if (yc < ay + zoneSize) { 
     1663                yc = ay; 
     1664        } 
     1665        else if (yc >= ay + ah - zoneSize) { 
     1666                yc = ay + ah - 1; 
     1667        } 
     1668 
     1669        // see if we should change screens 
     1670        EDirection dir; 
     1671        if (x < ax + zoneSize) { 
     1672                x  -= zoneSize; 
     1673                dir = kLeft; 
     1674        } 
     1675        else if (x >= ax + aw - zoneSize) { 
     1676                x  += zoneSize; 
     1677                dir = kRight; 
     1678        } 
     1679        else if (y < ay + zoneSize) { 
     1680                y  -= zoneSize; 
     1681                dir = kTop; 
     1682        } 
     1683        else if (y >= ay + ah - zoneSize) { 
     1684                y  += zoneSize; 
     1685                dir = kBottom; 
     1686        } 
     1687        else { 
     1688                // still on local screen 
     1689                noSwitch(x, y); 
     1690                return false; 
     1691        } 
     1692 
     1693        // get jump destination 
     1694        CBaseClientProxy* newScreen = mapToNeighbor(m_active, dir, x, y); 
     1695 
     1696        // should we switch or not? 
     1697        if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) { 
     1698                // switch screen 
     1699                switchScreen(newScreen, x, y, false); 
     1700                return true; 
     1701        } 
     1702        else { 
     1703                return false; 
     1704        } 
     1705} 
     1706 
     1707void 
     1708CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy) 
     1709{ 
     1710        LOG((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy)); 
     1711 
     1712        // mouse move on secondary (client's) screen 
     1713        assert(m_active != NULL); 
     1714        if (m_active == m_primaryClient) { 
     1715                // stale event -- we're actually on the primary screen 
     1716                return; 
     1717        } 
     1718 
     1719        // if doing relative motion on secondary screens and we're locked 
     1720        // to the screen (which activates relative moves) then send a 
     1721        // relative mouse motion.  when we're doing this we pretend as if 
     1722        // the mouse isn't actually moving because we're expecting some 
     1723        // program on the secondary screen to warp the mouse on us, so we 
     1724        // have no idea where it really is. 
     1725        if (m_relativeMoves && isLockedToScreenServer()) { 
     1726                LOG((CLOG_DEBUG2 "relative move on %s by %d,%d", getName(m_active).c_str(), dx, dy)); 
     1727                m_active->mouseRelativeMove(dx, dy); 
     1728                return; 
     1729        } 
     1730 
     1731        // save old position 
     1732        const SInt32 xOld = m_x; 
     1733        const SInt32 yOld = m_y; 
     1734 
     1735        // save last delta 
     1736        m_xDelta2 = m_xDelta; 
     1737        m_yDelta2 = m_yDelta; 
     1738 
     1739        // save current delta 
     1740        m_xDelta  = dx; 
     1741        m_yDelta  = dy; 
     1742 
     1743        // accumulate motion 
     1744        m_x      += dx; 
     1745        m_y      += dy; 
     1746 
     1747        // get screen shape 
     1748        SInt32 ax, ay, aw, ah; 
     1749        m_active->getShape(ax, ay, aw, ah); 
     1750 
     1751        // find direction of neighbor and get the neighbor 
     1752        bool jump = true; 
     1753        CBaseClientProxy* newScreen; 
     1754        do { 
     1755                // clamp position to screen 
     1756                SInt32 xc = m_x, yc = m_y; 
     1757                if (xc < ax) { 
     1758                        xc = ax; 
     1759                } 
     1760                else if (xc >= ax + aw) { 
     1761                        xc = ax + aw - 1; 
     1762                } 
     1763                if (yc < ay) { 
     1764                        yc = ay; 
     1765                } 
     1766                else if (yc >= ay + ah) { 
     1767                        yc = ay + ah - 1; 
     1768                } 
     1769 
     1770                EDirection dir; 
     1771                if (m_x < ax) { 
     1772                        dir = kLeft; 
     1773                } 
     1774                else if (m_x > ax + aw - 1) { 
     1775                        dir = kRight; 
     1776                } 
     1777                else if (m_y < ay) { 
     1778                        dir = kTop; 
     1779                } 
     1780                else if (m_y > ay + ah - 1) { 
     1781                        dir = kBottom; 
     1782                } 
     1783                else { 
     1784                        // we haven't left the screen 
     1785                        newScreen = m_active; 
     1786                        jump      = false; 
     1787 
     1788                        // if waiting and mouse is not on the border we're waiting 
     1789                        // on then stop waiting.  also if it's not on the border 
     1790                        // then arm the double tap. 
     1791                        if (m_switchScreen != NULL) { 
     1792                                bool clearWait; 
     1793                                SInt32 zoneSize = m_primaryClient->getJumpZoneSize(); 
     1794                                switch (m_switchDir) { 
     1795                                case kLeft: 
     1796                                        clearWait = (m_x >= ax + zoneSize); 
     1797                                        break; 
     1798 
     1799                                case kRight: 
     1800                                        clearWait = (m_x <= ax + aw - 1 - zoneSize); 
     1801                                        break; 
     1802 
     1803                                case kTop: 
     1804                                        clearWait = (m_y >= ay + zoneSize); 
     1805                                        break; 
     1806 
     1807                                case kBottom: 
     1808                                        clearWait = (m_y <= ay + ah - 1 + zoneSize); 
     1809                                        break; 
     1810 
     1811                                default: 
     1812                                        clearWait = false; 
     1813                                        break; 
     1814                                } 
     1815                                if (clearWait) { 
     1816                                        // still on local screen 
     1817                                        noSwitch(m_x, m_y); 
     1818                                } 
     1819                        } 
     1820 
     1821                        // skip rest of block 
     1822                        break; 
     1823                } 
     1824 
     1825                // try to switch screen.  get the neighbor. 
     1826                newScreen = mapToNeighbor(m_active, dir, m_x, m_y); 
     1827 
     1828                // see if we should switch 
     1829                if (!isSwitchOkay(newScreen, dir, m_x, m_y, xc, yc)) { 
     1830                        newScreen = m_active; 
     1831                        jump      = false; 
     1832                } 
     1833        } while (false); 
     1834 
     1835        if (jump) { 
     1836                // switch screens 
     1837                switchScreen(newScreen, m_x, m_y, false); 
     1838        } 
     1839        else { 
     1840                // same screen.  clamp mouse to edge. 
     1841                m_x = xOld + dx; 
     1842                m_y = yOld + dy; 
     1843                if (m_x < ax) { 
     1844                        m_x = ax; 
     1845                        LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", getName(m_active).c_str())); 
     1846                } 
     1847                else if (m_x > ax + aw - 1) { 
     1848                        m_x = ax + aw - 1; 
     1849                        LOG((CLOG_DEBUG2 "clamp to right of \"%s\"", getName(m_active).c_str())); 
     1850                } 
     1851                if (m_y < ay) { 
     1852                        m_y = ay; 
     1853                        LOG((CLOG_DEBUG2 "clamp to top of \"%s\"", getName(m_active).c_str())); 
     1854                } 
     1855                else if (m_y > ay + ah - 1) { 
     1856                        m_y = ay + ah - 1; 
     1857                        LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", getName(m_active).c_str())); 
     1858                } 
     1859 
     1860                // warp cursor if it moved. 
     1861                if (m_x != xOld || m_y != yOld) { 
     1862                        LOG((CLOG_DEBUG2 "move on %s to %d,%d", getName(m_active).c_str(), m_x, m_y)); 
     1863                        m_active->mouseMove(m_x, m_y); 
     1864                } 
     1865        } 
     1866} 
     1867 
     1868void 
     1869CServer::onMouseWheel(SInt32 xDelta, SInt32 yDelta) 
     1870{ 
     1871        LOG((CLOG_DEBUG1 "onMouseWheel %+d,%+d", xDelta, yDelta)); 
     1872        assert(m_active != NULL); 
     1873 
     1874        // relay 
     1875        m_active->mouseWheel(xDelta, yDelta); 
     1876} 
     1877 
     1878bool 
     1879CServer::addClient(CBaseClientProxy* client) 
     1880{ 
     1881        CString name = getName(client); 
     1882        if (m_clients.count(name) != 0) { 
     1883                return false; 
     1884        } 
     1885 
     1886        // add event handlers 
     1887        EVENTQUEUE->adoptHandler(IScreen::getShapeChangedEvent(), 
     1888                                                        client->getEventTarget(), 
     1889                                                        new TMethodEventJob<CServer>(this, 
     1890                                                                &CServer::handleShapeChanged, client)); 
     1891        EVENTQUEUE->adoptHandler(IScreen::getClipboardGrabbedEvent(), 
     1892                                                        client->getEventTarget(), 
     1893                                                        new TMethodEventJob<CServer>(this, 
     1894                                                                &CServer::handleClipboardGrabbed, client)); 
     1895        EVENTQUEUE->adoptHandler(CClientProxy::getClipboardChangedEvent(), 
     1896                                                        client->getEventTarget(), 
     1897                                                        new TMethodEventJob<CServer>(this, 
     1898                                                                &CServer::handleClipboardChanged, client)); 
     1899 
     1900        // add to list 
     1901        m_clientSet.insert(client); 
     1902        m_clients.insert(std::make_pair(name, client)); 
     1903 
     1904        // initialize client data 
     1905        SInt32 x, y; 
     1906        client->getCursorPos(x, y); 
     1907        client->setJumpCursorPos(x, y); 
     1908 
     1909        // tell primary client about the active sides 
     1910        m_primaryClient->reconfigure(getActivePrimarySides()); 
     1911 
     1912        return true; 
     1913} 
     1914 
     1915bool 
     1916CServer::removeClient(CBaseClientProxy* client) 
     1917{ 
     1918        // return false if not in list 
     1919        CClientSet::iterator i = m_clientSet.find(client); 
     1920        if (i == m_clientSet.end()) { 
     1921                return false; 
     1922        } 
     1923 
     1924        // remove event handlers 
     1925        EVENTQUEUE->removeHandler(IScreen::getShapeChangedEvent(), 
     1926                                                        client->getEventTarget()); 
     1927        EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(), 
     1928                                                        client->getEventTarget()); 
     1929        EVENTQUEUE->removeHandler(CClientProxy::getClipboardChangedEvent(), 
     1930                                                        client->getEventTarget()); 
     1931 
     1932        // remove from list 
     1933        m_clients.erase(getName(client)); 
     1934        m_clientSet.erase(i); 
     1935 
     1936        return true; 
     1937} 
     1938 
     1939void 
     1940CServer::closeClient(CBaseClientProxy* client, const char* msg) 
     1941{ 
     1942        assert(client != m_primaryClient); 
     1943        assert(msg != NULL); 
     1944 
     1945        // send message to client.  this message should cause the client 
     1946        // to disconnect.  we add this client to the closed client list 
     1947        // and install a timer to remove the client if it doesn't respond 
     1948        // quickly enough.  we also remove the client from the active 
     1949        // client list since we're not going to listen to it anymore. 
     1950        // note that this method also works on clients that are not in 
     1951        // the m_clients list.  adoptClient() may call us with such a 
     1952        // client. 
     1953        LOG((CLOG_NOTE "disconnecting client \"%s\"", getName(client).c_str())); 
     1954 
     1955        // send message 
     1956        // FIXME -- avoid type cast (kinda hard, though) 
     1957        ((CClientProxy*)client)->close(msg); 
     1958 
     1959        // install timer.  wait timeout seconds for client to close. 
     1960        double timeout = 5.0; 
     1961        CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL); 
     1962        EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, 
     1963                                                        new TMethodEventJob<CServer>(this, 
     1964                                                                &CServer::handleClientCloseTimeout, client)); 
     1965 
     1966        // move client to closing list 
     1967        removeClient(client); 
     1968        m_oldClients.insert(std::make_pair(client, timer)); 
     1969 
     1970        // if this client is the active screen then we have to 
     1971        // jump off of it 
     1972        forceLeaveClient(client); 
     1973} 
     1974 
     1975void 
     1976CServer::closeClients(const CConfig& config) 
     1977{ 
     1978        // collect the clients that are connected but are being dropped 
     1979        // from the configuration (or who's canonical name is changing). 
     1980        typedef std::set<CBaseClientProxy*> CRemovedClients; 
     1981        CRemovedClients removed; 
     1982        for (CClientList::iterator index = m_clients.begin(); 
     1983                                                                index != m_clients.end(); ++index) { 
     1984                if (!config.isCanonicalName(index->first)) { 
     1985                        removed.insert(index->second); 
     1986                } 
     1987        } 
     1988 
     1989        // don't close the primary client 
     1990        removed.erase(m_primaryClient); 
     1991 
     1992        // now close them.  we collect the list then close in two steps 
     1993        // because closeClient() modifies the collection we iterate over. 
     1994        for (CRemovedClients::iterator index = removed.begin(); 
     1995                                                                index != removed.end(); ++index) { 
     1996                closeClient(*index, kMsgCClose); 
     1997        } 
     1998} 
     1999 
     2000void 
     2001CServer::removeActiveClient(CBaseClientProxy* client) 
     2002{ 
     2003        if (removeClient(client)) { 
     2004                forceLeaveClient(client); 
     2005                EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); 
     2006                if (m_clients.size() == 1 && m_oldClients.empty()) { 
     2007                        EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this)); 
     2008                } 
     2009        } 
     2010} 
     2011 
     2012void 
     2013CServer::removeOldClient(CBaseClientProxy* client) 
     2014{ 
     2015        COldClients::iterator i = m_oldClients.find(client); 
     2016        if (i != m_oldClients.end()) { 
     2017                EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client); 
     2018                EVENTQUEUE->removeHandler(CEvent::kTimer, i->second); 
     2019                EVENTQUEUE->deleteTimer(i->second); 
     2020                m_oldClients.erase(i); 
     2021                if (m_clients.size() == 1 && m_oldClients.empty()) { 
     2022                        EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), this)); 
     2023                } 
     2024        } 
     2025} 
     2026 
     2027void 
     2028CServer::forceLeaveClient(CBaseClientProxy* client) 
     2029{ 
     2030        CBaseClientProxy* active = 
     2031                (m_activeSaver != NULL) ? m_activeSaver : m_active; 
     2032        if (active == client) { 
     2033                // record new position (center of primary screen) 
     2034                m_primaryClient->getCursorCenter(m_x, m_y); 
     2035 
     2036                // stop waiting to switch to this client 
     2037                if (active == m_switchScreen) { 
     2038                        stopSwitch(); 
     2039                } 
     2040 
     2041                // don't notify active screen since it has probably already 
     2042                // disconnected. 
     2043                LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", getName(active).c_str(), getName(m_primaryClient).c_str(), m_x, m_y)); 
     2044 
     2045                // cut over 
     2046                m_active = m_primaryClient; 
     2047 
     2048                // enter new screen (unless we already have because of the 
     2049                // screen saver) 
     2050                if (m_activeSaver == NULL) { 
     2051                        m_primaryClient->enter(m_x, m_y, m_seqNum, 
     2052                                                                m_primaryClient->getToggleMask(), false); 
     2053                } 
     2054        } 
     2055 
     2056        // if this screen had the cursor when the screen saver activated 
     2057        // then we can't switch back to it when the screen saver 
     2058        // deactivates. 
     2059        if (m_activeSaver == client) { 
     2060                m_activeSaver = NULL; 
     2061        } 
     2062 
     2063        // tell primary client about the active sides 
     2064        m_primaryClient->reconfigure(getActivePrimarySides()); 
     2065} 
     2066 
     2067 
     2068// 
     2069// CServer::CClipboardInfo 
     2070// 
     2071 
     2072CServer::CClipboardInfo::CClipboardInfo() : 
     2073        m_clipboard(), 
     2074        m_clipboardData(), 
     2075        m_clipboardOwner(), 
     2076        m_clipboardSeqNum(0) 
     2077{ 
     2078        // do nothing 
     2079} 
     2080 
     2081 
     2082// 
     2083// CServer::CLockCursorToScreenInfo 
     2084// 
     2085 
     2086CServer::CLockCursorToScreenInfo* 
     2087CServer::CLockCursorToScreenInfo::alloc(State state) 
     2088{ 
     2089        CLockCursorToScreenInfo* info = 
     2090                (CLockCursorToScreenInfo*)malloc(sizeof(CLockCursorToScreenInfo)); 
     2091        info->m_state = state; 
     2092        return info; 
     2093} 
     2094 
     2095 
     2096// 
     2097// CServer::CSwitchToScreenInfo 
     2098// 
     2099 
     2100CServer::CSwitchToScreenInfo* 
     2101CServer::CSwitchToScreenInfo::alloc(const CString& screen) 
     2102{ 
     2103        CSwitchToScreenInfo* info = 
     2104                (CSwitchToScreenInfo*)malloc(sizeof(CSwitchToScreenInfo) + 
     2105                                                                screen.size()); 
     2106        strcpy(info->m_screen, screen.c_str()); 
     2107        return info; 
     2108} 
     2109 
     2110 
     2111// 
     2112// CServer::CSwitchInDirectionInfo 
     2113// 
     2114 
     2115CServer::CSwitchInDirectionInfo* 
     2116CServer::CSwitchInDirectionInfo::alloc(EDirection direction) 
     2117{ 
     2118        CSwitchInDirectionInfo* info = 
     2119                (CSwitchInDirectionInfo*)malloc(sizeof(CSwitchInDirectionInfo)); 
     2120        info->m_direction = direction; 
     2121        return info; 
     2122} 
     2123 
     2124 
     2125// 
     2126// CServer::CScreenConnectedInfo 
     2127// 
     2128 
     2129CServer::CScreenConnectedInfo* 
     2130CServer::CScreenConnectedInfo::alloc(const CString& screen) 
     2131{ 
     2132        CScreenConnectedInfo* info = 
     2133                (CScreenConnectedInfo*)malloc(sizeof(CScreenConnectedInfo) + 
     2134                                                                screen.size()); 
     2135        strcpy(info->m_screen, screen.c_str()); 
     2136        return info; 
     2137} 
  • lib/server/CServer.h

    diff -uNr synergy-1.3.1/lib/server/CServer.h synergy-1.3.1-broadcast/lib/server/CServer.h
    old new  
    423423 
    424424        // screen locking (former scroll lock) 
    425425        bool                            m_lockedToScreen; 
     426        bool                            m_broadcast; 
    426427 
    427428        static CEvent::Type     s_errorEvent; 
    428429        static CEvent::Type     s_connectedEvent; 
  • lib/server/CServer.h.hack

    diff -uNr synergy-1.3.1/lib/server/CServer.h.hack synergy-1.3.1-broadcast/lib/server/CServer.h.hack
    old new  
     1/* 
     2 * synergy -- mouse and keyboard sharing utility 
     3 * Copyright (C) 2002 Chris Schoeneman 
     4 *  
     5 * This package is free software; you can redistribute it and/or 
     6 * modify it under the terms of the GNU General Public License 
     7 * found in the file COPYING that should have accompanied this file. 
     8 *  
     9 * This package is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 */ 
     14 
     15#ifndef CSERVER_H 
     16#define CSERVER_H 
     17 
     18#include "CConfig.h" 
     19#include "CClipboard.h" 
     20#include "ClipboardTypes.h" 
     21#include "KeyTypes.h" 
     22#include "MouseTypes.h" 
     23#include "CEvent.h" 
     24#include "CStopwatch.h" 
     25#include "stdmap.h" 
     26#include "stdset.h" 
     27#include "stdvector.h" 
     28 
     29#include "/Developer/SDKs/MacOSX10.4u.sdk/Developer/Headers/CFMCarbon/OSServices/SystemSound.h" 
     30 
     31class CBaseClientProxy; 
     32class CEventQueueTimer; 
     33class CPrimaryClient; 
     34class CInputFilter; 
     35 
     36//! Synergy server 
     37/*! 
     38This class implements the top-level server algorithms for synergy. 
     39*/ 
     40class CServer { 
     41public: 
     42        //! Lock cursor to screen data 
     43        class CLockCursorToScreenInfo { 
     44        public: 
     45                enum State { kOff, kOn, kToggle }; 
     46 
     47                static CLockCursorToScreenInfo* alloc(State state = kToggle); 
     48 
     49        public: 
     50                State                   m_state; 
     51        }; 
     52 
     53        //! Switch to screen data 
     54        class CSwitchToScreenInfo { 
     55        public: 
     56                static CSwitchToScreenInfo* alloc(const CString& screen); 
     57 
     58        public: 
     59                // this is a C-string;  this type is a variable size structure 
     60                char                    m_screen[1]; 
     61        }; 
     62 
     63        //! Switch in direction data 
     64        class CSwitchInDirectionInfo { 
     65        public: 
     66                static CSwitchInDirectionInfo* alloc(EDirection direction); 
     67 
     68        public: 
     69                EDirection              m_direction; 
     70        }; 
     71 
     72        //! Screen connected data 
     73        class CScreenConnectedInfo { 
     74        public: 
     75                static CScreenConnectedInfo* alloc(const CString& screen); 
     76 
     77        public: 
     78                // this is a C-string;  this type is a variable size structure 
     79                char                    m_screen[1]; 
     80        }; 
     81 
     82        /*! 
     83        Start the server with the configuration \p config and the primary 
     84        client (local screen) \p primaryClient.  The client retains 
     85        ownership of \p primaryClient. 
     86        */ 
     87        CServer(const CConfig& config, CPrimaryClient* primaryClient); 
     88        ~CServer(); 
     89 
     90        //! @name manipulators 
     91        //@{ 
     92 
     93        //! Set configuration 
     94        /*! 
     95        Change the server's configuration.  Returns true iff the new 
     96        configuration was accepted (it must include the server's name). 
     97        This will disconnect any clients no longer in the configuration. 
     98        */ 
     99        bool                            setConfig(const CConfig&); 
     100 
     101        //! Add a client 
     102        /*! 
     103        Adds \p client to the server.  The client is adopted and will be 
     104        destroyed when the client disconnects or is disconnected. 
     105        */ 
     106        void                            adoptClient(CBaseClientProxy* client); 
     107 
     108        //! Disconnect clients 
     109        /*! 
     110        Disconnect clients.  This tells them to disconnect but does not wait 
     111        for them to actually do so.  The server sends the disconnected event 
     112        when they're all disconnected (or immediately if none are connected). 
     113        The caller can also just destroy this object to force the disconnection. 
     114        */ 
     115        void                            disconnect(); 
     116 
     117        //@} 
     118        //! @name accessors 
     119        //@{ 
     120 
     121        //! Get number of connected clients 
     122        /*! 
     123        Returns the number of connected clients, including the server itself. 
     124        */ 
     125        UInt32                          getNumClients() const; 
     126 
     127        //! Get the list of connected clients 
     128        /*! 
     129        Set the \c list to the names of the currently connected clients. 
     130        */ 
     131        void                            getClients(std::vector<CString>& list) const; 
     132 
     133        //! Get error event type 
     134        /*! 
     135        Returns the error event type.  This is sent when the server fails 
     136        for some reason. 
     137        */ 
     138        static CEvent::Type     getErrorEvent(); 
     139 
     140        //! Get connected event type 
     141        /*! 
     142        Returns the connected event type.  This is sent when a client screen 
     143        has connected.  The event data is a \c CScreenConnectedInfo* that 
     144        indicates the connected screen. 
     145        */ 
     146        static CEvent::Type     getConnectedEvent(); 
     147 
     148        //! Get disconnected event type 
     149        /*! 
     150        Returns the disconnected event type.  This is sent when all the 
     151        clients have disconnected. 
     152        */ 
     153        static CEvent::Type     getDisconnectedEvent(); 
     154 
     155        //! Get switch to screen event type 
     156        /*! 
     157        Returns the switch to screen event type.  The server responds to this 
     158        by switching screens.  The event data is a \c CSwitchToScreenInfo* 
     159        that indicates the target screen. 
     160        */ 
     161        static CEvent::Type     getSwitchToScreenEvent(); 
     162 
     163        //! Get switch in direction event type 
     164        /*! 
     165        Returns the switch in direction event type.  The server responds to this 
     166        by switching screens.  The event data is a \c CSwitchInDirectionInfo* 
     167        that indicates the target direction. 
     168        */ 
     169        static CEvent::Type     getSwitchInDirectionEvent(); 
     170 
     171        //! Get lock cursor event type 
     172        /*! 
     173        Returns the lock cursor event type.  The server responds to this 
     174        by locking the cursor to the active screen or unlocking it.  The 
     175        event data is a \c CLockCursorToScreenInfo*. 
     176        */ 
     177        static CEvent::Type     getLockCursorToScreenEvent(); 
     178 
     179        //@} 
     180 
     181private: 
     182        // get canonical name of client 
     183        CString                         getName(const CBaseClientProxy*) const; 
     184 
     185        // get the sides of the primary screen that have neighbors 
     186        UInt32                          getActivePrimarySides() const; 
     187 
     188        // returns true iff mouse should be locked to the current screen 
     189        // according to this object only, ignoring what the primary client 
     190        // says. 
     191        bool                            isLockedToScreenServer() const; 
     192 
     193        // returns true iff mouse should be locked to the current screen 
     194        // according to this object or the primary client. 
     195        bool                            isLockedToScreen() const; 
     196 
     197        // returns the jump zone of the client 
     198        SInt32                          getJumpZoneSize(CBaseClientProxy*) const; 
     199 
     200        // change the active screen 
     201        void                            switchScreen(CBaseClientProxy*, 
     202                                                        SInt32 x, SInt32 y, bool forScreenSaver); 
     203 
     204        // jump to screen 
     205        void                            jumpToScreen(CBaseClientProxy*); 
     206 
     207        // convert pixel position to fraction, using x or y depending on the 
     208        // direction. 
     209        float                           mapToFraction(CBaseClientProxy*, EDirection, 
     210                                                        SInt32 x, SInt32 y) const; 
     211 
     212        // convert fraction to pixel position, writing only x or y depending 
     213        // on the direction. 
     214        void                            mapToPixel(CBaseClientProxy*, EDirection, float f, 
     215                                                        SInt32& x, SInt32& y) const; 
     216 
     217        // returns true if the client has a neighbor anywhere along the edge 
     218        // indicated by the direction. 
     219        bool                            hasAnyNeighbor(CBaseClientProxy*, EDirection) const; 
     220 
     221        // lookup neighboring screen, mapping the coordinate independent of 
     222        // the direction to the neighbor's coordinate space. 
     223        CBaseClientProxy*       getNeighbor(CBaseClientProxy*, EDirection, 
     224                                                        SInt32& x, SInt32& y) const; 
     225 
     226        // lookup neighboring screen.  given a position relative to the 
     227        // source screen, find the screen we should move onto and where. 
     228        // if the position is sufficiently far from the source then we 
     229        // cross multiple screens.  if there is no suitable screen then 
     230        // return NULL and x,y are not modified. 
     231        CBaseClientProxy*       mapToNeighbor(CBaseClientProxy*, EDirection, 
     232                                                        SInt32& x, SInt32& y) const; 
     233 
     234        // adjusts x and y or neither to avoid ending up in a jump zone 
     235        // after entering the client in the given direction. 
     236        void                            avoidJumpZone(CBaseClientProxy*, EDirection, 
     237                                                        SInt32& x, SInt32& y) const; 
     238 
     239        // test if a switch is permitted.  this includes testing user 
     240        // options like switch delay and tracking any state required to 
     241        // implement them.  returns true iff a switch is permitted. 
     242        bool                            isSwitchOkay(CBaseClientProxy* dst, EDirection, 
     243                                                        SInt32 x, SInt32 y, SInt32 xActive, SInt32 yActive); 
     244 
     245        // update switch state due to a mouse move at \p x, \p y that 
     246        // doesn't switch screens. 
     247        void                            noSwitch(SInt32 x, SInt32 y); 
     248 
     249        // stop switch timers 
     250        void                            stopSwitch(); 
     251 
     252        // start two tap switch timer 
     253        void                            startSwitchTwoTap(); 
     254 
     255        // arm the two tap switch timer if \p x, \p y is outside the tap zone 
     256        void                            armSwitchTwoTap(SInt32 x, SInt32 y); 
     257 
     258        // stop the two tap switch timer 
     259        void                            stopSwitchTwoTap(); 
     260 
     261        // returns true iff the two tap switch timer is started 
     262        bool                            isSwitchTwoTapStarted() const; 
     263 
     264        // returns true iff should switch because of two tap 
     265        bool                            shouldSwitchTwoTap() const; 
     266 
     267        // start delay switch timer 
     268        void                            startSwitchWait(SInt32 x, SInt32 y); 
     269 
     270        // stop delay switch timer 
     271        void                            stopSwitchWait(); 
     272 
     273        // returns true iff the delay switch timer is started 
     274        bool                            isSwitchWaitStarted() const; 
     275 
     276        // returns the corner (EScreenSwitchCornerMasks) where x,y is on the 
     277        // given client.  corners have the given size. 
     278        UInt32                          getCorner(CBaseClientProxy*, 
     279                                                        SInt32 x, SInt32 y, SInt32 size) const; 
     280 
     281        // stop relative mouse moves 
     282        void                            stopRelativeMoves(); 
     283 
     284        // send screen options to \c client 
     285        void                            sendOptions(CBaseClientProxy* client) const; 
     286 
     287        // process options from configuration 
     288        void                            processOptions(); 
     289 
     290        // event handlers 
     291        void                            handleShapeChanged(const CEvent&, void*); 
     292        void                            handleClipboardGrabbed(const CEvent&, void*); 
     293        void                            handleClipboardChanged(const CEvent&, void*); 
     294        void                            handleKeyDownEvent(const CEvent&, void*); 
     295        void                            handleKeyUpEvent(const CEvent&, void*); 
     296        void                            handleKeyRepeatEvent(const CEvent&, void*); 
     297        void                            handleButtonDownEvent(const CEvent&, void*); 
     298        void                            handleButtonUpEvent(const CEvent&, void*); 
     299        void                            handleMotionPrimaryEvent(const CEvent&, void*); 
     300        void                            handleMotionSecondaryEvent(const CEvent&, void*); 
     301        void                            handleWheelEvent(const CEvent&, void*); 
     302        void                            handleScreensaverActivatedEvent(const CEvent&, void*); 
     303        void                            handleScreensaverDeactivatedEvent(const CEvent&, void*); 
     304        void                            handleSwitchWaitTimeout(const CEvent&, void*); 
     305        void                            handleClientDisconnected(const CEvent&, void*); 
     306        void                            handleClientCloseTimeout(const CEvent&, void*); 
     307        void                            handleSwitchToScreenEvent(const CEvent&, void*); 
     308        void                            handleSwitchInDirectionEvent(const CEvent&, void*); 
     309        void                            handleLockCursorToScreenEvent(const CEvent&, void*); 
     310        void                            handleFakeInputBeginEvent(const CEvent&, void*); 
     311        void                            handleFakeInputEndEvent(const CEvent&, void*); 
     312 
     313        // event processing 
     314        void                            onClipboardChanged(CBaseClientProxy* sender, 
     315                                                        ClipboardID id, UInt32 seqNum); 
     316        void                            onScreensaver(bool activated); 
     317        void                            onKeyDown(KeyID, KeyModifierMask, KeyButton, 
     318                                                        const char* screens); 
     319        void                            onKeyUp(KeyID, KeyModifierMask, KeyButton, 
     320                                                        const char* screens); 
     321        void                            onKeyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton); 
     322        void                            onMouseDown(ButtonID); 
     323        void                            onMouseUp(ButtonID); 
     324        bool                            onMouseMovePrimary(SInt32 x, SInt32 y); 
     325        void                            onMouseMoveSecondary(SInt32 dx, SInt32 dy); 
     326        void                            onMouseWheel(SInt32 xDelta, SInt32 yDelta); 
     327 
     328        // add client to list and attach event handlers for client 
     329        bool                            addClient(CBaseClientProxy*); 
     330 
     331        // remove client from list and detach event handlers for client 
     332        bool                            removeClient(CBaseClientProxy*); 
     333 
     334        // close a client 
     335        void                            closeClient(CBaseClientProxy*, const char* msg); 
     336 
     337        // close clients not in \p config 
     338        void                            closeClients(const CConfig& config); 
     339 
     340        // close all clients whether they've completed the handshake or not, 
     341        // except the primary client 
     342        void                            closeAllClients(); 
     343 
     344        // remove clients from internal state 
     345        void                            removeActiveClient(CBaseClientProxy*); 
     346        void                            removeOldClient(CBaseClientProxy*); 
     347 
     348        // force the cursor off of \p client 
     349        void                            forceLeaveClient(CBaseClientProxy* client); 
     350 
     351private: 
     352        class CClipboardInfo { 
     353        public: 
     354                CClipboardInfo(); 
     355 
     356        public: 
     357                CClipboard              m_clipboard; 
     358                CString                 m_clipboardData; 
     359                CString                 m_clipboardOwner; 
     360                UInt32                  m_clipboardSeqNum; 
     361        }; 
     362 
     363        // the primary screen client 
     364        CPrimaryClient*         m_primaryClient; 
     365 
     366        // all clients (including the primary client) indexed by name 
     367        typedef std::map<CString, CBaseClientProxy*> CClientList; 
     368        typedef std::set<CBaseClientProxy*> CClientSet; 
     369        CClientList                     m_clients; 
     370        CClientSet                      m_clientSet; 
     371 
     372        // all old connections that we're waiting to hangup 
     373        typedef std::map<CBaseClientProxy*, CEventQueueTimer*> COldClients; 
     374        COldClients                     m_oldClients; 
     375 
     376        // the client with focus 
     377        CBaseClientProxy*       m_active; 
     378 
     379        // the sequence number of enter messages 
     380        UInt32                          m_seqNum; 
     381 
     382        // current mouse position (in absolute screen coordinates) on 
     383        // whichever screen is active 
     384        SInt32                          m_x, m_y; 
     385 
     386        // last mouse deltas.  this is needed to smooth out double tap 
     387        // on win32 which reports bogus mouse motion at the edge of 
     388        // the screen when using low level hooks, synthesizing motion 
     389        // in the opposite direction the mouse actually moved. 
     390        SInt32                          m_xDelta, m_yDelta; 
     391        SInt32                          m_xDelta2, m_yDelta2; 
     392 
     393        // current configuration 
     394        CConfig                         m_config; 
     395 
     396        // input filter (from m_config); 
     397        CInputFilter*           m_inputFilter; 
     398 
     399        // clipboard cache 
     400        CClipboardInfo          m_clipboards[kClipboardEnd]; 
     401 
     402        // state saved when screen saver activates 
     403        CBaseClientProxy*       m_activeSaver; 
     404        SInt32                          m_xSaver, m_ySaver; 
     405 
     406        // common state for screen switch tests.  all tests are always 
     407        // trying to reach the same screen in the same direction. 
     408        EDirection                      m_switchDir; 
     409        CBaseClientProxy*       m_switchScreen; 
     410 
     411        // state for delayed screen switching 
     412        double                          m_switchWaitDelay; 
     413        CEventQueueTimer*       m_switchWaitTimer; 
     414        SInt32                          m_switchWaitX, m_switchWaitY; 
     415 
     416        // state for double-tap screen switching 
     417        double                          m_switchTwoTapDelay; 
     418        CStopwatch                      m_switchTwoTapTimer; 
     419        bool                            m_switchTwoTapEngaged; 
     420        bool                            m_switchTwoTapArmed; 
     421        SInt32                          m_switchTwoTapZone; 
     422 
     423        // relative mouse move option 
     424        bool                            m_relativeMoves; 
     425 
     426        // screen locking (former scroll lock) 
     427        bool                            m_lockedToScreen; 
     428        bool                            m_broadcast; 
     429        SystemSoundActionID             m_broadcastOnID; 
     430        SystemSoundActionID             m_broadcastOffID; 
     431 
     432        static CEvent::Type     s_errorEvent; 
     433        static CEvent::Type     s_connectedEvent; 
     434        static CEvent::Type     s_disconnectedEvent; 
     435        static CEvent::Type     s_switchToScreen; 
     436        static CEvent::Type     s_switchInDirection; 
     437        static CEvent::Type s_lockCursorToScreen; 
     438}; 
     439 
     440#endif 
  • lib/server/CServer.h.ok

    diff -uNr synergy-1.3.1/lib/server/CServer.h.ok synergy-1.3.1-broadcast/lib/server/CServer.h.ok
    old new  
     1/* 
     2 * synergy -- mouse and keyboard sharing utility 
     3 * Copyright (C) 2002 Chris Schoeneman 
     4 *  
     5 * This package is free software; you can redistribute it and/or 
     6 * modify it under the terms of the GNU General Public License 
     7 * found in the file COPYING that should have accompanied this file. 
     8 *  
     9 * This package is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 */ 
     14 
     15#ifndef CSERVER_H 
     16#define CSERVER_H 
     17 
     18#include "CConfig.h" 
     19#include "CClipboard.h" 
     20#include "ClipboardTypes.h" 
     21#include "KeyTypes.h" 
     22#include "MouseTypes.h" 
     23#include "CEvent.h" 
     24#include "CStopwatch.h" 
     25#include "stdmap.h" 
     26#include "stdset.h" 
     27#include "stdvector.h" 
     28 
     29class CBaseClientProxy; 
     30class CEventQueueTimer; 
     31class CPrimaryClient; 
     32class CInputFilter; 
     33 
     34//! Synergy server 
     35/*! 
     36This class implements the top-level server algorithms for synergy. 
     37*/ 
     38class CServer { 
     39public: 
     40        //! Lock cursor to screen data 
     41        class CLockCursorToScreenInfo { 
     42        public: 
     43                enum State { kOff, kOn, kToggle }; 
     44 
     45                static CLockCursorToScreenInfo* alloc(State state = kToggle); 
     46 
     47        public: 
     48                State                   m_state; 
     49        }; 
     50 
     51        //! Switch to screen data 
     52        class CSwitchToScreenInfo { 
     53        public: 
     54                static CSwitchToScreenInfo* alloc(const CString& screen); 
     55 
     56        public: 
     57                // this is a C-string;  this type is a variable size structure 
     58                char                    m_screen[1]; 
     59        }; 
     60 
     61        //! Switch in direction data 
     62        class CSwitchInDirectionInfo { 
     63        public: 
     64                static CSwitchInDirectionInfo* alloc(EDirection direction); 
     65 
     66        public: 
     67                EDirection              m_direction; 
     68        }; 
     69 
     70        //! Screen connected data 
     71        class CScreenConnectedInfo { 
     72        public: 
     73                static CScreenConnectedInfo* alloc(const CString& screen); 
     74 
     75        public: 
     76                // this is a C-string;  this type is a variable size structure 
     77                char                    m_screen[1]; 
     78        }; 
     79 
     80        /*! 
     81        Start the server with the configuration \p config and the primary 
     82        client (local screen) \p primaryClient.  The client retains 
     83        ownership of \p primaryClient. 
     84        */ 
     85        CServer(const CConfig& config, CPrimaryClient* primaryClient); 
     86        ~CServer(); 
     87 
     88        //! @name manipulators 
     89        //@{ 
     90 
     91        //! Set configuration 
     92        /*! 
     93        Change the server's configuration.  Returns true iff the new 
     94        configuration was accepted (it must include the server's name). 
     95        This will disconnect any clients no longer in the configuration. 
     96        */ 
     97        bool                            setConfig(const CConfig&); 
     98 
     99        //! Add a client 
     100        /*! 
     101        Adds \p client to the server.  The client is adopted and will be 
     102        destroyed when the client disconnects or is disconnected. 
     103        */ 
     104        void                            adoptClient(CBaseClientProxy* client); 
     105 
     106        //! Disconnect clients 
     107        /*! 
     108        Disconnect clients.  This tells them to disconnect but does not wait 
     109        for them to actually do so.  The server sends the disconnected event 
     110        when they're all disconnected (or immediately if none are connected). 
     111        The caller can also just destroy this object to force the disconnection. 
     112        */ 
     113        void                            disconnect(); 
     114 
     115        //@} 
     116        //! @name accessors 
     117        //@{ 
     118 
     119        //! Get number of connected clients 
     120        /*! 
     121        Returns the number of connected clients, including the server itself. 
     122        */ 
     123        UInt32                          getNumClients() const; 
     124 
     125        //! Get the list of connected clients 
     126        /*! 
     127        Set the \c list to the names of the currently connected clients. 
     128        */ 
     129        void                            getClients(std::vector<CString>& list) const; 
     130 
     131        //! Get error event type 
     132        /*! 
     133        Returns the error event type.  This is sent when the server fails 
     134        for some reason. 
     135        */ 
     136        static CEvent::Type     getErrorEvent(); 
     137 
     138        //! Get connected event type 
     139        /*! 
     140        Returns the connected event type.  This is sent when a client screen 
     141        has connected.  The event data is a \c CScreenConnectedInfo* that 
     142        indicates the connected screen. 
     143        */ 
     144        static CEvent::Type     getConnectedEvent(); 
     145 
     146        //! Get disconnected event type 
     147        /*! 
     148        Returns the disconnected event type.  This is sent when all the 
     149        clients have disconnected. 
     150        */ 
     151        static CEvent::Type     getDisconnectedEvent(); 
     152 
     153        //! Get switch to screen event type 
     154        /*! 
     155        Returns the switch to screen event type.  The server responds to this 
     156        by switching screens.  The event data is a \c CSwitchToScreenInfo* 
     157        that indicates the target screen. 
     158        */ 
     159        static CEvent::Type     getSwitchToScreenEvent(); 
     160 
     161        //! Get switch in direction event type 
     162        /*! 
     163        Returns the switch in direction event type.  The server responds to this 
     164        by switching screens.  The event data is a \c CSwitchInDirectionInfo* 
     165        that indicates the target direction. 
     166        */ 
     167        static CEvent::Type     getSwitchInDirectionEvent(); 
     168 
     169        //! Get lock cursor event type 
     170        /*! 
     171        Returns the lock cursor event type.  The server responds to this 
     172        by locking the cursor to the active screen or unlocking it.  The 
     173        event data is a \c CLockCursorToScreenInfo*. 
     174        */ 
     175        static CEvent::Type     getLockCursorToScreenEvent(); 
     176 
     177        //@} 
     178 
     179private: 
     180        // get canonical name of client 
     181        CString                         getName(const CBaseClientProxy*) const; 
     182 
     183        // get the sides of the primary screen that have neighbors 
     184        UInt32                          getActivePrimarySides() const; 
     185 
     186        // returns true iff mouse should be locked to the current screen 
     187        // according to this object only, ignoring what the primary client 
     188        // says. 
     189        bool                            isLockedToScreenServer() const; 
     190 
     191        // returns true iff mouse should be locked to the current screen 
     192        // according to this object or the primary client. 
     193        bool                            isLockedToScreen() const; 
     194 
     195        // returns the jump zone of the client 
     196        SInt32                          getJumpZoneSize(CBaseClientProxy*) const; 
     197 
     198        // change the active screen 
     199        void                            switchScreen(CBaseClientProxy*, 
     200                                                        SInt32 x, SInt32 y, bool forScreenSaver); 
     201 
     202        // jump to screen 
     203        void                            jumpToScreen(CBaseClientProxy*); 
     204 
     205        // convert pixel position to fraction, using x or y depending on the 
     206        // direction. 
     207        float                           mapToFraction(CBaseClientProxy*, EDirection, 
     208                                                        SInt32 x, SInt32 y) const; 
     209 
     210        // convert fraction to pixel position, writing only x or y depending 
     211        // on the direction. 
     212        void                            mapToPixel(CBaseClientProxy*, EDirection, float f, 
     213                                                        SInt32& x, SInt32& y) const; 
     214 
     215        // returns true if the client has a neighbor anywhere along the edge 
     216        // indicated by the direction. 
     217        bool                            hasAnyNeighbor(CBaseClientProxy*, EDirection) const; 
     218 
     219        // lookup neighboring screen, mapping the coordinate independent of 
     220        // the direction to the neighbor's coordinate space. 
     221        CBaseClientProxy*       getNeighbor(CBaseClientProxy*, EDirection, 
     222                                                        SInt32& x, SInt32& y) const; 
     223 
     224        // lookup neighboring screen.  given a position relative to the 
     225        // source screen, find the screen we should move onto and where. 
     226        // if the position is sufficiently far from the source then we 
     227        // cross multiple screens.  if there is no suitable screen then 
     228        // return NULL and x,y are not modified. 
     229        CBaseClientProxy*       mapToNeighbor(CBaseClientProxy*, EDirection, 
     230                                                        SInt32& x, SInt32& y) const; 
     231 
     232        // adjusts x and y or neither to avoid ending up in a jump zone 
     233        // after entering the client in the given direction. 
     234        void                            avoidJumpZone(CBaseClientProxy*, EDirection, 
     235                                                        SInt32& x, SInt32& y) const; 
     236 
     237        // test if a switch is permitted.  this includes testing user 
     238        // options like switch delay and tracking any state required to 
     239        // implement them.  returns true iff a switch is permitted. 
     240        bool                            isSwitchOkay(CBaseClientProxy* dst, EDirection, 
     241                                                        SInt32 x, SInt32 y, SInt32 xActive, SInt32 yActive); 
     242 
     243        // update switch state due to a mouse move at \p x, \p y that 
     244        // doesn't switch screens. 
     245        void                            noSwitch(SInt32 x, SInt32 y); 
     246 
     247        // stop switch timers 
     248        void                            stopSwitch(); 
     249 
     250        // start two tap switch timer 
     251        void                            startSwitchTwoTap(); 
     252 
     253        // arm the two tap switch timer if \p x, \p y is outside the tap zone 
     254        void                            armSwitchTwoTap(SInt32 x, SInt32 y); 
     255 
     256        // stop the two tap switch timer 
     257        void                            stopSwitchTwoTap(); 
     258 
     259        // returns true iff the two tap switch timer is started 
     260        bool                            isSwitchTwoTapStarted() const; 
     261 
     262        // returns true iff should switch because of two tap 
     263        bool                            shouldSwitchTwoTap() const; 
     264 
     265        // start delay switch timer 
     266        void                            startSwitchWait(SInt32 x, SInt32 y); 
     267 
     268        // stop delay switch timer 
     269        void                            stopSwitchWait(); 
     270 
     271        // returns true iff the delay switch timer is started 
     272        bool                            isSwitchWaitStarted() const; 
     273 
     274        // returns the corner (EScreenSwitchCornerMasks) where x,y is on the 
     275        // given client.  corners have the given size. 
     276        UInt32                          getCorner(CBaseClientProxy*, 
     277                                                        SInt32 x, SInt32 y, SInt32 size) const; 
     278 
     279        // stop relative mouse moves 
     280        void                            stopRelativeMoves(); 
     281 
     282        // send screen options to \c client 
     283        void                            sendOptions(CBaseClientProxy* client) const; 
     284 
     285        // process options from configuration 
     286        void                            processOptions(); 
     287 
     288        // event handlers 
     289        void                            handleShapeChanged(const CEvent&, void*); 
     290        void                            handleClipboardGrabbed(const CEvent&, void*); 
     291        void                            handleClipboardChanged(const CEvent&, void*); 
     292        void                            handleKeyDownEvent(const CEvent&, void*); 
     293        void                            handleKeyUpEvent(const CEvent&, void*); 
     294        void                            handleKeyRepeatEvent(const CEvent&, void*); 
     295        void                            handleButtonDownEvent(const CEvent&, void*); 
     296        void                            handleButtonUpEvent(const CEvent&, void*); 
     297        void                            handleMotionPrimaryEvent(const CEvent&, void*); 
     298        void                            handleMotionSecondaryEvent(const CEvent&, void*); 
     299        void                            handleWheelEvent(const CEvent&, void*); 
     300        void                            handleScreensaverActivatedEvent(const CEvent&, void*); 
     301        void                            handleScreensaverDeactivatedEvent(const CEvent&, void*); 
     302        void                            handleSwitchWaitTimeout(const CEvent&, void*); 
     303        void                            handleClientDisconnected(const CEvent&, void*); 
     304        void                            handleClientCloseTimeout(const CEvent&, void*); 
     305        void                            handleSwitchToScreenEvent(const CEvent&, void*); 
     306        void                            handleSwitchInDirectionEvent(const CEvent&, void*); 
     307        void                            handleLockCursorToScreenEvent(const CEvent&, void*); 
     308        void                            handleFakeInputBeginEvent(const CEvent&, void*); 
     309        void                            handleFakeInputEndEvent(const CEvent&, void*); 
     310 
     311        // event processing 
     312        void                            onClipboardChanged(CBaseClientProxy* sender, 
     313                                                        ClipboardID id, UInt32 seqNum); 
     314        void                            onScreensaver(bool activated); 
     315        void                            onKeyDown(KeyID, KeyModifierMask, KeyButton, 
     316                                                        const char* screens); 
     317        void                            onKeyUp(KeyID, KeyModifierMask, KeyButton, 
     318                                                        const char* screens); 
     319        void                            onKeyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton); 
     320        void                            onMouseDown(ButtonID); 
     321        void                            onMouseUp(ButtonID); 
     322        bool                            onMouseMovePrimary(SInt32 x, SInt32 y); 
     323        void                            onMouseMoveSecondary(SInt32 dx, SInt32 dy); 
     324        void                            onMouseWheel(SInt32 xDelta, SInt32 yDelta); 
     325 
     326        // add client to list and attach event handlers for client 
     327        bool                            addClient(CBaseClientProxy*); 
     328 
     329        // remove client from list and detach event handlers for client 
     330        bool                            removeClient(CBaseClientProxy*); 
     331 
     332        // close a client 
     333        void                            closeClient(CBaseClientProxy*, const char* msg); 
     334 
     335        // close clients not in \p config 
     336        void                            closeClients(const CConfig& config); 
     337 
     338        // close all clients whether they've completed the handshake or not, 
     339        // except the primary client 
     340        void                            closeAllClients(); 
     341 
     342        // remove clients from internal state 
     343        void                            removeActiveClient(CBaseClientProxy*); 
     344        void                            removeOldClient(CBaseClientProxy*); 
     345 
     346        // force the cursor off of \p client 
     347        void                            forceLeaveClient(CBaseClientProxy* client); 
     348 
     349private: 
     350        class CClipboardInfo { 
     351        public: 
     352                CClipboardInfo(); 
     353 
     354        public: 
     355                CClipboard              m_clipboard; 
     356                CString                 m_clipboardData; 
     357                CString                 m_clipboardOwner; 
     358                UInt32                  m_clipboardSeqNum; 
     359        }; 
     360 
     361        // the primary screen client 
     362        CPrimaryClient*         m_primaryClient; 
     363 
     364        // all clients (including the primary client) indexed by name 
     365        typedef std::map<CString, CBaseClientProxy*> CClientList; 
     366        typedef std::set<CBaseClientProxy*> CClientSet; 
     367        CClientList                     m_clients; 
     368        CClientSet                      m_clientSet; 
     369 
     370        // all old connections that we're waiting to hangup 
     371        typedef std::map<CBaseClientProxy*, CEventQueueTimer*> COldClients; 
     372        COldClients                     m_oldClients; 
     373 
     374        // the client with focus 
     375        CBaseClientProxy*       m_active; 
     376 
     377        // the sequence number of enter messages 
     378        UInt32                          m_seqNum; 
     379 
     380        // current mouse position (in absolute screen coordinates) on 
     381        // whichever screen is active 
     382        SInt32                          m_x, m_y; 
     383 
     384        // last mouse deltas.  this is needed to smooth out double tap 
     385        // on win32 which reports bogus mouse motion at the edge of 
     386        // the screen when using low level hooks, synthesizing motion 
     387        // in the opposite direction the mouse actually moved. 
     388        SInt32                          m_xDelta, m_yDelta; 
     389        SInt32                          m_xDelta2, m_yDelta2; 
     390 
     391        // current configuration 
     392        CConfig                         m_config; 
     393 
     394        // input filter (from m_config); 
     395        CInputFilter*           m_inputFilter; 
     396 
     397        // clipboard cache 
     398        CClipboardInfo          m_clipboards[kClipboardEnd]; 
     399 
     400        // state saved when screen saver activates 
     401        CBaseClientProxy*       m_activeSaver; 
     402        SInt32                          m_xSaver, m_ySaver; 
     403 
     404        // common state for screen switch tests.  all tests are always 
     405        // trying to reach the same screen in the same direction. 
     406        EDirection                      m_switchDir; 
     407        CBaseClientProxy*       m_switchScreen; 
     408 
     409        // state for delayed screen switching 
     410        double                          m_switchWaitDelay; 
     411        CEventQueueTimer*       m_switchWaitTimer; 
     412        SInt32                          m_switchWaitX, m_switchWaitY; 
     413 
     414        // state for double-tap screen switching 
     415        double                          m_switchTwoTapDelay; 
     416        CStopwatch                      m_switchTwoTapTimer; 
     417        bool                            m_switchTwoTapEngaged; 
     418        bool                            m_switchTwoTapArmed; 
     419        SInt32                          m_switchTwoTapZone; 
     420 
     421        // relative mouse move option 
     422        bool                            m_relativeMoves; 
     423 
     424        // screen locking (former scroll lock) 
     425        bool                            m_lockedToScreen; 
     426        bool                            m_broadcast; 
     427 
     428        static CEvent::Type     s_errorEvent; 
     429        static CEvent::Type     s_connectedEvent; 
     430        static CEvent::Type     s_disconnectedEvent; 
     431        static CEvent::Type     s_switchToScreen; 
     432        static CEvent::Type     s_switchInDirection; 
     433        static CEvent::Type s_lockCursorToScreen; 
     434}; 
     435 
     436#endif 
  • lib/synergy/CScreen.cpp

    diff -uNr synergy-1.3.1/lib/synergy/CScreen.cpp synergy-1.3.1-broadcast/lib/synergy/CScreen.cpp
    old new  
    176176{ 
    177177        assert(!m_isPrimary || m_fakeInput); 
    178178 
     179        LOG((CLOG_DEBUG1 "MAIN keyDown!")); 
     180 
    179181        // check for ctrl+alt+del emulation 
    180182        if (id == kKeyDelete && 
    181183                (mask & (KeyModifierControl | KeyModifierAlt)) == 
     
    193195                                KeyModifierMask mask, SInt32 count, KeyButton button) 
    194196{ 
    195197        assert(!m_isPrimary); 
     198        LOG((CLOG_DEBUG1 "MAIN keyRepeat!")); 
    196199        m_screen->fakeKeyRepeat(id, mask, count, button); 
    197200} 
    198201 
     
    200203CScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) 
    201204{ 
    202205        assert(!m_isPrimary || m_fakeInput); 
     206        LOG((CLOG_DEBUG1 "MAIN keyUp!")); 
    203207        m_screen->fakeKeyUp(button); 
    204208} 
    205209 
  • synergy.conf

    diff -uNr synergy-1.3.1/synergy.conf synergy-1.3.1-broadcast/synergy.conf
    old new  
     1# sample synergy configuration file 
     2# 
     3# comments begin with the # character and continue to the end of 
     4# line.  comments may appear anywhere the syntax permits. 
     5 
     6section: screens 
     7        mac1: 
     8        winxp1: 
     9end 
     10 
     11section: links 
     12        mac1: 
     13                right = winxp1 
     14 
     15        winxp1: 
     16                left  = mac1 
     17 
     18end 
     19 
     20section: aliases 
     21        mac1: 
     22                main 
     23end 
     24 
     25section: options 
     26        relativeMouseMoves = true 
     27        mousebutton(3) = mouseDown(3) = lockCursorToScreen(on); mouseUp(3) = lockCursorToScreen(off) 
     28        #mousebutton(1) = mouseDown(1) = lockCursorToScreen(on); mouseUp(1) = lockCursorToScreen(off) 
     29end 
     30 
  • synergy.conf.ok

    diff -uNr synergy-1.3.1/synergy.conf.ok synergy-1.3.1-broadcast/synergy.conf.ok
    old new  
     1# sample synergy configuration file 
     2# 
     3# comments begin with the # character and continue to the end of 
     4# line.  comments may appear anywhere the syntax permits. 
     5 
     6section: screens 
     7        # three hosts named:  moe, larry, and curly 
     8        mac1: 
     9        winxp1: 
     10end 
     11 
     12section: links 
     13        # larry is to the right of moe and curly is above moe 
     14        mac1: 
     15                right = winxp1 
     16 
     17        # moe is to the left of larry and curly is above larry. 
     18        # note that curly is above both moe and larry and moe 
     19        # and larry have a symmetric connection (they're in 
     20        # opposite directions of each other). 
     21        winxp1: 
     22                left  = mac1 
     23 
     24end 
     25 
     26section: aliases 
     27        # curly is also known as shemp 
     28        mac1: 
     29                main 
     30end 
     31 
  • synergy.conf.orig

    diff -uNr synergy-1.3.1/synergy.conf.orig synergy-1.3.1-broadcast/synergy.conf.orig
    old new  
     1# sample synergy configuration file 
     2# 
     3# comments begin with the # character and continue to the end of 
     4# line.  comments may appear anywhere the syntax permits. 
     5 
     6section: screens 
     7        # three hosts named:  moe, larry, and curly 
     8        moe: 
     9        larry: 
     10        curly: 
     11end 
     12 
     13section: links 
     14        # larry is to the right of moe and curly is above moe 
     15        moe: 
     16                right = larry 
     17                up    = curly 
     18 
     19        # moe is to the left of larry and curly is above larry. 
     20        # note that curly is above both moe and larry and moe 
     21        # and larry have a symmetric connection (they're in 
     22        # opposite directions of each other). 
     23        larry: 
     24                left  = moe 
     25                up    = curly 
     26 
     27        # larry is below curly.  if you move up from moe and then 
     28        # down, you'll end up on larry. 
     29        curly: 
     30                down  = larry 
     31end 
     32 
     33section: aliases 
     34        # curly is also known as shemp 
     35        curly: 
     36                shemp 
     37end