Ticket #13797: synergy-1.3.1-broadcast.patch

File synergy-1.3.1-broadcast.patch, 151.2 KB (added by anton@…, 17 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