2
* synergy -- mouse and keyboard sharing utility
3
* Copyright (C) 2002 Chris Schoeneman
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.
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.
16
#include "CHTTPServer.h"
17
#include "CPrimaryClient.h"
18
#include "IPrimaryScreenFactory.h"
19
#include "CInputPacketStream.h"
20
#include "COutputPacketStream.h"
21
#include "CProtocolUtil.h"
22
#include "CClientProxy1_0.h"
23
#include "CClientProxy1_1.h"
24
#include "OptionTypes.h"
25
#include "ProtocolTypes.h"
28
#include "CTCPListenSocket.h"
29
#include "IDataSocket.h"
30
#include "ISocketFactory.h"
32
#include "IStreamFilterFactory.h"
35
#include "CTimerThread.h"
38
#include "CFunctionJob.h"
40
#include "CStopwatch.h"
41
#include "TMethodJob.h"
48
const SInt32 CServer::s_httpMaxSimultaneousRequests = 3;
50
CServer::CServer(const CString& serverName) :
53
m_bindTimeout(5.0 * 60.0),
54
m_screenFactory(NULL),
55
m_socketFactory(NULL),
56
m_streamFilterFactory(NULL),
57
m_acceptClientThread(NULL),
59
m_primaryClient(NULL),
63
m_httpAvailable(&m_mutex, s_httpMaxSimultaneousRequests),
64
m_switchDir(kNoDirection),
66
m_switchWaitDelay(0.0),
67
m_switchWaitEngaged(false),
68
m_switchTwoTapDelay(0.0),
69
m_switchTwoTapEngaged(false),
70
m_switchTwoTapArmed(false),
71
m_switchTwoTapZone(3),
79
delete m_screenFactory;
80
delete m_socketFactory;
81
delete m_streamFilterFactory;
89
LOG((CLOG_INFO "opening screen"));
91
setStatus(kNotRunning);
95
setStatus(kError, e.what());
96
LOG((CLOG_INFO "failed to open screen"));
99
catch (XUnknownClient& e) {
101
setStatus(kServerNameUnknown);
102
LOG((CLOG_CRIT "unknown screen name `%s'", e.getName().c_str()));
110
// check preconditions
112
CLock lock(&m_mutex);
113
assert(m_primaryClient != NULL);
117
setStatus(kNotRunning);
118
LOG((CLOG_NOTE "starting server"));
120
// start listening for new clients
121
m_acceptClientThread = new CThread(startThread(
122
new TMethodJob<CServer>(this,
123
&CServer::acceptClients)));
125
// start listening for HTTP requests
126
if (m_config.getHTTPAddress().isValid()) {
127
m_httpServer = new CHTTPServer(this);
128
startThread(new TMethodJob<CServer>(this,
129
&CServer::acceptHTTPClients));
133
m_primaryClient->mainLoop();
136
LOG((CLOG_NOTE "stopping server"));
138
// use a macro to write the stuff that should go into a finally
139
// block so we can repeat it easily. stroustrup's view that
140
// "resource acquistion is initialization" is a better solution
141
// than a finally block is parochial. they both have their
142
// place. adding finally to C++ would've been a drop in a big
144
#define FINALLY do { \
146
delete m_httpServer; \
147
m_httpServer = NULL; \
153
LOG((CLOG_ERR "server error: %s", e.what()));
154
setStatus(kError, e.what());
157
LOG((CLOG_NOTE "stopping server"));
162
LOG((CLOG_ERR "server error: %s", e.what()));
163
setStatus(kError, e.what());
166
LOG((CLOG_NOTE "stopping server"));
170
setStatus(kNotRunning);
173
LOG((CLOG_NOTE "stopping server"));
178
LOG((CLOG_DEBUG "unknown server error"));
182
LOG((CLOG_NOTE "stopping server"));
188
// throw if there was an error
190
LOG((CLOG_DEBUG "forwarding child thread exception"));
191
throw XServerRethrow();
196
CServer::exitMainLoop()
198
m_primaryClient->exitMainLoop();
202
CServer::exitMainLoopWithError()
205
CLock lock(&m_mutex);
214
if (m_primaryClient != NULL) {
215
closePrimaryScreen();
217
LOG((CLOG_INFO "closed screen"));
221
CServer::setConfig(const CConfig& config)
223
// refuse configuration if it doesn't include the primary screen
225
CLock lock(&m_mutex);
226
if (m_primaryClient != NULL &&
227
!config.isScreen(m_primaryClient->getName())) {
232
// close clients that are connected but being dropped from the
234
closeClients(config);
237
CLock lock(&m_mutex);
240
// process global options
241
const CConfig::CScreenOptions* options = m_config.getOptions("");
242
if (options != NULL && options->size() > 0) {
243
for (CConfig::CScreenOptions::const_iterator index = options->begin();
244
index != options->end(); ++index) {
245
const OptionID id = index->first;
246
const OptionValue value = index->second;
247
if (id == kOptionScreenSwitchDelay) {
248
m_switchWaitDelay = 1.0e-3 * static_cast<double>(value);
249
if (m_switchWaitDelay < 0.0) {
250
m_switchWaitDelay = 0.0;
252
m_switchWaitEngaged = false;
254
else if (id == kOptionScreenSwitchTwoTap) {
255
m_switchTwoTapDelay = 1.0e-3 * static_cast<double>(value);
256
if (m_switchTwoTapDelay < 0.0) {
257
m_switchTwoTapDelay = 0.0;
259
m_switchTwoTapEngaged = false;
264
// tell primary screen about reconfiguration
265
if (m_primaryClient != NULL) {
266
m_primaryClient->reconfigure(getActivePrimarySides());
269
// tell all (connected) clients about current options
270
for (CClientList::const_iterator index = m_clients.begin();
271
index != m_clients.end(); ++index) {
272
IClient* client = index->second;
283
CServer::setScreenFactory(IPrimaryScreenFactory* adopted)
285
CLock lock(&m_mutex);
286
delete m_screenFactory;
287
m_screenFactory = adopted;
291
CServer::setSocketFactory(ISocketFactory* adopted)
293
CLock lock(&m_mutex);
294
delete m_socketFactory;
295
m_socketFactory = adopted;
299
CServer::setStreamFilterFactory(IStreamFilterFactory* adopted)
301
CLock lock(&m_mutex);
302
delete m_streamFilterFactory;
303
m_streamFilterFactory = adopted;
307
CServer::addStatusJob(IJob* job)
309
m_statusJobs.addJob(job);
313
CServer::removeStatusJob(IJob* job)
315
m_statusJobs.removeJob(job);
319
CServer::getPrimaryScreenName() const
325
CServer::getNumClients() const
327
CLock lock(&m_mutex);
328
return m_clients.size();
332
CServer::getClients(std::vector<CString>& list) const
334
CLock lock(&m_mutex);
336
for (CClientList::const_iterator index = m_clients.begin();
337
index != m_clients.end(); ++index) {
338
list.push_back(index->first);
343
CServer::getStatus(CString* msg) const
345
CLock lock(&m_mutex);
347
*msg = m_statusMessage;
353
CServer::getConfig(CConfig* config) const
355
assert(config != NULL);
357
CLock lock(&m_mutex);
362
CServer::runStatusJobs() const
364
m_statusJobs.runJobs();
368
CServer::setStatus(EStatus status, const char* msg)
371
CLock lock(&m_mutex);
373
if (m_status == kError) {
374
m_statusMessage = (msg == NULL) ? "Error" : msg;
377
m_statusMessage = (msg == NULL) ? "" : msg;
384
CServer::getActivePrimarySides() const
386
// note -- m_mutex must be locked on entry
388
if (!m_config.getNeighbor(getPrimaryScreenName(), kLeft).empty()) {
391
if (!m_config.getNeighbor(getPrimaryScreenName(), kRight).empty()) {
394
if (!m_config.getNeighbor(getPrimaryScreenName(), kTop).empty()) {
397
if (!m_config.getNeighbor(getPrimaryScreenName(), kBottom).empty()) {
398
sides |= kBottomMask;
408
// stop all running threads but don't wait too long since some
409
// threads may be unable to proceed until this thread returns.
412
// done with the HTTP server
413
CLock lock(&m_mutex);
417
// note -- we do not attempt to close down the primary screen
421
CServer::onInfoChanged(const CString& name, const CClientInfo& info)
423
CLock lock(&m_mutex);
426
CClientList::iterator index = m_clients.find(name);
427
if (index == m_clients.end()) {
430
IClient* client = index->second;
431
assert(client != NULL);
433
// update the remote mouse coordinates
434
if (client == m_active) {
438
LOG((CLOG_INFO "screen \"%s\" shape=%d,%d %dx%d zone=%d pos=%d,%d", name.c_str(), info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my));
440
// handle resolution change to primary screen
441
if (client == m_primaryClient) {
442
if (client == m_active) {
443
onMouseMovePrimaryNoLock(m_x, m_y);
446
onMouseMoveSecondaryNoLock(0, 0);
452
CServer::onGrabClipboard(const CString& name, ClipboardID id, UInt32 seqNum)
454
CLock lock(&m_mutex);
456
// screen must be connected
457
CClientList::iterator grabber = m_clients.find(name);
458
if (grabber == m_clients.end()) {
462
// ignore grab if sequence number is old. always allow primary
464
CClipboardInfo& clipboard = m_clipboards[id];
465
if (name != m_primaryClient->getName() &&
466
seqNum < clipboard.m_clipboardSeqNum) {
467
LOG((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", name.c_str(), id));
471
// mark screen as owning clipboard
472
LOG((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", name.c_str(), id, clipboard.m_clipboardOwner.c_str()));
473
clipboard.m_clipboardOwner = name;
474
clipboard.m_clipboardSeqNum = seqNum;
476
// clear the clipboard data (since it's not known at this point)
477
if (clipboard.m_clipboard.open(0)) {
478
clipboard.m_clipboard.empty();
479
clipboard.m_clipboard.close();
481
clipboard.m_clipboardData = clipboard.m_clipboard.marshall();
483
// tell all other screens to take ownership of clipboard. tell the
484
// grabber that it's clipboard isn't dirty.
485
for (CClientList::iterator index = m_clients.begin();
486
index != m_clients.end(); ++index) {
487
IClient* client = index->second;
488
if (index == grabber) {
489
client->setClipboardDirty(id, false);
492
client->grabClipboard(id);
500
CServer::onClipboardChanged(ClipboardID id, UInt32 seqNum, const CString& data)
502
CLock lock(&m_mutex);
503
onClipboardChangedNoLock(id, seqNum, data);
507
CServer::onClipboardChangedNoLock(ClipboardID id,
508
UInt32 seqNum, const CString& data)
510
CClipboardInfo& clipboard = m_clipboards[id];
512
// ignore update if sequence number is old
513
if (seqNum < clipboard.m_clipboardSeqNum) {
514
LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", clipboard.m_clipboardOwner.c_str(), id));
518
// ignore if data hasn't changed
519
if (data == clipboard.m_clipboardData) {
520
LOG((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id));
524
// unmarshall into our clipboard buffer
525
LOG((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id));
526
clipboard.m_clipboardData = data;
527
clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0);
529
// tell all clients except the sender that the clipboard is dirty
530
CClientList::const_iterator sender =
531
m_clients.find(clipboard.m_clipboardOwner);
532
for (CClientList::const_iterator index = m_clients.begin();
533
index != m_clients.end(); ++index) {
534
IClient* client = index->second;
535
client->setClipboardDirty(id, index != sender);
538
// send the new clipboard to the active screen
539
m_active->setClipboard(id, m_clipboards[id].m_clipboardData);
543
CServer::onScreensaver(bool activated)
545
LOG((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated"));
546
CLock lock(&m_mutex);
549
// save current screen and position
550
m_activeSaver = m_active;
554
// jump to primary screen
555
if (m_active != m_primaryClient) {
556
switchScreen(m_primaryClient, 0, 0, true);
560
// jump back to previous screen and position. we must check
561
// that the position is still valid since the screen may have
562
// changed resolutions while the screen saver was running.
563
if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) {
565
IClient* screen = m_activeSaver;
567
screen->getShape(x, y, w, h);
568
SInt32 zoneSize = screen->getJumpZoneSize();
569
if (m_xSaver < x + zoneSize) {
570
m_xSaver = x + zoneSize;
572
else if (m_xSaver >= x + w - zoneSize) {
573
m_xSaver = x + w - zoneSize - 1;
575
if (m_ySaver < y + zoneSize) {
576
m_ySaver = y + zoneSize;
578
else if (m_ySaver >= y + h - zoneSize) {
579
m_ySaver = y + h - zoneSize - 1;
583
switchScreen(screen, m_xSaver, m_ySaver, false);
587
m_activeSaver = NULL;
590
// send message to all clients
591
for (CClientList::const_iterator index = m_clients.begin();
592
index != m_clients.end(); ++index) {
593
IClient* client = index->second;
594
client->screensaver(activated);
599
CServer::onOneShotTimerExpired(UInt32 id)
601
CLock lock(&m_mutex);
603
// ignore if it's an old timer or if switch wait isn't engaged anymore
604
if (!m_switchWaitEngaged || id != m_switchWaitTimer) {
608
// ignore if mouse is locked to screen
609
if (isLockedToScreenNoLock()) {
610
LOG((CLOG_DEBUG1 "locked to screen"));
616
switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false);
620
CServer::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button)
622
LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button));
623
CLock lock(&m_mutex);
624
assert(m_active != NULL);
626
// handle command keys
627
if (onCommandKey(id, mask, true)) {
632
m_active->keyDown(id, mask, button);
636
CServer::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button)
638
LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button));
639
CLock lock(&m_mutex);
640
assert(m_active != NULL);
642
// handle command keys
643
if (onCommandKey(id, mask, false)) {
648
m_active->keyUp(id, mask, button);
652
CServer::onKeyRepeat(KeyID id, KeyModifierMask mask,
653
SInt32 count, KeyButton button)
655
LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button));
656
CLock lock(&m_mutex);
657
assert(m_active != NULL);
659
// handle command keys
660
if (onCommandKey(id, mask, false)) {
661
onCommandKey(id, mask, true);
666
m_active->keyRepeat(id, mask, count, button);
670
CServer::onMouseDown(ButtonID id)
672
LOG((CLOG_DEBUG1 "onMouseDown id=%d", id));
673
CLock lock(&m_mutex);
674
assert(m_active != NULL);
677
m_active->mouseDown(id);
681
CServer::onMouseUp(ButtonID id)
683
LOG((CLOG_DEBUG1 "onMouseUp id=%d", id));
684
CLock lock(&m_mutex);
685
assert(m_active != NULL);
688
m_active->mouseUp(id);
692
CServer::onMouseMovePrimary(SInt32 x, SInt32 y)
694
LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y));
695
CLock lock(&m_mutex);
696
return onMouseMovePrimaryNoLock(x, y);
700
CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y)
702
// mouse move on primary (server's) screen
703
assert(m_primaryClient != NULL);
704
assert(m_active == m_primaryClient);
707
SInt32 ax, ay, aw, ah;
708
m_active->getShape(ax, ay, aw, ah);
709
SInt32 zoneSize = m_active->getJumpZoneSize();
711
// see if we should change screens
713
if (x < ax + zoneSize) {
717
else if (x >= ax + aw - zoneSize) {
721
else if (y < ay + zoneSize) {
725
else if (y >= ay + ah - zoneSize) {
730
// still on local screen. check if we're inside the tap region.
731
SInt32 tapZone = (zoneSize < m_switchTwoTapZone) ?
732
m_switchTwoTapZone : zoneSize;
733
bool inTapZone = (x < ax + tapZone ||
734
x >= ax + aw - tapZone ||
736
y >= ay + ah - tapZone);
739
onNoSwitch(inTapZone);
743
// get jump destination
744
IClient* newScreen = getNeighbor(m_active, dir, x, y);
746
// should we switch or not?
747
if (isSwitchOkay(newScreen, dir, x, y)) {
749
switchScreen(newScreen, x, y, false);
758
CServer::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
760
LOG((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy));
761
CLock lock(&m_mutex);
762
onMouseMoveSecondaryNoLock(dx, dy);
766
CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy)
768
// mouse move on secondary (client's) screen
769
assert(m_active != NULL);
770
if (m_active == m_primaryClient) {
771
// we're actually on the primary screen. this can happen
772
// when the primary screen begins processing a mouse move
773
// for a secondary screen, then the active (secondary)
774
// screen disconnects causing us to jump to the primary
775
// screen, and finally the primary screen finishes
776
// processing the mouse move, still thinking it's for
777
// a secondary screen. we just ignore the motion.
782
const SInt32 xOld = m_x;
783
const SInt32 yOld = m_y;
790
SInt32 ax, ay, aw, ah;
791
m_active->getShape(ax, ay, aw, ah);
793
// find direction of neighbor and get the neighbor
801
else if (m_x > ax + aw - 1) {
807
else if (m_y > ay + ah - 1) {
811
// we haven't left the screen
812
newScreen = m_active;
815
// if waiting and mouse is not on the border we're waiting
816
// on then stop waiting. also if it's not on the border
817
// then arm the double tap.
818
if (m_switchScreen != NULL) {
820
SInt32 zoneSize = m_primaryClient->getJumpZoneSize();
821
switch (m_switchDir) {
823
clearWait = (m_x >= ax + zoneSize);
827
clearWait = (m_x <= ax + aw - 1 - zoneSize);
831
clearWait = (m_y >= ay + zoneSize);
835
clearWait = (m_y <= ay + ah - 1 + zoneSize);
843
// still on local screen. check if we're inside the
845
SInt32 tapZone = (zoneSize < m_switchTwoTapZone) ?
846
m_switchTwoTapZone : zoneSize;
847
bool inTapZone = (m_x < ax + tapZone ||
848
m_x >= ax + aw - tapZone ||
849
m_y < ay + tapZone ||
850
m_y >= ay + ah - tapZone);
853
onNoSwitch(inTapZone);
857
// skip rest of block
861
// try to switch screen. get the neighbor.
862
newScreen = getNeighbor(m_active, dir, m_x, m_y);
864
// see if we should switch
865
if (!isSwitchOkay(newScreen, dir, m_x, m_y)) {
866
newScreen = m_active;
873
switchScreen(newScreen, m_x, m_y, false);
876
// same screen. clamp mouse to edge.
881
LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", m_active->getName().c_str()));
883
else if (m_x > ax + aw - 1) {
885
LOG((CLOG_DEBUG2 "clamp to right of \"%s\"", m_active->getName().c_str()));
889
LOG((CLOG_DEBUG2 "clamp to top of \"%s\"", m_active->getName().c_str()));
891
else if (m_y > ay + ah - 1) {
893
LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", m_active->getName().c_str()));
896
// warp cursor if it moved.
897
if (m_x != xOld || m_y != yOld) {
898
LOG((CLOG_DEBUG2 "move on %s to %d,%d", m_active->getName().c_str(), m_x, m_y));
899
m_active->mouseMove(m_x, m_y);
905
CServer::onMouseWheel(SInt32 delta)
907
LOG((CLOG_DEBUG1 "onMouseWheel %+d", delta));
908
CLock lock(&m_mutex);
909
assert(m_active != NULL);
912
m_active->mouseWheel(delta);
916
CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/)
922
CServer::isLockedToScreenNoLock() const
924
// locked if scroll-lock is toggled on
925
if ((m_primaryClient->getToggleMask() & KeyModifierScrollLock) != 0) {
926
LOG((CLOG_DEBUG "locked by ScrollLock"));
930
// locked if primary says we're locked
931
if (m_primaryClient->isLockedToScreen()) {
940
CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver)
942
// note -- must be locked on entry
947
SInt32 dx, dy, dw, dh;
948
dst->getShape(dx, dy, dw, dh);
949
assert(x >= dx && y >= dy && x < dx + dw && y < dy + dh);
952
assert(m_active != NULL);
954
LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", m_active->getName().c_str(), dst->getName().c_str(), x, y));
956
// stop waiting to switch
959
// record new position
963
// wrapping means leaving the active screen and entering it again.
964
// since that's a waste of time we skip that and just warp the
966
if (m_active != dst) {
967
// leave active screen
968
if (!m_active->leave()) {
969
// cannot leave screen
970
LOG((CLOG_WARN "can't leave screen"));
974
// update the primary client's clipboards if we're leaving the
976
if (m_active == m_primaryClient) {
977
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
978
CClipboardInfo& clipboard = m_clipboards[id];
979
if (clipboard.m_clipboardOwner == m_primaryClient->getName()) {
980
CString clipboardData;
981
m_primaryClient->getClipboard(id, clipboardData);
982
onClipboardChangedNoLock(id,
983
clipboard.m_clipboardSeqNum, clipboardData);
991
// increment enter sequence number
995
m_active->enter(x, y, m_seqNum,
996
m_primaryClient->getToggleMask(),
999
// send the clipboard data to new active screen
1000
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
1001
m_active->setClipboard(id, m_clipboards[id].m_clipboardData);
1005
m_active->mouseMove(x, y);
1010
CServer::getNeighbor(IClient* src, EDirection dir) const
1012
// note -- must be locked on entry
1014
assert(src != NULL);
1016
// get source screen name
1017
CString srcName = src->getName();
1018
assert(!srcName.empty());
1019
LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str()));
1021
// get first neighbor. if it's the source then the source jumps
1022
// to itself and we return the source.
1023
CString dstName(m_config.getNeighbor(srcName, dir));
1024
if (dstName == srcName) {
1025
LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str()));
1031
// if nothing in that direction then return NULL. if the
1032
// destination is the source then we can make no more
1033
// progress in this direction. since we haven't found a
1034
// connected neighbor we return NULL.
1035
if (dstName.empty() || dstName == srcName) {
1036
LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", CConfig::dirName(dir), srcName.c_str()));
1040
// look up neighbor cell. if the screen is connected and
1041
// ready then we can stop.
1042
CClientList::const_iterator index = m_clients.find(dstName);
1043
if (index != m_clients.end()) {
1044
LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str()));
1045
return index->second;
1048
// skip over unconnected screen
1049
LOG((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), CConfig::dirName(dir), srcName.c_str()));
1052
// look up name of neighbor of skipped screen
1053
dstName = m_config.getNeighbor(srcName, dir);
1058
CServer::getNeighbor(IClient* src,
1059
EDirection srcSide, SInt32& x, SInt32& y) const
1061
// note -- must be locked on entry
1063
assert(src != NULL);
1065
// get the first neighbor
1066
IClient* dst = getNeighbor(src, srcSide);
1071
// get the source screen's size (needed for kRight and kBottom)
1072
SInt32 sx, sy, sw, sh;
1073
SInt32 dx, dy, dw, dh;
1074
IClient* lastGoodScreen = src;
1075
lastGoodScreen->getShape(sx, sy, sw, sh);
1076
lastGoodScreen->getShape(dx, dy, dw, dh);
1078
// find destination screen, adjusting x or y (but not both). the
1079
// searches are done in a sort of canonical screen space where
1080
// the upper-left corner is 0,0 for each screen. we adjust from
1081
// actual to canonical position on entry to and from canonical to
1082
// actual on exit from the search.
1086
while (dst != NULL) {
1087
lastGoodScreen = dst;
1088
lastGoodScreen->getShape(dx, dy, dw, dh);
1093
LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str()));
1094
dst = getNeighbor(lastGoodScreen, srcSide);
1096
assert(lastGoodScreen != NULL);
1102
while (dst != NULL) {
1104
lastGoodScreen = dst;
1105
lastGoodScreen->getShape(dx, dy, dw, dh);
1109
LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str()));
1110
dst = getNeighbor(lastGoodScreen, srcSide);
1112
assert(lastGoodScreen != NULL);
1118
while (dst != NULL) {
1119
lastGoodScreen = dst;
1120
lastGoodScreen->getShape(dx, dy, dw, dh);
1125
LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str()));
1126
dst = getNeighbor(lastGoodScreen, srcSide);
1128
assert(lastGoodScreen != NULL);
1134
while (dst != NULL) {
1136
lastGoodScreen = dst;
1137
lastGoodScreen->getShape(dx, dy, dw, dh);
1141
LOG((CLOG_DEBUG2 "skipping over screen %s", dst->getName().c_str()));
1142
dst = getNeighbor(lastGoodScreen, srcSide);
1144
assert(lastGoodScreen != NULL);
1149
// save destination screen
1150
assert(lastGoodScreen != NULL);
1151
dst = lastGoodScreen;
1153
// if entering primary screen then be sure to move in far enough
1154
// to avoid the jump zone. if entering a side that doesn't have
1155
// a neighbor (i.e. an asymmetrical side) then we don't need to
1156
// move inwards because that side can't provoke a jump.
1157
if (dst == m_primaryClient) {
1158
const CString dstName(dst->getName());
1161
if (!m_config.getNeighbor(dstName, kRight).empty() &&
1162
x > dx + dw - 1 - dst->getJumpZoneSize())
1163
x = dx + dw - 1 - dst->getJumpZoneSize();
1167
if (!m_config.getNeighbor(dstName, kLeft).empty() &&
1168
x < dx + dst->getJumpZoneSize())
1169
x = dx + dst->getJumpZoneSize();
1173
if (!m_config.getNeighbor(dstName, kBottom).empty() &&
1174
y > dy + dh - 1 - dst->getJumpZoneSize())
1175
y = dy + dh - 1 - dst->getJumpZoneSize();
1179
if (!m_config.getNeighbor(dstName, kTop).empty() &&
1180
y < dy + dst->getJumpZoneSize())
1181
y = dy + dst->getJumpZoneSize();
1186
// adjust the coordinate orthogonal to srcSide to account for
1187
// resolution differences. for example, if y is 200 pixels from
1188
// the top on a screen 1000 pixels high (20% from the top) when
1189
// we cross the left edge onto a screen 600 pixels high then y
1190
// should be set 120 pixels from the top (again 20% from the
1203
y = static_cast<SInt32>(0.5 + y *
1204
static_cast<double>(dh - 1) / (sh - 1));
1219
x = static_cast<SInt32>(0.5 + x *
1220
static_cast<double>(dw - 1) / (sw - 1));
1230
CServer::isSwitchOkay(IClient* newScreen, EDirection dir, SInt32 x, SInt32 y)
1232
LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", m_active->getName().c_str(), CConfig::dirName(dir)));
1234
// is there a neighbor?
1235
if (newScreen == NULL) {
1236
// there's no neighbor. we don't want to switch and we don't
1237
// want to try to switch later.
1238
LOG((CLOG_DEBUG1 "no neighbor %s", CConfig::dirName(dir)));
1243
// should we switch or not?
1244
bool preventSwitch = false;
1245
bool allowSwitch = false;
1247
// note if the switch direction has changed. save the new
1248
// direction and screen if so.
1249
bool isNewDirection = (dir != m_switchDir);
1250
if (isNewDirection || m_switchScreen == NULL) {
1252
m_switchScreen = newScreen;
1255
// is this a double tap and do we care?
1256
if (!allowSwitch && m_switchTwoTapDelay > 0.0) {
1257
if (isNewDirection || !m_switchTwoTapEngaged) {
1258
// tapping a different or new edge. prepare for second tap.
1259
preventSwitch = true;
1260
m_switchTwoTapEngaged = true;
1261
m_switchTwoTapArmed = false;
1262
m_switchTwoTapTimer.reset();
1263
LOG((CLOG_DEBUG1 "waiting for second tap"));
1266
// second tap if we were armed. if soon enough then switch.
1267
if (m_switchTwoTapArmed &&
1268
m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay) {
1272
// not fast enough. reset the clock.
1273
preventSwitch = true;
1274
m_switchTwoTapEngaged = true;
1275
m_switchTwoTapArmed = false;
1276
m_switchTwoTapTimer.reset();
1277
LOG((CLOG_DEBUG1 "waiting for second tap"));
1282
// if waiting before a switch then prepare to switch later
1283
if (!allowSwitch && m_switchWaitDelay > 0.0) {
1284
if (isNewDirection || !m_switchWaitEngaged) {
1285
m_switchWaitEngaged = true;
1288
m_switchWaitTimer = m_primaryClient->addOneShotTimer(
1290
LOG((CLOG_DEBUG1 "waiting to switch"));
1292
preventSwitch = true;
1295
// ignore if mouse is locked to screen
1296
if (!preventSwitch && isLockedToScreenNoLock()) {
1297
LOG((CLOG_DEBUG1 "locked to screen"));
1298
preventSwitch = true;
1300
// don't try to switch later. it's possible that we might
1301
// not be locked to the screen when the wait delay expires
1302
// and could switch then but we'll base the decision on
1303
// when the user first attempts the switch. this also
1304
// ensures that all switch tests are using the same
1308
return !preventSwitch;
1312
CServer::onNoSwitch(bool inTapZone)
1314
if (m_switchTwoTapEngaged) {
1315
if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) {
1316
// second tap took too long. disengage.
1317
m_switchTwoTapEngaged = false;
1318
m_switchTwoTapArmed = false;
1320
else if (!inTapZone) {
1321
// we've moved away from the edge and there's still
1322
// time to get back for a double tap.
1323
m_switchTwoTapArmed = true;
1327
// once the mouse moves away from the edge we no longer want to
1328
// switch after a delay.
1329
m_switchWaitEngaged = false;
1333
CServer::clearSwitchState()
1335
if (m_switchScreen != NULL) {
1336
m_switchDir = kNoDirection;
1337
m_switchScreen = NULL;
1338
m_switchWaitEngaged = false;
1339
m_switchTwoTapEngaged = false;
1344
CServer::closeClients(const CConfig& config)
1346
CThreadList threads;
1348
CLock lock(&m_mutex);
1350
// get the set of clients that are connected but are being
1351
// dropped from the configuration (or who's canonical name
1352
// is changing) and tell them to disconnect. note that
1353
// since m_clientThreads doesn't include a thread for the
1354
// primary client we will not close it.
1355
for (CClientThreadList::iterator
1356
index = m_clientThreads.begin();
1357
index != m_clientThreads.end(); ) {
1358
const CString& name = index->first;
1359
if (!config.isCanonicalName(name)) {
1360
// lookup IClient with name
1361
CClientList::const_iterator index2 = m_clients.find(name);
1362
assert(index2 != m_clients.end());
1364
// save the thread and remove it from m_clientThreads
1365
threads.push_back(index->second);
1366
m_clientThreads.erase(index++);
1368
// close that client
1369
assert(index2->second != m_primaryClient);
1370
index2->second->close();
1372
// don't switch to it if we planned to
1373
if (index2->second == m_switchScreen) {
1383
// wait a moment to allow each client to close its connection
1384
// before we close it (to avoid having our socket enter TIME_WAIT).
1385
if (threads.size() > 0) {
1389
// cancel the old client threads
1390
for (CThreadList::iterator index = threads.begin();
1391
index != threads.end(); ++index) {
1395
// wait for old client threads to terminate. we must not hold
1396
// the lock while we do this so those threads can finish any
1397
// calls to this object.
1398
for (CThreadList::iterator index = threads.begin();
1399
index != threads.end(); ++index) {
1403
// clean up thread list
1408
CServer::startThread(IJob* job)
1410
CLock lock(&m_mutex);
1412
// reap completed threads
1413
doReapThreads(m_threads);
1415
// add new thread to list
1416
CThread thread(job);
1417
m_threads.push_back(thread);
1418
LOG((CLOG_DEBUG1 "started thread 0x%08x", thread.getID()));
1423
CServer::stopThreads(double timeout)
1425
LOG((CLOG_DEBUG1 "stopping threads"));
1427
// cancel the accept client thread to prevent more clients from
1428
// connecting while we're shutting down.
1429
CThread* acceptClientThread;
1431
CLock lock(&m_mutex);
1432
acceptClientThread = m_acceptClientThread;
1433
m_acceptClientThread = NULL;
1435
if (acceptClientThread != NULL) {
1436
acceptClientThread->cancel();
1437
acceptClientThread->wait(timeout);
1438
delete acceptClientThread;
1441
// close all clients (except the primary)
1443
CConfig emptyConfig;
1444
closeClients(emptyConfig);
1447
// swap thread list so nobody can mess with it
1448
CThreadList threads;
1450
CLock lock(&m_mutex);
1451
threads.swap(m_threads);
1454
// cancel every thread
1455
for (CThreadList::iterator index = threads.begin();
1456
index != threads.end(); ++index) {
1460
// now wait for the threads
1461
CStopwatch timer(true);
1462
while (threads.size() > 0 && (timeout < 0.0 || timer.getTime() < timeout)) {
1463
doReapThreads(threads);
1467
// delete remaining threads
1468
for (CThreadList::iterator index = threads.begin();
1469
index != threads.end(); ++index) {
1470
LOG((CLOG_DEBUG1 "reaped running thread 0x%08x", index->getID()));
1473
LOG((CLOG_DEBUG1 "stopped threads"));
1477
CServer::reapThreads()
1479
CLock lock(&m_mutex);
1480
doReapThreads(m_threads);
1484
CServer::doReapThreads(CThreadList& threads)
1486
for (CThreadList::iterator index = threads.begin();
1487
index != threads.end(); ) {
1488
if (index->wait(0.0)) {
1489
// thread terminated
1490
LOG((CLOG_DEBUG1 "reaped thread 0x%08x", index->getID()));
1491
index = threads.erase(index);
1494
// thread is running
1501
CServer::acceptClients(void*)
1503
LOG((CLOG_DEBUG1 "starting to wait for clients"));
1505
IListenSocket* listen = NULL;
1507
// create socket listener
1508
if (m_socketFactory != NULL) {
1509
listen = m_socketFactory->createListen();
1511
assert(listen != NULL);
1513
// bind to the desired port. keep retrying if we can't bind
1514
// the address immediately.
1518
LOG((CLOG_DEBUG1 "binding listen socket"));
1519
listen->bind(m_config.getSynergyAddress());
1522
catch (XSocketAddressInUse& e) {
1523
setStatus(kError, e.what());
1524
LOG((CLOG_WARN "bind failed: %s", e.what()));
1526
// give up if we've waited too long
1527
if (timer.getTime() >= m_bindTimeout) {
1528
LOG((CLOG_ERR "waited too long to bind, giving up"));
1532
// wait a bit before retrying
1537
// accept connections and begin processing them
1538
setStatus(kRunning);
1539
LOG((CLOG_DEBUG1 "waiting for client connections"));
1541
// accept connection
1542
CThread::testCancel();
1543
IDataSocket* socket = listen->accept();
1544
LOG((CLOG_NOTE "accepted client connection"));
1545
CThread::testCancel();
1547
// start handshake thread
1548
startThread(new TMethodJob<CServer>(
1549
this, &CServer::runClient, socket));
1553
setStatus(kError, e.what());
1554
LOG((CLOG_ERR "cannot listen for clients: %s", e.what()));
1556
exitMainLoopWithError();
1559
setStatus(kNotRunning);
1566
CServer::runClient(void* vsocket)
1568
// get the socket pointer from the argument
1569
assert(vsocket != NULL);
1570
IDataSocket* socket = reinterpret_cast<IDataSocket*>(vsocket);
1573
CClientProxy* proxy = NULL;
1575
proxy = handshakeClient(socket);
1576
if (proxy == NULL) {
1586
// add the connection
1588
addConnection(proxy);
1590
// save this client's thread
1591
CLock lock(&m_mutex);
1592
m_clientThreads.insert(std::make_pair(proxy->getName(),
1593
CThread::getCurrentThread()));
1595
// send configuration options
1598
catch (XDuplicateClient& e) {
1599
// client has duplicate name
1600
LOG((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str()));
1602
CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy);
1611
catch (XUnknownClient& e) {
1612
// client has unknown name
1613
LOG((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str()));
1615
CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown);
1630
// activate screen saver on new client if active on the primary screen
1632
CLock lock(&m_mutex);
1633
if (m_activeSaver != NULL) {
1634
proxy->screensaver(true);
1638
// handle client messages
1640
LOG((CLOG_NOTE "client \"%s\" has connected", proxy->getName().c_str()));
1643
catch (XBadClient&) {
1644
// client not behaving
1645
LOG((CLOG_WARN "protocol error from client \"%s\"", proxy->getName().c_str()));
1647
CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBad);
1650
// ignore. client probably aborted the connection.
1655
LOG((CLOG_WARN "error communicating with client \"%s\": %s", proxy->getName().c_str(), e.what()));
1658
// mainLoop() was probably cancelled
1659
removeConnection(proxy->getName());
1665
removeConnection(proxy->getName());
1670
CServer::handshakeClient(IDataSocket* socket)
1672
LOG((CLOG_DEBUG1 "negotiating with new client"));
1674
// get the input and output streams
1675
IInputStream* input = socket->getInputStream();
1676
IOutputStream* output = socket->getOutputStream();
1680
if (m_streamFilterFactory != NULL) {
1681
input = m_streamFilterFactory->createInput(input, own);
1682
output = m_streamFilterFactory->createOutput(output, own);
1686
// attach the packetizing filters
1687
input = new CInputPacketStream(input, own);
1688
output = new COutputPacketStream(output, own);
1691
CClientProxy* proxy = NULL;
1692
CString name("<unknown>");
1694
// give the client a limited time to complete the handshake
1695
CTimerThread timer(30.0);
1698
LOG((CLOG_DEBUG1 "saying hello"));
1699
CProtocolUtil::writef(output, kMsgHello,
1700
kProtocolMajorVersion,
1701
kProtocolMinorVersion);
1704
// wait for the reply
1705
LOG((CLOG_DEBUG1 "waiting for hello reply"));
1706
UInt32 n = input->getSize();
1708
// limit the maximum length of the hello
1709
if (n > kMaxHelloLength) {
1713
// get and parse the reply to hello
1714
SInt16 major, minor;
1716
LOG((CLOG_DEBUG1 "parsing hello reply"));
1717
CProtocolUtil::readf(input, kMsgHelloBack,
1718
&major, &minor, &name);
1724
// disallow invalid version numbers
1725
if (major <= 0 || minor < 0) {
1726
throw XIncompatibleClient(major, minor);
1729
// convert name to canonical form (if any)
1730
if (m_config.isScreen(name)) {
1731
name = m_config.getCanonicalName(name);
1734
// create client proxy for highest version supported by the client
1735
LOG((CLOG_DEBUG1 "creating proxy for client \"%s\" version %d.%d", name.c_str(), major, minor));
1739
proxy = new CClientProxy1_0(this, name, input, output);
1743
proxy = new CClientProxy1_1(this, name, input, output);
1748
// hangup (with error) if version isn't supported
1749
if (proxy == NULL) {
1750
throw XIncompatibleClient(major, minor);
1756
// ask and wait for the client's info
1757
LOG((CLOG_DEBUG1 "waiting for info for client \"%s\"", name.c_str()));
1762
catch (XIncompatibleClient& e) {
1763
// client is incompatible
1764
LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor()));
1766
CProtocolUtil::writef(output, kMsgEIncompatible,
1767
kProtocolMajorVersion, kProtocolMinorVersion);
1773
catch (XBadClient&) {
1774
// client not behaving
1775
LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str()));
1777
CProtocolUtil::writef(output, kMsgEBad);
1780
// ignore. client probably aborted the connection.
1785
LOG((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what()));
1788
// probably timed out
1789
if (proxy != NULL) {
1800
if (proxy != NULL) {
1812
CServer::acceptHTTPClients(void*)
1814
LOG((CLOG_DEBUG1 "starting to wait for HTTP clients"));
1816
IListenSocket* listen = NULL;
1818
// create socket listener
1819
listen = new CTCPListenSocket;
1821
// bind to the desired port. keep retrying if we can't bind
1822
// the address immediately.
1826
LOG((CLOG_DEBUG1 "binding HTTP listen socket"));
1827
listen->bind(m_config.getHTTPAddress());
1830
catch (XSocketBind& e) {
1831
LOG((CLOG_DEBUG1 "bind HTTP failed: %s", e.what()));
1833
// give up if we've waited too long
1834
if (timer.getTime() >= m_bindTimeout) {
1835
LOG((CLOG_DEBUG1 "waited too long to bind HTTP, giving up"));
1839
// wait a bit before retrying
1844
// accept connections and begin processing them
1845
LOG((CLOG_DEBUG1 "waiting for HTTP connections"));
1847
// limit the number of HTTP requests being handled at once
1849
CLock lock(&m_httpAvailable);
1850
while (m_httpAvailable == 0) {
1851
m_httpAvailable.wait();
1853
assert(m_httpAvailable > 0);
1854
m_httpAvailable = m_httpAvailable - 1;
1857
// accept connection
1858
CThread::testCancel();
1859
IDataSocket* socket = listen->accept();
1860
LOG((CLOG_NOTE "accepted HTTP connection"));
1861
CThread::testCancel();
1863
// handle HTTP request
1864
startThread(new TMethodJob<CServer>(
1865
this, &CServer::processHTTPRequest, socket));
1872
LOG((CLOG_ERR "cannot listen for HTTP clients: %s", e.what()));
1874
exitMainLoopWithError();
1883
CServer::processHTTPRequest(void* vsocket)
1885
IDataSocket* socket = reinterpret_cast<IDataSocket*>(vsocket);
1887
// process the request and force delivery
1888
m_httpServer->processRequest(socket);
1889
socket->getOutputStream()->flush();
1891
// wait a moment to give the client a chance to hangup first
1898
// increment available HTTP handlers
1900
CLock lock(&m_httpAvailable);
1901
m_httpAvailable = m_httpAvailable + 1;
1902
m_httpAvailable.signal();
1908
CLock lock(&m_httpAvailable);
1909
m_httpAvailable = m_httpAvailable + 1;
1910
m_httpAvailable.signal();
1917
CServer::sendOptions(IClient* client) const
1919
// note -- must be locked on entry
1921
COptionsList optionsList;
1923
// look up options for client
1924
const CConfig::CScreenOptions* options =
1925
m_config.getOptions(client->getName());
1926
if (options != NULL && options->size() > 0) {
1927
// convert options to a more convenient form for sending
1928
optionsList.reserve(2 * options->size());
1929
for (CConfig::CScreenOptions::const_iterator index = options->begin();
1930
index != options->end(); ++index) {
1931
optionsList.push_back(index->first);
1932
optionsList.push_back(static_cast<UInt32>(index->second));
1936
// look up global options
1937
options = m_config.getOptions("");
1938
if (options != NULL && options->size() > 0) {
1939
// convert options to a more convenient form for sending
1940
optionsList.reserve(optionsList.size() + 2 * options->size());
1941
for (CConfig::CScreenOptions::const_iterator index = options->begin();
1942
index != options->end(); ++index) {
1943
optionsList.push_back(index->first);
1944
optionsList.push_back(static_cast<UInt32>(index->second));
1949
client->setOptions(optionsList);
1953
CServer::openPrimaryScreen()
1955
assert(m_primaryClient == NULL);
1957
// reset sequence number
1960
// canonicalize the primary screen name
1961
CString primaryName = m_config.getCanonicalName(getPrimaryScreenName());
1962
if (primaryName.empty()) {
1963
throw XUnknownClient(getPrimaryScreenName());
1967
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
1968
CClipboardInfo& clipboard = m_clipboards[id];
1969
clipboard.m_clipboardOwner = primaryName;
1970
clipboard.m_clipboardSeqNum = m_seqNum;
1971
if (clipboard.m_clipboard.open(0)) {
1972
clipboard.m_clipboard.empty();
1973
clipboard.m_clipboard.close();
1975
clipboard.m_clipboardData = clipboard.m_clipboard.marshall();
1979
// create the primary client
1980
m_primaryClient = new CPrimaryClient(m_screenFactory,
1981
this, this, primaryName);
1984
addConnection(m_primaryClient);
1985
m_active = m_primaryClient;
1988
LOG((CLOG_DEBUG1 "opening primary screen"));
1989
m_primaryClient->open();
1991
// tell it about the active sides
1992
m_primaryClient->reconfigure(getActivePrimarySides());
1994
// tell primary client about its options
1995
sendOptions(m_primaryClient);
1998
// if m_active is NULL then we haven't added the connection
1999
// for the primary client so we don't try to remove it.
2000
if (m_active != NULL) {
2001
removeConnection(primaryName);
2004
delete m_primaryClient;
2007
m_primaryClient = NULL;
2013
CServer::closePrimaryScreen()
2015
assert(m_primaryClient != NULL);
2017
// close the primary screen
2019
LOG((CLOG_DEBUG1 "closing primary screen"));
2020
m_primaryClient->close();
2026
// remove connection
2027
removeConnection(m_primaryClient->getName());
2028
m_primaryClient = NULL;
2032
CServer::addConnection(IClient* client)
2034
assert(client != NULL);
2036
LOG((CLOG_DEBUG "adding connection \"%s\"", client->getName().c_str()));
2039
CLock lock(&m_mutex);
2041
// name must be in our configuration
2042
if (!m_config.isScreen(client->getName())) {
2043
throw XUnknownClient(client->getName());
2046
// can only have one screen with a given name at any given time
2047
if (m_clients.count(client->getName()) != 0) {
2048
throw XDuplicateClient(client->getName());
2052
m_clients.insert(std::make_pair(client->getName(), client));
2053
LOG((CLOG_DEBUG "added connection \"%s\"", client->getName().c_str()));
2059
CServer::removeConnection(const CString& name)
2061
LOG((CLOG_DEBUG "removing connection \"%s\"", name.c_str()));
2064
CLock lock(&m_mutex);
2067
CClientList::iterator index = m_clients.find(name);
2068
assert(index != m_clients.end());
2070
// if this is active screen then we have to jump off of it
2071
IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active;
2072
if (active == index->second && active != m_primaryClient) {
2073
// record new position (center of primary screen)
2074
m_primaryClient->getCursorCenter(m_x, m_y);
2076
// stop waiting to switch if we were
2077
if (active == m_switchScreen) {
2081
// don't notify active screen since it probably already
2083
LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y));
2086
m_active = m_primaryClient;
2088
// enter new screen (unless we already have because of the
2090
if (m_activeSaver == NULL) {
2091
m_primaryClient->enter(m_x, m_y, m_seqNum,
2092
m_primaryClient->getToggleMask(), false);
2096
// if this screen had the cursor when the screen saver activated
2097
// then we can't switch back to it when the screen saver
2099
if (m_activeSaver == index->second) {
2100
m_activeSaver = NULL;
2104
delete index->second;
2105
m_clients.erase(index);
2107
// remove any thread for this client
2108
m_clientThreads.erase(name);
2110
updateStatus = (m_clients.size() <= 1);
2120
// CServer::CClipboardInfo
2124
CServer::XServerRethrow::getWhat() const throw()
2126
return format("XServerRethrow", "child thread failed");
2131
// CServer::CClipboardInfo
2134
CServer::CClipboardInfo::CClipboardInfo() :
2138
m_clipboardSeqNum(0)