home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / winnt / switcher / switcher.c < prev    next >
Text File  |  1995-01-13  |  38KB  |  1,310 lines

  1. /****************************************************************************
  2. *  This is a part of the Microsoft Source Code Samples.
  3. *  Copyright (C) 1995 Microsoft Corporation.
  4. *  All rights reserved.
  5. *  This source code is only intended as a supplement to
  6. *  Microsoft Development Tools and/or WinHelp documentation.
  7. *  See these sources for detailed information regarding the
  8. *  Microsoft samples programs.
  9. *
  10. *****************************************************************************/
  11.  
  12. /****************************************************************************
  13.  
  14.         PROGRAM: desktop.c
  15.  
  16.         PURPOSE: Allow user to switch between active desktops on the User's
  17.         WindowStation
  18.  
  19.         USAGE: desktop [-t #threads]
  20.         #threads is the number of desktops and corresponding threads to create
  21.  
  22.         APIS of Importance:
  23.          CreateDesktop()
  24.          SwitchDesktop()
  25.          GetUserObjectInformation()
  26.          GetThreadDesktop()
  27.          SetThreadDesktop()
  28.          CloseDesktop()
  29.          OpenDesktop()
  30.  
  31.         FUNCTIONS:
  32.         WinMain
  33.         StartNewDesktop
  34.         CreateAllDesktops
  35.         LoadResources
  36.         InitApplication
  37.         ThreadInit
  38.         SaveScreen
  39.         PaintMainWnd
  40.         RunApp
  41.         GetFontHeight
  42.         TitleWindow
  43.         CreateControls
  44.         WndProc
  45.         PreviewWndProc
  46.         EditProc
  47.  
  48.  
  49.         COMMENTS: This application demonstrates the multiple desktop
  50.         capabilities of Windows NT 3.51, PPC release.
  51.  
  52.  
  53.  
  54. ****************************************************************************/
  55.  
  56. #include <windows.h>   // required for all Windows applications
  57.  
  58. #include "switcher.h"   // specific to this program
  59.  
  60. //
  61. // Array of string resources
  62. //
  63. TCHAR SwitchStrings[LAST_STRING-FIRST_STRING + 1][MAXSTRLEN];
  64. #define PSZ(x) SwitchStrings[x-FIRST_STRING]
  65.  
  66. //
  67. // Structure used for thread-specific data
  68. //
  69. typedef struct _tdata
  70. {
  71.    HDESK hDesk;     // desktop assigned to new thread
  72.    int index;       // index into deskArray
  73.    HWND hWndStatic; // "Run:" static control
  74.    HWND hWndEdit;   // edit control for user input
  75.    HWND hWndBtn;    // button for user input
  76.    HWND hWndNew;    // button for new desktop
  77. } ThreadData;
  78.  
  79. int     gMaxIndex;                  // Highest index for array of desk handles
  80. HWND    gBaseWindow;                // Window handle of default desktop window
  81. HDESK   gDeskArray[MAX_THREADS];    // Global array of desktop handles
  82. HWND    hWndArray[MAX_THREADS];     // Global array of window handles
  83. HDC     gHDCArray[MAX_THREADS];     // global array of memory device contexts
  84.                                     // these DCs store snapshots of the desktops
  85. int gWidth, gHeight;                // dimensions of desktop rectangles
  86.  
  87. //
  88. // Keep track of how big the controls need to be to match the
  89. // active system fixed font. These will help determine the
  90. // minimum size of the switcher window.
  91. //
  92. int gStaticWidth;    //  Edit control label
  93. int gEditWidth;      //  Edit control
  94. int gBtnWidth;       //  Button to run app in edit control
  95. int gNewWidth;       //  Button to create new desktop
  96.  
  97. HINSTANCE ghInst = NULL;               // Global hInstance
  98. #define DEFAULT_DESKTOP  gDeskArray[0] // For easy reading
  99. LONG APIENTRY EditProc (HWND, UINT, WPARAM, LPARAM);
  100. TCHAR szAppName[]     = TEXT("Desktop Switcher!");
  101. TCHAR szClassName[]   = TEXT("SwitcherWindow");
  102. TCHAR szPreviewClass[]= TEXT("PreviewWindow");
  103.  
  104. /****************************************************************************
  105.         FUNCTION: StartNewDesktop
  106.  
  107.         PURPOSE: Create or open a handle to a desktop and put a switcher thread
  108.         on it.
  109.  
  110.         ARGUMENTS:
  111.            int nCount - Which desktop number this is
  112.  
  113.         Assumes gDeskArray[nCount] == NULL
  114.  
  115. ****************************************************************************/
  116.  
  117. void StartNewDesktop (int nCount)
  118. {
  119.    ThreadData *ptd;
  120.    TCHAR szDesk[50];
  121.    DWORD tID;
  122.    ptd = (ThreadData*)GlobalAlloc(GMEM_FIXED,sizeof(ThreadData));
  123.    if (ptd)
  124.    {
  125.        ptd->index = nCount;
  126.        //
  127.        // Give the desktop a name.
  128.        //
  129.        wsprintf (szDesk, PSZ(IDS_DESKTOPNAME), nCount+1);
  130.        //
  131.        // First, try to open an existing desktop
  132.        //
  133.        if ( !(ptd->hDesk = OpenDesktop (szDesk, 0, FALSE, GENERIC_ALL)))
  134.        {
  135.        //
  136.        // Failing an open, Create it
  137.        //
  138.           if (!(ptd->hDesk= CreateDesktop (szDesk, NULL,
  139.                                         NULL,0,MAXIMUM_ALLOWED,
  140.                                         NULL)))
  141.           {
  142.  
  143.                  MessageBox (NULL, PSZ(IDS_CREATEERROR),
  144.                           PSZ(IDS_ERRCAPTION), MB_OK);
  145.                  //
  146.                  //Mark this array slot as invalid
  147.                  //
  148.                  gDeskArray[nCount] = NULL;
  149.  
  150.           }
  151.  
  152.        }
  153.        if (ptd->hDesk)
  154.        {
  155.        //
  156.        // Save the handle to the global array. Start the new thread
  157.        //
  158.            gDeskArray[ptd->index] = ptd->hDesk;
  159.            CloseHandle(CreateThread(NULL, 0,
  160.                         (LPTHREAD_START_ROUTINE)ThreadInit,
  161.                         (LPVOID)ptd, 0, &tID));
  162.        }
  163.    }
  164.     else
  165.    {
  166.        //
  167.        // Out of memory
  168.        //
  169.        MessageBox (NULL, PSZ(IDS_CREATEERROR),
  170.                       PSZ(IDS_MEMERRCAPTION), MB_OK);
  171.        //
  172.        //Mark this array slot as invalid
  173.        //
  174.        gDeskArray[nCount] = NULL;
  175.    }
  176.  
  177. }
  178. /****************************************************************************
  179.  
  180.         FUNCTION: CreateAllDesktops (cThreads)
  181.  
  182.         PURPOSE: Creates desktops and assigns a switcher thread to each.
  183.         Updates the global desktop array.
  184.  
  185.         ARGUMENTS:
  186.           int cThreads - Number of threads/desktops to open or create
  187.  
  188. ****************************************************************************/
  189.  
  190. void CreateAllDesktops (int cThreads)
  191. {
  192.    ThreadData *ptdDefault;
  193.  
  194.  
  195.    //
  196.    // Make sure we allocate for the default desktop first
  197.    //
  198.    ptdDefault = (ThreadData *)GlobalAlloc (GMEM_FIXED, sizeof(ThreadData));
  199.    if (!ptdDefault) {
  200.       return;
  201.    }
  202.    while (cThreads)
  203.    {
  204.       StartNewDesktop (cThreads);
  205.       cThreads--;
  206.    }
  207.    //
  208.    // Main thread is one of the running threads too
  209.    //
  210.    ptdDefault->index = 0;
  211.    ptdDefault->hDesk = DEFAULT_DESKTOP;
  212.    ThreadInit((LPVOID)ptdDefault);
  213.  
  214. }
  215.  
  216. /****************************************************************************
  217.  
  218.         FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
  219.  
  220.         PURPOSE: Creates the threads and desktops
  221.         COMMENTS: Each thread has a separate desktop assigned to it.
  222.  
  223.  
  224. ****************************************************************************/
  225. int CALLBACK WinMain(
  226.         HINSTANCE hInstance,
  227.         HINSTANCE hPrevInstance,
  228.         LPSTR lpCmdLine,
  229.         int nCmdShow)
  230. {
  231.  
  232.     int cThreads;                // number of desktops
  233.  
  234.     ghInst = hInstance;
  235.  
  236.     if (hPrevInstance)
  237.     {       // Other instances of app running?
  238.             return (FALSE);     // Exit
  239.     } else {
  240.        if (!InitApplication ()) {
  241.           return FALSE;
  242.        }
  243.     }
  244.  
  245.     // parse command line to determine number of desktops
  246.     // Assume 9 threads
  247.  
  248.     cThreads = 9;
  249.     lpCmdLine = GetCommandLineA();
  250.  
  251.     //
  252.     // Get past .exe name
  253.     //
  254.     while (*lpCmdLine != ' ' && *lpCmdLine != 0)
  255.         lpCmdLine++;
  256.  
  257.     //
  258.     // Find the parameters
  259.     //
  260.     while (*lpCmdLine != 0)
  261.     {
  262.  
  263.         // Eat white space
  264.  
  265.         if (*lpCmdLine == ' ')
  266.         {
  267.             lpCmdLine++;
  268.             continue;
  269.         }
  270.  
  271.         //
  272.         // Do we have a dash? If not, just exit the loop
  273.         //
  274.         if (*lpCmdLine++ != '-')
  275.             break;
  276.  
  277.         switch (*lpCmdLine++)
  278.         {
  279.            case 't':
  280.            case 'T':
  281.             //
  282.             // How many threads?
  283.             //
  284.  
  285.               while (*lpCmdLine == ' ')
  286.                 lpCmdLine++;
  287.  
  288.               if (*lpCmdLine == 0 || *lpCmdLine == '-')
  289.                 continue;
  290.  
  291.               cThreads = 0;
  292.               while (*lpCmdLine >= '0' && *lpCmdLine <= '9')
  293.                  cThreads = cThreads * 10 + (*lpCmdLine++ - 0x30);
  294.               break;
  295.  
  296.         }
  297.     }
  298.  
  299.     // Create the threads - if zero was specified, default to 1.
  300.     // What does 0 threads mean?
  301.     if (cThreads == 0)
  302.        cThreads = 1;
  303.     else if (cThreads > MAX_THREADS)
  304.        cThreads = MAX_THREADS;
  305.     //
  306.     // Account for the main thread - only create extras
  307.     //
  308.     cThreads--;
  309.     gMaxIndex = cThreads;          // Keep track of the highest array index
  310.  
  311.     //
  312.     // Assign this here, since threads reference it
  313.     //
  314.     DEFAULT_DESKTOP = GetThreadDesktop(GetCurrentThreadId());
  315.     CreateAllDesktops (cThreads);
  316.     return 0;
  317. }
  318.  
  319. /*************************************************************
  320.    FUNCTION: LoadResources
  321.  
  322.    PURPOSE: Load string table entries
  323.  
  324.    ARGUMENTS: None
  325.  
  326.    RETURNS: True if all strings are loaded, False otherwise
  327.  
  328. *************************************************************/
  329.  
  330. BOOL LoadResources (void)
  331. {
  332.    int i;
  333.    for (i=0;i<(LAST_STRING-FIRST_STRING+1);i++)
  334.    {
  335.       if (!LoadString (ghInst, FIRST_STRING + i, SwitchStrings[i], MAXSTRLEN))
  336.           return FALSE;
  337.    }
  338.    return TRUE;
  339. }
  340. /*************************************************************
  341.  
  342.    FUNCTION: InitApplication
  343.  
  344.    PURPOSE: Register the window class and init global variables
  345.  
  346.    ARGUMENTS:
  347.  
  348.    RETURNS:
  349.      TRUE if window class registration and other initialization succeeds
  350.  
  351. **************************************************************/
  352.  
  353. BOOL InitApplication (void) {
  354.  
  355.    WNDCLASS wc;
  356.    HWND hTemp;
  357.    HWINSTA hWndSta;
  358.    RECT rect;
  359.  
  360.    if (!LoadResources ())
  361.        return FALSE;
  362.    //
  363.    // Initialize the gHDCArray to all NULLS
  364.    //
  365.    ZeroMemory (gHDCArray, sizeof (gHDCArray));
  366.    hTemp = GetDesktopWindow(); // Call this so User will assign us a WindowStation.
  367.    //
  368.    // Initialize gWidth and gHeight
  369.    // Get the size of the screen, make gWidth/gHeight == scrnW/scrnH
  370.    //
  371.    GetClientRect (hTemp, &rect);
  372.    gWidth = rect.right/DIVISOR;
  373.    gHeight = rect.bottom/DIVISOR;
  374.  
  375.    //
  376.    // Make sure this app has a windowstation
  377.    //
  378.    hWndSta = GetProcessWindowStation();
  379.    if (!hWndSta)
  380.    {
  381.       MessageBox (NULL, PSZ(IDS_WNDSTAERROR), PSZ(IDS_ERRCAPTION), MB_OK);
  382.       return FALSE;
  383.    }
  384.    //
  385.    // Register the main window class
  386.    //
  387.    wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
  388.    wc.lpfnWndProc = WndProc;
  389.    wc.cbClsExtra = 0;
  390.    wc.cbWndExtra = 0;
  391.    wc.hInstance = ghInst;
  392.    wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
  393.    wc.hCursor = LoadCursor (NULL, IDC_ARROW);
  394.    wc.hbrBackground = (HBRUSH)GetStockObject (WHITE_BRUSH);
  395.    wc.lpszMenuName = NULL;
  396.    wc.lpszClassName = szClassName;
  397.  
  398.    if (!RegisterClass (&wc))
  399.    {
  400.       return FALSE;
  401.    }
  402.  
  403.    //
  404.    // Register the preview window class
  405.    //
  406.    wc.style = 0;
  407.    wc.lpfnWndProc = PreviewWndProc;
  408.    wc.lpszClassName = szPreviewClass;
  409.  
  410.    if (!RegisterClass (&wc)) {
  411.       return FALSE;
  412.    }
  413.  
  414.  
  415.    return TRUE;
  416. }
  417.  
  418. /*******************************************************************
  419.  
  420.    FUNCTION: ThreadInit
  421.  
  422.    PURPOSE: Given a desktop handle, create a window on it to allow switching
  423.    among desktops.
  424.  
  425.    ARGUMENTS:
  426.      LPVOID tData - Thread-specific data
  427.  
  428.    RETURNS:
  429.      nothing
  430. ********************************************************************/
  431.  
  432. void ThreadInit(LPVOID tData)
  433. {
  434.     MSG msg;
  435.     HWND hWnd;
  436.     HDC hTemp;
  437.     int width;           // window width
  438.     USEROBJECTFLAGS uof; // To set Desktop attributes
  439.  
  440.     uof.fInherit = FALSE;        // If an app inherits multiple desktop handles,
  441.                                  // it could run on any one of those desktops
  442.     uof.fReserved = FALSE;
  443.     //
  444.     // Let other account processes hook this desktop
  445.     //
  446.     uof.dwFlags = DF_ALLOWOTHERACCOUNTHOOK;
  447.     SetUserObjectInformation (((ThreadData*)tData)->hDesk,
  448.                               UOI_FLAGS,
  449.                               (LPVOID)&uof,
  450.                               sizeof(uof));
  451.     //
  452.     // Make sure the handle is valid
  453.     //
  454.     if (gDeskArray[((ThreadData*)tData)->index])
  455.     {
  456.       //
  457.       // Assign new desktop to this thread
  458.       //
  459.       SetThreadDesktop (((ThreadData*)tData)->hDesk);
  460.       //  create the cool switcher window
  461.       if ((gMaxIndex+1) * gWidth > MINWINDOWWIDTH)
  462.       {
  463.          width = (gMaxIndex+1) * gWidth;
  464.       }
  465.       else
  466.       {
  467.          width = MINWINDOWWIDTH;
  468.       }
  469.       hWnd = CreateWindow (szClassName,
  470.                      szAppName,
  471.                      WS_MINIMIZEBOX|WS_OVERLAPPED|WS_VISIBLE|WS_BORDER|WS_CAPTION|WS_SYSMENU,
  472.                      0, 0, width, 30+gHeight + CONTROLHEIGHT,
  473.                      NULL, NULL, ghInst, tData);
  474.       if (!hWnd) // bag it
  475.       {
  476.          gDeskArray[((ThreadData*)tData)->index] = NULL;
  477.          GlobalFree (tData);
  478.          return;
  479.       }
  480.  
  481.       //
  482.       //update the global window array
  483.       //
  484.       hWndArray[((ThreadData*)tData)->index] = hWnd;
  485.  
  486.     }
  487.     else
  488.     {
  489.  
  490.        GlobalFree (tData);
  491.        return;
  492.     }
  493.  
  494.     //
  495.     // Acquire and dispatch messages until a WM_QUIT message is received.
  496.     //
  497.     while (GetMessage(&msg, NULL,  0, 0))
  498.     {
  499.                  TranslateMessage(&msg);// Translates virtual key codes
  500.                  DispatchMessage(&msg); // Dispatches message to window
  501.     }
  502.     //
  503.     // Switch back to the default desktop and close the user-created one
  504.     //
  505.     SetThreadDesktop (DEFAULT_DESKTOP);
  506.     SwitchDesktop (DEFAULT_DESKTOP);
  507.     CloseDesktop (((ThreadData*)tData)->hDesk);
  508.     //
  509.     // NULL out the global array entry so other threads won't try to switch to
  510.     // this desktop
  511.     //
  512.     gDeskArray[((ThreadData*)tData)->index] = NULL;
  513.     //
  514.     // cleanup
  515.     //
  516.     hTemp = gHDCArray[((ThreadData*)tData)->index];
  517.     gHDCArray[((ThreadData*)tData)->index] = NULL;
  518.     DeleteObject (hTemp);
  519.     GlobalFree (tData);
  520.  
  521. }
  522.  
  523.  
  524. /***********************************************************************
  525.  
  526.    FUNCTION: SaveScreen
  527.  
  528.    PURPOSE: Save a snapshot of the desktop to its corresponding
  529.    memory DC. StretchBlt!
  530.  
  531.    ARGUMENTS:
  532.      int index - Index of memory DC to save bits to
  533.  
  534.    RETURNS: nothing
  535. ************************************************************************/
  536.  
  537. void SaveScreen (int index) {
  538.    HDC hdc;
  539.    int xSize, ySize;
  540.  
  541.    xSize = GetSystemMetrics (SM_CXSCREEN);
  542.    ySize = GetSystemMetrics (SM_CYSCREEN);
  543.  
  544.    if (hdc = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL))
  545.    {
  546.       //
  547.       // Copy the desktop to a memory DC
  548.       //
  549.       StretchBlt (gHDCArray[index], 0, 0, gWidth*2, gHeight*2,
  550.                hdc, 0, 0, xSize, ySize, SRCCOPY);
  551.       DeleteDC (hdc);
  552.    }
  553. }
  554.  
  555. /*******************************************************************************
  556.  
  557.      FUNCTION: PaintMainWnd
  558.  
  559.      PURPOSE: Draw the main window. This window has rectangles with miniature snapshots
  560.      of each desktop. The snapshots are retrieved from the gHDCArray and StretchBlt'd to
  561.      the right size.
  562.  
  563.      ARGUMENTS:
  564.        HWND hWnd - Window to draw
  565.  
  566.     RETURNS: nothing
  567.  
  568. *******************************************************************************/
  569.  
  570. void PaintMainWnd (HWND hWnd)
  571. {
  572.    PAINTSTRUCT ps;
  573.    RECT rect;
  574.    HPEN hPen, hOld;
  575.    ThreadData *ptd;
  576.    TCHAR szName[4];  // short name!
  577.    int myThread;
  578.    HDC hDC;          // always need a dc for drawing
  579.    int i;            // my favorite loop counter
  580.  
  581.    //
  582.    // get the array index of this window
  583.    //
  584.    ptd = (ThreadData *)GetWindowLong (hWnd, GWL_USERDATA);
  585.    myThread = ptd->index;
  586.  
  587.    //
  588.    //Draw a rectangle for each desktop
  589.    //
  590.    hDC = BeginPaint (hWnd, &ps);
  591.    if (GetClientRect (hWnd, &rect))
  592.    {
  593.       int right, left;
  594.       //
  595.       // leave space for edit control and button
  596.       //
  597.       rect.bottom -= CONTROLHEIGHT;
  598.       hPen = CreatePen (PS_SOLID | PS_INSIDEFRAME, 2, RGB(255,16,16));
  599.       hOld = SelectObject (hDC, hPen);
  600.  
  601.       //
  602.       // draw each desktop rectangle
  603.       //
  604.       for (i=0;i<=gMaxIndex;i++)
  605.       {
  606.          right = gWidth * i + gWidth;
  607.          left = right - gWidth;
  608.          //
  609.          // If no snapshot is available, be boring
  610.          //
  611.          if (!gHDCArray[i])
  612.          {
  613.             Rectangle (hDC, left, rect.top, right, gHeight);
  614.             wsprintf (szName, TEXT("%d"), i+1);
  615.             TextOut (hDC, left+(gWidth/2),
  616.                      gHeight/2, szName,
  617.                      lstrlen (szName));
  618.          }
  619.          else
  620.          {
  621.             //
  622.             // BitBlt the snapshot into the rectangle
  623.             //
  624.             StretchBlt (hDC, left, rect.top, gWidth, gHeight,
  625.                     gHDCArray[i], 0, 0, gWidth*2, gHeight*2, SRCCOPY);
  626.             //
  627.             // draw lines around the rectangle
  628.             //
  629.             MoveToEx (hDC, left, rect.top, NULL);
  630.             LineTo (hDC, left, gHeight);
  631.             MoveToEx (hDC, right, rect.top, NULL);
  632.             LineTo (hDC, right, gHeight);
  633.             //
  634.             // underline the active one
  635.             //
  636.             if (myThread == i)
  637.             {
  638.                MoveToEx (hDC, left, gHeight, NULL);
  639.                LineTo (hDC, right, gHeight);
  640.             }
  641.          }
  642.       }
  643.       //
  644.       // cleanup
  645.       //
  646.       SelectObject (hDC, hOld);
  647.       DeleteObject (hPen);
  648.    }
  649.  
  650.    EndPaint (hWnd, &ps);
  651.  
  652. }
  653.  
  654. /****************************************************************************
  655.  
  656.       FUNCTION: RunApp (HWND)
  657.  
  658.       PURPOSE: Create a process, using contents of HWND's edit control
  659.       as the command line.
  660.  
  661.       ARGUMENTS:
  662.         HWND hWnd - Handle of active switcher window
  663.  
  664.       RETURNS: nothing
  665.  
  666.       COMMENTS: Make sure proper desktop is passed in STARTUPINFO
  667.  
  668.  ****************************************************************************/
  669.  
  670. void RunApp (HWND hWnd)
  671. {
  672.    TCHAR szDesk[100];          // data holder
  673.    TCHAR szExec[100];       // Command line
  674.    STARTUPINFO sui;         // Process startup info
  675.    PROCESS_INFORMATION pi;  // info returned from CreateProcess
  676.    ThreadData *ptd;
  677.  
  678.    ptd = (ThreadData*)GetWindowLong (hWnd, GWL_USERDATA);
  679.    //
  680.    // Most sui members will be 0
  681.    //
  682.    ZeroMemory ((PVOID)&sui, sizeof(sui));
  683.    //
  684.    //Get the command line to execute
  685.    //
  686.    GetDlgItemText (hWnd, IDC_RUNME, szExec, 100*sizeof(TCHAR));
  687.    //
  688.    //Get the current desktop name
  689.    //
  690.    GetUserObjectInformation (GetThreadDesktop (GetCurrentThreadId()),
  691.                              UOI_NAME,
  692.                              szDesk,
  693.                              100*sizeof(TCHAR),
  694.                              NULL);
  695.    sui.cb = sizeof (sui);
  696.    //
  697.    // Need the lpDesktop member so the new process runs on this desktop
  698.    // The lpDesktop member was reserved in previous versions of NT
  699.    //
  700.    sui.lpDesktop = szDesk;
  701.    CreateProcess (NULL,   // image name
  702.                   szExec, // command line
  703.                   NULL,   // process security attributes
  704.                   NULL,   // thread security attributes
  705.                   TRUE,   // inherit handles
  706.                   CREATE_DEFAULT_ERROR_MODE|CREATE_SEPARATE_WOW_VDM,
  707.                   NULL,   // environment block
  708.                   NULL,   // current directory
  709.                   &sui,   // STARTUPINFO
  710.                   &pi);   // PROCESS_INFORMATION
  711.  
  712. }
  713. /****************************************************************************
  714.    FUNCTION: GetFontHeight
  715.  
  716.    PURPOSE: Set up widths for controls based on size of the system
  717.    font.
  718.  
  719.    ARGUMENTS:
  720.      HWND hWnd - Window whose DC to use.
  721.  
  722.    RETURNS:
  723.       Return the height of the system fixed font to use for
  724.    the controls.
  725.  
  726. ****************************************************************************/
  727.  
  728. LONG GetFontHeight (HWND hWnd)
  729. {
  730.    HDC hdc;
  731.    TEXTMETRIC tm;
  732.    SIZE size;
  733.    #define MARGIN 7  // extra space on the button around the text
  734.  
  735.    hdc = GetDC (hWnd);
  736.    if (!GetTextMetrics (hdc, &tm))
  737.    {
  738.       //
  739.       // Use defaults
  740.       //
  741.       gStaticWidth = STATICWIDTH;
  742.       gBtnWidth    = BTNWIDTH;
  743.       gEditWidth   = EDITWIDTH;
  744.       gNewWidth    = BTNWIDTH + 25;
  745.       return CONTROLHEIGHT;
  746.    }
  747.  
  748.    //
  749.    // GetTextExtentPoint32 fills in size with the width and height of
  750.    // a string.
  751.    //
  752.    GetTextExtentPoint32 (hdc, PSZ(IDS_RUNLABEL), lstrlen(PSZ(IDS_RUNLABEL)), &size);
  753.    gStaticWidth = size.cx + MARGIN;
  754.    gEditWidth   = EDITWIDTH;
  755.    GetTextExtentPoint32 (hdc, PSZ(IDS_BTNLABEL), lstrlen(PSZ(IDS_BTNLABEL)), &size);
  756.    gBtnWidth = size.cx + MARGIN;
  757.    GetTextExtentPoint32 (hdc, PSZ(IDS_NEWLABEL), lstrlen(PSZ(IDS_NEWLABEL)), &size);
  758.    gNewWidth = size.cx + MARGIN;
  759.    ReleaseDC (hWnd, hdc);
  760.    return tm.tmHeight + 2;
  761.  
  762. }
  763.  
  764. /****************************************************************************
  765.         FUNCTION: TitleWindow
  766.  
  767.         PURPOSE: Give a switcher window an appropriate title, using its
  768.         desktop name.
  769.  
  770.         ARGUMENTS:
  771.           HWND hWnd - Window to title
  772.  
  773.         RETURNS: nothing
  774.  
  775. ****************************************************************************/
  776.  
  777. void TitleWindow (HWND hWnd)
  778. {
  779.    TCHAR *szTitle, *szName;
  780.    UINT nBytes = 0;
  781.  
  782.    //
  783.    // How long is the desktop name?
  784.    //
  785.    GetUserObjectInformation (GetThreadDesktop(GetCurrentThreadId()),
  786.                              UOI_NAME,
  787.                              (LPVOID)&nBytes, // not used since cbInfo is 0
  788.                              0,
  789.                              &nBytes);
  790.    szName = (LPTSTR)GlobalAlloc (GPTR, nBytes);
  791.    if (!szName)
  792.    {
  793.       return;
  794.    }
  795.    //
  796.    // Now get the desktop name
  797.    //
  798.    GetUserObjectInformation (GetThreadDesktop(GetCurrentThreadId()),
  799.                              UOI_NAME,
  800.                              (LPVOID)szName,
  801.                              nBytes,
  802.                              &nBytes);
  803.    //
  804.    // Now make the window title
  805.    //
  806.    szTitle = (LPTSTR)GlobalAlloc (
  807.            GPTR,
  808.            (lstrlen(szAppName)+lstrlen(TEXT(" - "))) * sizeof(TCHAR) + nBytes);
  809.  
  810.    if (!szTitle)
  811.    {
  812.       GlobalFree (szName);
  813.       return;
  814.    }
  815.    wsprintf (szTitle, TEXT("%s - %s"), szAppName, szName);
  816.    SetWindowText (hWnd, szTitle);
  817.    //
  818.    // Cleanup
  819.    //
  820.    GlobalFree (szName);
  821.    GlobalFree (szTitle);
  822. }
  823.  
  824. /****************************************************************************
  825.  
  826.         FUNCTION: CreateControls (ThreadData *, HWND)
  827.  
  828.         PURPOSE: Creates UI controls on a switcher window
  829.  
  830.         ARGUMENTS:
  831.           ThreadData *ptd  - Thread specific data to use/init
  832.           HWND hWnd        - Parent window
  833.  
  834.         RETURNS:
  835.            nothing
  836.  
  837.  
  838. ****************************************************************************/
  839.  
  840. void CreateControls (ThreadData *ptd, HWND hWnd)
  841. {
  842.    LONG oldproc;
  843.  
  844.    //
  845.    // Create the edit control label
  846.    //
  847.    ptd->hWndStatic = CreateWindow (TEXT("static"), PSZ(IDS_RUNLABELHOT),
  848.                                    WS_CHILD | WS_VISIBLE,
  849.                                    0,0,0,0, hWnd, (HMENU)IDC_STATIC,
  850.                                    ghInst, NULL);
  851.    //
  852.    // Create the edit control
  853.    //
  854.    ptd->hWndEdit = CreateWindow (TEXT("Edit"), TEXT(""),
  855.                          WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,
  856.                          0,0,0,0, hWnd, (HMENU)IDC_RUNME,
  857.                          ghInst, NULL);
  858.  
  859.    //
  860.    // set the edit control proc and save the default one
  861.    //
  862.    oldproc = GetWindowLong (ptd->hWndEdit, GWL_WNDPROC);
  863.    SetWindowLong (ptd->hWndEdit, GWL_WNDPROC, (LONG)EditProc);
  864.    SetWindowLong (ptd->hWndEdit, GWL_USERDATA, oldproc);
  865.  
  866.    //
  867.    // Create the execution button
  868.    //
  869.    ptd->hWndBtn = CreateWindow (TEXT("Button"), PSZ(IDS_BTNLABEL),
  870.                                 WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
  871.                                 0,0,0,0, hWnd, (HMENU)IDC_RUNMEBTN,
  872.                                 ghInst, NULL);
  873.    //
  874.    // Create a button for creating new desktops
  875.    //
  876.    ptd->hWndNew = CreateWindow (TEXT("button"), PSZ(IDS_NEWLABELHOT),
  877.                                 WS_CHILD | WS_VISIBLE,
  878.                                 0,0,0,0, hWnd, (HMENU)IDC_NEWDSKBTN,
  879.                                 ghInst, NULL);
  880.  
  881. }
  882. /****************************************************************************
  883.  
  884.         FUNCTION: WndProc(UINT, WPARAM, LPARAM)
  885.  
  886.         PURPOSE:  Processes messages to the Switcher window
  887.  
  888.         MESSAGES:
  889.           WM_RBUTTONDOWN - Switch to desktop whose rectangle is under the mouse
  890.           WM_CLOSE       - Send WM_CLOSE to all windows in hWndArray
  891.           WM_LBUTTONDOWN - Create a preview window to display a larger view of
  892.                            a desktop until WM_LBUTTONUP
  893.           WM_COMMAND     - Respond to button pushes or edit control entry
  894.           WM_SYSCHAR     - ALT+R sets focus to the edit control
  895.                            ALT+N creates a new desktop
  896.           WM_CHAR        - Carriage return executes command line from
  897.                            the edit control
  898.           WM_HOTKEY
  899.           WM_KEYDOWN     - Respond to function keys by switching to
  900.                            the appropriate desktop. Example, F2 means switch
  901.                            to Desktop2. Ctrl-F# allows switching without
  902.                            giving focus to the switcher window
  903.           WM_LBUTTONUP   - Close the active preview window
  904.           WM_CREATE      - Initialize controls and global array entries
  905.           WM_SIZE        - Size the child controls correctly
  906.  
  907.  
  908.         COMMENTS:
  909.  
  910. ****************************************************************************/
  911.  
  912. LONG APIENTRY WndProc(
  913.     HWND hWnd,
  914.     UINT message,      // type of message
  915.     WPARAM wParam,     // additional information
  916.     LPARAM lParam)     // additional information
  917. {
  918.  
  919.     int newThread;      // Thread index to switch to
  920.     int i;
  921.     ThreadData *ptd;
  922.     static HWND hShowing = NULL;           // which preview window is being shown
  923.     static LONG fntHeight = CONTROLHEIGHT; // height for the edit control
  924.     switch (message)
  925.     {
  926.        case WM_CREATE:
  927.        {
  928.           HDC hDC;
  929.           HBITMAP hBmp;
  930.  
  931.          // Create edit control, button, and label at the bottom of the window
  932.          // This will allow the user to input a program to run
  933.  
  934.          SetWindowLong (hWnd, GWL_USERDATA,
  935.                         (LONG)((CREATESTRUCT *)lParam)->lpCreateParams);
  936.          ptd = (ThreadData *)GetWindowLong (hWnd, GWL_USERDATA);
  937.          CreateControls (ptd, hWnd);
  938.          fntHeight = GetFontHeight (hWnd);
  939.          //
  940.          // initialize the DC array entry
  941.          //
  942.          hDC = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL);
  943.          gHDCArray[ptd->index] = CreateCompatibleDC (hDC);
  944.          //
  945.          // Halftone is the best stretching algorithm
  946.          //
  947.          SetStretchBltMode (gHDCArray[ptd->index], HALFTONE);
  948.          SetBrushOrgEx (gHDCArray[ptd->index], 0, 0, NULL);
  949.          //
  950.          // Use a bitmap the same size as the desktop preview rectangles
  951.          //
  952.          hBmp = CreateCompatibleBitmap (hDC, gWidth*2, gHeight*2);
  953.          SelectObject (gHDCArray[ptd->index], hBmp);
  954.          DeleteDC (hDC);
  955.          SaveScreen (ptd->index);
  956.          TitleWindow (hWnd);
  957.          //
  958.          // Register hot keys
  959.          //
  960.          for (i=0;i<10;i++)
  961.          {
  962.             RegisterHotKey (hWnd, VK_F1+i, MOD_CONTROL, VK_F1+i);
  963.          }
  964.          return 0;
  965.        }
  966.  
  967.        case WM_SIZE:
  968.        {
  969.          //
  970.          // Put the child controls at the right places
  971.          //
  972.           #define PADDING 5
  973.  
  974.           RECT rect;
  975.           ThreadData *ptd;
  976.           if (GetClientRect (hWnd, &rect))
  977.           {
  978.             ptd = (ThreadData *)GetWindowLong (hWnd, GWL_USERDATA);
  979.             MoveWindow (ptd->hWndStatic, 0, rect.bottom - CONTROLHEIGHT,
  980.                         gStaticWidth, fntHeight + PADDING, TRUE);
  981.  
  982.             MoveWindow (ptd->hWndEdit, gStaticWidth + 5,
  983.                         rect.bottom - fntHeight - PADDING,
  984.                         gEditWidth, fntHeight+PADDING, TRUE);
  985.  
  986.             MoveWindow (ptd->hWndBtn, gStaticWidth + gEditWidth + 10,
  987.                         rect.bottom - fntHeight - PADDING,
  988.                         gBtnWidth, fntHeight+PADDING, TRUE);
  989.  
  990.             MoveWindow (ptd->hWndNew, gStaticWidth+gEditWidth+gBtnWidth+15,
  991.                         rect.bottom - fntHeight- PADDING,
  992.                         gNewWidth, fntHeight+PADDING, TRUE);
  993.  
  994.  
  995.           }
  996.           return 0;
  997.        }
  998.        case WM_PAINT:
  999.           PaintMainWnd (hWnd);
  1000.           return 0;
  1001.  
  1002.        case WM_RBUTTONDOWN:
  1003.        {
  1004.           //
  1005.           // Find the rectangle in which the button was pressed
  1006.           //
  1007.           POINTS pts;
  1008.           ThreadData *ptd;
  1009.           ptd = (ThreadData *)GetWindowLong(hWnd, GWL_USERDATA);
  1010.           pts = MAKEPOINTS (lParam);
  1011.           if (pts.y > gHeight)
  1012.           {
  1013.              return 1;
  1014.           }
  1015.           newThread = pts.x/gWidth;
  1016.  
  1017.           //
  1018.           // Get a snapshot of the current desktop
  1019.           //
  1020.           SaveScreen (ptd->index);
  1021.  
  1022.           //
  1023.           // Switch to the selected desktop
  1024.           //
  1025.           if (!gDeskArray[newThread])
  1026.           {
  1027.              StartNewDesktop (newThread);
  1028.           }
  1029.           if (!SwitchDesktop (gDeskArray[newThread]))
  1030.              MessageBox (hWnd,
  1031.                          PSZ(IDS_BADDESKTOP),
  1032.                          PSZ(IDS_ERRCAPTION), MB_OK);
  1033.  
  1034.           return 0;
  1035.        }
  1036.  
  1037.        case WM_LBUTTONDOWN:
  1038.        //
  1039.        // show the preview window
  1040.        //
  1041.        {
  1042.           POINTS pts;
  1043.           POINT ptl;
  1044.           int *index;
  1045.  
  1046.           pts = MAKEPOINTS (lParam);
  1047.           if (pts.y > gHeight)
  1048.           {
  1049.              return 1;
  1050.           }
  1051.           newThread = pts.x/gWidth;
  1052.           index = GlobalAlloc (GMEM_FIXED, sizeof(int));
  1053.           if (!index)
  1054.           {
  1055.              return 1;
  1056.           }
  1057.           *index = newThread;
  1058.           //
  1059.           // Want to show the preview window where the button was clicked.
  1060.           // Map the given points to screen coords.
  1061.           // ClientToScreen is expecting a POINT structure, not a POINTS
  1062.           //
  1063.           ptl.x = (LONG)pts.x;
  1064.           ptl.y = (LONG)pts.y;
  1065.           ClientToScreen (hWnd, &ptl);
  1066.           hShowing = CreateWindow (szPreviewClass, TEXT(""),
  1067.                                   WS_POPUP | WS_VISIBLE | WS_BORDER,
  1068.                                   ptl.x+3,
  1069.                                   ptl.y+3,
  1070.                                   gWidth*2,
  1071.                                   gHeight*2,
  1072.                                   hWnd,
  1073.                                   (HMENU)0, ghInst, (LPVOID)index);
  1074.           return 0;
  1075.        }
  1076.  
  1077.        case WM_CHAR:
  1078.           if (wParam == VK_RETURN)
  1079.           {
  1080.               PostMessage (hWnd, WM_COMMAND, (WPARAM)IDC_RUNMEBTN, 0);
  1081.           }
  1082.           return 0;
  1083.  
  1084.        case WM_SYSCHAR:
  1085.        {
  1086.           ThreadData *ptd;
  1087.           ptd = (ThreadData *)GetWindowLong(hWnd, GWL_USERDATA);
  1088.           switch (wParam)
  1089.           {
  1090.              // alt+r == focus on the edit control
  1091.              case TEXT('r'):
  1092.              case TEXT('R'):
  1093.                 if (GetKeyState (VK_MENU))
  1094.                 {
  1095.                    SetFocus (ptd->hWndEdit);
  1096.                 }
  1097.                 return 0;
  1098.              // alt+n = create a new desktop
  1099.              case TEXT('n'):
  1100.              case TEXT('N'):
  1101.                 if (GetKeyState (VK_MENU))
  1102.                 {
  1103.                    PostMessage (hWnd, WM_COMMAND, (WPARAM)IDC_NEWDSKBTN, 0);
  1104.                 }
  1105.           }
  1106.           return 0;
  1107.        }
  1108.        case WM_HOTKEY:
  1109.        case WM_KEYDOWN:
  1110.           //
  1111.           // F1-F9 switches to corresponding desktop
  1112.           //
  1113.  
  1114.           if ((wParam >= VK_F1 && wParam <= VK_F10)
  1115.               && (wParam - VK_F1 <= (UINT)gMaxIndex))
  1116.           {
  1117.              LONG x, y;
  1118.              x = (wParam - VK_F1) * gWidth + 2;
  1119.              y = gHeight - 4;
  1120.              PostMessage (hWnd, WM_RBUTTONDOWN, 0, MAKELPARAM (x, y));
  1121.           }
  1122.           return 0;
  1123.  
  1124.        case WM_SETFOCUS:
  1125.        case WM_NCLBUTTONUP:
  1126.        case WM_LBUTTONUP:
  1127.  
  1128.          //
  1129.          // destroy the preview window
  1130.          //
  1131.          if (hShowing)
  1132.          {
  1133.             DestroyWindow (hShowing);
  1134.             hShowing = NULL;
  1135.          }
  1136.          return 0;
  1137.  
  1138.  
  1139.  
  1140.        case WM_CLOSE:
  1141.          //
  1142.          // to be safe, check for a preview window
  1143.          //
  1144.          if (hShowing)
  1145.          {
  1146.             DestroyWindow (hShowing);
  1147.             hShowing = NULL;
  1148.          }
  1149.          //
  1150.          // go to the default desktop so the DestroyWindow calls all succeed
  1151.          //
  1152.          SwitchDesktop (DEFAULT_DESKTOP);
  1153.          //
  1154.          // kill the window on this desktop
  1155.          // all the windows will be destroyed if this is the default desktop
  1156.          //
  1157.          for (i=gMaxIndex;i>=0;i--)
  1158.          {
  1159.             DestroyWindow (hWndArray[i]);
  1160.          }
  1161.          //
  1162.          // Unregister the hot keys
  1163.          //
  1164.          for (i=0;i<10;i++)
  1165.          {
  1166.             UnregisterHotKey (hWnd,VK_F1+i);
  1167.          }
  1168.          return 0;
  1169.  
  1170.        case WM_DESTROY:  // message: window being destroyed
  1171.  
  1172.          PostQuitMessage(0);
  1173.          return 0;
  1174.  
  1175.  
  1176.        case WM_COMMAND:
  1177.        {
  1178.  
  1179.           switch (LOWORD(wParam))
  1180.           {
  1181.              case IDC_RUNMEBTN:
  1182.              {
  1183.                 RunApp (hWnd);
  1184.                 return 0;
  1185.              }
  1186.  
  1187.              case IDC_NEWDSKBTN:
  1188.              //
  1189.              // Create a new desktop and resize the windows to show it.
  1190.              //
  1191.              {
  1192.                 RECT rect;
  1193.                 int i;
  1194.                 if (gMaxIndex + 1 < MAX_THREADS)
  1195.                 {
  1196.                    gMaxIndex++;
  1197.                    StartNewDesktop (gMaxIndex);
  1198.                    GetWindowRect (hWnd,&rect);
  1199.                    for (i=0;i<gMaxIndex;i++)
  1200.                    {
  1201.                       MoveWindow (hWndArray[i],
  1202.                                rect.left, rect.top,
  1203.                                rect.right + gWidth, rect.bottom-rect.top,
  1204.                                TRUE);
  1205.  
  1206.                    }
  1207.                 }
  1208.                return 0;
  1209.  
  1210.              }
  1211.  
  1212.              default:
  1213.                 return DefWindowProc (hWnd, message, wParam, lParam);
  1214.           }
  1215.        }
  1216.  
  1217.     default:          // Passes it on if unprocessed
  1218.         return (DefWindowProc (hWnd, message, wParam, lParam));
  1219.     }
  1220.  
  1221. }
  1222.  
  1223. /***********************************************************
  1224.  
  1225.      FUNCTION: PreviewWndProc
  1226.  
  1227.      PURPOSE: Displays an enlarged view of the last snapshot of a desktop
  1228.  
  1229. ************************************************************/
  1230.  
  1231. LONG APIENTRY PreviewWndProc (HWND hWnd,
  1232.                               UINT msg,
  1233.                               WPARAM wParam,
  1234.                               LPARAM lParam)
  1235. {
  1236.  
  1237.    int *index;
  1238.    switch (msg)
  1239.    {
  1240.       case WM_CREATE:
  1241.          //
  1242.          // save the index
  1243.          //
  1244.          SetWindowLong (hWnd, GWL_USERDATA,
  1245.                        (LONG)((CREATESTRUCT *)lParam)->lpCreateParams);
  1246.  
  1247.          return 0;
  1248.  
  1249.       case WM_PAINT:
  1250.       {
  1251.          HDC hdc;
  1252.          PAINTSTRUCT ps;
  1253.          index = (int *)GetWindowLong (hWnd, GWL_USERDATA);
  1254.          hdc = BeginPaint (hWnd, &ps);
  1255.          //
  1256.          // slap in the desktop picture
  1257.          //
  1258.          BitBlt (hdc, 0, 0, gWidth*2, gHeight*2,
  1259.                      gHDCArray[*index], 0, 0, SRCCOPY);
  1260.          EndPaint (hWnd, &ps);
  1261.          return 0;
  1262.       }
  1263.       case WM_LBUTTONUP:
  1264.       {
  1265.          //
  1266.          // In case the button is released in my client area
  1267.          //
  1268.          HWND hp;
  1269.          hp = GetWindow (hWnd, GW_OWNER);
  1270.          PostMessage (hp, msg, wParam, lParam);
  1271.          return 0;
  1272.       }
  1273.       case WM_CLOSE:
  1274.       {
  1275.          //
  1276.          // cleanup the index pointer
  1277.          //
  1278.          index = (int *)GetWindowLong (hWnd, GWL_USERDATA);
  1279.          GlobalFree (index);
  1280.  
  1281.          return DefWindowProc (hWnd, msg, wParam, lParam);
  1282.       }
  1283.       default:
  1284.           return DefWindowProc (hWnd, msg, wParam, lParam);
  1285.  
  1286.    }
  1287. }
  1288.  
  1289. /********************************************
  1290.  
  1291.      FUNCTION: EditProc
  1292.  
  1293.      PURPOSE: subclass the edit control to handle carriage returns
  1294.  
  1295.  ********************************************/
  1296.  
  1297.  LONG APIENTRY EditProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1298.  {
  1299.     if (WM_CHAR == msg && (TCHAR)wParam == 0xD)
  1300.     {
  1301.        PostMessage (GetParent (hWnd), WM_COMMAND, (WPARAM)IDC_RUNMEBTN, 0);
  1302.        return 0;
  1303.     }
  1304.     //
  1305.     // call the default edit control procedure
  1306.     //
  1307.     return CallWindowProc ( (WNDPROC)GetWindowLong (hWnd, GWL_USERDATA),
  1308.                             hWnd,  msg, wParam, lParam);
  1309.  }
  1310.