1311 lines
36 KiB
C
1311 lines
36 KiB
C
|
/****************************************************************************
|
||
|
* This is a part of the Microsoft Source Code Samples.
|
||
|
* Copyright (C) 1995 Microsoft Corporation.
|
||
|
* All rights reserved.
|
||
|
* This source code is only intended as a supplement to
|
||
|
* Microsoft Development Tools and/or WinHelp documentation.
|
||
|
* See these sources for detailed information regarding the
|
||
|
* Microsoft samples programs.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
/****************************************************************************
|
||
|
|
||
|
PROGRAM: desktop.c
|
||
|
|
||
|
PURPOSE: Allow user to switch between active desktops on the User's
|
||
|
WindowStation
|
||
|
|
||
|
USAGE: desktop [-t #threads]
|
||
|
#threads is the number of desktops and corresponding threads to create
|
||
|
|
||
|
APIS of Importance:
|
||
|
CreateDesktop()
|
||
|
SwitchDesktop()
|
||
|
GetUserObjectInformation()
|
||
|
GetThreadDesktop()
|
||
|
SetThreadDesktop()
|
||
|
CloseDesktop()
|
||
|
OpenDesktop()
|
||
|
|
||
|
FUNCTIONS:
|
||
|
WinMain
|
||
|
StartNewDesktop
|
||
|
CreateAllDesktops
|
||
|
LoadResources
|
||
|
InitApplication
|
||
|
ThreadInit
|
||
|
SaveScreen
|
||
|
PaintMainWnd
|
||
|
RunApp
|
||
|
GetFontHeight
|
||
|
TitleWindow
|
||
|
CreateControls
|
||
|
WndProc
|
||
|
PreviewWndProc
|
||
|
EditProc
|
||
|
|
||
|
|
||
|
COMMENTS: This application demonstrates the multiple desktop
|
||
|
capabilities of Windows NT 3.51, PPC release.
|
||
|
|
||
|
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include <windows.h> // required for all Windows applications
|
||
|
|
||
|
#include "switcher.h" // specific to this program
|
||
|
|
||
|
//
|
||
|
// Array of string resources
|
||
|
//
|
||
|
TCHAR SwitchStrings[LAST_STRING-FIRST_STRING + 1][MAXSTRLEN];
|
||
|
#define PSZ(x) SwitchStrings[x-FIRST_STRING]
|
||
|
|
||
|
//
|
||
|
// Structure used for thread-specific data
|
||
|
//
|
||
|
typedef struct _tdata
|
||
|
{
|
||
|
HDESK hDesk; // desktop assigned to new thread
|
||
|
int index; // index into deskArray
|
||
|
HWND hWndStatic; // "Run:" static control
|
||
|
HWND hWndEdit; // edit control for user input
|
||
|
HWND hWndBtn; // button for user input
|
||
|
HWND hWndNew; // button for new desktop
|
||
|
} ThreadData;
|
||
|
|
||
|
int gMaxIndex; // Highest index for array of desk handles
|
||
|
HWND gBaseWindow; // Window handle of default desktop window
|
||
|
HDESK gDeskArray[MAX_THREADS]; // Global array of desktop handles
|
||
|
HWND hWndArray[MAX_THREADS]; // Global array of window handles
|
||
|
HDC gHDCArray[MAX_THREADS]; // global array of memory device contexts
|
||
|
// these DCs store snapshots of the desktops
|
||
|
int gWidth, gHeight; // dimensions of desktop rectangles
|
||
|
|
||
|
//
|
||
|
// Keep track of how big the controls need to be to match the
|
||
|
// active system fixed font. These will help determine the
|
||
|
// minimum size of the switcher window.
|
||
|
//
|
||
|
int gStaticWidth; // Edit control label
|
||
|
int gEditWidth; // Edit control
|
||
|
int gBtnWidth; // Button to run app in edit control
|
||
|
int gNewWidth; // Button to create new desktop
|
||
|
|
||
|
HINSTANCE ghInst = NULL; // Global hInstance
|
||
|
#define DEFAULT_DESKTOP gDeskArray[0] // For easy reading
|
||
|
LONG APIENTRY EditProc (HWND, UINT, WPARAM, LPARAM);
|
||
|
TCHAR szAppName[] = TEXT("Desktop Switcher!");
|
||
|
TCHAR szClassName[] = TEXT("SwitcherWindow");
|
||
|
TCHAR szPreviewClass[]= TEXT("PreviewWindow");
|
||
|
|
||
|
/****************************************************************************
|
||
|
FUNCTION: StartNewDesktop
|
||
|
|
||
|
PURPOSE: Create or open a handle to a desktop and put a switcher thread
|
||
|
on it.
|
||
|
|
||
|
ARGUMENTS:
|
||
|
int nCount - Which desktop number this is
|
||
|
|
||
|
Assumes gDeskArray[nCount] == NULL
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
void StartNewDesktop (int nCount)
|
||
|
{
|
||
|
ThreadData *ptd;
|
||
|
TCHAR szDesk[50];
|
||
|
DWORD tID;
|
||
|
ptd = (ThreadData*)GlobalAlloc(GMEM_FIXED,sizeof(ThreadData));
|
||
|
if (ptd)
|
||
|
{
|
||
|
ptd->index = nCount;
|
||
|
//
|
||
|
// Give the desktop a name.
|
||
|
//
|
||
|
wsprintf (szDesk, PSZ(IDS_DESKTOPNAME), nCount+1);
|
||
|
//
|
||
|
// First, try to open an existing desktop
|
||
|
//
|
||
|
if ( !(ptd->hDesk = OpenDesktop (szDesk, 0, FALSE, GENERIC_ALL)))
|
||
|
{
|
||
|
//
|
||
|
// Failing an open, Create it
|
||
|
//
|
||
|
if (!(ptd->hDesk= CreateDesktop (szDesk, NULL,
|
||
|
NULL,0,MAXIMUM_ALLOWED,
|
||
|
NULL)))
|
||
|
{
|
||
|
|
||
|
MessageBox (NULL, PSZ(IDS_CREATEERROR),
|
||
|
PSZ(IDS_ERRCAPTION), MB_OK);
|
||
|
//
|
||
|
//Mark this array slot as invalid
|
||
|
//
|
||
|
gDeskArray[nCount] = NULL;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
if (ptd->hDesk)
|
||
|
{
|
||
|
//
|
||
|
// Save the handle to the global array. Start the new thread
|
||
|
//
|
||
|
gDeskArray[ptd->index] = ptd->hDesk;
|
||
|
CloseHandle(CreateThread(NULL, 0,
|
||
|
(LPTHREAD_START_ROUTINE)ThreadInit,
|
||
|
(LPVOID)ptd, 0, &tID));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Out of memory
|
||
|
//
|
||
|
MessageBox (NULL, PSZ(IDS_CREATEERROR),
|
||
|
PSZ(IDS_MEMERRCAPTION), MB_OK);
|
||
|
//
|
||
|
//Mark this array slot as invalid
|
||
|
//
|
||
|
gDeskArray[nCount] = NULL;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
/****************************************************************************
|
||
|
|
||
|
FUNCTION: CreateAllDesktops (cThreads)
|
||
|
|
||
|
PURPOSE: Creates desktops and assigns a switcher thread to each.
|
||
|
Updates the global desktop array.
|
||
|
|
||
|
ARGUMENTS:
|
||
|
int cThreads - Number of threads/desktops to open or create
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
void CreateAllDesktops (int cThreads)
|
||
|
{
|
||
|
ThreadData *ptdDefault;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Make sure we allocate for the default desktop first
|
||
|
//
|
||
|
ptdDefault = (ThreadData *)GlobalAlloc (GMEM_FIXED, sizeof(ThreadData));
|
||
|
if (!ptdDefault) {
|
||
|
return;
|
||
|
}
|
||
|
while (cThreads)
|
||
|
{
|
||
|
StartNewDesktop (cThreads);
|
||
|
cThreads--;
|
||
|
}
|
||
|
//
|
||
|
// Main thread is one of the running threads too
|
||
|
//
|
||
|
ptdDefault->index = 0;
|
||
|
ptdDefault->hDesk = DEFAULT_DESKTOP;
|
||
|
ThreadInit((LPVOID)ptdDefault);
|
||
|
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
|
||
|
FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||
|
|
||
|
PURPOSE: Creates the threads and desktops
|
||
|
COMMENTS: Each thread has a separate desktop assigned to it.
|
||
|
|
||
|
|
||
|
****************************************************************************/
|
||
|
int CALLBACK WinMain(
|
||
|
HINSTANCE hInstance,
|
||
|
HINSTANCE hPrevInstance,
|
||
|
LPSTR lpCmdLine,
|
||
|
int nCmdShow)
|
||
|
{
|
||
|
|
||
|
int cThreads; // number of desktops
|
||
|
|
||
|
ghInst = hInstance;
|
||
|
|
||
|
if (hPrevInstance)
|
||
|
{ // Other instances of app running?
|
||
|
return (FALSE); // Exit
|
||
|
} else {
|
||
|
if (!InitApplication ()) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// parse command line to determine number of desktops
|
||
|
// Assume 9 threads
|
||
|
|
||
|
cThreads = 9;
|
||
|
lpCmdLine = GetCommandLineA();
|
||
|
|
||
|
//
|
||
|
// Get past .exe name
|
||
|
//
|
||
|
while (*lpCmdLine != ' ' && *lpCmdLine != 0)
|
||
|
lpCmdLine++;
|
||
|
|
||
|
//
|
||
|
// Find the parameters
|
||
|
//
|
||
|
while (*lpCmdLine != 0)
|
||
|
{
|
||
|
|
||
|
// Eat white space
|
||
|
|
||
|
if (*lpCmdLine == ' ')
|
||
|
{
|
||
|
lpCmdLine++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Do we have a dash? If not, just exit the loop
|
||
|
//
|
||
|
if (*lpCmdLine++ != '-')
|
||
|
break;
|
||
|
|
||
|
switch (*lpCmdLine++)
|
||
|
{
|
||
|
case 't':
|
||
|
case 'T':
|
||
|
//
|
||
|
// How many threads?
|
||
|
//
|
||
|
|
||
|
while (*lpCmdLine == ' ')
|
||
|
lpCmdLine++;
|
||
|
|
||
|
if (*lpCmdLine == 0 || *lpCmdLine == '-')
|
||
|
continue;
|
||
|
|
||
|
cThreads = 0;
|
||
|
while (*lpCmdLine >= '0' && *lpCmdLine <= '9')
|
||
|
cThreads = cThreads * 10 + (*lpCmdLine++ - 0x30);
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create the threads - if zero was specified, default to 1.
|
||
|
// What does 0 threads mean?
|
||
|
if (cThreads == 0)
|
||
|
cThreads = 1;
|
||
|
else if (cThreads > MAX_THREADS)
|
||
|
cThreads = MAX_THREADS;
|
||
|
//
|
||
|
// Account for the main thread - only create extras
|
||
|
//
|
||
|
cThreads--;
|
||
|
gMaxIndex = cThreads; // Keep track of the highest array index
|
||
|
|
||
|
//
|
||
|
// Assign this here, since threads reference it
|
||
|
//
|
||
|
DEFAULT_DESKTOP = GetThreadDesktop(GetCurrentThreadId());
|
||
|
CreateAllDesktops (cThreads);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*************************************************************
|
||
|
FUNCTION: LoadResources
|
||
|
|
||
|
PURPOSE: Load string table entries
|
||
|
|
||
|
ARGUMENTS: None
|
||
|
|
||
|
RETURNS: True if all strings are loaded, False otherwise
|
||
|
|
||
|
*************************************************************/
|
||
|
|
||
|
BOOL LoadResources (void)
|
||
|
{
|
||
|
int i;
|
||
|
for (i=0;i<(LAST_STRING-FIRST_STRING+1);i++)
|
||
|
{
|
||
|
if (!LoadString (ghInst, FIRST_STRING + i, SwitchStrings[i], MAXSTRLEN))
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
/*************************************************************
|
||
|
|
||
|
FUNCTION: InitApplication
|
||
|
|
||
|
PURPOSE: Register the window class and init global variables
|
||
|
|
||
|
ARGUMENTS:
|
||
|
|
||
|
RETURNS:
|
||
|
TRUE if window class registration and other initialization succeeds
|
||
|
|
||
|
**************************************************************/
|
||
|
|
||
|
BOOL InitApplication (void) {
|
||
|
|
||
|
WNDCLASS wc;
|
||
|
HWND hTemp;
|
||
|
HWINSTA hWndSta;
|
||
|
RECT rect;
|
||
|
|
||
|
if (!LoadResources ())
|
||
|
return FALSE;
|
||
|
//
|
||
|
// Initialize the gHDCArray to all NULLS
|
||
|
//
|
||
|
ZeroMemory (gHDCArray, sizeof (gHDCArray));
|
||
|
hTemp = GetDesktopWindow(); // Call this so User will assign us a WindowStation.
|
||
|
//
|
||
|
// Initialize gWidth and gHeight
|
||
|
// Get the size of the screen, make gWidth/gHeight == scrnW/scrnH
|
||
|
//
|
||
|
GetClientRect (hTemp, &rect);
|
||
|
gWidth = rect.right/DIVISOR;
|
||
|
gHeight = rect.bottom/DIVISOR;
|
||
|
|
||
|
//
|
||
|
// Make sure this app has a windowstation
|
||
|
//
|
||
|
hWndSta = GetProcessWindowStation();
|
||
|
if (!hWndSta)
|
||
|
{
|
||
|
MessageBox (NULL, PSZ(IDS_WNDSTAERROR), PSZ(IDS_ERRCAPTION), MB_OK);
|
||
|
return FALSE;
|
||
|
}
|
||
|
//
|
||
|
// Register the main window class
|
||
|
//
|
||
|
wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
|
||
|
wc.lpfnWndProc = WndProc;
|
||
|
wc.cbClsExtra = 0;
|
||
|
wc.cbWndExtra = 0;
|
||
|
wc.hInstance = ghInst;
|
||
|
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
|
||
|
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
|
||
|
wc.hbrBackground = (HBRUSH)GetStockObject (WHITE_BRUSH);
|
||
|
wc.lpszMenuName = NULL;
|
||
|
wc.lpszClassName = szClassName;
|
||
|
|
||
|
if (!RegisterClass (&wc))
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Register the preview window class
|
||
|
//
|
||
|
wc.style = 0;
|
||
|
wc.lpfnWndProc = PreviewWndProc;
|
||
|
wc.lpszClassName = szPreviewClass;
|
||
|
|
||
|
if (!RegisterClass (&wc)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*******************************************************************
|
||
|
|
||
|
FUNCTION: ThreadInit
|
||
|
|
||
|
PURPOSE: Given a desktop handle, create a window on it to allow switching
|
||
|
among desktops.
|
||
|
|
||
|
ARGUMENTS:
|
||
|
LPVOID tData - Thread-specific data
|
||
|
|
||
|
RETURNS:
|
||
|
nothing
|
||
|
********************************************************************/
|
||
|
|
||
|
void ThreadInit(LPVOID tData)
|
||
|
{
|
||
|
MSG msg;
|
||
|
HWND hWnd;
|
||
|
HDC hTemp;
|
||
|
int width; // window width
|
||
|
USEROBJECTFLAGS uof; // To set Desktop attributes
|
||
|
|
||
|
uof.fInherit = FALSE; // If an app inherits multiple desktop handles,
|
||
|
// it could run on any one of those desktops
|
||
|
uof.fReserved = FALSE;
|
||
|
//
|
||
|
// Let other account processes hook this desktop
|
||
|
//
|
||
|
uof.dwFlags = DF_ALLOWOTHERACCOUNTHOOK;
|
||
|
SetUserObjectInformation (((ThreadData*)tData)->hDesk,
|
||
|
UOI_FLAGS,
|
||
|
(LPVOID)&uof,
|
||
|
sizeof(uof));
|
||
|
//
|
||
|
// Make sure the handle is valid
|
||
|
//
|
||
|
if (gDeskArray[((ThreadData*)tData)->index])
|
||
|
{
|
||
|
//
|
||
|
// Assign new desktop to this thread
|
||
|
//
|
||
|
SetThreadDesktop (((ThreadData*)tData)->hDesk);
|
||
|
// create the cool switcher window
|
||
|
if ((gMaxIndex+1) * gWidth > MINWINDOWWIDTH)
|
||
|
{
|
||
|
width = (gMaxIndex+1) * gWidth;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
width = MINWINDOWWIDTH;
|
||
|
}
|
||
|
hWnd = CreateWindow (szClassName,
|
||
|
szAppName,
|
||
|
WS_MINIMIZEBOX|WS_OVERLAPPED|WS_VISIBLE|WS_BORDER|WS_CAPTION|WS_SYSMENU,
|
||
|
0, 0, width, 30+gHeight + CONTROLHEIGHT,
|
||
|
NULL, NULL, ghInst, tData);
|
||
|
if (!hWnd) // bag it
|
||
|
{
|
||
|
gDeskArray[((ThreadData*)tData)->index] = NULL;
|
||
|
GlobalFree (tData);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
//update the global window array
|
||
|
//
|
||
|
hWndArray[((ThreadData*)tData)->index] = hWnd;
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
GlobalFree (tData);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Acquire and dispatch messages until a WM_QUIT message is received.
|
||
|
//
|
||
|
while (GetMessage(&msg, NULL, 0, 0))
|
||
|
{
|
||
|
TranslateMessage(&msg);// Translates virtual key codes
|
||
|
DispatchMessage(&msg); // Dispatches message to window
|
||
|
}
|
||
|
//
|
||
|
// Switch back to the default desktop and close the user-created one
|
||
|
//
|
||
|
SetThreadDesktop (DEFAULT_DESKTOP);
|
||
|
SwitchDesktop (DEFAULT_DESKTOP);
|
||
|
CloseDesktop (((ThreadData*)tData)->hDesk);
|
||
|
//
|
||
|
// NULL out the global array entry so other threads won't try to switch to
|
||
|
// this desktop
|
||
|
//
|
||
|
gDeskArray[((ThreadData*)tData)->index] = NULL;
|
||
|
//
|
||
|
// cleanup
|
||
|
//
|
||
|
hTemp = gHDCArray[((ThreadData*)tData)->index];
|
||
|
gHDCArray[((ThreadData*)tData)->index] = NULL;
|
||
|
DeleteObject (hTemp);
|
||
|
GlobalFree (tData);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************
|
||
|
|
||
|
FUNCTION: SaveScreen
|
||
|
|
||
|
PURPOSE: Save a snapshot of the desktop to its corresponding
|
||
|
memory DC. StretchBlt!
|
||
|
|
||
|
ARGUMENTS:
|
||
|
int index - Index of memory DC to save bits to
|
||
|
|
||
|
RETURNS: nothing
|
||
|
************************************************************************/
|
||
|
|
||
|
void SaveScreen (int index) {
|
||
|
HDC hdc;
|
||
|
int xSize, ySize;
|
||
|
|
||
|
xSize = GetSystemMetrics (SM_CXSCREEN);
|
||
|
ySize = GetSystemMetrics (SM_CYSCREEN);
|
||
|
|
||
|
if (hdc = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL))
|
||
|
{
|
||
|
//
|
||
|
// Copy the desktop to a memory DC
|
||
|
//
|
||
|
StretchBlt (gHDCArray[index], 0, 0, gWidth*2, gHeight*2,
|
||
|
hdc, 0, 0, xSize, ySize, SRCCOPY);
|
||
|
DeleteDC (hdc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*******************************************************************************
|
||
|
|
||
|
FUNCTION: PaintMainWnd
|
||
|
|
||
|
PURPOSE: Draw the main window. This window has rectangles with miniature snapshots
|
||
|
of each desktop. The snapshots are retrieved from the gHDCArray and StretchBlt'd to
|
||
|
the right size.
|
||
|
|
||
|
ARGUMENTS:
|
||
|
HWND hWnd - Window to draw
|
||
|
|
||
|
RETURNS: nothing
|
||
|
|
||
|
*******************************************************************************/
|
||
|
|
||
|
void PaintMainWnd (HWND hWnd)
|
||
|
{
|
||
|
PAINTSTRUCT ps;
|
||
|
RECT rect;
|
||
|
HPEN hPen, hOld;
|
||
|
ThreadData *ptd;
|
||
|
TCHAR szName[4]; // short name!
|
||
|
int myThread;
|
||
|
HDC hDC; // always need a dc for drawing
|
||
|
int i; // my favorite loop counter
|
||
|
|
||
|
//
|
||
|
// get the array index of this window
|
||
|
//
|
||
|
ptd = (ThreadData *)GetWindowLong (hWnd, GWL_USERDATA);
|
||
|
myThread = ptd->index;
|
||
|
|
||
|
//
|
||
|
//Draw a rectangle for each desktop
|
||
|
//
|
||
|
hDC = BeginPaint (hWnd, &ps);
|
||
|
if (GetClientRect (hWnd, &rect))
|
||
|
{
|
||
|
int right, left;
|
||
|
//
|
||
|
// leave space for edit control and button
|
||
|
//
|
||
|
rect.bottom -= CONTROLHEIGHT;
|
||
|
hPen = CreatePen (PS_SOLID | PS_INSIDEFRAME, 2, RGB(255,16,16));
|
||
|
hOld = SelectObject (hDC, hPen);
|
||
|
|
||
|
//
|
||
|
// draw each desktop rectangle
|
||
|
//
|
||
|
for (i=0;i<=gMaxIndex;i++)
|
||
|
{
|
||
|
right = gWidth * i + gWidth;
|
||
|
left = right - gWidth;
|
||
|
//
|
||
|
// If no snapshot is available, be boring
|
||
|
//
|
||
|
if (!gHDCArray[i])
|
||
|
{
|
||
|
Rectangle (hDC, left, rect.top, right, gHeight);
|
||
|
wsprintf (szName, TEXT("%d"), i+1);
|
||
|
TextOut (hDC, left+(gWidth/2),
|
||
|
gHeight/2, szName,
|
||
|
lstrlen (szName));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// BitBlt the snapshot into the rectangle
|
||
|
//
|
||
|
StretchBlt (hDC, left, rect.top, gWidth, gHeight,
|
||
|
gHDCArray[i], 0, 0, gWidth*2, gHeight*2, SRCCOPY);
|
||
|
//
|
||
|
// draw lines around the rectangle
|
||
|
//
|
||
|
MoveToEx (hDC, left, rect.top, NULL);
|
||
|
LineTo (hDC, left, gHeight);
|
||
|
MoveToEx (hDC, right, rect.top, NULL);
|
||
|
LineTo (hDC, right, gHeight);
|
||
|
//
|
||
|
// underline the active one
|
||
|
//
|
||
|
if (myThread == i)
|
||
|
{
|
||
|
MoveToEx (hDC, left, gHeight, NULL);
|
||
|
LineTo (hDC, right, gHeight);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// cleanup
|
||
|
//
|
||
|
SelectObject (hDC, hOld);
|
||
|
DeleteObject (hPen);
|
||
|
}
|
||
|
|
||
|
EndPaint (hWnd, &ps);
|
||
|
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
|
||
|
FUNCTION: RunApp (HWND)
|
||
|
|
||
|
PURPOSE: Create a process, using contents of HWND's edit control
|
||
|
as the command line.
|
||
|
|
||
|
ARGUMENTS:
|
||
|
HWND hWnd - Handle of active switcher window
|
||
|
|
||
|
RETURNS: nothing
|
||
|
|
||
|
COMMENTS: Make sure proper desktop is passed in STARTUPINFO
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
void RunApp (HWND hWnd)
|
||
|
{
|
||
|
TCHAR szDesk[100]; // data holder
|
||
|
TCHAR szExec[100]; // Command line
|
||
|
STARTUPINFO sui; // Process startup info
|
||
|
PROCESS_INFORMATION pi; // info returned from CreateProcess
|
||
|
ThreadData *ptd;
|
||
|
|
||
|
ptd = (ThreadData*)GetWindowLong (hWnd, GWL_USERDATA);
|
||
|
//
|
||
|
// Most sui members will be 0
|
||
|
//
|
||
|
ZeroMemory ((PVOID)&sui, sizeof(sui));
|
||
|
//
|
||
|
//Get the command line to execute
|
||
|
//
|
||
|
GetDlgItemText (hWnd, IDC_RUNME, szExec, 100*sizeof(TCHAR));
|
||
|
//
|
||
|
//Get the current desktop name
|
||
|
//
|
||
|
GetUserObjectInformation (GetThreadDesktop (GetCurrentThreadId()),
|
||
|
UOI_NAME,
|
||
|
szDesk,
|
||
|
100*sizeof(TCHAR),
|
||
|
NULL);
|
||
|
sui.cb = sizeof (sui);
|
||
|
//
|
||
|
// Need the lpDesktop member so the new process runs on this desktop
|
||
|
// The lpDesktop member was reserved in previous versions of NT
|
||
|
//
|
||
|
sui.lpDesktop = szDesk;
|
||
|
CreateProcess (NULL, // image name
|
||
|
szExec, // command line
|
||
|
NULL, // process security attributes
|
||
|
NULL, // thread security attributes
|
||
|
TRUE, // inherit handles
|
||
|
CREATE_DEFAULT_ERROR_MODE|CREATE_SEPARATE_WOW_VDM,
|
||
|
NULL, // environment block
|
||
|
NULL, // current directory
|
||
|
&sui, // STARTUPINFO
|
||
|
&pi); // PROCESS_INFORMATION
|
||
|
|
||
|
}
|
||
|
/****************************************************************************
|
||
|
FUNCTION: GetFontHeight
|
||
|
|
||
|
PURPOSE: Set up widths for controls based on size of the system
|
||
|
font.
|
||
|
|
||
|
ARGUMENTS:
|
||
|
HWND hWnd - Window whose DC to use.
|
||
|
|
||
|
RETURNS:
|
||
|
Return the height of the system fixed font to use for
|
||
|
the controls.
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
LONG GetFontHeight (HWND hWnd)
|
||
|
{
|
||
|
HDC hdc;
|
||
|
TEXTMETRIC tm;
|
||
|
SIZE size;
|
||
|
#define MARGIN 7 // extra space on the button around the text
|
||
|
|
||
|
hdc = GetDC (hWnd);
|
||
|
if (!GetTextMetrics (hdc, &tm))
|
||
|
{
|
||
|
//
|
||
|
// Use defaults
|
||
|
//
|
||
|
gStaticWidth = STATICWIDTH;
|
||
|
gBtnWidth = BTNWIDTH;
|
||
|
gEditWidth = EDITWIDTH;
|
||
|
gNewWidth = BTNWIDTH + 25;
|
||
|
return CONTROLHEIGHT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// GetTextExtentPoint32 fills in size with the width and height of
|
||
|
// a string.
|
||
|
//
|
||
|
GetTextExtentPoint32 (hdc, PSZ(IDS_RUNLABEL), lstrlen(PSZ(IDS_RUNLABEL)), &size);
|
||
|
gStaticWidth = size.cx + MARGIN;
|
||
|
gEditWidth = EDITWIDTH;
|
||
|
GetTextExtentPoint32 (hdc, PSZ(IDS_BTNLABEL), lstrlen(PSZ(IDS_BTNLABEL)), &size);
|
||
|
gBtnWidth = size.cx + MARGIN;
|
||
|
GetTextExtentPoint32 (hdc, PSZ(IDS_NEWLABEL), lstrlen(PSZ(IDS_NEWLABEL)), &size);
|
||
|
gNewWidth = size.cx + MARGIN;
|
||
|
ReleaseDC (hWnd, hdc);
|
||
|
return tm.tmHeight + 2;
|
||
|
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
FUNCTION: TitleWindow
|
||
|
|
||
|
PURPOSE: Give a switcher window an appropriate title, using its
|
||
|
desktop name.
|
||
|
|
||
|
ARGUMENTS:
|
||
|
HWND hWnd - Window to title
|
||
|
|
||
|
RETURNS: nothing
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
void TitleWindow (HWND hWnd)
|
||
|
{
|
||
|
TCHAR *szTitle, *szName;
|
||
|
UINT nBytes = 0;
|
||
|
|
||
|
//
|
||
|
// How long is the desktop name?
|
||
|
//
|
||
|
GetUserObjectInformation (GetThreadDesktop(GetCurrentThreadId()),
|
||
|
UOI_NAME,
|
||
|
(LPVOID)&nBytes, // not used since cbInfo is 0
|
||
|
0,
|
||
|
&nBytes);
|
||
|
szName = (LPTSTR)GlobalAlloc (GPTR, nBytes);
|
||
|
if (!szName)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
//
|
||
|
// Now get the desktop name
|
||
|
//
|
||
|
GetUserObjectInformation (GetThreadDesktop(GetCurrentThreadId()),
|
||
|
UOI_NAME,
|
||
|
(LPVOID)szName,
|
||
|
nBytes,
|
||
|
&nBytes);
|
||
|
//
|
||
|
// Now make the window title
|
||
|
//
|
||
|
szTitle = (LPTSTR)GlobalAlloc (
|
||
|
GPTR,
|
||
|
(lstrlen(szAppName)+lstrlen(TEXT(" - "))) * sizeof(TCHAR) + nBytes);
|
||
|
|
||
|
if (!szTitle)
|
||
|
{
|
||
|
GlobalFree (szName);
|
||
|
return;
|
||
|
}
|
||
|
wsprintf (szTitle, TEXT("%s - %s"), szAppName, szName);
|
||
|
SetWindowText (hWnd, szTitle);
|
||
|
//
|
||
|
// Cleanup
|
||
|
//
|
||
|
GlobalFree (szName);
|
||
|
GlobalFree (szTitle);
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
|
||
|
FUNCTION: CreateControls (ThreadData *, HWND)
|
||
|
|
||
|
PURPOSE: Creates UI controls on a switcher window
|
||
|
|
||
|
ARGUMENTS:
|
||
|
ThreadData *ptd - Thread specific data to use/init
|
||
|
HWND hWnd - Parent window
|
||
|
|
||
|
RETURNS:
|
||
|
nothing
|
||
|
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
void CreateControls (ThreadData *ptd, HWND hWnd)
|
||
|
{
|
||
|
LONG oldproc;
|
||
|
|
||
|
//
|
||
|
// Create the edit control label
|
||
|
//
|
||
|
ptd->hWndStatic = CreateWindow (TEXT("static"), PSZ(IDS_RUNLABELHOT),
|
||
|
WS_CHILD | WS_VISIBLE,
|
||
|
0,0,0,0, hWnd, (HMENU)IDC_STATIC,
|
||
|
ghInst, NULL);
|
||
|
//
|
||
|
// Create the edit control
|
||
|
//
|
||
|
ptd->hWndEdit = CreateWindow (TEXT("Edit"), TEXT(""),
|
||
|
WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,
|
||
|
0,0,0,0, hWnd, (HMENU)IDC_RUNME,
|
||
|
ghInst, NULL);
|
||
|
|
||
|
//
|
||
|
// set the edit control proc and save the default one
|
||
|
//
|
||
|
oldproc = GetWindowLong (ptd->hWndEdit, GWL_WNDPROC);
|
||
|
SetWindowLong (ptd->hWndEdit, GWL_WNDPROC, (LONG)EditProc);
|
||
|
SetWindowLong (ptd->hWndEdit, GWL_USERDATA, oldproc);
|
||
|
|
||
|
//
|
||
|
// Create the execution button
|
||
|
//
|
||
|
ptd->hWndBtn = CreateWindow (TEXT("Button"), PSZ(IDS_BTNLABEL),
|
||
|
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
|
||
|
0,0,0,0, hWnd, (HMENU)IDC_RUNMEBTN,
|
||
|
ghInst, NULL);
|
||
|
//
|
||
|
// Create a button for creating new desktops
|
||
|
//
|
||
|
ptd->hWndNew = CreateWindow (TEXT("button"), PSZ(IDS_NEWLABELHOT),
|
||
|
WS_CHILD | WS_VISIBLE,
|
||
|
0,0,0,0, hWnd, (HMENU)IDC_NEWDSKBTN,
|
||
|
ghInst, NULL);
|
||
|
|
||
|
}
|
||
|
/****************************************************************************
|
||
|
|
||
|
FUNCTION: WndProc(UINT, WPARAM, LPARAM)
|
||
|
|
||
|
PURPOSE: Processes messages to the Switcher window
|
||
|
|
||
|
MESSAGES:
|
||
|
WM_RBUTTONDOWN - Switch to desktop whose rectangle is under the mouse
|
||
|
WM_CLOSE - Send WM_CLOSE to all windows in hWndArray
|
||
|
WM_LBUTTONDOWN - Create a preview window to display a larger view of
|
||
|
a desktop until WM_LBUTTONUP
|
||
|
WM_COMMAND - Respond to button pushes or edit control entry
|
||
|
WM_SYSCHAR - ALT+R sets focus to the edit control
|
||
|
ALT+N creates a new desktop
|
||
|
WM_CHAR - Carriage return executes command line from
|
||
|
the edit control
|
||
|
WM_HOTKEY
|
||
|
WM_KEYDOWN - Respond to function keys by switching to
|
||
|
the appropriate desktop. Example, F2 means switch
|
||
|
to Desktop2. Ctrl-F# allows switching without
|
||
|
giving focus to the switcher window
|
||
|
WM_LBUTTONUP - Close the active preview window
|
||
|
WM_CREATE - Initialize controls and global array entries
|
||
|
WM_SIZE - Size the child controls correctly
|
||
|
|
||
|
|
||
|
COMMENTS:
|
||
|
|
||
|
****************************************************************************/
|
||
|
|
||
|
LONG APIENTRY WndProc(
|
||
|
HWND hWnd,
|
||
|
UINT message, // type of message
|
||
|
WPARAM wParam, // additional information
|
||
|
LPARAM lParam) // additional information
|
||
|
{
|
||
|
|
||
|
int newThread; // Thread index to switch to
|
||
|
int i;
|
||
|
ThreadData *ptd;
|
||
|
static HWND hShowing = NULL; // which preview window is being shown
|
||
|
static LONG fntHeight = CONTROLHEIGHT; // height for the edit control
|
||
|
switch (message)
|
||
|
{
|
||
|
case WM_CREATE:
|
||
|
{
|
||
|
HDC hDC;
|
||
|
HBITMAP hBmp;
|
||
|
|
||
|
// Create edit control, button, and label at the bottom of the window
|
||
|
// This will allow the user to input a program to run
|
||
|
|
||
|
SetWindowLong (hWnd, GWL_USERDATA,
|
||
|
(LONG)((CREATESTRUCT *)lParam)->lpCreateParams);
|
||
|
ptd = (ThreadData *)GetWindowLong (hWnd, GWL_USERDATA);
|
||
|
CreateControls (ptd, hWnd);
|
||
|
fntHeight = GetFontHeight (hWnd);
|
||
|
//
|
||
|
// initialize the DC array entry
|
||
|
//
|
||
|
hDC = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL);
|
||
|
gHDCArray[ptd->index] = CreateCompatibleDC (hDC);
|
||
|
//
|
||
|
// Halftone is the best stretching algorithm
|
||
|
//
|
||
|
SetStretchBltMode (gHDCArray[ptd->index], HALFTONE);
|
||
|
SetBrushOrgEx (gHDCArray[ptd->index], 0, 0, NULL);
|
||
|
//
|
||
|
// Use a bitmap the same size as the desktop preview rectangles
|
||
|
//
|
||
|
hBmp = CreateCompatibleBitmap (hDC, gWidth*2, gHeight*2);
|
||
|
SelectObject (gHDCArray[ptd->index], hBmp);
|
||
|
DeleteDC (hDC);
|
||
|
SaveScreen (ptd->index);
|
||
|
TitleWindow (hWnd);
|
||
|
//
|
||
|
// Register hot keys
|
||
|
//
|
||
|
for (i=0;i<10;i++)
|
||
|
{
|
||
|
RegisterHotKey (hWnd, VK_F1+i, MOD_CONTROL, VK_F1+i);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
case WM_SIZE:
|
||
|
{
|
||
|
//
|
||
|
// Put the child controls at the right places
|
||
|
//
|
||
|
#define PADDING 5
|
||
|
|
||
|
RECT rect;
|
||
|
ThreadData *ptd;
|
||
|
if (GetClientRect (hWnd, &rect))
|
||
|
{
|
||
|
ptd = (ThreadData *)GetWindowLong (hWnd, GWL_USERDATA);
|
||
|
MoveWindow (ptd->hWndStatic, 0, rect.bottom - CONTROLHEIGHT,
|
||
|
gStaticWidth, fntHeight + PADDING, TRUE);
|
||
|
|
||
|
MoveWindow (ptd->hWndEdit, gStaticWidth + 5,
|
||
|
rect.bottom - fntHeight - PADDING,
|
||
|
gEditWidth, fntHeight+PADDING, TRUE);
|
||
|
|
||
|
MoveWindow (ptd->hWndBtn, gStaticWidth + gEditWidth + 10,
|
||
|
rect.bottom - fntHeight - PADDING,
|
||
|
gBtnWidth, fntHeight+PADDING, TRUE);
|
||
|
|
||
|
MoveWindow (ptd->hWndNew, gStaticWidth+gEditWidth+gBtnWidth+15,
|
||
|
rect.bottom - fntHeight- PADDING,
|
||
|
gNewWidth, fntHeight+PADDING, TRUE);
|
||
|
|
||
|
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
case WM_PAINT:
|
||
|
PaintMainWnd (hWnd);
|
||
|
return 0;
|
||
|
|
||
|
case WM_RBUTTONDOWN:
|
||
|
{
|
||
|
//
|
||
|
// Find the rectangle in which the button was pressed
|
||
|
//
|
||
|
POINTS pts;
|
||
|
ThreadData *ptd;
|
||
|
ptd = (ThreadData *)GetWindowLong(hWnd, GWL_USERDATA);
|
||
|
pts = MAKEPOINTS (lParam);
|
||
|
if (pts.y > gHeight)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
newThread = pts.x/gWidth;
|
||
|
|
||
|
//
|
||
|
// Get a snapshot of the current desktop
|
||
|
//
|
||
|
SaveScreen (ptd->index);
|
||
|
|
||
|
//
|
||
|
// Switch to the selected desktop
|
||
|
//
|
||
|
if (!gDeskArray[newThread])
|
||
|
{
|
||
|
StartNewDesktop (newThread);
|
||
|
}
|
||
|
if (!SwitchDesktop (gDeskArray[newThread]))
|
||
|
MessageBox (hWnd,
|
||
|
PSZ(IDS_BADDESKTOP),
|
||
|
PSZ(IDS_ERRCAPTION), MB_OK);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
case WM_LBUTTONDOWN:
|
||
|
//
|
||
|
// show the preview window
|
||
|
//
|
||
|
{
|
||
|
POINTS pts;
|
||
|
POINT ptl;
|
||
|
int *index;
|
||
|
|
||
|
pts = MAKEPOINTS (lParam);
|
||
|
if (pts.y > gHeight)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
newThread = pts.x/gWidth;
|
||
|
index = GlobalAlloc (GMEM_FIXED, sizeof(int));
|
||
|
if (!index)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
*index = newThread;
|
||
|
//
|
||
|
// Want to show the preview window where the button was clicked.
|
||
|
// Map the given points to screen coords.
|
||
|
// ClientToScreen is expecting a POINT structure, not a POINTS
|
||
|
//
|
||
|
ptl.x = (LONG)pts.x;
|
||
|
ptl.y = (LONG)pts.y;
|
||
|
ClientToScreen (hWnd, &ptl);
|
||
|
hShowing = CreateWindow (szPreviewClass, TEXT(""),
|
||
|
WS_POPUP | WS_VISIBLE | WS_BORDER,
|
||
|
ptl.x+3,
|
||
|
ptl.y+3,
|
||
|
gWidth*2,
|
||
|
gHeight*2,
|
||
|
hWnd,
|
||
|
(HMENU)0, ghInst, (LPVOID)index);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
case WM_CHAR:
|
||
|
if (wParam == VK_RETURN)
|
||
|
{
|
||
|
PostMessage (hWnd, WM_COMMAND, (WPARAM)IDC_RUNMEBTN, 0);
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
case WM_SYSCHAR:
|
||
|
{
|
||
|
ThreadData *ptd;
|
||
|
ptd = (ThreadData *)GetWindowLong(hWnd, GWL_USERDATA);
|
||
|
switch (wParam)
|
||
|
{
|
||
|
// alt+r == focus on the edit control
|
||
|
case TEXT('r'):
|
||
|
case TEXT('R'):
|
||
|
if (GetKeyState (VK_MENU))
|
||
|
{
|
||
|
SetFocus (ptd->hWndEdit);
|
||
|
}
|
||
|
return 0;
|
||
|
// alt+n = create a new desktop
|
||
|
case TEXT('n'):
|
||
|
case TEXT('N'):
|
||
|
if (GetKeyState (VK_MENU))
|
||
|
{
|
||
|
PostMessage (hWnd, WM_COMMAND, (WPARAM)IDC_NEWDSKBTN, 0);
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
case WM_HOTKEY:
|
||
|
case WM_KEYDOWN:
|
||
|
//
|
||
|
// F1-F9 switches to corresponding desktop
|
||
|
//
|
||
|
|
||
|
if ((wParam >= VK_F1 && wParam <= VK_F10)
|
||
|
&& (wParam - VK_F1 <= (UINT)gMaxIndex))
|
||
|
{
|
||
|
LONG x, y;
|
||
|
x = (wParam - VK_F1) * gWidth + 2;
|
||
|
y = gHeight - 4;
|
||
|
PostMessage (hWnd, WM_RBUTTONDOWN, 0, MAKELPARAM (x, y));
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
case WM_SETFOCUS:
|
||
|
case WM_NCLBUTTONUP:
|
||
|
case WM_LBUTTONUP:
|
||
|
|
||
|
//
|
||
|
// destroy the preview window
|
||
|
//
|
||
|
if (hShowing)
|
||
|
{
|
||
|
DestroyWindow (hShowing);
|
||
|
hShowing = NULL;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
|
||
|
|
||
|
case WM_CLOSE:
|
||
|
//
|
||
|
// to be safe, check for a preview window
|
||
|
//
|
||
|
if (hShowing)
|
||
|
{
|
||
|
DestroyWindow (hShowing);
|
||
|
hShowing = NULL;
|
||
|
}
|
||
|
//
|
||
|
// go to the default desktop so the DestroyWindow calls all succeed
|
||
|
//
|
||
|
SwitchDesktop (DEFAULT_DESKTOP);
|
||
|
//
|
||
|
// kill the window on this desktop
|
||
|
// all the windows will be destroyed if this is the default desktop
|
||
|
//
|
||
|
for (i=gMaxIndex;i>=0;i--)
|
||
|
{
|
||
|
DestroyWindow (hWndArray[i]);
|
||
|
}
|
||
|
//
|
||
|
// Unregister the hot keys
|
||
|
//
|
||
|
for (i=0;i<10;i++)
|
||
|
{
|
||
|
UnregisterHotKey (hWnd,VK_F1+i);
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
case WM_DESTROY: // message: window being destroyed
|
||
|
|
||
|
PostQuitMessage(0);
|
||
|
return 0;
|
||
|
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
{
|
||
|
|
||
|
switch (LOWORD(wParam))
|
||
|
{
|
||
|
case IDC_RUNMEBTN:
|
||
|
{
|
||
|
RunApp (hWnd);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
case IDC_NEWDSKBTN:
|
||
|
//
|
||
|
// Create a new desktop and resize the windows to show it.
|
||
|
//
|
||
|
{
|
||
|
RECT rect;
|
||
|
int i;
|
||
|
if (gMaxIndex + 1 < MAX_THREADS)
|
||
|
{
|
||
|
gMaxIndex++;
|
||
|
StartNewDesktop (gMaxIndex);
|
||
|
GetWindowRect (hWnd,&rect);
|
||
|
for (i=0;i<gMaxIndex;i++)
|
||
|
{
|
||
|
MoveWindow (hWndArray[i],
|
||
|
rect.left, rect.top,
|
||
|
rect.right + gWidth, rect.bottom-rect.top,
|
||
|
TRUE);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
return DefWindowProc (hWnd, message, wParam, lParam);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
default: // Passes it on if unprocessed
|
||
|
return (DefWindowProc (hWnd, message, wParam, lParam));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/***********************************************************
|
||
|
|
||
|
FUNCTION: PreviewWndProc
|
||
|
|
||
|
PURPOSE: Displays an enlarged view of the last snapshot of a desktop
|
||
|
|
||
|
************************************************************/
|
||
|
|
||
|
LONG APIENTRY PreviewWndProc (HWND hWnd,
|
||
|
UINT msg,
|
||
|
WPARAM wParam,
|
||
|
LPARAM lParam)
|
||
|
{
|
||
|
|
||
|
int *index;
|
||
|
switch (msg)
|
||
|
{
|
||
|
case WM_CREATE:
|
||
|
//
|
||
|
// save the index
|
||
|
//
|
||
|
SetWindowLong (hWnd, GWL_USERDATA,
|
||
|
(LONG)((CREATESTRUCT *)lParam)->lpCreateParams);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
case WM_PAINT:
|
||
|
{
|
||
|
HDC hdc;
|
||
|
PAINTSTRUCT ps;
|
||
|
index = (int *)GetWindowLong (hWnd, GWL_USERDATA);
|
||
|
hdc = BeginPaint (hWnd, &ps);
|
||
|
//
|
||
|
// slap in the desktop picture
|
||
|
//
|
||
|
BitBlt (hdc, 0, 0, gWidth*2, gHeight*2,
|
||
|
gHDCArray[*index], 0, 0, SRCCOPY);
|
||
|
EndPaint (hWnd, &ps);
|
||
|
return 0;
|
||
|
}
|
||
|
case WM_LBUTTONUP:
|
||
|
{
|
||
|
//
|
||
|
// In case the button is released in my client area
|
||
|
//
|
||
|
HWND hp;
|
||
|
hp = GetWindow (hWnd, GW_OWNER);
|
||
|
PostMessage (hp, msg, wParam, lParam);
|
||
|
return 0;
|
||
|
}
|
||
|
case WM_CLOSE:
|
||
|
{
|
||
|
//
|
||
|
// cleanup the index pointer
|
||
|
//
|
||
|
index = (int *)GetWindowLong (hWnd, GWL_USERDATA);
|
||
|
GlobalFree (index);
|
||
|
|
||
|
return DefWindowProc (hWnd, msg, wParam, lParam);
|
||
|
}
|
||
|
default:
|
||
|
return DefWindowProc (hWnd, msg, wParam, lParam);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/********************************************
|
||
|
|
||
|
FUNCTION: EditProc
|
||
|
|
||
|
PURPOSE: subclass the edit control to handle carriage returns
|
||
|
|
||
|
********************************************/
|
||
|
|
||
|
LONG APIENTRY EditProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
if (WM_CHAR == msg && (TCHAR)wParam == 0xD)
|
||
|
{
|
||
|
PostMessage (GetParent (hWnd), WM_COMMAND, (WPARAM)IDC_RUNMEBTN, 0);
|
||
|
return 0;
|
||
|
}
|
||
|
//
|
||
|
// call the default edit control procedure
|
||
|
//
|
||
|
return CallWindowProc ( (WNDPROC)GetWindowLong (hWnd, GWL_USERDATA),
|
||
|
hWnd, msg, wParam, lParam);
|
||
|
}
|
||
|
|