source: vendor/synergy/current/lib/client/CClient.cpp

Last change on this file was 2749, checked in by bird, 19 years ago

synergy v1.3.1 sources (zip).

File size: 14.6 KB
Line 
1/*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2002 Chris Schoeneman
4 *
5 * This package is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * found in the file COPYING that should have accompanied this file.
8 *
9 * This package is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include "CClient.h"
16#include "CServerProxy.h"
17#include "CScreen.h"
18#include "CClipboard.h"
19#include "CPacketStreamFilter.h"
20#include "CProtocolUtil.h"
21#include "ProtocolTypes.h"
22#include "XSynergy.h"
23#include "IDataSocket.h"
24#include "ISocketFactory.h"
25#include "IStreamFilterFactory.h"
26#include "CLog.h"
27#include "IEventQueue.h"
28#include "TMethodEventJob.h"
29
30//
31// CClient
32//
33
34CEvent::Type CClient::s_connectedEvent = CEvent::kUnknown;
35CEvent::Type CClient::s_connectionFailedEvent = CEvent::kUnknown;
36CEvent::Type CClient::s_disconnectedEvent = CEvent::kUnknown;
37
38CClient::CClient(const CString& name, const CNetworkAddress& address,
39 ISocketFactory* socketFactory,
40 IStreamFilterFactory* streamFilterFactory,
41 CScreen* screen) :
42 m_name(name),
43 m_serverAddress(address),
44 m_socketFactory(socketFactory),
45 m_streamFilterFactory(streamFilterFactory),
46 m_screen(screen),
47 m_stream(NULL),
48 m_timer(NULL),
49 m_server(NULL),
50 m_ready(false),
51 m_active(false),
52 m_suspended(false),
53 m_connectOnResume(false)
54{
55 assert(m_socketFactory != NULL);
56 assert(m_screen != NULL);
57
58 // register suspend/resume event handlers
59 EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(),
60 getEventTarget(),
61 new TMethodEventJob<CClient>(this,
62 &CClient::handleSuspend));
63 EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(),
64 getEventTarget(),
65 new TMethodEventJob<CClient>(this,
66 &CClient::handleResume));
67}
68
69CClient::~CClient()
70{
71 EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(),
72 getEventTarget());
73 EVENTQUEUE->removeHandler(IScreen::getResumeEvent(),
74 getEventTarget());
75
76 cleanupTimer();
77 cleanupScreen();
78 cleanupConnecting();
79 cleanupConnection();
80 delete m_socketFactory;
81 delete m_streamFilterFactory;
82}
83
84void
85CClient::connect()
86{
87 if (m_stream != NULL) {
88 return;
89 }
90 if (m_suspended) {
91 m_connectOnResume = true;
92 return;
93 }
94
95 try {
96 // resolve the server hostname. do this every time we connect
97 // in case we couldn't resolve the address earlier or the address
98 // has changed (which can happen frequently if this is a laptop
99 // being shuttled between various networks). patch by Brent
100 // Priddy.
101 m_serverAddress.resolve();
102
103 // create the socket
104 IDataSocket* socket = m_socketFactory->create();
105
106 // filter socket messages, including a packetizing filter
107 m_stream = socket;
108 if (m_streamFilterFactory != NULL) {
109 m_stream = m_streamFilterFactory->create(m_stream, true);
110 }
111 m_stream = new CPacketStreamFilter(m_stream, true);
112
113 // connect
114 LOG((CLOG_DEBUG1 "connecting to server"));
115 setupConnecting();
116 setupTimer();
117 socket->connect(m_serverAddress);
118 }
119 catch (XBase& e) {
120 cleanupTimer();
121 cleanupConnecting();
122 delete m_stream;
123 m_stream = NULL;
124 LOG((CLOG_DEBUG1 "connection failed"));
125 sendConnectionFailedEvent(e.what());
126 return;
127 }
128}
129
130void
131CClient::disconnect(const char* msg)
132{
133 m_connectOnResume = false;
134 cleanupTimer();
135 cleanupScreen();
136 cleanupConnecting();
137 cleanupConnection();
138 if (msg != NULL) {
139 sendConnectionFailedEvent(msg);
140 }
141 else {
142 sendEvent(getDisconnectedEvent(), NULL);
143 }
144}
145
146void
147CClient::handshakeComplete()
148{
149 m_ready = true;
150 m_screen->enable();
151 sendEvent(getConnectedEvent(), NULL);
152}
153
154bool
155CClient::isConnected() const
156{
157 return (m_server != NULL);
158}
159
160bool
161CClient::isConnecting() const
162{
163 return (m_timer != NULL);
164}
165
166CNetworkAddress
167CClient::getServerAddress() const
168{
169 return m_serverAddress;
170}
171
172CEvent::Type
173CClient::getConnectedEvent()
174{
175 return CEvent::registerTypeOnce(s_connectedEvent,
176 "CClient::connected");
177}
178
179CEvent::Type
180CClient::getConnectionFailedEvent()
181{
182 return CEvent::registerTypeOnce(s_connectionFailedEvent,
183 "CClient::failed");
184}
185
186CEvent::Type
187CClient::getDisconnectedEvent()
188{
189 return CEvent::registerTypeOnce(s_disconnectedEvent,
190 "CClient::disconnected");
191}
192
193void*
194CClient::getEventTarget() const
195{
196 return m_screen->getEventTarget();
197}
198
199bool
200CClient::getClipboard(ClipboardID id, IClipboard* clipboard) const
201{
202 return m_screen->getClipboard(id, clipboard);
203}
204
205void
206CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
207{
208 m_screen->getShape(x, y, w, h);
209}
210
211void
212CClient::getCursorPos(SInt32& x, SInt32& y) const
213{
214 m_screen->getCursorPos(x, y);
215}
216
217void
218CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool)
219{
220 m_active = true;
221 m_screen->mouseMove(xAbs, yAbs);
222 m_screen->enter(mask);
223}
224
225bool
226CClient::leave()
227{
228 m_screen->leave();
229
230 m_active = false;
231
232 // send clipboards that we own and that have changed
233 for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
234 if (m_ownClipboard[id]) {
235 sendClipboard(id);
236 }
237 }
238
239 return true;
240}
241
242void
243CClient::setClipboard(ClipboardID id, const IClipboard* clipboard)
244{
245 m_screen->setClipboard(id, clipboard);
246 m_ownClipboard[id] = false;
247 m_sentClipboard[id] = false;
248}
249
250void
251CClient::grabClipboard(ClipboardID id)
252{
253 m_screen->grabClipboard(id);
254 m_ownClipboard[id] = false;
255 m_sentClipboard[id] = false;
256}
257
258void
259CClient::setClipboardDirty(ClipboardID, bool)
260{
261 assert(0 && "shouldn't be called");
262}
263
264void
265CClient::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
266{
267 m_screen->keyDown(id, mask, button);
268}
269
270void
271CClient::keyRepeat(KeyID id, KeyModifierMask mask,
272 SInt32 count, KeyButton button)
273{
274 m_screen->keyRepeat(id, mask, count, button);
275}
276
277void
278CClient::keyUp(KeyID id, KeyModifierMask mask, KeyButton button)
279{
280 m_screen->keyUp(id, mask, button);
281}
282
283void
284CClient::mouseDown(ButtonID id)
285{
286 m_screen->mouseDown(id);
287}
288
289void
290CClient::mouseUp(ButtonID id)
291{
292 m_screen->mouseUp(id);
293}
294
295void
296CClient::mouseMove(SInt32 x, SInt32 y)
297{
298 m_screen->mouseMove(x, y);
299}
300
301void
302CClient::mouseRelativeMove(SInt32 dx, SInt32 dy)
303{
304 m_screen->mouseRelativeMove(dx, dy);
305}
306
307void
308CClient::mouseWheel(SInt32 xDelta, SInt32 yDelta)
309{
310 m_screen->mouseWheel(xDelta, yDelta);
311}
312
313void
314CClient::screensaver(bool activate)
315{
316 m_screen->screensaver(activate);
317}
318
319void
320CClient::resetOptions()
321{
322 m_screen->resetOptions();
323}
324
325void
326CClient::setOptions(const COptionsList& options)
327{
328 m_screen->setOptions(options);
329}
330
331CString
332CClient::getName() const
333{
334 return m_name;
335}
336
337void
338CClient::sendClipboard(ClipboardID id)
339{
340 // note -- m_mutex must be locked on entry
341 assert(m_screen != NULL);
342 assert(m_server != NULL);
343
344 // get clipboard data. set the clipboard time to the last
345 // clipboard time before getting the data from the screen
346 // as the screen may detect an unchanged clipboard and
347 // avoid copying the data.
348 CClipboard clipboard;
349 if (clipboard.open(m_timeClipboard[id])) {
350 clipboard.close();
351 }
352 m_screen->getClipboard(id, &clipboard);
353
354 // check time
355 if (m_timeClipboard[id] == 0 ||
356 clipboard.getTime() != m_timeClipboard[id]) {
357 // save new time
358 m_timeClipboard[id] = clipboard.getTime();
359
360 // marshall the data
361 CString data = clipboard.marshall();
362
363 // save and send data if different or not yet sent
364 if (!m_sentClipboard[id] || data != m_dataClipboard[id]) {
365 m_sentClipboard[id] = true;
366 m_dataClipboard[id] = data;
367 m_server->onClipboardChanged(id, &clipboard);
368 }
369 }
370}
371
372void
373CClient::sendEvent(CEvent::Type type, void* data)
374{
375 EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data));
376}
377
378void
379CClient::sendConnectionFailedEvent(const char* msg)
380{
381 CFailInfo* info = (CFailInfo*)malloc(sizeof(CFailInfo) + strlen(msg));
382 info->m_retry = true;
383 strcpy(info->m_what, msg);
384 sendEvent(getConnectionFailedEvent(), info);
385}
386
387void
388CClient::setupConnecting()
389{
390 assert(m_stream != NULL);
391
392 EVENTQUEUE->adoptHandler(IDataSocket::getConnectedEvent(),
393 m_stream->getEventTarget(),
394 new TMethodEventJob<CClient>(this,
395 &CClient::handleConnected));
396 EVENTQUEUE->adoptHandler(IDataSocket::getConnectionFailedEvent(),
397 m_stream->getEventTarget(),
398 new TMethodEventJob<CClient>(this,
399 &CClient::handleConnectionFailed));
400}
401
402void
403CClient::setupConnection()
404{
405 assert(m_stream != NULL);
406
407 EVENTQUEUE->adoptHandler(ISocket::getDisconnectedEvent(),
408 m_stream->getEventTarget(),
409 new TMethodEventJob<CClient>(this,
410 &CClient::handleDisconnected));
411 EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(),
412 m_stream->getEventTarget(),
413 new TMethodEventJob<CClient>(this,
414 &CClient::handleHello));
415 EVENTQUEUE->adoptHandler(IStream::getOutputErrorEvent(),
416 m_stream->getEventTarget(),
417 new TMethodEventJob<CClient>(this,
418 &CClient::handleOutputError));
419 EVENTQUEUE->adoptHandler(IStream::getInputShutdownEvent(),
420 m_stream->getEventTarget(),
421 new TMethodEventJob<CClient>(this,
422 &CClient::handleDisconnected));
423 EVENTQUEUE->adoptHandler(IStream::getOutputShutdownEvent(),
424 m_stream->getEventTarget(),
425 new TMethodEventJob<CClient>(this,
426 &CClient::handleDisconnected));
427}
428
429void
430CClient::setupScreen()
431{
432 assert(m_server == NULL);
433
434 m_ready = false;
435 m_server = new CServerProxy(this, m_stream);
436 EVENTQUEUE->adoptHandler(IScreen::getShapeChangedEvent(),
437 getEventTarget(),
438 new TMethodEventJob<CClient>(this,
439 &CClient::handleShapeChanged));
440 EVENTQUEUE->adoptHandler(IScreen::getClipboardGrabbedEvent(),
441 getEventTarget(),
442 new TMethodEventJob<CClient>(this,
443 &CClient::handleClipboardGrabbed));
444}
445
446void
447CClient::setupTimer()
448{
449 assert(m_timer == NULL);
450
451 m_timer = EVENTQUEUE->newOneShotTimer(15.0, NULL);
452 EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer,
453 new TMethodEventJob<CClient>(this,
454 &CClient::handleConnectTimeout));
455}
456
457void
458CClient::cleanupConnecting()
459{
460 if (m_stream != NULL) {
461 EVENTQUEUE->removeHandler(IDataSocket::getConnectedEvent(),
462 m_stream->getEventTarget());
463 EVENTQUEUE->removeHandler(IDataSocket::getConnectionFailedEvent(),
464 m_stream->getEventTarget());
465 }
466}
467
468void
469CClient::cleanupConnection()
470{
471 if (m_stream != NULL) {
472 EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(),
473 m_stream->getEventTarget());
474 EVENTQUEUE->removeHandler(IStream::getOutputErrorEvent(),
475 m_stream->getEventTarget());
476 EVENTQUEUE->removeHandler(IStream::getInputShutdownEvent(),
477 m_stream->getEventTarget());
478 EVENTQUEUE->removeHandler(IStream::getOutputShutdownEvent(),
479 m_stream->getEventTarget());
480 EVENTQUEUE->removeHandler(ISocket::getDisconnectedEvent(),
481 m_stream->getEventTarget());
482 delete m_stream;
483 m_stream = NULL;
484 }
485}
486
487void
488CClient::cleanupScreen()
489{
490 if (m_server != NULL) {
491 if (m_ready) {
492 m_screen->disable();
493 m_ready = false;
494 }
495 EVENTQUEUE->removeHandler(IScreen::getShapeChangedEvent(),
496 getEventTarget());
497 EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(),
498 getEventTarget());
499 delete m_server;
500 m_server = NULL;
501 }
502}
503
504void
505CClient::cleanupTimer()
506{
507 if (m_timer != NULL) {
508 EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer);
509 EVENTQUEUE->deleteTimer(m_timer);
510 m_timer = NULL;
511 }
512}
513
514void
515CClient::handleConnected(const CEvent&, void*)
516{
517 LOG((CLOG_DEBUG1 "connected; wait for hello"));
518 cleanupConnecting();
519 setupConnection();
520
521 // reset clipboard state
522 for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
523 m_ownClipboard[id] = false;
524 m_sentClipboard[id] = false;
525 m_timeClipboard[id] = 0;
526 }
527}
528
529void
530CClient::handleConnectionFailed(const CEvent& event, void*)
531{
532 IDataSocket::CConnectionFailedInfo* info =
533 reinterpret_cast<IDataSocket::CConnectionFailedInfo*>(event.getData());
534
535 cleanupTimer();
536 cleanupConnecting();
537 delete m_stream;
538 m_stream = NULL;
539 LOG((CLOG_DEBUG1 "connection failed"));
540 sendConnectionFailedEvent(info->m_what);
541}
542
543void
544CClient::handleConnectTimeout(const CEvent&, void*)
545{
546 cleanupTimer();
547 cleanupConnecting();
548 cleanupConnection();
549 delete m_stream;
550 m_stream = NULL;
551 LOG((CLOG_DEBUG1 "connection timed out"));
552 sendConnectionFailedEvent("Timed out");
553}
554
555void
556CClient::handleOutputError(const CEvent&, void*)
557{
558 cleanupTimer();
559 cleanupScreen();
560 cleanupConnection();
561 LOG((CLOG_WARN "error sending to server"));
562 sendEvent(getDisconnectedEvent(), NULL);
563}
564
565void
566CClient::handleDisconnected(const CEvent&, void*)
567{
568 cleanupTimer();
569 cleanupScreen();
570 cleanupConnection();
571 LOG((CLOG_DEBUG1 "disconnected"));
572 sendEvent(getDisconnectedEvent(), NULL);
573}
574
575void
576CClient::handleShapeChanged(const CEvent&, void*)
577{
578 LOG((CLOG_DEBUG "resolution changed"));
579 m_server->onInfoChanged();
580}
581
582void
583CClient::handleClipboardGrabbed(const CEvent& event, void*)
584{
585 const IScreen::CClipboardInfo* info =
586 reinterpret_cast<const IScreen::CClipboardInfo*>(event.getData());
587
588 // grab ownership
589 m_server->onGrabClipboard(info->m_id);
590
591 // we now own the clipboard and it has not been sent to the server
592 m_ownClipboard[info->m_id] = true;
593 m_sentClipboard[info->m_id] = false;
594 m_timeClipboard[info->m_id] = 0;
595
596 // if we're not the active screen then send the clipboard now,
597 // otherwise we'll wait until we leave.
598 if (!m_active) {
599 sendClipboard(info->m_id);
600 }
601}
602
603void
604CClient::handleHello(const CEvent&, void*)
605{
606 SInt16 major, minor;
607 if (!CProtocolUtil::readf(m_stream, kMsgHello, &major, &minor)) {
608 sendConnectionFailedEvent("Protocol error from server");
609 cleanupTimer();
610 cleanupConnection();
611 return;
612 }
613
614 // check versions
615 LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor));
616 if (major < kProtocolMajorVersion ||
617 (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) {
618 sendConnectionFailedEvent(XIncompatibleClient(major, minor).what());
619 cleanupTimer();
620 cleanupConnection();
621 return;
622 }
623
624 // say hello back
625 LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion));
626 CProtocolUtil::writef(m_stream, kMsgHelloBack,
627 kProtocolMajorVersion,
628 kProtocolMinorVersion, &m_name);
629
630 // now connected but waiting to complete handshake
631 setupScreen();
632 cleanupTimer();
633
634 // make sure we process any remaining messages later. we won't
635 // receive another event for already pending messages so we fake
636 // one.
637 if (m_stream->isReady()) {
638 EVENTQUEUE->addEvent(CEvent(IStream::getInputReadyEvent(),
639 m_stream->getEventTarget()));
640 }
641}
642
643void
644CClient::handleSuspend(const CEvent&, void*)
645{
646 LOG((CLOG_INFO "suspend"));
647 m_suspended = true;
648 bool wasConnected = isConnected();
649 disconnect(NULL);
650 m_connectOnResume = wasConnected;
651}
652
653void
654CClient::handleResume(const CEvent&, void*)
655{
656 LOG((CLOG_INFO "resume"));
657 m_suspended = false;
658 if (m_connectOnResume) {
659 m_connectOnResume = false;
660 connect();
661 }
662}
Note: See TracBrowser for help on using the repository browser.