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 "CConfig.h"
|
---|
16 | #include "KeyTypes.h"
|
---|
17 | #include "OptionTypes.h"
|
---|
18 | #include "ProtocolTypes.h"
|
---|
19 | #include "CLog.h"
|
---|
20 | #include "CStringUtil.h"
|
---|
21 | #include "CArch.h"
|
---|
22 | #include "CArchMiscWindows.h"
|
---|
23 | #include "XArch.h"
|
---|
24 | #include "Version.h"
|
---|
25 | #include "stdvector.h"
|
---|
26 | #include "resource.h"
|
---|
27 |
|
---|
28 | // these must come after the above because it includes windows.h
|
---|
29 | #include "LaunchUtil.h"
|
---|
30 | #include "CAddScreen.h"
|
---|
31 | #include "CAdvancedOptions.h"
|
---|
32 | #include "CAutoStart.h"
|
---|
33 | #include "CGlobalOptions.h"
|
---|
34 | #include "CHotkeyOptions.h"
|
---|
35 | #include "CInfo.h"
|
---|
36 | #include "CScreensLinks.h"
|
---|
37 |
|
---|
38 | typedef std::vector<CString> CStringList;
|
---|
39 |
|
---|
40 | class CChildWaitInfo {
|
---|
41 | public:
|
---|
42 | HWND m_dialog;
|
---|
43 | HANDLE m_child;
|
---|
44 | DWORD m_childID;
|
---|
45 | HANDLE m_ready;
|
---|
46 | HANDLE m_stop;
|
---|
47 | };
|
---|
48 |
|
---|
49 | static const char* s_debugName[][2] = {
|
---|
50 | { TEXT("Error"), "ERROR" },
|
---|
51 | { TEXT("Warning"), "WARNING" },
|
---|
52 | { TEXT("Note"), "NOTE" },
|
---|
53 | { TEXT("Info"), "INFO" },
|
---|
54 | { TEXT("Debug"), "DEBUG" },
|
---|
55 | { TEXT("Debug1"), "DEBUG1" },
|
---|
56 | { TEXT("Debug2"), "DEBUG2" }
|
---|
57 | };
|
---|
58 | static const int s_defaultDebug = 1; // WARNING
|
---|
59 | static const int s_minTestDebug = 3; // INFO
|
---|
60 |
|
---|
61 | HINSTANCE s_instance = NULL;
|
---|
62 |
|
---|
63 | static CGlobalOptions* s_globalOptions = NULL;
|
---|
64 | static CAdvancedOptions* s_advancedOptions = NULL;
|
---|
65 | static CHotkeyOptions* s_hotkeyOptions = NULL;
|
---|
66 | static CScreensLinks* s_screensLinks = NULL;
|
---|
67 | static CInfo* s_info = NULL;
|
---|
68 |
|
---|
69 | static bool s_userConfig = true;
|
---|
70 | static time_t s_configTime = 0;
|
---|
71 | static CConfig s_lastConfig;
|
---|
72 |
|
---|
73 | static const TCHAR* s_mainClass = TEXT("GoSynergy");
|
---|
74 | static const TCHAR* s_layoutClass = TEXT("SynergyLayout");
|
---|
75 |
|
---|
76 | enum SaveMode {
|
---|
77 | SAVE_QUITING,
|
---|
78 | SAVE_NORMAL,
|
---|
79 | SAVE_QUIET
|
---|
80 | };
|
---|
81 |
|
---|
82 | //
|
---|
83 | // program arguments
|
---|
84 | //
|
---|
85 |
|
---|
86 | #define ARG CArgs::s_instance
|
---|
87 |
|
---|
88 | class CArgs {
|
---|
89 | public:
|
---|
90 | CArgs() { s_instance = this; }
|
---|
91 | ~CArgs() { s_instance = NULL; }
|
---|
92 |
|
---|
93 | public:
|
---|
94 | static CArgs* s_instance;
|
---|
95 | CConfig m_config;
|
---|
96 | CStringList m_screens;
|
---|
97 | };
|
---|
98 |
|
---|
99 | CArgs* CArgs::s_instance = NULL;
|
---|
100 |
|
---|
101 |
|
---|
102 | static
|
---|
103 | BOOL CALLBACK
|
---|
104 | addDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
|
---|
105 |
|
---|
106 | static
|
---|
107 | bool
|
---|
108 | isClientChecked(HWND hwnd)
|
---|
109 | {
|
---|
110 | HWND child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO);
|
---|
111 | return isItemChecked(child);
|
---|
112 | }
|
---|
113 |
|
---|
114 | static
|
---|
115 | void
|
---|
116 | enableMainWindowControls(HWND hwnd)
|
---|
117 | {
|
---|
118 | bool client = isClientChecked(hwnd);
|
---|
119 | enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_LABEL, client);
|
---|
120 | enableItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT, client);
|
---|
121 | enableItem(hwnd, IDC_MAIN_SERVER_SCREENS_LABEL, !client);
|
---|
122 | enableItem(hwnd, IDC_MAIN_SCREENS, !client);
|
---|
123 | enableItem(hwnd, IDC_MAIN_OPTIONS, !client);
|
---|
124 | enableItem(hwnd, IDC_MAIN_HOTKEYS, !client);
|
---|
125 | }
|
---|
126 |
|
---|
127 | static
|
---|
128 | bool
|
---|
129 | execApp(const char* app, const CString& cmdLine, PROCESS_INFORMATION* procInfo)
|
---|
130 | {
|
---|
131 | // prepare startup info
|
---|
132 | STARTUPINFO startup;
|
---|
133 | startup.cb = sizeof(startup);
|
---|
134 | startup.lpReserved = NULL;
|
---|
135 | startup.lpDesktop = NULL;
|
---|
136 | startup.lpTitle = NULL;
|
---|
137 | startup.dwX = (DWORD)CW_USEDEFAULT;
|
---|
138 | startup.dwY = (DWORD)CW_USEDEFAULT;
|
---|
139 | startup.dwXSize = (DWORD)CW_USEDEFAULT;
|
---|
140 | startup.dwYSize = (DWORD)CW_USEDEFAULT;
|
---|
141 | startup.dwXCountChars = 0;
|
---|
142 | startup.dwYCountChars = 0;
|
---|
143 | startup.dwFillAttribute = 0;
|
---|
144 | startup.dwFlags = STARTF_FORCEONFEEDBACK;
|
---|
145 | startup.wShowWindow = SW_SHOWDEFAULT;
|
---|
146 | startup.cbReserved2 = 0;
|
---|
147 | startup.lpReserved2 = NULL;
|
---|
148 | startup.hStdInput = NULL;
|
---|
149 | startup.hStdOutput = NULL;
|
---|
150 | startup.hStdError = NULL;
|
---|
151 |
|
---|
152 | // prepare path to app
|
---|
153 | CString appPath = getAppPath(app);
|
---|
154 |
|
---|
155 | // put path to app in command line
|
---|
156 | CString commandLine = "\"";
|
---|
157 | commandLine += appPath;
|
---|
158 | commandLine += "\" ";
|
---|
159 | commandLine += cmdLine;
|
---|
160 |
|
---|
161 | // start child
|
---|
162 | if (CreateProcess(NULL, (char*)commandLine.c_str(),
|
---|
163 | NULL,
|
---|
164 | NULL,
|
---|
165 | FALSE,
|
---|
166 | CREATE_DEFAULT_ERROR_MODE |
|
---|
167 | CREATE_NEW_PROCESS_GROUP |
|
---|
168 | NORMAL_PRIORITY_CLASS,
|
---|
169 | NULL,
|
---|
170 | NULL,
|
---|
171 | &startup,
|
---|
172 | procInfo) == 0) {
|
---|
173 | return false;
|
---|
174 | }
|
---|
175 | else {
|
---|
176 | return true;
|
---|
177 | }
|
---|
178 | }
|
---|
179 |
|
---|
180 | static
|
---|
181 | CString
|
---|
182 | getCommandLine(HWND hwnd, bool testing, bool silent)
|
---|
183 | {
|
---|
184 | CString cmdLine;
|
---|
185 |
|
---|
186 | // add constant testing args
|
---|
187 | if (testing) {
|
---|
188 | cmdLine += " -z --no-restart --no-daemon";
|
---|
189 | }
|
---|
190 |
|
---|
191 | // can't start as service on NT
|
---|
192 | else if (!CArchMiscWindows::isWindows95Family()) {
|
---|
193 | cmdLine += " --no-daemon";
|
---|
194 | }
|
---|
195 |
|
---|
196 | // get the server name
|
---|
197 | CString server;
|
---|
198 | bool isClient = isClientChecked(hwnd);
|
---|
199 | if (isClient) {
|
---|
200 | // check server name
|
---|
201 | HWND child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
|
---|
202 | server = getWindowText(child);
|
---|
203 | if (!ARG->m_config.isValidScreenName(server)) {
|
---|
204 | if (!silent) {
|
---|
205 | showError(hwnd, CStringUtil::format(
|
---|
206 | getString(IDS_INVALID_SERVER_NAME).c_str(),
|
---|
207 | server.c_str()));
|
---|
208 | }
|
---|
209 | SetFocus(child);
|
---|
210 | return CString();
|
---|
211 | }
|
---|
212 |
|
---|
213 | // compare server name to local host. a common error
|
---|
214 | // is to provide the client's name for the server. we
|
---|
215 | // don't bother to check the addresses though that'd be
|
---|
216 | // more accurate.
|
---|
217 | if (CStringUtil::CaselessCmp::equal(ARCH->getHostName(), server)) {
|
---|
218 | if (!silent) {
|
---|
219 | showError(hwnd, CStringUtil::format(
|
---|
220 | getString(IDS_SERVER_IS_CLIENT).c_str(),
|
---|
221 | server.c_str()));
|
---|
222 | }
|
---|
223 | SetFocus(child);
|
---|
224 | return CString();
|
---|
225 | }
|
---|
226 | }
|
---|
227 |
|
---|
228 | // debug level. always include this.
|
---|
229 | if (true) {
|
---|
230 | HWND child = getItem(hwnd, IDC_MAIN_DEBUG);
|
---|
231 | int debug = (int)SendMessage(child, CB_GETCURSEL, 0, 0);
|
---|
232 |
|
---|
233 | // if testing then we force the debug level to be no less than
|
---|
234 | // s_minTestDebug. what's the point of testing if you can't
|
---|
235 | // see the debugging info?
|
---|
236 | if (testing && debug < s_minTestDebug) {
|
---|
237 | debug = s_minTestDebug;
|
---|
238 | }
|
---|
239 |
|
---|
240 | cmdLine += " --debug ";
|
---|
241 | cmdLine += s_debugName[debug][1];
|
---|
242 | }
|
---|
243 |
|
---|
244 | // add advanced options
|
---|
245 | cmdLine += s_advancedOptions->getCommandLine(isClient, server);
|
---|
246 |
|
---|
247 | return cmdLine;
|
---|
248 | }
|
---|
249 |
|
---|
250 | static
|
---|
251 | bool
|
---|
252 | launchApp(HWND hwnd, bool testing, HANDLE* thread, DWORD* threadID)
|
---|
253 | {
|
---|
254 | if (thread != NULL) {
|
---|
255 | *thread = NULL;
|
---|
256 | }
|
---|
257 | if (threadID != NULL) {
|
---|
258 | *threadID = 0;
|
---|
259 | }
|
---|
260 |
|
---|
261 | // start daemon if it's installed and we're not testing
|
---|
262 | if (!testing && CAutoStart::startDaemon()) {
|
---|
263 | return true;
|
---|
264 | }
|
---|
265 |
|
---|
266 | // decide if client or server
|
---|
267 | const bool isClient = isClientChecked(hwnd);
|
---|
268 | const char* app = isClient ? CLIENT_APP : SERVER_APP;
|
---|
269 |
|
---|
270 | // prepare command line
|
---|
271 | CString cmdLine = getCommandLine(hwnd, testing, false);
|
---|
272 | if (cmdLine.empty()) {
|
---|
273 | return false;
|
---|
274 | }
|
---|
275 |
|
---|
276 | // start child
|
---|
277 | PROCESS_INFORMATION procInfo;
|
---|
278 | if (!execApp(app, cmdLine, &procInfo)) {
|
---|
279 | showError(hwnd, CStringUtil::format(
|
---|
280 | getString(IDS_STARTUP_FAILED).c_str(),
|
---|
281 | getErrorString(GetLastError()).c_str()));
|
---|
282 | return false;
|
---|
283 | }
|
---|
284 |
|
---|
285 | // don't need process handle
|
---|
286 | CloseHandle(procInfo.hProcess);
|
---|
287 |
|
---|
288 | // save thread handle and thread ID if desired
|
---|
289 | if (thread != NULL) {
|
---|
290 | *thread = procInfo.hThread;
|
---|
291 | }
|
---|
292 | if (threadID != NULL) {
|
---|
293 | *threadID = procInfo.dwThreadId;
|
---|
294 | }
|
---|
295 |
|
---|
296 | return true;
|
---|
297 | }
|
---|
298 |
|
---|
299 | static
|
---|
300 | BOOL CALLBACK
|
---|
301 | waitDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
---|
302 | {
|
---|
303 | // only one wait dialog at a time!
|
---|
304 | static CChildWaitInfo* info = NULL;
|
---|
305 |
|
---|
306 | switch (message) {
|
---|
307 | case WM_INITDIALOG:
|
---|
308 | // save info pointer
|
---|
309 | info = reinterpret_cast<CChildWaitInfo*>(lParam);
|
---|
310 |
|
---|
311 | // save hwnd
|
---|
312 | info->m_dialog = hwnd;
|
---|
313 |
|
---|
314 | // signal ready
|
---|
315 | SetEvent(info->m_ready);
|
---|
316 |
|
---|
317 | return TRUE;
|
---|
318 |
|
---|
319 | case WM_COMMAND:
|
---|
320 | switch (LOWORD(wParam)) {
|
---|
321 | case IDCANCEL:
|
---|
322 | case IDOK:
|
---|
323 | // signal stop
|
---|
324 | SetEvent(info->m_stop);
|
---|
325 |
|
---|
326 | // done
|
---|
327 | EndDialog(hwnd, 0);
|
---|
328 | return TRUE;
|
---|
329 | }
|
---|
330 | }
|
---|
331 |
|
---|
332 | return FALSE;
|
---|
333 | }
|
---|
334 |
|
---|
335 | static
|
---|
336 | DWORD WINAPI
|
---|
337 | waitForChildThread(LPVOID vinfo)
|
---|
338 | {
|
---|
339 | CChildWaitInfo* info = reinterpret_cast<CChildWaitInfo*>(vinfo);
|
---|
340 |
|
---|
341 | // wait for ready
|
---|
342 | WaitForSingleObject(info->m_ready, INFINITE);
|
---|
343 |
|
---|
344 | // wait for thread to complete or stop event
|
---|
345 | HANDLE handles[2];
|
---|
346 | handles[0] = info->m_child;
|
---|
347 | handles[1] = info->m_stop;
|
---|
348 | DWORD n = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
|
---|
349 |
|
---|
350 | // if stop was raised then terminate child and wait for it
|
---|
351 | if (n == WAIT_OBJECT_0 + 1) {
|
---|
352 | PostThreadMessage(info->m_childID, WM_QUIT, 0, 0);
|
---|
353 | WaitForSingleObject(info->m_child, INFINITE);
|
---|
354 | }
|
---|
355 |
|
---|
356 | // otherwise post IDOK to dialog box
|
---|
357 | else {
|
---|
358 | PostMessage(info->m_dialog, WM_COMMAND, MAKEWPARAM(IDOK, 0), 0);
|
---|
359 | }
|
---|
360 |
|
---|
361 | return 0;
|
---|
362 | }
|
---|
363 |
|
---|
364 | static
|
---|
365 | void
|
---|
366 | waitForChild(HWND hwnd, HANDLE thread, DWORD threadID)
|
---|
367 | {
|
---|
368 | // prepare info for child wait dialog and thread
|
---|
369 | CChildWaitInfo info;
|
---|
370 | info.m_dialog = NULL;
|
---|
371 | info.m_child = thread;
|
---|
372 | info.m_childID = threadID;
|
---|
373 | info.m_ready = CreateEvent(NULL, TRUE, FALSE, NULL);
|
---|
374 | info.m_stop = CreateEvent(NULL, TRUE, FALSE, NULL);
|
---|
375 |
|
---|
376 | // create a thread to wait on the child thread and event
|
---|
377 | DWORD id;
|
---|
378 | HANDLE waiter = CreateThread(NULL, 0, &waitForChildThread, &info,0, &id);
|
---|
379 |
|
---|
380 | // do dialog that let's the user terminate the test
|
---|
381 | DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_WAIT), hwnd,
|
---|
382 | waitDlgProc, (LPARAM)&info);
|
---|
383 |
|
---|
384 | // force the waiter thread to finish and wait for it
|
---|
385 | SetEvent(info.m_ready);
|
---|
386 | SetEvent(info.m_stop);
|
---|
387 | WaitForSingleObject(waiter, INFINITE);
|
---|
388 |
|
---|
389 | // clean up
|
---|
390 | CloseHandle(waiter);
|
---|
391 | CloseHandle(info.m_ready);
|
---|
392 | CloseHandle(info.m_stop);
|
---|
393 | }
|
---|
394 |
|
---|
395 | static
|
---|
396 | void
|
---|
397 | initMainWindow(HWND hwnd)
|
---|
398 | {
|
---|
399 | // append version number to title
|
---|
400 | CString titleFormat = getString(IDS_TITLE);
|
---|
401 | setWindowText(hwnd, CStringUtil::format(titleFormat.c_str(), VERSION));
|
---|
402 |
|
---|
403 | // load configuration
|
---|
404 | bool configLoaded =
|
---|
405 | loadConfig(ARG->m_config, s_configTime, s_userConfig);
|
---|
406 | if (configLoaded) {
|
---|
407 | s_lastConfig = ARG->m_config;
|
---|
408 | }
|
---|
409 |
|
---|
410 | // get settings from registry
|
---|
411 | bool isServer = configLoaded;
|
---|
412 | int debugLevel = s_defaultDebug;
|
---|
413 | CString server;
|
---|
414 | HKEY key = CArchMiscWindows::openKey(HKEY_CURRENT_USER, getSettingsPath());
|
---|
415 | if (key != NULL) {
|
---|
416 | if (isServer && CArchMiscWindows::hasValue(key, "isServer")) {
|
---|
417 | isServer = (CArchMiscWindows::readValueInt(key, "isServer") != 0);
|
---|
418 | }
|
---|
419 | if (CArchMiscWindows::hasValue(key, "debug")) {
|
---|
420 | debugLevel = static_cast<int>(
|
---|
421 | CArchMiscWindows::readValueInt(key, "debug"));
|
---|
422 | if (debugLevel < 0) {
|
---|
423 | debugLevel = 0;
|
---|
424 | }
|
---|
425 | else if (debugLevel > CLog::kDEBUG2) {
|
---|
426 | debugLevel = CLog::kDEBUG2;
|
---|
427 | }
|
---|
428 | }
|
---|
429 | server = CArchMiscWindows::readValueString(key, "server");
|
---|
430 | CArchMiscWindows::closeKey(key);
|
---|
431 | }
|
---|
432 |
|
---|
433 | // choose client/server radio buttons
|
---|
434 | HWND child;
|
---|
435 | child = getItem(hwnd, IDC_MAIN_CLIENT_RADIO);
|
---|
436 | setItemChecked(child, !isServer);
|
---|
437 | child = getItem(hwnd, IDC_MAIN_SERVER_RADIO);
|
---|
438 | setItemChecked(child, isServer);
|
---|
439 |
|
---|
440 | // set server name
|
---|
441 | child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
|
---|
442 | setWindowText(child, server);
|
---|
443 |
|
---|
444 | // debug level
|
---|
445 | child = getItem(hwnd, IDC_MAIN_DEBUG);
|
---|
446 | for (unsigned int i = 0; i < sizeof(s_debugName) /
|
---|
447 | sizeof(s_debugName[0]); ++i) {
|
---|
448 | SendMessage(child, CB_ADDSTRING, 0, (LPARAM)s_debugName[i][0]);
|
---|
449 | }
|
---|
450 | SendMessage(child, CB_SETCURSEL, debugLevel, 0);
|
---|
451 |
|
---|
452 | // update controls
|
---|
453 | enableMainWindowControls(hwnd);
|
---|
454 | }
|
---|
455 |
|
---|
456 | static
|
---|
457 | bool
|
---|
458 | saveMainWindow(HWND hwnd, SaveMode mode, CString* cmdLineOut = NULL)
|
---|
459 | {
|
---|
460 | DWORD errorID = 0;
|
---|
461 | CString arg;
|
---|
462 | CString cmdLine;
|
---|
463 |
|
---|
464 | // save dialog state
|
---|
465 | bool isClient = isClientChecked(hwnd);
|
---|
466 | HKEY key = CArchMiscWindows::addKey(HKEY_CURRENT_USER, getSettingsPath());
|
---|
467 | if (key != NULL) {
|
---|
468 | HWND child;
|
---|
469 | child = getItem(hwnd, IDC_MAIN_CLIENT_SERVER_NAME_EDIT);
|
---|
470 | CArchMiscWindows::setValue(key, "server", getWindowText(child));
|
---|
471 | child = getItem(hwnd, IDC_MAIN_DEBUG);
|
---|
472 | CArchMiscWindows::setValue(key, "debug",
|
---|
473 | SendMessage(child, CB_GETCURSEL, 0, 0));
|
---|
474 | CArchMiscWindows::setValue(key, "isServer", isClient ? 0 : 1);
|
---|
475 | CArchMiscWindows::closeKey(key);
|
---|
476 | }
|
---|
477 |
|
---|
478 | // save user's configuration
|
---|
479 | if (!s_userConfig || ARG->m_config != s_lastConfig) {
|
---|
480 | time_t t;
|
---|
481 | if (!saveConfig(ARG->m_config, false, t)) {
|
---|
482 | errorID = IDS_SAVE_FAILED;
|
---|
483 | arg = getErrorString(GetLastError());
|
---|
484 | goto failed;
|
---|
485 | }
|
---|
486 | if (s_userConfig) {
|
---|
487 | s_configTime = t;
|
---|
488 | s_lastConfig = ARG->m_config;
|
---|
489 | }
|
---|
490 | }
|
---|
491 |
|
---|
492 | // save autostart configuration
|
---|
493 | if (CAutoStart::isDaemonInstalled()) {
|
---|
494 | if (s_userConfig || ARG->m_config != s_lastConfig) {
|
---|
495 | time_t t;
|
---|
496 | if (!saveConfig(ARG->m_config, true, t)) {
|
---|
497 | errorID = IDS_AUTOSTART_SAVE_FAILED;
|
---|
498 | arg = getErrorString(GetLastError());
|
---|
499 | goto failed;
|
---|
500 | }
|
---|
501 | if (!s_userConfig) {
|
---|
502 | s_configTime = t;
|
---|
503 | s_lastConfig = ARG->m_config;
|
---|
504 | }
|
---|
505 | }
|
---|
506 | }
|
---|
507 |
|
---|
508 | // get autostart command
|
---|
509 | cmdLine = getCommandLine(hwnd, false, mode == SAVE_QUITING);
|
---|
510 | if (cmdLineOut != NULL) {
|
---|
511 | *cmdLineOut = cmdLine;
|
---|
512 | }
|
---|
513 | if (cmdLine.empty()) {
|
---|
514 | return (mode == SAVE_QUITING);
|
---|
515 | }
|
---|
516 |
|
---|
517 | // save autostart command
|
---|
518 | if (CAutoStart::isDaemonInstalled()) {
|
---|
519 | try {
|
---|
520 | CAutoStart::reinstallDaemon(isClient, cmdLine);
|
---|
521 | CAutoStart::uninstallDaemons(!isClient);
|
---|
522 | }
|
---|
523 | catch (XArchDaemon& e) {
|
---|
524 | errorID = IDS_INSTALL_GENERIC_ERROR;
|
---|
525 | arg = e.what();
|
---|
526 | goto failed;
|
---|
527 | }
|
---|
528 | }
|
---|
529 |
|
---|
530 | return true;
|
---|
531 |
|
---|
532 | failed:
|
---|
533 | CString errorMessage =
|
---|
534 | CStringUtil::format(getString(errorID).c_str(), arg.c_str());
|
---|
535 | if (mode == SAVE_QUITING) {
|
---|
536 | errorMessage += "\n";
|
---|
537 | errorMessage += getString(IDS_UNSAVED_DATA_REALLY_QUIT);
|
---|
538 | if (askVerify(hwnd, errorMessage)) {
|
---|
539 | return true;
|
---|
540 | }
|
---|
541 | }
|
---|
542 | else if (mode == SAVE_NORMAL) {
|
---|
543 | showError(hwnd, errorMessage);
|
---|
544 | }
|
---|
545 | return false;
|
---|
546 | }
|
---|
547 |
|
---|
548 | static
|
---|
549 | LRESULT CALLBACK
|
---|
550 | mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
---|
551 | {
|
---|
552 | switch (message) {
|
---|
553 | case WM_ACTIVATE:
|
---|
554 | if (LOWORD(wParam) != WA_INACTIVE) {
|
---|
555 | // activated
|
---|
556 |
|
---|
557 | // see if the configuration changed
|
---|
558 | if (isConfigNewer(s_configTime, s_userConfig)) {
|
---|
559 | CString message = getString(IDS_CONFIG_CHANGED);
|
---|
560 | if (askVerify(hwnd, message)) {
|
---|
561 | time_t configTime;
|
---|
562 | bool userConfig;
|
---|
563 | CConfig newConfig;
|
---|
564 | if (loadConfig(newConfig, configTime, userConfig) &&
|
---|
565 | userConfig == s_userConfig) {
|
---|
566 | ARG->m_config = newConfig;
|
---|
567 | s_lastConfig = ARG->m_config;
|
---|
568 | }
|
---|
569 | else {
|
---|
570 | message = getString(IDS_LOAD_FAILED);
|
---|
571 | showError(hwnd, message);
|
---|
572 | s_lastConfig = CConfig();
|
---|
573 | }
|
---|
574 | }
|
---|
575 | }
|
---|
576 | }
|
---|
577 | else {
|
---|
578 | // deactivated; write configuration
|
---|
579 | if (!isShowingDialog()) {
|
---|
580 | saveMainWindow(hwnd, SAVE_QUIET);
|
---|
581 | }
|
---|
582 | }
|
---|
583 | break;
|
---|
584 |
|
---|
585 | case WM_COMMAND:
|
---|
586 | switch (LOWORD(wParam)) {
|
---|
587 | case IDCANCEL:
|
---|
588 | // save data
|
---|
589 | if (saveMainWindow(hwnd, SAVE_QUITING)) {
|
---|
590 | // quit
|
---|
591 | PostQuitMessage(0);
|
---|
592 | }
|
---|
593 | return 0;
|
---|
594 |
|
---|
595 | case IDOK:
|
---|
596 | case IDC_MAIN_TEST: {
|
---|
597 | // note if testing
|
---|
598 | const bool testing = (LOWORD(wParam) == IDC_MAIN_TEST);
|
---|
599 |
|
---|
600 | // save data
|
---|
601 | if (saveMainWindow(hwnd, SAVE_NORMAL)) {
|
---|
602 | // launch child app
|
---|
603 | DWORD threadID;
|
---|
604 | HANDLE thread;
|
---|
605 | if (!launchApp(hwnd, testing, &thread, &threadID)) {
|
---|
606 | return 0;
|
---|
607 | }
|
---|
608 |
|
---|
609 | // handle child program
|
---|
610 | if (testing) {
|
---|
611 | // wait for process to stop, allowing the user to kill it
|
---|
612 | waitForChild(hwnd, thread, threadID);
|
---|
613 |
|
---|
614 | // clean up
|
---|
615 | CloseHandle(thread);
|
---|
616 | }
|
---|
617 | else {
|
---|
618 | // don't need thread handle
|
---|
619 | if (thread != NULL) {
|
---|
620 | CloseHandle(thread);
|
---|
621 | }
|
---|
622 |
|
---|
623 | // notify of success
|
---|
624 | askOkay(hwnd, getString(IDS_STARTED_TITLE),
|
---|
625 | getString(IDS_STARTED));
|
---|
626 |
|
---|
627 | // quit
|
---|
628 | PostQuitMessage(0);
|
---|
629 | }
|
---|
630 | }
|
---|
631 | return 0;
|
---|
632 | }
|
---|
633 |
|
---|
634 | case IDC_MAIN_AUTOSTART: {
|
---|
635 | CString cmdLine;
|
---|
636 | if (saveMainWindow(hwnd, SAVE_NORMAL, &cmdLine)) {
|
---|
637 | // run dialog
|
---|
638 | CAutoStart autoStart(hwnd, !isClientChecked(hwnd), cmdLine);
|
---|
639 | autoStart.doModal();
|
---|
640 | }
|
---|
641 | return 0;
|
---|
642 | }
|
---|
643 |
|
---|
644 | case IDC_MAIN_CLIENT_RADIO:
|
---|
645 | case IDC_MAIN_SERVER_RADIO:
|
---|
646 | enableMainWindowControls(hwnd);
|
---|
647 | return 0;
|
---|
648 |
|
---|
649 | case IDC_MAIN_SCREENS:
|
---|
650 | s_screensLinks->doModal();
|
---|
651 | break;
|
---|
652 |
|
---|
653 | case IDC_MAIN_OPTIONS:
|
---|
654 | s_globalOptions->doModal();
|
---|
655 | break;
|
---|
656 |
|
---|
657 | case IDC_MAIN_ADVANCED:
|
---|
658 | s_advancedOptions->doModal(isClientChecked(hwnd));
|
---|
659 | break;
|
---|
660 |
|
---|
661 | case IDC_MAIN_HOTKEYS:
|
---|
662 | s_hotkeyOptions->doModal();
|
---|
663 | break;
|
---|
664 |
|
---|
665 | case IDC_MAIN_INFO:
|
---|
666 | s_info->doModal();
|
---|
667 | break;
|
---|
668 | }
|
---|
669 |
|
---|
670 | default:
|
---|
671 | break;
|
---|
672 | }
|
---|
673 | return DefDlgProc(hwnd, message, wParam, lParam);
|
---|
674 | }
|
---|
675 |
|
---|
676 | int WINAPI
|
---|
677 | WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmdLine, int nCmdShow)
|
---|
678 | {
|
---|
679 | CArch arch(instance);
|
---|
680 | CLOG;
|
---|
681 | CArgs args;
|
---|
682 |
|
---|
683 | s_instance = instance;
|
---|
684 |
|
---|
685 | // if "/uninstall" is on the command line then just stop and
|
---|
686 | // uninstall the service and quit. this is the only option
|
---|
687 | // but we ignore any others.
|
---|
688 | if (CString(cmdLine).find("/uninstall") != CString::npos) {
|
---|
689 | CAutoStart::uninstallDaemons(false);
|
---|
690 | CAutoStart::uninstallDaemons(true);
|
---|
691 | return 0;
|
---|
692 | }
|
---|
693 |
|
---|
694 | // register main window (dialog) class
|
---|
695 | WNDCLASSEX classInfo;
|
---|
696 | classInfo.cbSize = sizeof(classInfo);
|
---|
697 | classInfo.style = CS_HREDRAW | CS_VREDRAW;
|
---|
698 | classInfo.lpfnWndProc = &mainWndProc;
|
---|
699 | classInfo.cbClsExtra = 0;
|
---|
700 | classInfo.cbWndExtra = DLGWINDOWEXTRA;
|
---|
701 | classInfo.hInstance = instance;
|
---|
702 | classInfo.hIcon = (HICON)LoadImage(instance,
|
---|
703 | MAKEINTRESOURCE(IDI_SYNERGY),
|
---|
704 | IMAGE_ICON,
|
---|
705 | 32, 32, LR_SHARED);
|
---|
706 | classInfo.hCursor = LoadCursor(NULL, IDC_ARROW);
|
---|
707 | classInfo.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1);
|
---|
708 | classInfo.lpszMenuName = NULL;
|
---|
709 | classInfo.lpszClassName = s_mainClass;
|
---|
710 | classInfo.hIconSm = (HICON)LoadImage(instance,
|
---|
711 | MAKEINTRESOURCE(IDI_SYNERGY),
|
---|
712 | IMAGE_ICON,
|
---|
713 | 16, 16, LR_SHARED);
|
---|
714 | RegisterClassEx(&classInfo);
|
---|
715 |
|
---|
716 | // create main window
|
---|
717 | HWND mainWindow = CreateDialog(s_instance,
|
---|
718 | MAKEINTRESOURCE(IDD_MAIN), 0, NULL);
|
---|
719 |
|
---|
720 | // prep windows
|
---|
721 | initMainWindow(mainWindow);
|
---|
722 | s_globalOptions = new CGlobalOptions(mainWindow, &ARG->m_config);
|
---|
723 | s_advancedOptions = new CAdvancedOptions(mainWindow, &ARG->m_config);
|
---|
724 | s_hotkeyOptions = new CHotkeyOptions(mainWindow, &ARG->m_config);
|
---|
725 | s_screensLinks = new CScreensLinks(mainWindow, &ARG->m_config);
|
---|
726 | s_info = new CInfo(mainWindow);
|
---|
727 |
|
---|
728 | // show window
|
---|
729 | ShowWindow(mainWindow, nCmdShow);
|
---|
730 |
|
---|
731 | // main loop
|
---|
732 | MSG msg;
|
---|
733 | bool done = false;
|
---|
734 | do {
|
---|
735 | switch (GetMessage(&msg, NULL, 0, 0)) {
|
---|
736 | case -1:
|
---|
737 | // error
|
---|
738 | break;
|
---|
739 |
|
---|
740 | case 0:
|
---|
741 | // quit
|
---|
742 | done = true;
|
---|
743 | break;
|
---|
744 |
|
---|
745 | default:
|
---|
746 | if (!IsDialogMessage(mainWindow, &msg)) {
|
---|
747 | TranslateMessage(&msg);
|
---|
748 | DispatchMessage(&msg);
|
---|
749 | }
|
---|
750 | break;
|
---|
751 | }
|
---|
752 | } while (!done);
|
---|
753 |
|
---|
754 | return msg.wParam;
|
---|
755 | }
|
---|