2
* Copyright (c) 2005 Alexander Gottwald
4
* Permission is hereby granted, free of charge, to any person obtaining a
5
* copy of this software and associated documentation files (the "Software"),
6
* to deal in the Software without restriction, including without limitation
7
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
* and/or sell copies of the Software, and to permit persons to whom the
9
* Software is furnished to do so, subject to the following conditions:
11
* The above copyright notice and this permission notice shall be included in
12
* all copies or substantial portions of the Software.
14
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
* THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
* DEALINGS IN THE SOFTWARE.
22
* Except as contained in this notice, the name(s) of the above copyright
23
* holders shall not be used in advertising or otherwise to promote the sale,
24
* use or other dealings in this Software without prior written authorization.
26
#include "window/util.h"
27
#include "window/wizard.h"
28
#include "resources/resources.h"
37
/// @brief Send WM_ENDSESSION to all program windows.
38
/// This will shutdown the started xserver
39
BOOL CALLBACK KillWindowsProc(HWND hwnd, LPARAM lParam)
41
SendMessage(hwnd, WM_ENDSESSION, 0, 0);
45
/// @brief Actual wizard implementation.
46
/// This is based on generic CWizard but handles the special dialogs
47
class CMyWizard : public CWizard
51
CConfig config; /// Storage for config options.
53
/// @brief Constructor.
55
CMyWizard() : CWizard()
57
AddPage(IDD_DISPLAY, IDS_DISPLAY_TITLE, IDS_DISPLAY_SUBTITLE);
58
AddPage(IDD_CLIENTS, IDS_CLIENTS_TITLE, IDS_CLIENTS_SUBTITLE);
59
AddPage(IDD_PROGRAM, IDS_PROGRAM_TITLE, IDS_PROGRAM_SUBTITLE);
60
AddPage(IDD_XDMCP, IDS_XDMCP_TITLE, IDS_XDMCP_SUBTITLE);
61
//AddPage(IDD_FONTPATH, IDS_FONTPATH_TITLE, IDS_FONTPATH_SUBTITLE);
62
AddPage(IDD_CLIPBOARD, IDS_CLIPBOARD_TITLE, IDS_CLIPBOARD_SUBTITLE);
63
AddPage(IDD_FINISH, IDS_FINISH_TITLE, IDS_FINISH_SUBTITLE);
66
virtual void LoadConfig(const char *filename)
69
config.Load(filename);
70
} catch (std::runtime_error &e)
72
printf("Fehler: %s\n", e.what());
76
/// @brief Handle the PSN_WIZNEXT message.
77
/// @param hwndDlg Handle to active page dialog.
78
/// @param index Index of current page.
79
/// @return TRUE if the message was handled. FALSE otherwise.
80
virtual BOOL WizardNext(HWND hwndDlg, unsigned index)
83
printf("%s %d\n", __FUNCTION__, index);
85
switch (PageID(index))
88
// Check for select window mode
89
if (IsDlgButtonChecked(hwndDlg, IDC_MULTIWINDOW))
90
config.window = CConfig::MultiWindow;
91
else if (IsDlgButtonChecked(hwndDlg, IDC_FULLSCREEN))
92
config.window = CConfig::Fullscreen;
93
else if (IsDlgButtonChecked(hwndDlg, IDC_WINDOWED))
94
config.window = CConfig::Windowed;
95
else if (IsDlgButtonChecked(hwndDlg, IDC_NODECORATION))
96
config.window = CConfig::Nodecoration;
99
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
102
// Get selected display number
105
GetDlgItemText(hwndDlg, IDC_DISPLAY, buffer, 512);
107
config.display = buffer;
109
// Check for valid input
110
if (config.display.empty())
111
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
113
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIENTS);
116
// Check for select client startup method
117
if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT))
119
config.client = CConfig::StartProgram;
120
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_PROGRAM);
121
} else if (IsDlgButtonChecked(hwndDlg, IDC_XDMCP))
123
config.client = CConfig::XDMCP;
124
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_XDMCP);
125
} else if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT_NONE))
127
config.client = CConfig::NoClient;
128
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIPBOARD);
130
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
133
// Check wether local or remote client should be started
134
if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT_LOCAL))
136
else if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT_REMOTE))
137
config.local = false;
140
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
143
// Read program, user and host name
146
GetDlgItemText(hwndDlg, IDC_CLIENT_USER, buffer, 512);
148
config.user = buffer;
149
GetDlgItemText(hwndDlg, IDC_CLIENT_HOST, buffer, 512);
151
config.host = buffer;
152
GetDlgItemText(hwndDlg, IDC_CLIENT_PROGRAM, buffer, 512);
154
config.program = buffer;
156
// Check for valid input
157
if (!config.local && (config.host.empty() || config.program.empty()))
158
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
160
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIPBOARD);
163
// Check for broadcast
164
if (IsDlgButtonChecked(hwndDlg, IDC_XDMCP_BROADCAST))
165
config.broadcast = true;
166
else if (IsDlgButtonChecked(hwndDlg, IDC_XDMCP_QUERY))
167
config.broadcast = false;
170
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
173
// Check for indirect mode
174
if (IsDlgButtonChecked(hwndDlg, IDC_XDMCP_INDIRECT))
175
config.indirect = true;
177
config.indirect = false;
181
GetDlgItemText(hwndDlg, IDC_XDMCP_HOST, buffer, 512);
183
config.xdmcp_host = buffer;
185
// Check for valid input
186
if (!config.broadcast && config.xdmcp_host.empty())
187
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
189
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIPBOARD);
192
// check for clipboard
193
if (IsDlgButtonChecked(hwndDlg, IDC_CLIPBOARD))
194
config.clipboard = true;
196
config.clipboard = false;
200
GetDlgItemText(hwndDlg, IDC_EXTRA_PARAMS, buffer, 512);
202
config.extra_params = buffer;
204
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_FINISH);
211
/// @brief Handle PSN_WIZFINISH message.
212
/// @param hwndDlg Handle to active page dialog.
213
/// @param index Index of current page.
214
/// @return TRUE if the message was handled. FALSE otherwise.
215
virtual BOOL WizardFinish(HWND hwndDlg, unsigned index)
218
printf("finish %d\n", index);
222
/// @brief Handle PSN_WIZBACK message.
223
/// Basicly handles switching to proper page (skipping XDMCP or program page
225
/// @param hwndDlg Handle to active page dialog.
226
/// @param index Index of current page.
227
/// @return TRUE if the message was handled. FALSE otherwise.
228
virtual BOOL WizardBack(HWND hwndDlg, unsigned index)
230
switch (PageID(index))
234
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIENTS);
237
case IDD_CLIPBOARD: // temporary. fontpath is disabled
238
switch (config.client)
240
case CConfig::NoClient:
241
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIENTS);
243
case CConfig::StartProgram:
244
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_PROGRAM);
247
SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_XDMCP);
254
/// @brief Handle PSN_SETACTIVE message.
255
/// @param hwndDlg Handle to active page dialog.
256
/// @param index Index of current page.
257
/// @return TRUE if the message was handled. FALSE otherwise.
258
virtual BOOL WizardActivate(HWND hwndDlg, unsigned index)
261
printf("%s %d\n", __FUNCTION__, index);
263
switch (PageID(index))
266
// Enable or disable XDMCP radiobutton and text
267
EnableWindow(GetDlgItem(hwndDlg, IDC_XDMCP), config.window != CConfig::MultiWindow);
268
EnableWindow(GetDlgItem(hwndDlg, IDC_XDMCP_DESC), config.window != CConfig::MultiWindow);
274
/// @brief Enable or disable the control for remote clients.
275
/// @param hwndDlg Handle to active page dialog.
276
/// @param state State of control group.
277
void EnableRemoteProgramGroup(HWND hwndDlg, BOOL state)
279
EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_PROTOCOL), state);
280
EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_HOST), state);
281
EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_USER), state);
282
EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_PROTOCOL_DESC), state);
283
EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_HOST_DESC), state);
284
EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_USER_DESC), state);
286
/// @brief Enable or disable the control for XDMCP connection.
287
/// @param hwndDlg Handle to active page dialog.
288
/// @param state State of control group.
289
void EnableXDMCPQueryGroup(HWND hwndDlg, BOOL state)
291
EnableWindow(GetDlgItem(hwndDlg, IDC_XDMCP_HOST), state);
292
EnableWindow(GetDlgItem(hwndDlg, IDC_XDMCP_INDIRECT), state);
294
/// @brief Fill program box with default values.
295
/// @param hwndDlg Handle to active page dialog.
296
void FillProgramBox(HWND hwndDlg)
298
HWND cbwnd = GetDlgItem(hwndDlg, IDC_CLIENT_PROGRAM);
301
SendMessage(cbwnd, CB_RESETCONTENT, 0, 0);
302
SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "xterm");
303
SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "startkde");
304
SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "gnome-session");
305
SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) ".xinitrc");
306
SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "wmaker");
307
SendMessage(cbwnd, CB_SETCURSEL, 0, 0);
309
/// @brief Fill protocol box with default values.
310
/// @param hwndDlg Handle to active page dialog.
311
void FillProtocolBox(HWND hwndDlg)
313
HWND cbwnd = GetDlgItem(hwndDlg, IDC_CLIENT_PROTOCOL);
316
SendMessage(cbwnd, CB_RESETCONTENT, 0, 0);
317
SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "Putty");
318
//SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "OpenSSH");
319
SendMessage(cbwnd, CB_SETCURSEL, 0, 0);
321
void ShowSaveDialog(HWND parent)
325
char szFileTitle[512];
326
char szFile[MAX_PATH];
327
HINSTANCE hInst = GetModuleHandle(NULL);
329
LoadString(hInst, IDS_SAVE_TITLE, szTitle, sizeof(szTitle));
330
LoadString(hInst, IDS_SAVE_FILETITLE, szFileTitle, sizeof(szFileTitle));
331
LoadString(hInst, IDS_SAVE_FILTER, szFilter, sizeof(szFilter));
332
for (unsigned i=0; szFilter[i]; i++)
333
if (szFilter[i] == '%')
336
strcpy(szFile, "config.xlaunch");
339
memset(&ofn, 0, sizeof(OPENFILENAME));
340
ofn.lStructSize = sizeof(OPENFILENAME);
341
ofn.hwndOwner = parent;
342
ofn.lpstrFilter = szFilter;
343
ofn.lpstrFile= szFile;
344
ofn.nMaxFile = sizeof(szFile)/ sizeof(*szFile);
345
ofn.lpstrFileTitle = szFileTitle;
346
ofn.nMaxFileTitle = sizeof(szFileTitle);
347
ofn.lpstrInitialDir = (LPSTR)NULL;
348
ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
349
ofn.lpstrTitle = szTitle;
351
if (GetSaveFileName(&ofn))
354
config.Save(ofn.lpstrFile);
355
} catch (std::runtime_error &e)
357
printf("Fehler: %s\n", e.what());
363
/// @brief Handle messages fo the dialog pages.
364
/// @param hwndDlg Handle of active dialog.
365
/// @param uMsg Message code.
366
/// @param wParam Message parameter.
367
/// @param lParam Message parameter.
368
/// @param psp Handle to sheet paramters.
369
virtual INT_PTR PageDispatch(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam, PROPSHEETPAGE *psp)
375
switch (PageID(PageIndex(psp)))
378
// Init display dialog. Enable correct check buttons
379
switch (config.window)
382
case CConfig::MultiWindow:
383
CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_MULTIWINDOW);
385
case CConfig::Fullscreen:
386
CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_FULLSCREEN);
388
case CConfig::Windowed:
389
CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_WINDOWED);
391
case CConfig::Nodecoration:
392
CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_NODECORATION);
395
// Set display number
396
SetDlgItemText(hwndDlg, IDC_DISPLAY, config.display.c_str());
399
// Init client dialog. Enable correct check buttons
400
switch (config.client)
403
case CConfig::NoClient:
404
CheckRadioButton(hwndDlg, IDC_CLIENT_NONE, IDC_CLIENT, IDC_CLIENT_NONE);
406
case CConfig::StartProgram:
407
CheckRadioButton(hwndDlg, IDC_CLIENT_NONE, IDC_CLIENT, IDC_CLIENT);
410
CheckRadioButton(hwndDlg, IDC_CLIENT_NONE, IDC_CLIENT, IDC_XDMCP);
415
// Init program dialog. Check local and remote buttons
416
CheckRadioButton(hwndDlg, IDC_CLIENT_LOCAL, IDC_CLIENT_REMOTE, config.local?IDC_CLIENT_LOCAL:IDC_CLIENT_REMOTE);
417
EnableRemoteProgramGroup(hwndDlg, config.local?FALSE:TRUE);
419
FillProgramBox(hwndDlg);
420
FillProtocolBox(hwndDlg);
422
if (!config.program.empty())
423
SetDlgItemText(hwndDlg, IDC_CLIENT_PROGRAM, config.program.c_str());
424
SetDlgItemText(hwndDlg, IDC_CLIENT_USER, config.user.c_str());
425
SetDlgItemText(hwndDlg, IDC_CLIENT_HOST, config.host.c_str());
428
// Init XDMCP dialog. Check broadcast and indirect button
429
CheckRadioButton(hwndDlg, IDC_XDMCP_QUERY, IDC_XDMCP_BROADCAST, config.broadcast?IDC_XDMCP_BROADCAST:IDC_XDMCP_QUERY);
430
CheckDlgButton(hwndDlg, IDC_XDMCP_INDIRECT, config.indirect?BST_CHECKED:BST_UNCHECKED);
431
EnableXDMCPQueryGroup(hwndDlg, config.broadcast?FALSE:TRUE);
433
SetDlgItemText(hwndDlg, IDC_XDMCP_HOST, config.xdmcp_host.c_str());
436
CheckDlgButton(hwndDlg, IDC_CLIPBOARD, config.clipboard?BST_CHECKED:BST_UNCHECKED);
437
SetDlgItemText(hwndDlg, IDC_EXTRA_PARAMS, config.extra_params.c_str());
442
// Handle control messages
443
switch (LOWORD(wParam))
445
// Handle clicks on images. Check proper radiobutton
446
case IDC_MULTIWINDOW_IMG:
447
case IDC_FULLSCREEN_IMG:
448
case IDC_WINDOWED_IMG:
449
case IDC_NODECORATION_IMG:
450
CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, LOWORD(wParam)-4);
451
SetFocus(GetDlgItem(hwndDlg, LOWORD(wParam)-4));
453
// Disable unavailable controls
454
case IDC_CLIENT_REMOTE:
455
case IDC_CLIENT_LOCAL:
456
EnableRemoteProgramGroup(hwndDlg, LOWORD(wParam) == IDC_CLIENT_REMOTE);
458
case IDC_XDMCP_QUERY:
459
case IDC_XDMCP_BROADCAST:
460
EnableXDMCPQueryGroup(hwndDlg, LOWORD(wParam) == IDC_XDMCP_QUERY);
462
case IDC_FINISH_SAVE:
463
ShowSaveDialog(hwndDlg);
467
// pass messages to parent
468
return CWizard::PageDispatch(hwndDlg, uMsg, wParam, lParam, psp);
471
/// @brief Try to connect to server.
472
/// Repeat until successful, server died or maximum number of retries
474
Display *WaitForServer(HANDLE serverProcess)
476
int ncycles = 120; /* # of cycles to wait */
477
int cycles; /* Wait cycle count */
480
for (cycles = 0; cycles < ncycles; cycles++) {
481
if ((xd = XOpenDisplay(NULL))) {
485
if (WaitForSingleObject(serverProcess, 1000) == WAIT_TIMEOUT)
492
/// @brief Do the actual start of Xming and clients
498
// Construct display strings
499
std::string display_id = ":" + config.display;
500
std::string display = "localhost" + display_id + ":0";
503
// Debug only: Switch to Xming installation directory
504
SetCurrentDirectory("C:\\Programme\\Xming");
507
// Build Xming commandline
508
buffer = "Xming " + display_id + " ";
509
switch (config.window)
511
case CConfig::MultiWindow:
512
buffer += "-multiwindow ";
514
case CConfig::Fullscreen:
515
buffer += "-fullscreen ";
517
case CConfig::Nodecoration:
518
buffer += "-nodecoration ";
523
// Add XDMCP parameter
524
if (config.client == CConfig::XDMCP)
526
if (config.broadcast)
527
buffer += "-broadcast ";
531
buffer += "-indirect ";
534
buffer += config.xdmcp_host;
538
if (config.clipboard)
539
buffer += "-clipboard ";
540
if (!config.extra_params.empty())
542
buffer += config.extra_params;
546
// Construct client commandline
547
if (config.client == CConfig::StartProgram)
552
std::string host = config.host;
553
if (!config.user.empty())
554
host = config.user + "@" + config.host;
555
if (config.protocol == "Putty")
556
snprintf(cmdline,512,"plink -X %s %s",
557
host.c_str(),config.program.c_str());
559
snprintf(cmdline,512,"ssh -Y %s %s",
560
host.c_str(),config.program.c_str());
563
client += config.program.c_str();
566
// Prepare program startup
568
PROCESS_INFORMATION pi, pic;
573
ZeroMemory( &si, sizeof(si) );
575
ZeroMemory( &pi, sizeof(pi) );
576
ZeroMemory( &sic, sizeof(sic) );
577
sic.cb = sizeof(sic);
578
ZeroMemory( &pic, sizeof(pic) );
580
// Start Xming process.
582
printf("%s\n", buffer.c_str());
584
if( !CreateProcess( NULL, (CHAR*)buffer.c_str(), NULL, NULL,
585
FALSE, 0, NULL, NULL, &si, &pi ))
586
throw win32_error("CreateProcess failed");
587
handles[hcount++] = pi.hProcess;
591
// Set DISPLAY variable
592
SetEnvironmentVariable("DISPLAY",display.c_str());
594
// Wait for server to startup
595
dpy = WaitForServer(pi.hProcess);
599
TerminateProcess(handles[hcount], (DWORD)-1);
600
throw std::runtime_error("Connection to server failed");
604
printf("%s\n", client.c_str());
607
// Hide a console window
608
// FIXME: This may make it impossible to enter the password
609
sic.dwFlags = STARTF_USESHOWWINDOW;
610
sic.wShowWindow = SW_HIDE;
612
// Start the child process.
613
if( !CreateProcess( NULL, (CHAR*)client.c_str(), NULL, NULL,
614
FALSE, 0, NULL, NULL, &sic, &pic ))
616
DWORD err = GetLastError();
618
TerminateProcess(handles[hcount], (DWORD)-1);
619
throw win32_error("CreateProcess failed", err);
621
handles[hcount++] = pic.hProcess;
624
// Wait until any child process exits.
625
DWORD ret = WaitForMultipleObjects(hcount, handles, FALSE, INFINITE );
628
printf("killing process!\n");
630
// Check if Xming is still running
632
GetExitCodeProcess(pi.hProcess, &exitcode);
633
unsigned counter = 0;
634
while (exitcode == STILL_ACTIVE)
637
TerminateProcess(pi.hProcess, (DWORD)-1);
639
// Shutdown Xming (the soft way!)
640
EnumThreadWindows(pi.dwThreadId, KillWindowsProc, 0);
642
GetExitCodeProcess(pi.hProcess, &exitcode);
645
TerminateProcess(pic.hProcess, (DWORD)-1);
647
// Close process and thread handles.
648
CloseHandle( pi.hProcess );
649
CloseHandle( pi.hThread );
650
CloseHandle( pic.hProcess );
651
CloseHandle( pic.hThread );
655
int main(int argc, char **argv)
658
InitCommonControls();
661
bool skip_wizard = false;
663
for (int i = 1; i < argc; i++)
668
std::string arg(argv[i]);
669
if (arg == "-load" && i + 1 < argc)
672
dialog.LoadConfig(argv[i]);
675
if (arg == "-run" && i + 1 < argc)
678
dialog.LoadConfig(argv[i]);
685
if (skip_wizard || (ret =dialog.ShowModal()) != 0)
688
printf("return %d\n", ret);
691
} catch (std::runtime_error &e)
693
printf("Fehler: %s\n", e.what());