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