main.cc   [plain text]


/*
 * Copyright (c) 2005 Alexander Gottwald
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name(s) of the above copyright
 * holders shall not be used in advertising or otherwise to promote the sale,
 * use or other dealings in this Software without prior written authorization.
 */
#include "window/util.h"
#include "window/wizard.h"
#include "resources/resources.h"
#include "config.h"
#include <prsht.h>
#include <commctrl.h>

#include <stdexcept>

#include <X11/Xlib.h>

/// @brief Send WM_ENDSESSION to all program windows.
/// This will shutdown the started xserver
BOOL CALLBACK KillWindowsProc(HWND hwnd, LPARAM lParam)
{
    SendMessage(hwnd, WM_ENDSESSION, 0, 0);
    return TRUE;
}

/// @brief Actual wizard implementation.
/// This is based on generic CWizard but handles the special dialogs
class CMyWizard : public CWizard 
{
    public:
    private:
	CConfig config; /// Storage for config options.
    public:
        /// @brief Constructor.
        /// Set wizard pages.
        CMyWizard() : CWizard() 
        {
            AddPage(IDD_DISPLAY, IDS_DISPLAY_TITLE, IDS_DISPLAY_SUBTITLE);
            AddPage(IDD_CLIENTS, IDS_CLIENTS_TITLE, IDS_CLIENTS_SUBTITLE);
            AddPage(IDD_PROGRAM, IDS_PROGRAM_TITLE, IDS_PROGRAM_SUBTITLE);
            AddPage(IDD_XDMCP, IDS_XDMCP_TITLE, IDS_XDMCP_SUBTITLE);
            //AddPage(IDD_FONTPATH, IDS_FONTPATH_TITLE, IDS_FONTPATH_SUBTITLE);
            AddPage(IDD_CLIPBOARD, IDS_CLIPBOARD_TITLE, IDS_CLIPBOARD_SUBTITLE);
            AddPage(IDD_FINISH, IDS_FINISH_TITLE, IDS_FINISH_SUBTITLE);
        }

	virtual void LoadConfig(const char *filename)
	{
	    try {
		config.Load(filename);
	    } catch (std::runtime_error &e)
	    {
		printf("Fehler: %s\n", e.what());
	    }
	}

