1/*
2 * Copyright (c) 2005 Alexander Gottwald
3 *
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:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
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.
21 *
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.
25 */
26#include "window/util.h"
27#include "window/wizard.h"
28#include "resources/resources.h"
29#include "config.h"
30#include <prsht.h>
31#include <commctrl.h>
32
33#include <stdexcept>
34
35#include <X11/Xlib.h>
36
37/// @brief Send WM_ENDSESSION to all program windows.
38/// This will shutdown the started xserver
39BOOL CALLBACK KillWindowsProc(HWND hwnd, LPARAM lParam)
40{
41    SendMessage(hwnd, WM_ENDSESSION, 0, 0);
42    return TRUE;
43}
44
45/// @brief Actual wizard implementation.
46/// This is based on generic CWizard but handles the special dialogs
47class CMyWizard : public CWizard
48{
49    public:
50    private:
51	CConfig config; /// Storage for config options.
52    public:
53        /// @brief Constructor.
54        /// Set wizard pages.
55        CMyWizard() : CWizard()
56        {
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);
64        }
65
66	virtual void LoadConfig(const char *filename)
67	{
68	    try {
69		config.Load(filename);
70	    } catch (std::runtime_error &e)
71	    {
72		printf("Fehler: %s\n", e.what());
73	    }
74	}
75
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)
81	{
82#ifdef _DEBUG
83	    printf("%s %d\n", __FUNCTION__, index);
84#endif
85	    switch (PageID(index))
86	    {
87		case IDD_DISPLAY:
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;
97		    else
98		    {
99			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
100			return TRUE;
101		    }
102                    // Get selected display number
103		    {
104			char buffer[512];
105			GetDlgItemText(hwndDlg, IDC_DISPLAY, buffer, 512);
106			buffer[511] = 0;
107			config.display = buffer;
108                    }
109                    // Check for valid input
110                    if (config.display.empty())
111			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
112                    else
113                        SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIENTS);
114		    return TRUE;
115		case IDD_CLIENTS:
116                    // Check for select client startup method
117		    if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT))
118		    {
119			config.client = CConfig::StartProgram;
120			SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_PROGRAM);
121		    } else if (IsDlgButtonChecked(hwndDlg, IDC_XDMCP))
122		    {
123			config.client = CConfig::XDMCP;
124			SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_XDMCP);
125		    } else if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT_NONE))
126		    {
127			config.client = CConfig::NoClient;
128			SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIPBOARD);
129		    } else
130			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
131		    return TRUE;
132		case IDD_PROGRAM:
133                    // Check wether local or remote client should be started
134		    if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT_LOCAL))
135			config.local = true;
136		    else if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT_REMOTE))
137			config.local = false;
138		    else
139		    {
140			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
141			return TRUE;
142		    }
143                    // Read program, user and host name
144		    {
145			char buffer[512];
146			GetDlgItemText(hwndDlg, IDC_CLIENT_USER, buffer, 512);
147			buffer[511] = 0;
148			config.user = buffer;
149			GetDlgItemText(hwndDlg, IDC_CLIENT_HOST, buffer, 512);
150			buffer[511] = 0;
151			config.host = buffer;
152			GetDlgItemText(hwndDlg, IDC_CLIENT_PROGRAM, buffer, 512);
153			buffer[511] = 0;
154			config.program = buffer;
155		    }
156                    // Check for valid input
157		    if (!config.local && (config.host.empty() || config.program.empty()))
158			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
159		    else
160			SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIPBOARD);
161		    return TRUE;
162		case IDD_XDMCP:
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;
168		    else
169		    {
170			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
171			return TRUE;
172		    }
173                    // Check for indirect mode
174		    if (IsDlgButtonChecked(hwndDlg, IDC_XDMCP_INDIRECT))
175			config.indirect = true;
176		    else
177			config.indirect = false;
178                    // Read hostname
179		    {
180			char buffer[512];
181			GetDlgItemText(hwndDlg, IDC_XDMCP_HOST, buffer, 512);
182			buffer[511] = 0;
183			config.xdmcp_host = buffer;
184		    }
185                    // Check for valid input
186		    if (!config.broadcast && config.xdmcp_host.empty())
187			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
188		    else
189			SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIPBOARD);
190		    return TRUE;
191                case IDD_CLIPBOARD:
192                    // check for clipboard
193                    if (IsDlgButtonChecked(hwndDlg, IDC_CLIPBOARD))
194                        config.clipboard = true;
195                    else
196                        config.clipboard = false;
197                    // read parameters
198		    {
199			char buffer[512];
200			GetDlgItemText(hwndDlg, IDC_EXTRA_PARAMS, buffer, 512);
201			buffer[511] = 0;
202			config.extra_params = buffer;
203		    }
204                    SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_FINISH);
205                    return TRUE;
206		default:
207		    break;
208	    }
209	    return FALSE;
210	}
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)
216	{
217#ifdef _DEBUG
218	    printf("finish %d\n", index);
219#endif
220	    return FALSE;
221	}
222        /// @brief Handle PSN_WIZBACK message.
223        /// Basicly handles switching to proper page (skipping XDMCP or program page
224        /// if required).
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)
229	{
230	    switch (PageID(index))
231	    {
232		case IDD_PROGRAM:
233		case IDD_XDMCP:
234		    SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIENTS);
235		    return TRUE;
236		case IDD_FONTPATH:
237                case IDD_CLIPBOARD: // temporary. fontpath is disabled
238		    switch (config.client)
239		    {
240			case CConfig::NoClient:
241			    SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIENTS);
242			    return TRUE;
243			case CConfig::StartProgram:
244			    SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_PROGRAM);
245			    return TRUE;
246			case CConfig::XDMCP:
247			    SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_XDMCP);
248			    return TRUE;
249		    }
250		    break;
251	    }
252	    return FALSE;
253	}
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)
259	{
260#ifdef _DEBUG
261	    printf("%s %d\n", __FUNCTION__, index);
262#endif
263	    switch (PageID(index))
264	    {
265		case IDD_CLIENTS:
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);
269		    break;
270	    }
271	    return FALSE;
272	}
273    protected:
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)
278	{
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);
285	}
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)
290	{
291	    EnableWindow(GetDlgItem(hwndDlg, IDC_XDMCP_HOST), state);
292	    EnableWindow(GetDlgItem(hwndDlg, IDC_XDMCP_INDIRECT), state);
293	}
294        /// @brief Fill program box with default values.
295        /// @param hwndDlg Handle to active page dialog.
296	void FillProgramBox(HWND hwndDlg)
297	{
298	    HWND cbwnd = GetDlgItem(hwndDlg, IDC_CLIENT_PROGRAM);
299	    if (cbwnd == NULL)
300		return;
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);
308	}
309        /// @brief Fill protocol box with default values.
310        /// @param hwndDlg Handle to active page dialog.
311	void FillProtocolBox(HWND hwndDlg)
312	{
313	    HWND cbwnd = GetDlgItem(hwndDlg, IDC_CLIENT_PROTOCOL);
314	    if (cbwnd == NULL)
315		return;
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);
320	}
321	void ShowSaveDialog(HWND parent)
322	{
323	    char szTitle[512];
324	    char szFilter[512];
325	    char szFileTitle[512];
326	    char szFile[MAX_PATH];
327	    HINSTANCE hInst = GetModuleHandle(NULL);
328
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] == '%')
334		    szFilter[i] = '\0';
335
336	    strcpy(szFile, "config.xlaunch");
337
338	    OPENFILENAME ofn;
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;
350
351	    if (GetSaveFileName(&ofn))
352	    {
353		try {
354      		    config.Save(ofn.lpstrFile);
355		} catch (std::runtime_error &e)
356		{
357		    printf("Fehler: %s\n", e.what());
358		}
359	    }
360	}
361    public:
362
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)
370        {
371            HWND hwnd;
372            switch (uMsg)
373            {
374                case WM_INITDIALOG:
375                    switch (PageID(PageIndex(psp)))
376                    {
377                        case IDD_DISPLAY:
378                            // Init display dialog. Enable correct check buttons
379			    switch (config.window)
380			    {
381				default:
382				case CConfig::MultiWindow:
383				    CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_MULTIWINDOW);
384				    break;
385				case  CConfig::Fullscreen:
386				    CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_FULLSCREEN);
387				    break;
388				case  CConfig::Windowed:
389				    CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_WINDOWED);
390				    break;
391				case  CConfig::Nodecoration:
392				    CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_NODECORATION);
393				    break;
394			    }
395                            // Set display number
396                            SetDlgItemText(hwndDlg, IDC_DISPLAY, config.display.c_str());
397                            break;
398                        case IDD_CLIENTS:
399                            // Init client dialog. Enable correct check buttons
400			    switch (config.client)
401			    {
402				default:
403				case CConfig::NoClient:
404				    CheckRadioButton(hwndDlg, IDC_CLIENT_NONE, IDC_CLIENT, IDC_CLIENT_NONE);
405				    break;
406				case CConfig::StartProgram:
407				    CheckRadioButton(hwndDlg, IDC_CLIENT_NONE, IDC_CLIENT, IDC_CLIENT);
408				    break;
409				case CConfig::XDMCP:
410				    CheckRadioButton(hwndDlg, IDC_CLIENT_NONE, IDC_CLIENT, IDC_XDMCP);
411				    break;
412			    }
413                            break;
414			case IDD_PROGRAM:
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);
418                            // Fill combo boxes
419			    FillProgramBox(hwndDlg);
420			    FillProtocolBox(hwndDlg);
421                            // Set edit fields
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());
426			    break;
427			case IDD_XDMCP:
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);
432                            // Set hostname
433			    SetDlgItemText(hwndDlg, IDC_XDMCP_HOST, config.xdmcp_host.c_str());
434			    break;
435                        case IDD_CLIPBOARD:
436                            CheckDlgButton(hwndDlg, IDC_CLIPBOARD, config.clipboard?BST_CHECKED:BST_UNCHECKED);
437                            SetDlgItemText(hwndDlg, IDC_EXTRA_PARAMS, config.extra_params.c_str());
438                            break;
439
440                    }
441                case WM_COMMAND:
442                    // Handle control messages
443                    switch (LOWORD(wParam))
444                    {
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));
452                            break;
453                        // Disable unavailable controls
454                        case IDC_CLIENT_REMOTE:
455                        case IDC_CLIENT_LOCAL:
456			    EnableRemoteProgramGroup(hwndDlg, LOWORD(wParam) == IDC_CLIENT_REMOTE);
457                            break;
458			case IDC_XDMCP_QUERY:
459			case IDC_XDMCP_BROADCAST:
460			    EnableXDMCPQueryGroup(hwndDlg, LOWORD(wParam) == IDC_XDMCP_QUERY);
461			    break;
462			case IDC_FINISH_SAVE:
463			    ShowSaveDialog(hwndDlg);
464			    break;
465                    }
466            }
467            // pass messages to parent
468            return CWizard::PageDispatch(hwndDlg, uMsg, wParam, lParam, psp);
469        }
470
471        /// @brief Try to connect to server.
472        /// Repeat until successful, server died or maximum number of retries
473        /// reached.
474        Display *WaitForServer(HANDLE serverProcess)
475        {
476            int     ncycles  = 120;         /* # of cycles to wait */
477            int     cycles;                 /* Wait cycle count */
478            Display *xd;
479
480            for (cycles = 0; cycles < ncycles; cycles++) {
481                if ((xd = XOpenDisplay(NULL))) {
482                    return xd;
483                }
484                else {
485                    if (WaitForSingleObject(serverProcess, 1000) == WAIT_TIMEOUT)
486                        continue;
487                }
488            }
489            return NULL;
490        }
491
492        /// @brief Do the actual start of Xming and clients
493	void StartUp()
494	{
495	    std::string buffer;
496	    std::string client;
497
498            // Construct display strings
499	    std::string display_id = ":" + config.display;
500	    std::string display = "localhost" + display_id + ":0";
501
502#ifdef _DEBUG
503            // Debug only: Switch to Xming installation directory
504	    SetCurrentDirectory("C:\\Programme\\Xming");
505#endif
506
507            // Build Xming commandline
508	    buffer = "Xming " + display_id + " ";
509	    switch (config.window)
510	    {
511		case CConfig::MultiWindow:
512		    buffer += "-multiwindow ";
513		    break;
514		case CConfig::Fullscreen:
515		    buffer += "-fullscreen ";
516		    break;
517		case CConfig::Nodecoration:
518		    buffer += "-nodecoration ";
519		    break;
520		default:
521		    break;
522	    }
523            // Add XDMCP parameter
524	    if (config.client == CConfig::XDMCP)
525	    {
526		if (config.broadcast)
527		    buffer += "-broadcast ";
528		else
529		{
530		    if (config.indirect)
531			buffer += "-indirect ";
532		    else
533			buffer += "-query ";
534		    buffer += config.xdmcp_host;
535            buffer += " ";
536		}
537	    }
538            if (config.clipboard)
539                buffer += "-clipboard ";
540            if (!config.extra_params.empty())
541            {
542                buffer += config.extra_params;
543                buffer += " ";
544            }
545
546            // Construct client commandline
547	    if (config.client == CConfig::StartProgram)
548	    {
549		if (!config.local)
550		{
551		    char cmdline[512];
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());
558		    else
559			snprintf(cmdline,512,"ssh -Y %s %s",
560                                host.c_str(),config.program.c_str());
561		    client += cmdline;
562		} else
563		    client += config.program.c_str();
564	    }
565
566            // Prepare program startup
567     	    STARTUPINFO si, sic;
568	    PROCESS_INFORMATION pi, pic;
569	    HANDLE handles[2];
570	    DWORD hcount = 0;
571            Display *dpy = NULL;
572
573	    ZeroMemory( &si, sizeof(si) );
574	    si.cb = sizeof(si);
575	    ZeroMemory( &pi, sizeof(pi) );
576	    ZeroMemory( &sic, sizeof(sic) );
577	    sic.cb = sizeof(sic);
578	    ZeroMemory( &pic, sizeof(pic) );
579
580	    // Start Xming process.
581#ifdef _DEBUG
582	    printf("%s\n", buffer.c_str());
583#endif
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;
588
589	    if (!client.empty())
590	    {
591                // Set DISPLAY variable
592		SetEnvironmentVariable("DISPLAY",display.c_str());
593
594                // Wait for server to startup
595                dpy = WaitForServer(pi.hProcess);
596                if (dpy == NULL)
597                {
598                    while (hcount--)
599                        TerminateProcess(handles[hcount], (DWORD)-1);
600		    throw std::runtime_error("Connection to server failed");
601                }
602
603#ifdef _DEBUG
604		printf("%s\n", client.c_str());
605#endif
606
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;
611
612		// Start the child process.
613		if( !CreateProcess( NULL, (CHAR*)client.c_str(), NULL, NULL,
614                            FALSE, 0, NULL, NULL, &sic, &pic ))
615		{
616                    DWORD err = GetLastError();
617                    while (hcount--)
618                        TerminateProcess(handles[hcount], (DWORD)-1);
619		    throw win32_error("CreateProcess failed", err);
620		}
621		handles[hcount++] = pic.hProcess;
622	    }
623
624	    // Wait until any child process exits.
625	    DWORD ret = WaitForMultipleObjects(hcount, handles, FALSE, INFINITE );
626
627#ifdef _DEBUG
628	    printf("killing process!\n");
629#endif
630            // Check if Xming is still running
631	    DWORD exitcode;
632	    GetExitCodeProcess(pi.hProcess, &exitcode);
633	    unsigned counter = 0;
634	    while (exitcode == STILL_ACTIVE)
635	    {
636		if (++counter > 10)
637		    TerminateProcess(pi.hProcess, (DWORD)-1);
638		else
639		    // Shutdown Xming (the soft way!)
640		    EnumThreadWindows(pi.dwThreadId, KillWindowsProc, 0);
641		Sleep(500);
642		GetExitCodeProcess(pi.hProcess, &exitcode);
643	    }
644	    // Kill the client
645    	    TerminateProcess(pic.hProcess, (DWORD)-1);
646
647	    // Close process and thread handles.
648	    CloseHandle( pi.hProcess );
649	    CloseHandle( pi.hThread );
650	    CloseHandle( pic.hProcess );
651	    CloseHandle( pic.hThread );
652	}
653};
654
655int main(int argc, char **argv)
656{
657    try {
658        InitCommonControls();
659        CMyWizard dialog;
660
661	bool skip_wizard = false;
662
663	for (int i = 1; i < argc; i++)
664	{
665	    if (argv[i] == NULL)
666		continue;
667
668	    std::string arg(argv[i]);
669	    if (arg == "-load" && i + 1 < argc)
670	    {
671		i++;
672		dialog.LoadConfig(argv[i]);
673		continue;
674	    }
675	    if (arg == "-run" && i + 1 < argc)
676	    {
677		i++;
678		dialog.LoadConfig(argv[i]);
679		skip_wizard = true;
680		continue;
681	    }
682	}
683
684	int ret = 0;
685        if (skip_wizard || (ret =dialog.ShowModal()) != 0)
686	    dialog.StartUp();
687#ifdef _DEBUG
688	printf("return %d\n", ret);
689#endif
690	return 0;
691    } catch (std::runtime_error &e)
692    {
693        printf("Fehler: %s\n", e.what());
694        return -1;
695    }
696}
697
698
699
700
701