00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "CConfig.h"
00016 #include "KeyTypes.h"
00017 #include "OptionTypes.h"
00018 #include "ProtocolTypes.h"
00019 #include "CLog.h"
00020 #include "CStringUtil.h"
00021 #include "CArch.h"
00022 #include "CArchMiscWindows.h"
00023 #include "XArch.h"
00024 #include "Version.h"
00025 #include "stdvector.h"
00026 #include "resource.h"
00027
00028
00029 #include "LaunchUtil.h"
00030 #include "CAddScreen.h"
00031 #include "CAdvancedOptions.h"
00032 #include "CAutoStart.h"
00033 #include "CGlobalOptions.h"
00034 #include "CHotkeyOptions.h"
00035 #include "CInfo.h"
00036 #include "CScreensLinks.h"
00037
00038 typedef std::vector<CString> CStringList;
00039
00040 class CChildWaitInfo {
00041 public:
00042 HWND m_dialog;
00043 HANDLE m_child;
00044 DWORD m_childID;
00045 HANDLE m_ready;
00046 HANDLE m_stop;
00047 };
00048
00049 static const char* s_debugName[][2] = {
00050 { TEXT("Error"), "ERROR" },
00051 { TEXT("Warning"), "WARNING" },
00052 { TEXT("Note"), "NOTE" },
00053 { TEXT("Info"), "INFO" },
00054 { TEXT("Debug"), "DEBUG" },
00055 { TEXT("Debug1"), "DEBUG1" },
00056 { TEXT("Debug2"), "DEBUG2" }
00057 };
00058 static const int s_defaultDebug = 1;
00059 static const int s_minTestDebug = 3;
00060
00061 HINSTANCE s_instance = NULL;
00062
00063 static CGlobalOptions* s_globalOptions = NULL;
00064 static CAdvancedOptions* s_advancedOptions = NULL;
00065 static CHotkeyOptions* s_hotkeyOptions = NULL;
00066 static CScreensLinks* s_screensLinks = NULL;
00067 static CInfo* s_info = NULL;
00068
00069 static bool s_userConfig = true;
00070 static time_t s_configTime = 0;
00071 static CConfig s_lastConfig;
00072
00073 static const TCHAR* s_mainClass = TEXT("GoSynergy");
00074 static const TCHAR* s_layoutClass = TEXT("SynergyLayout");
00075
00076 enum SaveMode {
00077 SAVE_QUITING,
00078 SAVE_NORMAL,
00079 SAVE_QUIET
00080 };
00081
00082
00083
00084
00085
00086 #define ARG CArgs::s_instance
00087
00088 class CArgs {
00089 public:
00090 CArgs() { s_instance = this; }
00091 ~CArgs() { s_instance = NULL; }
00092
00093 public:
00094 static CArgs* s_instance;
00095 CConfig m_config;
00096 CStringList m_screens;
00097 };
00098
00099 CArgs* CArgs::s_instance = NULL;
00100
00101
00102 static
00103 BOOL CALLBACK
00104 addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
00105
00106 static
00107 bool
00108 isClientChecked(HWND hwnd)
00109 {
00110 HWND child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO);
00111 return isItemChecked(child);
00112 }
00113
00114 static
00115 void
00116 enableMainWindowControls(HWND hwnd)
00117 {
00118 bool client = isClientChecked(hwnd);
00119 enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_LABEL, client);
00120 enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT, client);
00121 enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LABEL, !client);
00122 enableItem(hwnd, IDC_MAIN_SCREENS, !client);
00123 enableItem(hwnd, IDC_MAIN_OPTIONS, !client);
00124 enableItem(hwnd, IDC_MAIN_HOTKEYS, !client);
00125 }
00126
00127 static
00128 bool
00129 execApp(const char* app, const CString& cmdLine, PROCESS_INFORMATION* procInfo)
00130 {
00131
00132 STARTUPINFO startup;
00133 startup.cb = sizeof(startup);
00134 startup.lpReserved = NULL;
00135 startup.lpDesktop = NULL;
00136 startup.lpTitle = NULL;
00137 startup.dwX = (DWORD)CW_USEDEFAULT;
00138 startup.dwY = (DWORD)CW_USEDEFAULT;
00139 startup.dwXSize = (DWORD)CW_USEDEFAULT;
00140 startup.dwYSize = (DWORD)CW_USEDEFAULT;
00141 startup.dwXCountChars = 0;
00142 startup.dwYCountChars = 0;
00143 startup.dwFillAttribute = 0;
00144 startup.dwFlags = STARTF_FORCEONFEEDBACK;
00145 startup.wShowWindow = SW_SHOWDEFAULT;
00146 startup.cbReserved2 = 0;
00147 startup.lpReserved2 = NULL;
00148 startup.hStdInput = NULL;
00149 startup.hStdOutput = NULL;
00150 startup.hStdError = NULL;
00151
00152
00153 CString appPath = getAppPath(app);
00154
00155
00156 CString commandLine = "\"";
00157 commandLine += appPath;
00158 commandLine += "\" ";
00159 commandLine += cmdLine;
00160
00161
00162 if (CreateProcess(NULL, (char*)commandLine.c_str(),
00163 NULL,
00164 NULL,
00165 FALSE,
00166 CREATE_DEFAULT_ERROR_MODE |
00167 CREATE_NEW_PROCESS_GROUP |
00168 NORMAL_PRIORITY_CLASS,
00169 NULL,
00170 NULL,
00171 &startup,
00172 procInfo) == 0) {
00173 return false;
00174 }
00175 else {
00176 return true;
00177 }
00178 }
00179
00180 static
00181 CString
00182 getCommandLine(HWND hwnd, bool testing, bool silent)
00183 {
00184 CString cmdLine;
00185
00186
00187 if (testing) {
00188 cmdLine += " -z --no-restart --no-daemon";
00189 }
00190
00191
00192 else if (!CArchMiscWindows::isWindows95Family()) {
00193 cmdLine += " --no-daemon";
00194 }
00195
00196
00197 CString server;
00198 bool isClient = isClientChecked(hwnd);
00199 if (isClient) {
00200
00201 HWND child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
00202 server = getWindowText(child);
00203 if (!ARG->m_config.isValidScreenName(server)) {
00204 if (!silent) {
00205 showError(hwnd, CStringUtil::format(
00206 getString(IDS_INVALID_SERVER_NAME).c_str(),
00207 server.c_str()));
00208 }
00209 SetFocus(child);
00210 return CString();
00211 }
00212
00213
00214
00215
00216
00217 if (CStringUtil::CaselessCmp::equal(ARCH->getHostName(), server)) {
00218 if (!silent) {
00219 showError(hwnd, CStringUtil::format(
00220 getString(IDS_SERVER_IS_CLIENT).c_str(),
00221 server.c_str()));
00222 }
00223 SetFocus(child);
00224 return CString();
00225 }
00226 }
00227
00228
00229 if (true) {
00230 HWND child = getItem(hwnd, IDC_MAIN_DEBUG);
00231 int debug = (int)SendMessage(child, CB_GETCURSEL, 0, 0);
00232
00233
00234
00235
00236 if (testing && debug < s_minTestDebug) {
00237 debug = s_minTestDebug;
00238 }
00239
00240 cmdLine += " --debug ";
00241 cmdLine += s_debugName[debug][1];
00242 }
00243
00244
00245 cmdLine += s_advancedOptions->getCommandLine(isClient, server);
00246
00247 return cmdLine;
00248 }
00249
00250 static
00251 bool
00252 launchApp(HWND hwnd, bool testing, HANDLE* thread, DWORD* threadID)
00253 {
00254 if (thread != NULL) {
00255 *thread = NULL;
00256 }
00257 if (threadID != NULL) {
00258 *threadID = 0;
00259 }
00260
00261
00262 if (!testing && CAutoStart::startDaemon()) {
00263 return true;
00264 }
00265
00266
00267 const bool isClient = isClientChecked(hwnd);
00268 const char* app = isClient ? CLIENT_APP : SERVER_APP;
00269
00270
00271 CString cmdLine = getCommandLine(hwnd, testing, false);
00272 if (cmdLine.empty()) {
00273 return false;
00274 }
00275
00276
00277 PROCESS_INFORMATION procInfo;
00278 if (!execApp(app, cmdLine, &procInfo)) {
00279 showError(hwnd, CStringUtil::format(
00280 getString(IDS_STARTUP_FAILED).c_str(),
00281 getErrorString(GetLastError()).c_str()));
00282 return false;
00283 }
00284
00285
00286 CloseHandle(procInfo.hProcess);
00287
00288
00289 if (thread != NULL) {
00290 *thread = procInfo.hThread;
00291 }
00292 if (threadID != NULL) {
00293 *threadID = procInfo.dwThreadId;
00294 }
00295
00296 return true;
00297 }
00298
00299 static
00300 BOOL CALLBACK
00301 waitDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
00302 {
00303
00304 static CChildWaitInfo* info = NULL;
00305
00306 switch (message) {
00307 case WM_INITDIALOG:
00308
00309 info = reinterpret_cast<CChildWaitInfo*>(lParam);
00310
00311
00312 info->m_dialog = hwnd;
00313
00314
00315 SetEvent(info->m_ready);
00316
00317 return TRUE;
00318
00319 case WM_COMMAND:
00320 switch (LOWORD(wParam)) {
00321 case IDCANCEL:
00322 case IDOK:
00323
00324 SetEvent(info->m_stop);
00325
00326
00327 EndDialog(hwnd, 0);
00328 return TRUE;
00329 }
00330 }
00331
00332 return FALSE;
00333 }
00334
00335 static
00336 DWORD WINAPI
00337 waitForChildThread(LPVOID vinfo)
00338 {
00339 CChildWaitInfo* info = reinterpret_cast<CChildWaitInfo*>(vinfo);
00340
00341
00342 WaitForSingleObject(info->m_ready, INFINITE);
00343
00344
00345 HANDLE handles[2];
00346 handles[0] = info->m_child;
00347 handles[1] = info->m_stop;
00348 DWORD n = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
00349
00350
00351 if (n == WAIT_OBJECT_0 + 1) {
00352 PostThreadMessage(info->m_childID, WM_QUIT, 0, 0);
00353 WaitForSingleObject(info->m_child, INFINITE);
00354 }
00355
00356
00357 else {
00358 PostMessage(info->m_dialog, WM_COMMAND, MAKEWPARAM(IDOK, 0), 0);
00359 }
00360
00361 return 0;
00362 }
00363
00364 static
00365 void
00366 waitForChild(HWND hwnd, HANDLE thread, DWORD threadID)
00367 {
00368
00369 CChildWaitInfo info;
00370 info.m_dialog = NULL;
00371 info.m_child = thread;
00372 info.m_childID = threadID;
00373 info.m_ready = CreateEvent(NULL, TRUE, FALSE, NULL);
00374 info.m_stop = CreateEvent(NULL, TRUE, FALSE, NULL);
00375
00376
00377 DWORD id;
00378 HANDLE waiter = CreateThread(NULL, 0, &waitForChildThread, &info,0, &id);
00379
00380
00381 DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_WAIT), hwnd,
00382 (DLGPROC)waitDlgProc, (LPARAM)&info);
00383
00384
00385 SetEvent(info.m_ready);
00386 SetEvent(info.m_stop);
00387 WaitForSingleObject(waiter, INFINITE);
00388
00389
00390 CloseHandle(waiter);
00391 CloseHandle(info.m_ready);
00392 CloseHandle(info.m_stop);
00393 }
00394
00395 static
00396 void
00397 initMainWindow(HWND hwnd)
00398 {
00399
00400 CString titleFormat = getString(IDS_TITLE);
00401 setWindowText(hwnd, CStringUtil::format(titleFormat.c_str(), kApplication, kVersion));
00402
00403
00404 bool configLoaded =
00405 loadConfig(ARG->m_config, s_configTime, s_userConfig);
00406 if (configLoaded) {
00407 s_lastConfig = ARG->m_config;
00408 }
00409
00410
00411 bool isServer = configLoaded;
00412 int debugLevel = s_defaultDebug;
00413 CString server;
00414 HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath());
00415 if (key != NULL) {
00416 if (isServer && CArchMiscWindows::hasValue(key, "isServer")) {
00417 isServer = (CArchMiscWindows::readValueInt(key, "isServer") != 0);
00418 }
00419 if (CArchMiscWindows::hasValue(key, "debug")) {
00420 debugLevel = static_cast<int>(
00421 CArchMiscWindows::readValueInt(key, "debug"));
00422 if (debugLevel < 0) {
00423 debugLevel = 0;
00424 }
00425 else if (debugLevel > CLog::kDEBUG2) {
00426 debugLevel = CLog::kDEBUG2;
00427 }
00428 }
00429 server = CArchMiscWindows::readValueString(key, "server");
00430 CArchMiscWindows::closeKey(key);
00431 }
00432
00433
00434 HWND child;
00435 child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO);
00436 setItemChecked(child, !isServer);
00437 child = getItem(hwnd, IDC_MAIN_SERVER_RADIO);
00438 setItemChecked(child, isServer);
00439
00440
00441 child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
00442 setWindowText(child, server);
00443
00444
00445 child = getItem(hwnd, IDC_MAIN_DEBUG);
00446 for (unsigned int i = 0; i < sizeof(s_debugName) /
00447 sizeof(s_debugName[0]); ++i) {
00448 SendMessage(child, CB_ADDSTRING, 0, (LPARAM)s_debugName[i][0]);
00449 }
00450 SendMessage(child, CB_SETCURSEL, debugLevel, 0);
00451
00452
00453 enableMainWindowControls(hwnd);
00454 }
00455
00456 static
00457 bool
00458 saveMainWindow(HWND hwnd, SaveMode mode, CString* cmdLineOut = NULL)
00459 {
00460 DWORD errorID = 0;
00461 CString arg;
00462 CString cmdLine;
00463
00464
00465 bool isClient = isClientChecked(hwnd);
00466 HKEY key = CArchMiscWindows::addKey(HKEY_CURRENT_USER, getSettingsPath());
00467 if (key != NULL) {
00468 HWND child;
00469 child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
00470 CArchMiscWindows::setValue(key, "server", getWindowText(child));
00471 child = getItem(hwnd, IDC_MAIN_DEBUG);
00472 CArchMiscWindows::setValue(key, "debug",
00473 SendMessage(child, CB_GETCURSEL, 0, 0));
00474 CArchMiscWindows::setValue(key, "isServer", isClient ? 0 : 1);
00475 CArchMiscWindows::closeKey(key);
00476 }
00477
00478
00479 if (!s_userConfig || ARG->m_config != s_lastConfig) {
00480 time_t t;
00481 if (!saveConfig(ARG->m_config, false, t)) {
00482 errorID = IDS_SAVE_FAILED;
00483 arg = getErrorString(GetLastError());
00484 goto failed;
00485 }
00486 if (s_userConfig) {
00487 s_configTime = t;
00488 s_lastConfig = ARG->m_config;
00489 }
00490 }
00491
00492
00493 if (CAutoStart::isDaemonInstalled()) {
00494 if (s_userConfig || ARG->m_config != s_lastConfig) {
00495 time_t t;
00496 if (!saveConfig(ARG->m_config, true, t)) {
00497 errorID = IDS_AUTOSTART_SAVE_FAILED;
00498 arg = getErrorString(GetLastError());
00499 goto failed;
00500 }
00501 if (!s_userConfig) {
00502 s_configTime = t;
00503 s_lastConfig = ARG->m_config;
00504 }
00505 }
00506 }
00507
00508
00509 cmdLine = getCommandLine(hwnd, false, mode == SAVE_QUITING);
00510 if (cmdLineOut != NULL) {
00511 *cmdLineOut = cmdLine;
00512 }
00513 if (cmdLine.empty()) {
00514 return (mode == SAVE_QUITING);
00515 }
00516
00517
00518 if (CAutoStart::isDaemonInstalled()) {
00519 try {
00520 CAutoStart::reinstallDaemon(isClient, cmdLine);
00521 CAutoStart::uninstallDaemons(!isClient);
00522 }
00523 catch (XArchDaemon& e) {
00524 errorID = IDS_INSTALL_GENERIC_ERROR;
00525 arg = e.what();
00526 goto failed;
00527 }
00528 }
00529
00530 return true;
00531
00532 failed:
00533 CString errorMessage =
00534 CStringUtil::format(getString(errorID).c_str(), arg.c_str());
00535 if (mode == SAVE_QUITING) {
00536 errorMessage += "\n";
00537 errorMessage += getString(IDS_UNSAVED_DATA_REALLY_QUIT);
00538 if (askVerify(hwnd, errorMessage)) {
00539 return true;
00540 }
00541 }
00542 else if (mode == SAVE_NORMAL) {
00543 showError(hwnd, errorMessage);
00544 }
00545 return false;
00546 }
00547
00548 static
00549 LRESULT CALLBACK
00550 mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
00551 {
00552 switch (message) {
00553 case WM_ACTIVATE:
00554 if (LOWORD(wParam) != WA_INACTIVE) {
00555
00556
00557
00558 if (isConfigNewer(s_configTime, s_userConfig)) {
00559 CString message = getString(IDS_CONFIG_CHANGED);
00560 if (askVerify(hwnd, message)) {
00561 time_t configTime;
00562 bool userConfig;
00563 CConfig newConfig;
00564 if (loadConfig(newConfig, configTime, userConfig) &&
00565 userConfig == s_userConfig) {
00566 ARG->m_config = newConfig;
00567 s_lastConfig = ARG->m_config;
00568 }
00569 else {
00570 message = getString(IDS_LOAD_FAILED);
00571 showError(hwnd, message);
00572 s_lastConfig = CConfig();
00573 }
00574 }
00575 }
00576 }
00577 else {
00578
00579 if (!isShowingDialog()) {
00580 saveMainWindow(hwnd, SAVE_QUIET);
00581 }
00582 }
00583 break;
00584
00585 case WM_COMMAND:
00586 switch (LOWORD(wParam)) {
00587 case IDCANCEL:
00588
00589 if (saveMainWindow(hwnd, SAVE_QUITING)) {
00590
00591 PostQuitMessage(0);
00592 }
00593 return 0;
00594
00595 case IDOK:
00596 case IDC_MAIN_TEST: {
00597
00598 const bool testing = (LOWORD(wParam) == IDC_MAIN_TEST);
00599
00600
00601 if (saveMainWindow(hwnd, SAVE_NORMAL)) {
00602
00603 DWORD threadID;
00604 HANDLE thread;
00605 if (!launchApp(hwnd, testing, &thread, &threadID)) {
00606 return 0;
00607 }
00608
00609
00610 if (testing) {
00611
00612 waitForChild(hwnd, thread, threadID);
00613
00614
00615 CloseHandle(thread);
00616 }
00617 else {
00618
00619 if (thread != NULL) {
00620 CloseHandle(thread);
00621 }
00622
00623
00624
00625
00626
00627 PostQuitMessage(0);
00628 }
00629 }
00630 return 0;
00631 }
00632
00633 case IDC_MAIN_AUTOSTART: {
00634 CString cmdLine;
00635 if (saveMainWindow(hwnd, SAVE_NORMAL, &cmdLine)) {
00636
00637 CAutoStart autoStart(hwnd, !isClientChecked(hwnd), cmdLine);
00638 autoStart.doModal();
00639 }
00640 return 0;
00641 }
00642
00643 case IDC_MAIN_CLIENT_RADIO:
00644 case IDC_MAIN_SERVER_RADIO:
00645 enableMainWindowControls(hwnd);
00646 return 0;
00647
00648 case IDC_MAIN_SCREENS:
00649 s_screensLinks->doModal();
00650 break;
00651
00652 case IDC_MAIN_OPTIONS:
00653 s_globalOptions->doModal();
00654 break;
00655
00656 case IDC_MAIN_ADVANCED:
00657 s_advancedOptions->doModal(isClientChecked(hwnd));
00658 break;
00659
00660 case IDC_MAIN_HOTKEYS:
00661 s_hotkeyOptions->doModal();
00662 break;
00663
00664 case IDC_MAIN_INFO:
00665 s_info->doModal();
00666 break;
00667 }
00668
00669 default:
00670 break;
00671 }
00672 return DefDlgProc(hwnd, message, wParam, lParam);
00673 }
00674
00675 int WINAPI
00676 WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmdLine, int nCmdShow)
00677 {
00678 CArch arch(instance);
00679 CLOG;
00680 CArgs args;
00681
00682 s_instance = instance;
00683
00684
00685
00686
00687 if (CString(cmdLine).find("/uninstall") != CString::npos) {
00688 CAutoStart::uninstallDaemons(false);
00689 CAutoStart::uninstallDaemons(true);
00690 return 0;
00691 }
00692
00693
00694 WNDCLASSEX classInfo;
00695 classInfo.cbSize = sizeof(classInfo);
00696 classInfo.style = CS_HREDRAW | CS_VREDRAW;
00697 classInfo.lpfnWndProc = &mainWndProc;
00698 classInfo.cbClsExtra = 0;
00699 classInfo.cbWndExtra = DLGWINDOWEXTRA;
00700 classInfo.hInstance = instance;
00701 classInfo.hIcon = (HICON)LoadImage(instance,
00702 MAKEINTRESOURCE(IDI_SYNERGY),
00703 IMAGE_ICON,
00704 32, 32, LR_SHARED);
00705 classInfo.hCursor = LoadCursor(NULL, IDC_ARROW);
00706 classInfo.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1);
00707 classInfo.lpszMenuName = NULL;
00708 classInfo.lpszClassName = s_mainClass;
00709 classInfo.hIconSm = (HICON)LoadImage(instance,
00710 MAKEINTRESOURCE(IDI_SYNERGY),
00711 IMAGE_ICON,
00712 16, 16, LR_SHARED);
00713 RegisterClassEx(&classInfo);
00714
00715
00716 HWND mainWindow = CreateDialog(s_instance,
00717 MAKEINTRESOURCE(IDD_MAIN), 0, NULL);
00718
00719
00720 initMainWindow(mainWindow);
00721 s_globalOptions = new CGlobalOptions(mainWindow, &ARG->m_config);
00722 s_advancedOptions = new CAdvancedOptions(mainWindow, &ARG->m_config);
00723 s_hotkeyOptions = new CHotkeyOptions(mainWindow, &ARG->m_config);
00724 s_screensLinks = new CScreensLinks(mainWindow, &ARG->m_config);
00725 s_info = new CInfo(mainWindow);
00726
00727
00728 ShowWindow(mainWindow, nCmdShow);
00729
00730
00731 MSG msg;
00732 bool done = false;
00733 do {
00734 switch (GetMessage(&msg, NULL, 0, 0)) {
00735 case -1:
00736
00737 break;
00738
00739 case 0:
00740
00741 done = true;
00742 break;
00743
00744 default:
00745 if (!IsDialogMessage(mainWindow, &msg)) {
00746 TranslateMessage(&msg);
00747 DispatchMessage(&msg);
00748 }
00749 break;
00750 }
00751 } while (!done);
00752
00753 return msg.wParam;
00754 }