00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "CClientApp.h"
00020 #include "CLog.h"
00021 #include "CArch.h"
00022 #include "XSocket.h"
00023 #include "Version.h"
00024 #include "ProtocolTypes.h"
00025 #include "CString.h"
00026 #include "CScreen.h"
00027 #include "CEvent.h"
00028 #include "CClient.h"
00029 #include "CNetworkAddress.h"
00030 #include "IArchTaskBarReceiver.h"
00031 #include "IEventQueue.h"
00032 #include "TMethodEventJob.h"
00033 #include "CTCPSocketFactory.h"
00034 #include "XScreen.h"
00035 #include "LogOutputters.h"
00036 #include "CSocketMultiplexer.h"
00037 #include "CEventQueue.h"
00038 #include "CThread.h"
00039 #include "TMethodJob.h"
00040
00041 #if SYSAPI_WIN32
00042 #include "CArchMiscWindows.h"
00043 #endif
00044
00045 #if SYSAPI_WIN32 && GAME_DEVICE_SUPPORT
00046 #include <Windows.h>
00047 #include "XInputHook.h"
00048 #endif
00049
00050 #if WINAPI_MSWINDOWS
00051 #include "CMSWindowsScreen.h"
00052 #elif WINAPI_XWINDOWS
00053 #include "CXWindowsScreen.h"
00054 #elif WINAPI_CARBON
00055 #include "COSXScreen.h"
00056 #endif
00057
00058 #include <iostream>
00059 #include <stdio.h>
00060
00061 #define RETRY_TIME 1.0
00062
00063 CClientApp::CClientApp(CreateTaskBarReceiverFunc createTaskBarReceiver) :
00064 CApp(createTaskBarReceiver, new CArgs()),
00065 s_client(NULL),
00066 s_clientScreen(NULL)
00067 {
00068 }
00069
00070 CClientApp::~CClientApp()
00071 {
00072 }
00073
00074 CClientApp::CArgs::CArgs() :
00075 m_yscroll(0),
00076 m_serverAddress(NULL)
00077 {
00078 }
00079
00080 CClientApp::CArgs::~CArgs()
00081 {
00082 }
00083
00084 bool
00085 CClientApp::parseArg(const int& argc, const char* const* argv, int& i)
00086 {
00087 if (CApp::parseArg(argc, argv, i)) {
00088
00089 return true;
00090 }
00091
00092 else if (isArg(i, argc, argv, NULL, "--camp")) {
00093
00094 }
00095
00096 else if (isArg(i, argc, argv, NULL, "--no-camp")) {
00097
00098 }
00099
00100 else if (isArg(i, argc, argv, NULL, "--yscroll", 1)) {
00101
00102 args().m_yscroll = atoi(argv[++i]);
00103 }
00104
00105 else {
00106
00107 return false;
00108 }
00109
00110
00111 return true;
00112 }
00113
00114 void
00115 CClientApp::parseArgs(int argc, const char* const* argv)
00116 {
00117
00118 int i;
00119 CApp::parseArgs(argc, argv, i);
00120
00121
00122 if (i == argc) {
00123 LOG((CLOG_PRINT "%s: a server address or name is required" BYE,
00124 args().m_pname, args().m_pname));
00125 m_bye(kExitArgs);
00126 }
00127 if (i + 1 != argc) {
00128 LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE,
00129 args().m_pname, argv[i], args().m_pname));
00130 m_bye(kExitArgs);
00131 }
00132
00133
00134 try {
00135 *args().m_serverAddress = CNetworkAddress(argv[i], kDefaultPort);
00136 args().m_serverAddress->resolve();
00137 }
00138 catch (XSocketAddress& e) {
00139
00140
00141
00142
00143 if (!args().m_restartable || e.getError() == XSocketAddress::kBadPort) {
00144 LOG((CLOG_PRINT "%s: %s" BYE,
00145 args().m_pname, e.what(), args().m_pname));
00146 m_bye(kExitFailed);
00147 }
00148 }
00149
00150
00151 if (!CLOG->setFilter(args().m_logFilter)) {
00152 LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
00153 args().m_pname, args().m_logFilter, args().m_pname));
00154 m_bye(kExitArgs);
00155 }
00156
00157
00158 LOG((CLOG_INFO "%s Client on %s %s", kAppVersion, ARCH->getOSName().c_str(), ARCH->getPlatformName().c_str()));
00159
00160 loggingFilterWarning();
00161 }
00162
00163 void
00164 CClientApp::help()
00165 {
00166 #if WINAPI_XWINDOWS
00167 # define WINAPI_ARG \
00168 " [--display <display>] [--no-xinitthreads]"
00169 # define WINAPI_INFO \
00170 " --display <display> connect to the X server at <display>\n" \
00171 " --no-xinitthreads do not call XInitThreads()\n"
00172 #else
00173 # define WINAPI_ARG
00174 # define WINAPI_INFO
00175 #endif
00176
00177 char buffer[2000];
00178 sprintf(
00179 buffer,
00180 "Usage: %s"
00181 " [--yscroll <delta>]"
00182 WINAPI_ARG
00183 HELP_SYS_ARGS
00184 HELP_COMMON_ARGS
00185 " <server-address>"
00186 "\n\n"
00187 "Connect to a synergy mouse/keyboard sharing server.\n"
00188 "\n"
00189 HELP_COMMON_INFO_1
00190 WINAPI_INFO
00191 HELP_SYS_INFO
00192 " --yscroll <delta> defines the vertical scrolling delta, which is\n"
00193 " 120 by default.\n"
00194 HELP_COMMON_INFO_2
00195 "\n"
00196 "* marks defaults.\n"
00197 "\n"
00198 "The server address is of the form: [<hostname>][:<port>]. The hostname\n"
00199 "must be the address or hostname of the server. The port overrides the\n"
00200 "default port, %d.\n",
00201 args().m_pname, kDefaultPort
00202 );
00203
00204 LOG((CLOG_PRINT "%s", buffer));
00205 }
00206
00207 const char*
00208 CClientApp::daemonName() const
00209 {
00210 #if SYSAPI_WIN32
00211 return "Synergy Client";
00212 #elif SYSAPI_UNIX
00213 return "synergyc";
00214 #endif
00215 }
00216
00217 const char*
00218 CClientApp::daemonInfo() const
00219 {
00220 #if SYSAPI_WIN32
00221 return "Allows another computer to share it's keyboard and mouse with this computer.";
00222 #elif SYSAPI_UNIX
00223 return "";
00224 #endif
00225 }
00226
00227 CScreen*
00228 CClientApp::createScreen()
00229 {
00230 #if WINAPI_MSWINDOWS
00231 return new CScreen(new CMSWindowsScreen(
00232 false, args().m_noHooks, args().m_gameDevice, args().m_stopOnDeskSwitch));
00233 #elif WINAPI_XWINDOWS
00234 return new CScreen(new CXWindowsScreen(
00235 args().m_display, false, args().m_disableXInitThreads,
00236 args().m_yscroll, *EVENTQUEUE));
00237 #elif WINAPI_CARBON
00238 return new CScreen(new COSXScreen(false));
00239 #endif
00240 }
00241
00242 void
00243 CClientApp::updateStatus()
00244 {
00245 updateStatus("");
00246 }
00247
00248
00249 void
00250 CClientApp::updateStatus(const CString& msg)
00251 {
00252 if (m_taskBarReceiver)
00253 {
00254 m_taskBarReceiver->updateStatus(s_client, msg);
00255 }
00256 }
00257
00258
00259 void
00260 CClientApp::resetRestartTimeout()
00261 {
00262
00263
00264 }
00265
00266
00267 double
00268 CClientApp::nextRestartTimeout()
00269 {
00270
00271 return RETRY_TIME;
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287 }
00288
00289
00290 void
00291 CClientApp::handleScreenError(const CEvent&, void*)
00292 {
00293 LOG((CLOG_CRIT "error on screen"));
00294 EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00295 }
00296
00297
00298 CScreen*
00299 CClientApp::openClientScreen()
00300 {
00301 CScreen* screen = createScreen();
00302 EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
00303 screen->getEventTarget(),
00304 new TMethodEventJob<CClientApp>(
00305 this, &CClientApp::handleScreenError));
00306 return screen;
00307 }
00308
00309
00310 void
00311 CClientApp::closeClientScreen(CScreen* screen)
00312 {
00313 if (screen != NULL) {
00314 EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
00315 screen->getEventTarget());
00316 delete screen;
00317 }
00318 }
00319
00320
00321 void
00322 CClientApp::handleClientRestart(const CEvent&, void* vtimer)
00323 {
00324
00325 CEventQueueTimer* timer = reinterpret_cast<CEventQueueTimer*>(vtimer);
00326 EVENTQUEUE->deleteTimer(timer);
00327 EVENTQUEUE->removeHandler(CEvent::kTimer, timer);
00328
00329
00330 startClient();
00331 }
00332
00333
00334 void
00335 CClientApp::scheduleClientRestart(double retryTime)
00336 {
00337
00338 LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
00339 CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
00340 EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
00341 new TMethodEventJob<CClientApp>(this, &CClientApp::handleClientRestart, timer));
00342 }
00343
00344
00345 void
00346 CClientApp::handleClientConnected(const CEvent&, void*)
00347 {
00348 LOG((CLOG_NOTE "connected to server"));
00349 resetRestartTimeout();
00350 updateStatus();
00351 }
00352
00353
00354 void
00355 CClientApp::handleClientFailed(const CEvent& e, void*)
00356 {
00357 CClient::CFailInfo* info =
00358 reinterpret_cast<CClient::CFailInfo*>(e.getData());
00359
00360 updateStatus(CString("Failed to connect to server: ") + info->m_what);
00361 if (!args().m_restartable || !info->m_retry) {
00362 LOG((CLOG_ERR "failed to connect to server: %s", info->m_what.c_str()));
00363 EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00364 }
00365 else {
00366 LOG((CLOG_WARN "failed to connect to server: %s", info->m_what.c_str()));
00367 if (!m_suspended) {
00368 scheduleClientRestart(nextRestartTimeout());
00369 }
00370 }
00371 delete info;
00372 }
00373
00374
00375 void
00376 CClientApp::handleClientDisconnected(const CEvent&, void*)
00377 {
00378 LOG((CLOG_NOTE "disconnected from server"));
00379 if (!args().m_restartable) {
00380 EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00381 }
00382 else if (!m_suspended) {
00383 s_client->connect();
00384 }
00385 updateStatus();
00386 }
00387
00388
00389 CClient*
00390 CClientApp::openClient(const CString& name, const CNetworkAddress& address, CScreen* screen, const CCryptoOptions& crypto)
00391 {
00392 CClient* client = new CClient(
00393 EVENTQUEUE, name, address, new CTCPSocketFactory, NULL, screen, crypto);
00394
00395 try {
00396 EVENTQUEUE->adoptHandler(
00397 CClient::getConnectedEvent(),
00398 client->getEventTarget(),
00399 new TMethodEventJob<CClientApp>(this, &CClientApp::handleClientConnected));
00400
00401 EVENTQUEUE->adoptHandler(
00402 CClient::getConnectionFailedEvent(),
00403 client->getEventTarget(),
00404 new TMethodEventJob<CClientApp>(this, &CClientApp::handleClientFailed));
00405
00406 EVENTQUEUE->adoptHandler(
00407 CClient::getDisconnectedEvent(),
00408 client->getEventTarget(),
00409 new TMethodEventJob<CClientApp>(this, &CClientApp::handleClientDisconnected));
00410
00411 } catch (std::bad_alloc &ba) {
00412 delete client;
00413 throw ba;
00414 }
00415
00416 return client;
00417 }
00418
00419
00420 void
00421 CClientApp::closeClient(CClient* client)
00422 {
00423 if (client == NULL) {
00424 return;
00425 }
00426
00427 EVENTQUEUE->removeHandler(CClient::getConnectedEvent(), client);
00428 EVENTQUEUE->removeHandler(CClient::getConnectionFailedEvent(), client);
00429 EVENTQUEUE->removeHandler(CClient::getDisconnectedEvent(), client);
00430 delete client;
00431 }
00432
00433 int
00434 CClientApp::foregroundStartup(int argc, char** argv)
00435 {
00436 initApp(argc, argv);
00437
00438
00439 return mainLoop();
00440 }
00441
00442 bool
00443 CClientApp::startClient()
00444 {
00445 double retryTime;
00446 CScreen* clientScreen = NULL;
00447 try {
00448 if (s_clientScreen == NULL) {
00449 clientScreen = openClientScreen();
00450 s_client = openClient(args().m_name,
00451 *args().m_serverAddress, clientScreen, args().m_crypto);
00452 s_clientScreen = clientScreen;
00453 LOG((CLOG_NOTE "started client"));
00454 }
00455
00456 #if SYSAPI_WIN32 && GAME_DEVICE_SUPPORT
00457 if (args().m_gameDevice.m_mode == CGameDeviceInfo::kGameModeXInput)
00458 {
00459
00460
00461
00462
00463 LOG((CLOG_DEBUG "installing xinput hook"));
00464 InstallXInputHook();
00465 }
00466 #endif
00467
00468 s_client->connect();
00469 updateStatus();
00470 return true;
00471 }
00472 catch (XScreenUnavailable& e) {
00473 LOG((CLOG_WARN "secondary screen unavailable: %s", e.what()));
00474 closeClientScreen(clientScreen);
00475 updateStatus(CString("secondary screen unavailable: ") + e.what());
00476 retryTime = e.getRetryTime();
00477 }
00478 catch (XScreenOpenFailure& e) {
00479 LOG((CLOG_CRIT "failed to start client: %s", e.what()));
00480 closeClientScreen(clientScreen);
00481 return false;
00482 }
00483 catch (XBase& e) {
00484 LOG((CLOG_CRIT "failed to start client: %s", e.what()));
00485 closeClientScreen(clientScreen);
00486 return false;
00487 }
00488
00489 if (args().m_restartable) {
00490 scheduleClientRestart(retryTime);
00491 return true;
00492 }
00493 else {
00494
00495 return false;
00496 }
00497 }
00498
00499
00500 void
00501 CClientApp::stopClient()
00502 {
00503 #if SYSAPI_WIN32 && GAME_DEVICE_SUPPORT
00504 if (args().m_gameDevice.m_mode == CGameDeviceInfo::kGameModeXInput)
00505 {
00506 LOG((CLOG_DEBUG "removing xinput hook"));
00507 RemoveXInputHook();
00508 }
00509 #endif
00510
00511 closeClient(s_client);
00512 closeClientScreen(s_clientScreen);
00513 s_client = NULL;
00514 s_clientScreen = NULL;
00515 }
00516
00517
00518 int
00519 CClientApp::mainLoop()
00520 {
00521
00522
00523 CSocketMultiplexer multiplexer;
00524
00525
00526 appUtil().startNode();
00527
00528
00529
00530 if (argsBase().m_enableIpc) {
00531 initIpcClient();
00532 }
00533
00534
00535 ARCH->plugin().init(s_clientScreen->getEventTarget());
00536
00537
00538
00539
00540 DAEMON_RUNNING(true);
00541 EVENTQUEUE->loop();
00542 DAEMON_RUNNING(false);
00543
00544
00545 LOG((CLOG_DEBUG1 "stopping client"));
00546 stopClient();
00547 updateStatus();
00548 LOG((CLOG_NOTE "stopped client"));
00549
00550 if (argsBase().m_enableIpc) {
00551 cleanupIpcClient();
00552 }
00553
00554 return kExitSuccess;
00555 }
00556
00557 static
00558 int
00559 daemonMainLoopStatic(int argc, const char** argv)
00560 {
00561 return CClientApp::instance().daemonMainLoop(argc, argv);
00562 }
00563
00564 int
00565 CClientApp::standardStartup(int argc, char** argv)
00566 {
00567 initApp(argc, argv);
00568
00569
00570 if (args().m_daemon) {
00571 return ARCH->daemonize(daemonName(), &daemonMainLoopStatic);
00572 }
00573 else {
00574 return mainLoop();
00575 }
00576 }
00577
00578 int
00579 CClientApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
00580 {
00581
00582 args().m_serverAddress = new CNetworkAddress;
00583 args().m_pname = ARCH->getBasename(argv[0]);
00584
00585
00586 if (outputter != NULL) {
00587 CLOG->insert(outputter);
00588 }
00589
00590 int result;
00591 try
00592 {
00593
00594 result = startup(argc, argv);
00595 }
00596 catch (...)
00597 {
00598 if (m_taskBarReceiver)
00599 {
00600
00601 delete m_taskBarReceiver;
00602 }
00603
00604 delete args().m_serverAddress;
00605
00606 throw;
00607 }
00608
00609 return result;
00610 }
00611
00612 void
00613 CClientApp::startNode()
00614 {
00615
00616
00617 LOG((CLOG_DEBUG1 "starting client"));
00618 if (!startClient()) {
00619 m_bye(kExitFailed);
00620 }
00621 }