Ticket #13797: synergy-1.3.1-broadcast.patch
File synergy-1.3.1-broadcast.patch, 151.2 KB (added by anton@…, 17 years ago) |
---|
-
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 120 120 &inputBounds, &m_userInputWindow); 121 121 122 122 SetWindowAlpha(m_userInputWindow, 0); 123 124 // Install global event handlers, so we can broadcast. 125 InstallMyEventHandlers(); 123 126 } 124 127 125 128 // install display manager notification handler … … 233 236 delete m_screensaver; 234 237 } 235 238 239 static OSStatus MonitorHandler( EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon ) 240 { 241 // nothing 242 return noErr; 243 } 244 245 void 246 COSXScreen::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 236 261 void* 237 262 COSXScreen::getEventTarget() const 238 263 { … … 803 828 804 829 UInt32 eventClass = GetEventClass(*carbonEvent); 805 830 831 LOG((CLOG_DEBUG1 "OSX system event.")); 832 806 833 switch (eventClass) { 807 834 case kEventClassMouse: 808 835 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 25 25 #include <mach/mach_init.h> 26 26 #include <IOKit/pwr_mgt/IOPMLib.h> 27 27 #include <IOKit/IOMessage.h> 28 #include <ApplicationServices/ApplicationServices.h> 28 29 29 30 template <class T> 30 31 class CCondVar; … … 147 148 void handlePowerChangeRequest(natural_t messageType, 148 149 void* messageArgument); 149 150 151 void InstallMyEventHandlers(void); 152 150 153 static CEvent::Type getConfirmSleepEvent(); 151 154 void handleConfirmSleep(const CEvent& event, void*); 152 155 … … 227 230 EventHandlerRef m_switchEventHandlerRef; 228 231 229 232 // sleep / wakeup 230 CMutex* 231 CThread* 232 CCondVar<bool>* m_pmThreadReady;233 CMutex* m_pmMutex; 234 CThread* m_pmWatchThread; 235 CCondVar<bool>* m_pmThreadReady; 233 236 CFRunLoopRef m_pmRunloop; 234 237 io_connect_t m_pmRootPort; 235 238 -
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 181 181 void 182 182 CPrimaryClient::keyDown(KeyID key, KeyModifierMask mask, KeyButton button) 183 183 { 184 LOG((CLOG_DEBUG1 "KeyDownMain id=%d mask=0x%04x button=0x%04x", key, mask, button)); 185 184 186 if (m_fakeInputCount > 0) { 185 187 // XXX -- don't forward keystrokes to primary screen for now 186 188 (void)key; … … 191 193 } 192 194 193 195 void 194 CPrimaryClient::keyRepeat(KeyID , KeyModifierMask, SInt32, KeyButton)196 CPrimaryClient::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button) 195 197 { 196 198 // ignore 199 LOG((CLOG_DEBUG1 "KeyRepeatMain id=%d mask=0x%04x count=%d button=0x%04x", key, mask, count, button)); 197 200 } 198 201 199 202 void 200 203 CPrimaryClient::keyUp(KeyID key, KeyModifierMask mask, KeyButton button) 201 204 { 205 LOG((CLOG_DEBUG1 "KeyUpMain id=%d mask=0x%04x button=0x%04x", key, mask, button)); 202 206 if (m_fakeInputCount > 0) { 203 207 // XXX -- don't forward keystrokes to primary screen for now 204 208 (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 61 61 m_switchTwoTapArmed(false), 62 62 m_switchTwoTapZone(3), 63 63 m_relativeMoves(false), 64 m_lockedToScreen(false) 64 m_lockedToScreen(false), 65 m_broadcast(false) 65 66 { 66 67 // must have a primary client and it must have a canonical name 67 68 assert(m_primaryClient != NULL); … … 1388 1389 1389 1390 // enter new state 1390 1391 if (newState != m_lockedToScreen) { 1392 SInt32 x, y; 1391 1393 m_lockedToScreen = newState; 1394 1392 1395 LOG((CLOG_DEBUG "cursor %s current screen", m_lockedToScreen ? "locked to" : "unlocked from")); 1393 1396 1394 1397 m_primaryClient->reconfigure(getActivePrimarySides()); 1395 1398 if (!isLockedToScreenServer()) { 1396 stopRelativeMoves();1399 //stopRelativeMoves(); 1397 1400 } 1398 1401 } 1399 1402 } … … 1514 1517 1515 1518 // relay 1516 1519 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 } 1518 1545 } 1519 1546 else { 1520 1547 for (CClientList::const_iterator index = m_clients.begin(); … … 1535 1562 1536 1563 // relay 1537 1564 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 } 1539 1575 } 1540 1576 else { 1541 1577 for (CClientList::const_iterator index = m_clients.begin(); … … 1555 1591 assert(m_active != NULL); 1556 1592 1557 1593 // 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 } 1559 1604 } 1560 1605 1561 1606 void -
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 37 CEvent::Type CServer::s_errorEvent = CEvent::kUnknown; 38 CEvent::Type CServer::s_connectedEvent = CEvent::kUnknown; 39 CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; 40 CEvent::Type CServer::s_switchToScreen = CEvent::kUnknown; 41 CEvent::Type CServer::s_switchInDirection = CEvent::kUnknown; 42 CEvent::Type CServer::s_lockCursorToScreen = CEvent::kUnknown; 43 44 CServer::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 181 CServer::~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 239 bool 240 CServer::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 283 void 284 CServer::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 324 void 325 CServer::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 337 UInt32 338 CServer::getNumClients() const 339 { 340 return m_clients.size(); 341 } 342 343 void 344 CServer::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 353 CEvent::Type 354 CServer::getErrorEvent() 355 { 356 return CEvent::registerTypeOnce(s_errorEvent, 357 "CServer::error"); 358 } 359 360 CEvent::Type 361 CServer::getConnectedEvent() 362 { 363 return CEvent::registerTypeOnce(s_connectedEvent, 364 "CServer::connected"); 365 } 366 367 CEvent::Type 368 CServer::getDisconnectedEvent() 369 { 370 return CEvent::registerTypeOnce(s_disconnectedEvent, 371 "CServer::disconnected"); 372 } 373 374 CEvent::Type 375 CServer::getSwitchToScreenEvent() 376 { 377 return CEvent::registerTypeOnce(s_switchToScreen, 378 "CServer::switchToScreen"); 379 } 380 381 CEvent::Type 382 CServer::getSwitchInDirectionEvent() 383 { 384 return CEvent::registerTypeOnce(s_switchInDirection, 385 "CServer::switchInDirection"); 386 } 387 388 CEvent::Type 389 CServer::getLockCursorToScreenEvent() 390 { 391 return CEvent::registerTypeOnce(s_lockCursorToScreen, 392 "CServer::lockCursorToScreen"); 393 } 394 395 CString 396 CServer::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 405 UInt32 406 CServer::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 426 bool 427 CServer::isLockedToScreenServer() const 428 { 429 // locked if scroll-lock is toggled on 430 return m_lockedToScreen; 431 } 432 433 bool 434 CServer::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 451 SInt32 452 CServer::getJumpZoneSize(CBaseClientProxy* client) const 453 { 454 if (client == m_primaryClient) { 455 return m_primaryClient->getJumpZoneSize(); 456 } 457 else { 458 return 0; 459 } 460 } 461 462 void 463 CServer::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 533 void 534 CServer::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 548 float 549 CServer::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 570 void 571 CServer::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 593 bool 594 CServer::hasAnyNeighbor(CBaseClientProxy* client, EDirection dir) const 595 { 596 assert(client != NULL); 597 598 return m_config.hasNeighbor(getName(client), dir); 599 } 600 601 CBaseClientProxy* 602 CServer::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 649 CBaseClientProxy* 650 CServer::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 756 void 757 CServer::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 804 bool 805 CServer::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 892 void 893 CServer::noSwitch(SInt32 x, SInt32 y) 894 { 895 armSwitchTwoTap(x, y); 896 stopSwitchWait(); 897 } 898 899 void 900 CServer::stopSwitch() 901 { 902 if (m_switchScreen != NULL) { 903 m_switchScreen = NULL; 904 m_switchDir = kNoDirection; 905 stopSwitchTwoTap(); 906 stopSwitchWait(); 907 } 908 } 909 910 void 911 CServer::startSwitchTwoTap() 912 { 913 m_switchTwoTapEngaged = true; 914 m_switchTwoTapArmed = false; 915 m_switchTwoTapTimer.reset(); 916 LOG((CLOG_DEBUG1 "waiting for second tap")); 917 } 918 919 void 920 CServer::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 966 void 967 CServer::stopSwitchTwoTap() 968 { 969 m_switchTwoTapEngaged = false; 970 m_switchTwoTapArmed = false; 971 } 972 973 bool 974 CServer::isSwitchTwoTapStarted() const 975 { 976 return m_switchTwoTapEngaged; 977 } 978 979 bool 980 CServer::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 988 void 989 CServer::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 998 void 999 CServer::stopSwitchWait() 1000 { 1001 if (m_switchWaitTimer != NULL) { 1002 EVENTQUEUE->deleteTimer(m_switchWaitTimer); 1003 m_switchWaitTimer = NULL; 1004 } 1005 } 1006 1007 bool 1008 CServer::isSwitchWaitStarted() const 1009 { 1010 return (m_switchWaitTimer != NULL); 1011 } 1012 1013 UInt32 1014 CServer::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 1070 void 1071 CServer::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 1088 void 1089 CServer::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 1123 void 1124 CServer::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 1161 void 1162 CServer::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 1194 void 1195 CServer::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 1240 void 1241 CServer::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 1253 void 1254 CServer::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 1261 void 1262 CServer::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 1269 void 1270 CServer::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 1277 void 1278 CServer::handleButtonDownEvent(const CEvent& event, void*) 1279 { 1280 IPlatformScreen::CButtonInfo* info = 1281 reinterpret_cast<IPlatformScreen::CButtonInfo*>(event.getData()); 1282 onMouseDown(info->m_button); 1283 } 1284 1285 void 1286 CServer::handleButtonUpEvent(const CEvent& event, void*) 1287 { 1288 IPlatformScreen::CButtonInfo* info = 1289 reinterpret_cast<IPlatformScreen::CButtonInfo*>(event.getData()); 1290 onMouseUp(info->m_button); 1291 } 1292 1293 void 1294 CServer::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 1301 void 1302 CServer::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 1309 void 1310 CServer::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 1317 void 1318 CServer::handleScreensaverActivatedEvent(const CEvent&, void*) 1319 { 1320 onScreensaver(true); 1321 } 1322 1323 void 1324 CServer::handleScreensaverDeactivatedEvent(const CEvent&, void*) 1325 { 1326 onScreensaver(false); 1327 } 1328 1329 void 1330 CServer::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 1343 void 1344 CServer::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 1354 void 1355 CServer::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 1364 void 1365 CServer::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 1379 void 1380 CServer::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 1397 void 1398 CServer::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 1433 void 1434 CServer::handleFakeInputBeginEvent(const CEvent&, void*) 1435 { 1436 m_primaryClient->fakeInputBegin(); 1437 } 1438 1439 void 1440 CServer::handleFakeInputEndEvent(const CEvent&, void*) 1441 { 1442 m_primaryClient->fakeInputEnd(); 1443 } 1444 1445 void 1446 CServer::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 1485 void 1486 CServer::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 1540 void 1541 CServer::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 1587 void 1588 CServer::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 1617 void 1618 CServer::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 1637 void 1638 CServer::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 1647 void 1648 CServer::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 1657 bool 1658 CServer::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 1738 void 1739 CServer::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 1899 void 1900 CServer::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 1909 bool 1910 CServer::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 1946 bool 1947 CServer::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 1970 void 1971 CServer::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 2006 void 2007 CServer::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 2031 void 2032 CServer::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 2043 void 2044 CServer::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 2058 void 2059 CServer::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 2103 CServer::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 2117 CServer::CLockCursorToScreenInfo* 2118 CServer::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 2131 CServer::CSwitchToScreenInfo* 2132 CServer::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 2146 CServer::CSwitchInDirectionInfo* 2147 CServer::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 2160 CServer::CScreenConnectedInfo* 2161 CServer::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 37 CEvent::Type CServer::s_errorEvent = CEvent::kUnknown; 38 CEvent::Type CServer::s_connectedEvent = CEvent::kUnknown; 39 CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; 40 CEvent::Type CServer::s_switchToScreen = CEvent::kUnknown; 41 CEvent::Type CServer::s_switchInDirection = CEvent::kUnknown; 42 CEvent::Type CServer::s_lockCursorToScreen = CEvent::kUnknown; 43 44 CServer::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 161 CServer::~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 210 bool 211 CServer::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 254 void 255 CServer::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 295 void 296 CServer::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 308 UInt32 309 CServer::getNumClients() const 310 { 311 return m_clients.size(); 312 } 313 314 void 315 CServer::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 324 CEvent::Type 325 CServer::getErrorEvent() 326 { 327 return CEvent::registerTypeOnce(s_errorEvent, 328 "CServer::error"); 329 } 330 331 CEvent::Type 332 CServer::getConnectedEvent() 333 { 334 return CEvent::registerTypeOnce(s_connectedEvent, 335 "CServer::connected"); 336 } 337 338 CEvent::Type 339 CServer::getDisconnectedEvent() 340 { 341 return CEvent::registerTypeOnce(s_disconnectedEvent, 342 "CServer::disconnected"); 343 } 344 345 CEvent::Type 346 CServer::getSwitchToScreenEvent() 347 { 348 return CEvent::registerTypeOnce(s_switchToScreen, 349 "CServer::switchToScreen"); 350 } 351 352 CEvent::Type 353 CServer::getSwitchInDirectionEvent() 354 { 355 return CEvent::registerTypeOnce(s_switchInDirection, 356 "CServer::switchInDirection"); 357 } 358 359 CEvent::Type 360 CServer::getLockCursorToScreenEvent() 361 { 362 return CEvent::registerTypeOnce(s_lockCursorToScreen, 363 "CServer::lockCursorToScreen"); 364 } 365 366 CString 367 CServer::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 376 UInt32 377 CServer::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 397 bool 398 CServer::isLockedToScreenServer() const 399 { 400 // locked if scroll-lock is toggled on 401 return m_lockedToScreen; 402 } 403 404 bool 405 CServer::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 422 SInt32 423 CServer::getJumpZoneSize(CBaseClientProxy* client) const 424 { 425 if (client == m_primaryClient) { 426 return m_primaryClient->getJumpZoneSize(); 427 } 428 else { 429 return 0; 430 } 431 } 432 433 void 434 CServer::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 504 void 505 CServer::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 519 float 520 CServer::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 541 void 542 CServer::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 564 bool 565 CServer::hasAnyNeighbor(CBaseClientProxy* client, EDirection dir) const 566 { 567 assert(client != NULL); 568 569 return m_config.hasNeighbor(getName(client), dir); 570 } 571 572 CBaseClientProxy* 573 CServer::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 620 CBaseClientProxy* 621 CServer::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 727 void 728 CServer::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 775 bool 776 CServer::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 863 void 864 CServer::noSwitch(SInt32 x, SInt32 y) 865 { 866 armSwitchTwoTap(x, y); 867 stopSwitchWait(); 868 } 869 870 void 871 CServer::stopSwitch() 872 { 873 if (m_switchScreen != NULL) { 874 m_switchScreen = NULL; 875 m_switchDir = kNoDirection; 876 stopSwitchTwoTap(); 877 stopSwitchWait(); 878 } 879 } 880 881 void 882 CServer::startSwitchTwoTap() 883 { 884 m_switchTwoTapEngaged = true; 885 m_switchTwoTapArmed = false; 886 m_switchTwoTapTimer.reset(); 887 LOG((CLOG_DEBUG1 "waiting for second tap")); 888 } 889 890 void 891 CServer::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 937 void 938 CServer::stopSwitchTwoTap() 939 { 940 m_switchTwoTapEngaged = false; 941 m_switchTwoTapArmed = false; 942 } 943 944 bool 945 CServer::isSwitchTwoTapStarted() const 946 { 947 return m_switchTwoTapEngaged; 948 } 949 950 bool 951 CServer::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 959 void 960 CServer::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 969 void 970 CServer::stopSwitchWait() 971 { 972 if (m_switchWaitTimer != NULL) { 973 EVENTQUEUE->deleteTimer(m_switchWaitTimer); 974 m_switchWaitTimer = NULL; 975 } 976 } 977 978 bool 979 CServer::isSwitchWaitStarted() const 980 { 981 return (m_switchWaitTimer != NULL); 982 } 983 984 UInt32 985 CServer::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 1041 void 1042 CServer::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 1059 void 1060 CServer::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 1094 void 1095 CServer::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 1132 void 1133 CServer::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 1165 void 1166 CServer::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 1211 void 1212 CServer::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 1224 void 1225 CServer::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 1232 void 1233 CServer::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 1240 void 1241 CServer::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 1248 void 1249 CServer::handleButtonDownEvent(const CEvent& event, void*) 1250 { 1251 IPlatformScreen::CButtonInfo* info = 1252 reinterpret_cast<IPlatformScreen::CButtonInfo*>(event.getData()); 1253 onMouseDown(info->m_button); 1254 } 1255 1256 void 1257 CServer::handleButtonUpEvent(const CEvent& event, void*) 1258 { 1259 IPlatformScreen::CButtonInfo* info = 1260 reinterpret_cast<IPlatformScreen::CButtonInfo*>(event.getData()); 1261 onMouseUp(info->m_button); 1262 } 1263 1264 void 1265 CServer::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 1272 void 1273 CServer::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 1280 void 1281 CServer::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 1288 void 1289 CServer::handleScreensaverActivatedEvent(const CEvent&, void*) 1290 { 1291 onScreensaver(true); 1292 } 1293 1294 void 1295 CServer::handleScreensaverDeactivatedEvent(const CEvent&, void*) 1296 { 1297 onScreensaver(false); 1298 } 1299 1300 void 1301 CServer::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 1314 void 1315 CServer::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 1325 void 1326 CServer::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 1335 void 1336 CServer::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 1350 void 1351 CServer::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 1368 void 1369 CServer::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 1404 void 1405 CServer::handleFakeInputBeginEvent(const CEvent&, void*) 1406 { 1407 m_primaryClient->fakeInputBegin(); 1408 } 1409 1410 void 1411 CServer::handleFakeInputEndEvent(const CEvent&, void*) 1412 { 1413 m_primaryClient->fakeInputEnd(); 1414 } 1415 1416 void 1417 CServer::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 1456 void 1457 CServer::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 1511 void 1512 CServer::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 1556 void 1557 CServer::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 1586 void 1587 CServer::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 1606 void 1607 CServer::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 1616 void 1617 CServer::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 1626 bool 1627 CServer::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 1707 void 1708 CServer::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 1868 void 1869 CServer::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 1878 bool 1879 CServer::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 1915 bool 1916 CServer::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 1939 void 1940 CServer::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 1975 void 1976 CServer::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 2000 void 2001 CServer::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 2012 void 2013 CServer::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 2027 void 2028 CServer::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 2072 CServer::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 2086 CServer::CLockCursorToScreenInfo* 2087 CServer::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 2100 CServer::CSwitchToScreenInfo* 2101 CServer::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 2115 CServer::CSwitchInDirectionInfo* 2116 CServer::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 2129 CServer::CScreenConnectedInfo* 2130 CServer::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 423 423 424 424 // screen locking (former scroll lock) 425 425 bool m_lockedToScreen; 426 bool m_broadcast; 426 427 427 428 static CEvent::Type s_errorEvent; 428 429 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 31 class CBaseClientProxy; 32 class CEventQueueTimer; 33 class CPrimaryClient; 34 class CInputFilter; 35 36 //! Synergy server 37 /*! 38 This class implements the top-level server algorithms for synergy. 39 */ 40 class CServer { 41 public: 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 181 private: 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 351 private: 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 29 class CBaseClientProxy; 30 class CEventQueueTimer; 31 class CPrimaryClient; 32 class CInputFilter; 33 34 //! Synergy server 35 /*! 36 This class implements the top-level server algorithms for synergy. 37 */ 38 class CServer { 39 public: 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 179 private: 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 349 private: 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 176 176 { 177 177 assert(!m_isPrimary || m_fakeInput); 178 178 179 LOG((CLOG_DEBUG1 "MAIN keyDown!")); 180 179 181 // check for ctrl+alt+del emulation 180 182 if (id == kKeyDelete && 181 183 (mask & (KeyModifierControl | KeyModifierAlt)) == … … 193 195 KeyModifierMask mask, SInt32 count, KeyButton button) 194 196 { 195 197 assert(!m_isPrimary); 198 LOG((CLOG_DEBUG1 "MAIN keyRepeat!")); 196 199 m_screen->fakeKeyRepeat(id, mask, count, button); 197 200 } 198 201 … … 200 203 CScreen::keyUp(KeyID, KeyModifierMask, KeyButton button) 201 204 { 202 205 assert(!m_isPrimary || m_fakeInput); 206 LOG((CLOG_DEBUG1 "MAIN keyUp!")); 203 207 m_screen->fakeKeyUp(button); 204 208 } 205 209 -
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 6 section: screens 7 mac1: 8 winxp1: 9 end 10 11 section: links 12 mac1: 13 right = winxp1 14 15 winxp1: 16 left = mac1 17 18 end 19 20 section: aliases 21 mac1: 22 main 23 end 24 25 section: 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) 29 end 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 6 section: screens 7 # three hosts named: moe, larry, and curly 8 mac1: 9 winxp1: 10 end 11 12 section: 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 24 end 25 26 section: aliases 27 # curly is also known as shemp 28 mac1: 29 main 30 end 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 6 section: screens 7 # three hosts named: moe, larry, and curly 8 moe: 9 larry: 10 curly: 11 end 12 13 section: 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 31 end 32 33 section: aliases 34 # curly is also known as shemp 35 curly: 36 shemp 37 end