        /// @brief Handle the PSN_WIZNEXT message.
        /// @param hwndDlg Handle to active page dialog.
        /// @param index Index of current page.
        /// @return TRUE if the message was handled. FALSE otherwise. 
	virtual BOOL WizardNext(HWND hwndDlg, unsigned index)
	{
#ifdef _DEBUG
	    printf("%s %d\n", __FUNCTION__, index);
#endif
	    switch (PageID(index))
	    {
		case IDD_DISPLAY:
                    // Check for select window mode
		    if (IsDlgButtonChecked(hwndDlg, IDC_MULTIWINDOW))
			config.window = CConfig::MultiWindow;
		    else if (IsDlgButtonChecked(hwndDlg, IDC_FULLSCREEN))
			config.window = CConfig::Fullscreen;
		    else if (IsDlgButtonChecked(hwndDlg, IDC_WINDOWED))
			config.window = CConfig::Windowed;
		    else if (IsDlgButtonChecked(hwndDlg, IDC_NODECORATION))
			config.window = CConfig::Nodecoration;
		    else
		    {
			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
			return TRUE;
		    }
                    // Get selected display number
		    {
			char buffer[512];
			GetDlgItemText(hwndDlg, IDC_DISPLAY, buffer, 512);
			buffer[511] = 0;
			config.display = buffer;
                    }
                    // Check for valid input
                    if (config.display.empty())
			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
                    else
                        SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIENTS);
		    return TRUE;
		case IDD_CLIENTS:
                    // Check for select client startup method
		    if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT))
		    {
			config.client = CConfig::StartProgram;
			SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_PROGRAM);
		    } else if (IsDlgButtonChecked(hwndDlg, IDC_XDMCP))
		    {
			config.client = CConfig::XDMCP;
			SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_XDMCP);
		    } else if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT_NONE))
		    {
			config.client = CConfig::NoClient;
			SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIPBOARD);
		    } else
			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
		    return TRUE;
		case IDD_PROGRAM:
                    // Check wether local or remote client should be started
		    if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT_LOCAL))
			config.local = true;
		    else if (IsDlgButtonChecked(hwndDlg, IDC_CLIENT_REMOTE))
			config.local = false;
		    else
		    {
			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
			return TRUE;
		    }
                    // Read program, user and host name
		    {
			char buffer[512];
			GetDlgItemText(hwndDlg, IDC_CLIENT_USER, buffer, 512);
			buffer[511] = 0;
			config.user = buffer;
			GetDlgItemText(hwndDlg, IDC_CLIENT_HOST, buffer, 512);
			buffer[511] = 0;
			config.host = buffer;
			GetDlgItemText(hwndDlg, IDC_CLIENT_PROGRAM, buffer, 512);
			buffer[511] = 0;
			config.program = buffer;
		    }
                    // Check for valid input
		    if (!config.local && (config.host.empty() || config.program.empty()))
			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
		    else
			SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIPBOARD);
		    return TRUE;
		case IDD_XDMCP:
                    // Check for broadcast
		    if (IsDlgButtonChecked(hwndDlg, IDC_XDMCP_BROADCAST))
			config.broadcast = true;
		    else if (IsDlgButtonChecked(hwndDlg, IDC_XDMCP_QUERY))
			config.broadcast = false;
		    else
		    {
			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
			return TRUE;
		    }
                    // Check for indirect mode
		    if (IsDlgButtonChecked(hwndDlg, IDC_XDMCP_INDIRECT))
			config.indirect = true;
		    else
			config.indirect = false;
                    // Read hostname
		    {
			char buffer[512];
			GetDlgItemText(hwndDlg, IDC_XDMCP_HOST, buffer, 512);
			buffer[511] = 0;
			config.xdmcp_host = buffer;
		    }
                    // Check for valid input
		    if (!config.broadcast && config.xdmcp_host.empty())
			SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
		    else	
			SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIPBOARD);
		    return TRUE;
                case IDD_CLIPBOARD:
                    // check for clipboard
                    if (IsDlgButtonChecked(hwndDlg, IDC_CLIPBOARD))
                        config.clipboard = true;
                    else
                        config.clipboard = false;
                    // read parameters
		    {
			char buffer[512];
			GetDlgItemText(hwndDlg, IDC_EXTRA_PARAMS, buffer, 512);
			buffer[511] = 0;
			config.extra_params = buffer;
		    }
                    SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_FINISH);
                    return TRUE;
		default:
		    break;
	    }
	    return FALSE;
	}
        /// @brief Handle PSN_WIZFINISH message.
        /// @param hwndDlg Handle to active page dialog.
        /// @param index Index of current page.
        /// @return TRUE if the message was handled. FALSE otherwise. 
	virtual BOOL WizardFinish(HWND hwndDlg, unsigned index)
	{
#ifdef _DEBUG
	    printf("finish %d\n", index);
#endif
	    return FALSE;
	}
        /// @brief Handle PSN_WIZBACK message.
        /// Basicly handles switching to proper page (skipping XDMCP or program page
        /// if required).
        /// @param hwndDlg Handle to active page dialog.
        /// @param index Index of current page.
        /// @return TRUE if the message was handled. FALSE otherwise. 
	virtual BOOL WizardBack(HWND hwndDlg, unsigned index)
	{
	    switch (PageID(index))
	    {
		case IDD_PROGRAM:
		case IDD_XDMCP:
		    SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIENTS);
		    return TRUE;
		case IDD_FONTPATH:
                case IDD_CLIPBOARD: // temporary. fontpath is disabled
		    switch (config.client)
		    {
			case CConfig::NoClient:	
			    SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_CLIENTS);
			    return TRUE;
			case CConfig::StartProgram:
			    SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_PROGRAM);
			    return TRUE;
			case CConfig::XDMCP:
			    SetWindowLong(hwndDlg, DWL_MSGRESULT, IDD_XDMCP);
			    return TRUE;
		    }
		    break;
	    }
	    return FALSE;
	}
        /// @brief Handle PSN_SETACTIVE message.
        /// @param hwndDlg Handle to active page dialog.
        /// @param index Index of current page.
        /// @return TRUE if the message was handled. FALSE otherwise. 
	virtual BOOL WizardActivate(HWND hwndDlg, unsigned index)
	{
#ifdef _DEBUG
	    printf("%s %d\n", __FUNCTION__, index);
#endif
	    switch (PageID(index))
	    {
		case IDD_CLIENTS:
                    // Enable or disable XDMCP radiobutton and text
		    EnableWindow(GetDlgItem(hwndDlg, IDC_XDMCP), config.window != CConfig::MultiWindow);
		    EnableWindow(GetDlgItem(hwndDlg, IDC_XDMCP_DESC), config.window != CConfig::MultiWindow);
		    break;
	    }
	    return FALSE;
	}
    protected:
        /// @brief Enable or disable the control for remote clients.
        /// @param hwndDlg Handle to active page dialog.
        /// @param state State of control group.
	void EnableRemoteProgramGroup(HWND hwndDlg, BOOL state)
	{
	    EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_PROTOCOL), state);
	    EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_HOST), state);
	    EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_USER), state);
	    EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_PROTOCOL_DESC), state);
	    EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_HOST_DESC), state);
	    EnableWindow(GetDlgItem(hwndDlg, IDC_CLIENT_USER_DESC), state);
	}
        /// @brief Enable or disable the control for XDMCP connection.
        /// @param hwndDlg Handle to active page dialog.
        /// @param state State of control group.
	void EnableXDMCPQueryGroup(HWND hwndDlg, BOOL state)
	{
	    EnableWindow(GetDlgItem(hwndDlg, IDC_XDMCP_HOST), state);
	    EnableWindow(GetDlgItem(hwndDlg, IDC_XDMCP_INDIRECT), state);
	}
        /// @brief Fill program box with default values.
        /// @param hwndDlg Handle to active page dialog.
	void FillProgramBox(HWND hwndDlg)
	{
	    HWND cbwnd = GetDlgItem(hwndDlg, IDC_CLIENT_PROGRAM);
	    if (cbwnd == NULL)
		return;
	    SendMessage(cbwnd, CB_RESETCONTENT, 0, 0);
	    SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "xterm");
	    SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "startkde");
	    SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "gnome-session");
	    SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) ".xinitrc");
	    SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "wmaker");
	    SendMessage(cbwnd, CB_SETCURSEL, 0, 0);
	}
        /// @brief Fill protocol box with default values.
        /// @param hwndDlg Handle to active page dialog.
	void FillProtocolBox(HWND hwndDlg)
	{
	    HWND cbwnd = GetDlgItem(hwndDlg, IDC_CLIENT_PROTOCOL);
	    if (cbwnd == NULL)
		return;
	    SendMessage(cbwnd, CB_RESETCONTENT, 0, 0);
	    SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "Putty");
	    //SendMessage(cbwnd, CB_ADDSTRING, 0, (LPARAM) "OpenSSH");
	    SendMessage(cbwnd, CB_SETCURSEL, 0, 0);
	}
	void ShowSaveDialog(HWND parent)
	{
	    char szTitle[512];
	    char szFilter[512];
	    char szFileTitle[512];
	    char szFile[MAX_PATH];
	    HINSTANCE hInst = GetModuleHandle(NULL);
	    
	    LoadString(hInst, IDS_SAVE_TITLE, szTitle, sizeof(szTitle));
	    LoadString(hInst, IDS_SAVE_FILETITLE, szFileTitle, sizeof(szFileTitle));
	    LoadString(hInst, IDS_SAVE_FILTER, szFilter, sizeof(szFilter));
	    for (unsigned i=0; szFilter[i]; i++) 
		if (szFilter[i] == '%') 
		    szFilter[i] = '\0'; 

	    strcpy(szFile, "config.xlaunch");

	    OPENFILENAME ofn;
	    memset(&ofn, 0, sizeof(OPENFILENAME));
	    ofn.lStructSize = sizeof(OPENFILENAME); 
	    ofn.hwndOwner = parent; 
	    ofn.lpstrFilter = szFilter; 
	    ofn.lpstrFile= szFile; 
	    ofn.nMaxFile = sizeof(szFile)/ sizeof(*szFile); 
	    ofn.lpstrFileTitle = szFileTitle; 
	    ofn.nMaxFileTitle = sizeof(szFileTitle); 
	    ofn.lpstrInitialDir = (LPSTR)NULL; 
	    ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT; 
	    ofn.lpstrTitle = szTitle;

	    if (GetSaveFileName(&ofn))
	    {
		try {
      		    config.Save(ofn.lpstrFile);
		} catch (std::runtime_error &e)
		{
		    printf("Fehler: %s\n", e.what());
		}
	    } 
	}
    public:
	   
        /// @brief Handle messages fo the dialog pages.
        /// @param hwndDlg Handle of active dialog.
        /// @param uMsg Message code.
        /// @param wParam Message parameter.
        /// @param lParam Message parameter.
        /// @param psp Handle to sheet paramters. 
        virtual INT_PTR PageDispatch(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam, PROPSHEETPAGE *psp)
        {
            HWND hwnd;
            switch (uMsg)
            {
                case WM_INITDIALOG:
                    switch (PageID(PageIndex(psp)))
                    {
                        case IDD_DISPLAY:
                            // Init display dialog. Enable correct check buttons
			    switch (config.window)
			    {
				default:
				case CConfig::MultiWindow:
				    CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_MULTIWINDOW);
				    break;
				case  CConfig::Fullscreen:
				    CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_FULLSCREEN);
				    break;
				case  CConfig::Windowed:
				    CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_WINDOWED);
				    break;
				case  CConfig::Nodecoration:
				    CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, IDC_NODECORATION);
				    break;
			    }
                            // Set display number
                            SetDlgItemText(hwndDlg, IDC_DISPLAY, config.display.c_str());
                            break;
                        case IDD_CLIENTS:
                            // Init client dialog. Enable correct check buttons
			    switch (config.client)
			    {
				default:
				case CConfig::NoClient:
				    CheckRadioButton(hwndDlg, IDC_CLIENT_NONE, IDC_CLIENT, IDC_CLIENT_NONE);
				    break;
				case CConfig::StartProgram:
				    CheckRadioButton(hwndDlg, IDC_CLIENT_NONE, IDC_CLIENT, IDC_CLIENT);
				    break;
				case CConfig::XDMCP:
				    CheckRadioButton(hwndDlg, IDC_CLIENT_NONE, IDC_CLIENT, IDC_XDMCP);
				    break;
			    }
                            break;
			case IDD_PROGRAM:
                            // Init program dialog. Check local and remote buttons 
                            CheckRadioButton(hwndDlg, IDC_CLIENT_LOCAL, IDC_CLIENT_REMOTE, config.local?IDC_CLIENT_LOCAL:IDC_CLIENT_REMOTE);
			    EnableRemoteProgramGroup(hwndDlg, config.local?FALSE:TRUE);
                            // Fill combo boxes
			    FillProgramBox(hwndDlg);
			    FillProtocolBox(hwndDlg);
                            // Set edit fields
			    if (!config.program.empty())
			       	SetDlgItemText(hwndDlg, IDC_CLIENT_PROGRAM, config.program.c_str());
			    SetDlgItemText(hwndDlg, IDC_CLIENT_USER, config.user.c_str());
			    SetDlgItemText(hwndDlg, IDC_CLIENT_HOST, config.host.c_str());
			    break;
			case IDD_XDMCP:
                            // Init XDMCP dialog. Check broadcast and indirect button
                            CheckRadioButton(hwndDlg, IDC_XDMCP_QUERY, IDC_XDMCP_BROADCAST, config.broadcast?IDC_XDMCP_BROADCAST:IDC_XDMCP_QUERY);
                            CheckDlgButton(hwndDlg, IDC_XDMCP_INDIRECT, config.indirect?BST_CHECKED:BST_UNCHECKED);
			    EnableXDMCPQueryGroup(hwndDlg, config.broadcast?FALSE:TRUE);
                            // Set hostname
			    SetDlgItemText(hwndDlg, IDC_XDMCP_HOST, config.xdmcp_host.c_str());
			    break;
                        case IDD_CLIPBOARD:
                            CheckDlgButton(hwndDlg, IDC_CLIPBOARD, config.clipboard?BST_CHECKED:BST_UNCHECKED);
                            SetDlgItemText(hwndDlg, IDC_EXTRA_PARAMS, config.extra_params.c_str());
                            break;

                    }
                case WM_COMMAND:
                    // Handle control messages
                    switch (LOWORD(wParam))
                    {
                        // Handle clicks on images. Check proper radiobutton
                        case IDC_MULTIWINDOW_IMG:
                        case IDC_FULLSCREEN_IMG:
                        case IDC_WINDOWED_IMG:
                        case IDC_NODECORATION_IMG:
                            CheckRadioButton(hwndDlg, IDC_MULTIWINDOW, IDC_NODECORATION, LOWORD(wParam)-4);
                            SetFocus(GetDlgItem(hwndDlg, LOWORD(wParam)-4));
                            break;
                        // Disable unavailable controls 
                        case IDC_CLIENT_REMOTE:
                        case IDC_CLIENT_LOCAL:
			    EnableRemoteProgramGroup(hwndDlg, LOWORD(wParam) == IDC_CLIENT_REMOTE);
                            break;
			case IDC_XDMCP_QUERY:
			case IDC_XDMCP_BROADCAST:
			    EnableXDMCPQueryGroup(hwndDlg, LOWORD(wParam) == IDC_XDMCP_QUERY);
			    break;
			case IDC_FINISH_SAVE:
			    ShowSaveDialog(hwndDlg);
			    break;
                    }
            }
            // pass messages to parent
            return CWizard::PageDispatch(hwndDlg, uMsg, wParam, lParam, psp);
        }
        
        /// @brief Try to connect to server.
        /// Repeat until successful, server died or maximum number of retries
        /// reached.
        Display *WaitForServer(HANDLE serverProcess)
        {
            int     ncycles  = 120;         /* # of cycles to wait */
            int     cycles;                 /* Wait cycle count */
            Display *xd;

            for (cycles = 0; cycles < ncycles; cycles++) {
                if ((xd = XOpenDisplay(NULL))) {
                    return xd;
                }
                else {
                    if (WaitForSingleObject(serverProcess, 1000) == WAIT_TIMEOUT)
                        continue;
                }
            }
            return NULL;
        }
               
        /// @brief Do the actual start of Xming and clients
	void StartUp()
	{
	    std::string buffer;
	    std::string client;

            // Construct display strings
	    std::string display_id = ":" + config.display;
	    std::string display = "localhost" + display_id + ":0";

#ifdef _DEBUG
            // Debug only: Switch to Xming installation directory
	    SetCurrentDirectory("C:\\Programme\\Xming");
#endif	    

            // Build Xming commandline
	    buffer = "Xming " + display_id + " ";
	    switch (config.window)
	    {
		case CConfig::MultiWindow:
		    buffer += "-multiwindow ";
		    break;
		case CConfig::Fullscreen:
		    buffer += "-fullscreen ";
		    break;
		case CConfig::Nodecoration:
		    buffer += "-nodecoration ";
		    break;
		default:
		    break;
	    }
            // Add XDMCP parameter
	    if (config.client == CConfig::XDMCP)
	    {
		if (config.broadcast)
		    buffer += "-broadcast ";
		else 
		{
		    if (config.indirect)
			buffer += "-indirect ";
		    else
			buffer += "-query ";
		    buffer += config.xdmcp_host;
            buffer += " ";
		}
	    }
            if (config.clipboard)
                buffer += "-clipboard ";
            if (!config.extra_params.empty())
            {
                buffer += config.extra_params;
                buffer += " ";
            }
            
            // Construct client commandline
	    if (config.client == CConfig::StartProgram)
	    {
		if (!config.local)
		{
		    char cmdline[512];
                    std::string host = config.host;
                    if (!config.user.empty())
                        host = config.user + "@" + config.host;
		    if (config.protocol == "Putty")
			snprintf(cmdline,512,"plink -X %s %s", 
                                host.c_str(),config.program.c_str());
		    else
			snprintf(cmdline,512,"ssh -Y %s %s", 
                                host.c_str(),config.program.c_str());
		    client += cmdline;
		} else
		    client += config.program.c_str();
	    }

            // Prepare program startup
     	    STARTUPINFO si, sic;
	    PROCESS_INFORMATION pi, pic;
	    HANDLE handles[2];
	    DWORD hcount = 0; 
            Display *dpy = NULL;

	    ZeroMemory( &si, sizeof(si) );
	    si.cb = sizeof(si);
	    ZeroMemory( &pi, sizeof(pi) );
	    ZeroMemory( &sic, sizeof(sic) );
	    sic.cb = sizeof(sic);
	    ZeroMemory( &pic, sizeof(pic) );

	    // Start Xming process. 
#ifdef _DEBUG
	    printf("%s\n", buffer.c_str());
#endif
	    if( !CreateProcess( NULL, (CHAR*)buffer.c_str(), NULL, NULL, 
                        FALSE, 0, NULL, NULL, &si, &pi )) 
		throw win32_error("CreateProcess failed");
	    handles[hcount++] = pi.hProcess;

	    if (!client.empty())
	    {
                // Set DISPLAY variable
		SetEnvironmentVariable("DISPLAY",display.c_str());

                // Wait for server to startup
                dpy = WaitForServer(pi.hProcess);
                if (dpy == NULL)
                {
                    while (hcount--)
                        TerminateProcess(handles[hcount], (DWORD)-1);
		    throw std::runtime_error("Connection to server failed");
                }
                
#ifdef _DEBUG
		printf("%s\n", client.c_str());
#endif

                // Hide a console window 
                // FIXME: This may make it impossible to enter the password
		sic.dwFlags = STARTF_USESHOWWINDOW;
		sic.wShowWindow = SW_HIDE;

		// Start the child process. 
		if( !CreateProcess( NULL, (CHAR*)client.c_str(), NULL, NULL,
                            FALSE, 0, NULL, NULL, &sic, &pic )) 
		{
                    DWORD err = GetLastError();
                    while (hcount--)
                        TerminateProcess(handles[hcount], (DWORD)-1);
		    throw win32_error("CreateProcess failed", err);
		}
		handles[hcount++] = pic.hProcess;
	    }

	    // Wait until any child process exits.
	    DWORD ret = WaitForMultipleObjects(hcount, handles, FALSE, INFINITE );

#ifdef _DEBUG
	    printf("killing process!\n");
#endif
            // Check if Xming is still running
	    DWORD exitcode;
	    GetExitCodeProcess(pi.hProcess, &exitcode);
	    unsigned counter = 0;
	    while (exitcode == STILL_ACTIVE)
	    {
		if (++counter > 10)
		    TerminateProcess(pi.hProcess, (DWORD)-1);
		else
		    // Shutdown Xming (the soft way!)
		    EnumThreadWindows(pi.dwThreadId, KillWindowsProc, 0);
		Sleep(500);
		GetExitCodeProcess(pi.hProcess, &exitcode);
	    }
	    // Kill the client
    	    TerminateProcess(pic.hProcess, (DWORD)-1);

	    // Close process and thread handles. 
	    CloseHandle( pi.hProcess );
	    CloseHandle( pi.hThread );
	    CloseHandle( pic.hProcess );
	    CloseHandle( pic.hThread );
	}
};

int main(int argc, char **argv)
{
    try {
        InitCommonControls();
        CMyWizard dialog;

	bool skip_wizard = false;

	for (int i = 1; i < argc; i++)
	{
	    if (argv[i] == NULL)
		continue;
	    
	    std::string arg(argv[i]);
	    if (arg == "-load" && i + 1 < argc)
	    {
		i++;
		dialog.LoadConfig(argv[i]);
		continue;
	    }
	    if (arg == "-run" && i + 1 < argc)
	    {
		i++;
		dialog.LoadConfig(argv[i]);
		skip_wizard = true;
		continue;
	    }
	}

	int ret = 0; 
        if (skip_wizard || (ret =dialog.ShowModal()) != 0)
	    dialog.StartUp();
#ifdef _DEBUG
	printf("return %d\n", ret);
#endif
	return 0;
    } catch (std::runtime_error &e)
    {
        printf("Fehler: %s\n", e.what());
        return -1;
    }
}