/****************************** Module Header ******************************\
* Module Name: demo.c
*
* Copyright (c) 1991, Microsoft Corporation
*
* Yet another User test program.
*
* History:
* 04-17-90 ScottLu	Created.
* 09-09-91 PetrusW	Rewrote.
*			create as many threads (windows) as we wish
*			change the UI to MDI
\***************************************************************************/

#include <stdlib.h>

#include "mltithrd.h"
#include <stdarg.h>

#define CCOLORS 16
#define CPOINTS 20
#define COBJS	1

#define DbgPrint(a); {}

/*
 * Some handy globals.
 */
HANDLE ghModule;
HWND   ghwndMain = NULL;
HBRUSH ghbrWhite, ghbrBlack;
HBRUSH gahbrColor[CCOLORS];
int    cmdDemo	 = 0;
BOOL   bKillMe = FALSE;

HMENU  hMenu, hMenuWindow;

DWORD aclr[CCOLORS] = {
    0x00000000,    // black
    0x007F0000,    // red
    0x00007F00,    // green
    0x007F7F00,    // mustard/brown
    0x0000007F,    // blue
    0x007F007F,    // magenta
    0x00007F7F,    // cyan
    0x00BFBFBF,    // light gray
    0x007F7F7F,    // dark gray
    0x00FF0000,    // bright red
    0x0000FF00,    // bright green
    0x00FFFF00,    // bright yellow
    0x000000FF,    // bright blue
    0x00FF00FF,    // bright magenta
    0x0000FFFF,    // bright cyan
    0x00FFFFFF     // bright white
};


typedef struct _b {
    POINT apt[CPOINTS];
    int dx;
    int dy;
    int vx;
    int vy;
    int ax;
    int ay;
    int iColor;
} B;


typedef struct _ThreadBlockInfo {
	HANDLE	hThread;
	BOOL	bKillThrd;
	HWND	hwndClient;
	HWND	hwndThreadWindow;
	LONG	lThreadId;
	char	CaptionBarText[SIZEOFCAPTIONTEXT];
	B	gab[COBJS];
	RECT	rcClient;
	HDC	hdcThreadWindow;
	HANDLE	hSem;
} THREADBLOCKINFO, *PTHREADBLOCKINFO;


typedef struct _node {
    THREADBLOCKINFO ThreadWindow;
    HANDLE	    hNext;
} NODE, *PNODE;


/*
 * Forward declarations.
 */
BOOL InitializeApp   (void);
LONG MainWndProc     (HWND, UINT, DWORD, LONG);
LONG ThreadWndProc   (HWND, UINT, DWORD, LONG);
LONG About	     (HWND, UINT, DWORD, LONG);
LONG StartBounce     (PTHREADBLOCKINFO);
void DrawBox	     (int, int, HBRUSH,	PTHREADBLOCKINFO);
void MoveBox	     (int *px, int *py, int *pdx, int *pdy, int *pvx, int *pvy, int ax, int ay,	PTHREADBLOCKINFO pThreadBlockInfo);
void BounceProc      (PTHREADBLOCKINFO);
BOOL StartDemo	     (int, PTHREADBLOCKINFO);
int  GetRandomVector ();
void InitializeBoxes (BOOL, PTHREADBLOCKINFO);

/***************************************************************************\
* main
*
*
* History:
* 04-17-91 ScottLu      Created.
\***************************************************************************/

int main(
    int argc,
    char *argv[])
{
    MSG msg;

    // this will change to something more reasonable

    ghModule = GetModuleHandle(NULL);

    if (!InitializeApp()) {
        DbgPrint("MLTITHRD: InitializeApp failure!\n");
        return 0;
    }

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 1;

    argc;
    argv;
}


/***************************************************************************\
* InitializeApp
*
* History:
* 04-17-91 ScottLu	Created.
* 09-09-91 PetrusW	Rewrote.
\***************************************************************************/

BOOL InitializeApp(void)
{
    WNDCLASS wc;
    int i;

    srand(51537);

    for (i = 0; i < CCOLORS; i++) {
        gahbrColor[i] = CreateSolidBrush(aclr[i]);
    }

    ghbrWhite = CreateSolidBrush(0x00FFFFFF);
    ghbrBlack = CreateSolidBrush(0x00000000);

    wc.style            = CS_OWNDC;
    wc.lpfnWndProc      = MainWndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra	= sizeof(LONG);
    wc.hInstance        = ghModule;
    wc.hIcon            = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = ghbrWhite;
    wc.lpszMenuName     = "MainMenu";
    wc.lpszClassName    = "MltithrdClass";

    if (!RegisterClass(&wc))
	return FALSE;

    wc.style		= CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc      = ThreadWndProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra	= NULL;
    wc.hInstance        = ghModule;
    wc.hIcon            = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = ghbrWhite;
    wc.lpszMenuName	= NULL;
    wc.lpszClassName    = "ThreadClass";

    if (!RegisterClass(&wc))
        return FALSE;

    hMenu = LoadMenu(ghModule, "MainMenu");
    hMenuWindow = GetSubMenu(hMenu, 0);

    ghwndMain = CreateWindowEx(0L, "MltithrdClass", "Mltithrd",
            WS_OVERLAPPED | WS_CAPTION | WS_BORDER | WS_THICKFRAME |
            WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN |
            WS_VISIBLE | WS_SYSMENU,
            80, 70, 400, 300,
	    NULL, hMenu, ghModule, NULL);

    if (ghwndMain == NULL)
	return FALSE;

    SetWindowLong(ghwndMain, GWL_USERDATA, NULL);

    SetFocus(ghwndMain);    /* set initial focus */

    return TRUE;
}


/***************************************************************************\
* MainWndProc
*
* History:
* 04-17-91 ScottLu	Created.
* 09-09-91 PetrusW	Rewrote.
\***************************************************************************/

long MainWndProc(
    HWND hwnd,
    UINT message,
    DWORD wParam,
    LONG lParam)
{
    static int	       iCount=1;
    static HWND        hwndClient;
    CLIENTCREATESTRUCT clientcreate;
    HWND               hwndChildWindow;


    switch (message) {
	  /*
      case WM_SIZE: {
	HANDLE hHead, hTmp;
	PNODE  pHead;

	if (hHead = (HANDLE) GetWindowLong(hwnd, 0) )  {
	    if ((pHead = (PNODE) LocalLock(hHead))==NULL)
		DbgPrint("Failed in LocalLock, hHead\n");
	    while (pHead->hNext != NULL) {
		SendMessage(pHead->ThreadWindow.hwndThreadWindow,
			    WM_SIZE, wParam, lParam);
		hTmp = hHead;
		hHead = pHead->hNext;
		if (LocalUnlock(hTmp))
		    DbgPrint("3.hTmp lock count NOT zero - MainWndProc/WM_SIZE\n");
		if ((pHead = (PNODE) LocalLock(hHead))==NULL)
		    DbgPrint("Failed in LocalLock\n");
	    }
	    SendMessage(pHead->ThreadWindow.hwndThreadWindow,
		    WM_SIZE, wParam, lParam);
	    if (LocalUnlock(hHead))
		DbgPrint("4.hHead count NOt zero - MainWndProc/WM_SIZE\n");
	}
	return DefFrameProc(hwnd, hwndClient, message, wParam, lParam);
      } */

      case WM_CREATE:
	SetWindowLong(hwnd, 0, (LONG)NULL);

	clientcreate.hWindowMenu  = hMenuWindow;
	clientcreate.idFirstChild = 1;

	hwndClient = CreateWindow("MDICLIENT", NULL,
				    WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
				    0,0,0,0,
				    hwnd, NULL, ghModule, (LPVOID)&clientcreate);
	return 0L;

      case WM_DESTROY: {
	HANDLE hHead, hTmp;
	PNODE  pHead;

	bKillMe = TRUE;

	if (hHead = (HANDLE) GetWindowLong(hwnd, 0) ) {
	  if ((pHead = (PNODE) LocalLock(hHead))==NULL)
	    DbgPrint("Failed in LocalLock\n");
	  while (pHead->hNext != NULL) {

	    //SendMessage(pHead->ThreadWindow.hwndThreadWindow,
	    //		WM_CLOSE, 0L, 0L);
	    hTmp = hHead;
	    hHead = pHead->hNext;
	    if (LocalUnlock(hTmp))
		DbgPrint("5.hTmp count NOT zero - MainWndProc/WM_DESTROY\n"); //<--
	    if (LocalFree(hTmp)!=NULL)
		DbgPrint("Failed in LocalFree\n");
	    if ((pHead = (PNODE) LocalLock(hHead))==NULL)
		DbgPrint("Failed in LocalLock, hHead\n");
	  }
	  //SendMessage(pHead->ThreadWindow.hwndThreadWindow,
	  //		WM_CLOSE, 0L, 0L);
	  if (LocalUnlock(hHead))
	    DbgPrint("6.hHead lock count NOT zero - MainWndProc/WM_DESTROY\n"); //<--
	  if (LocalFree(hHead)!=NULL)
	    DbgPrint("LocalFree failed to free hHead\n");
	}
	//bKillMe = TRUE;
	PostQuitMessage(0);
	return 0L;
      }

#ifdef LATER
      case WM_LBUTTONDOWN:
	InitializeBoxes(TRUE, &ThreadWindow);
	break;
#endif

      case WM_COMMAND:

	switch (LOWORD(wParam)) {
	    case IDM_TILE:
		SendMessage(hwndClient, WM_MDITILE, 0L, 0L);
		return 0L;
	    case IDM_CASCADE:
		SendMessage(hwndClient, WM_MDICASCADE, 0L, 0L);
		return 0L;
	    case IDM_ARRANGE:
		SendMessage(hwndClient, WM_MDIICONARRANGE, 0L, 0L);
		return 0L;
	    case MM_BOUNCE: {
		HANDLE hNode, hHead;
		HANDLE hThrd;
		PNODE  pNode;
                MDICREATESTRUCT mdicreate;

		if (hNode = LocalAlloc(LHND, (WORD) sizeof(NODE))) {
		    if ((pNode = (PNODE)LocalLock(hNode))==NULL)
			DbgPrint("Failed in LocalLock, hNode\n");

		    wsprintf(&(pNode->ThreadWindow.CaptionBarText), "Thread Window %d", iCount);

		    mdicreate.szClass = "ThreadClass";
		    mdicreate.szTitle = &(pNode->ThreadWindow.CaptionBarText);
		    mdicreate.hOwner  = ghModule;
		    mdicreate.x	      =
		    mdicreate.y	      =
		    mdicreate.cx      =
		    mdicreate.cy      = CW_USEDEFAULT;
		    mdicreate.style   = 0l;
		    mdicreate.lParam  = NULL;

		    /*Create Child Window*/
		    hwndChildWindow = SendMessage(hwndClient, WM_MDICREATE,
						  NULL,
						  (LONG)(LPMDICREATESTRUCT)&mdicreate);

		    if (hwndChildWindow == NULL) {
			DbgPrint("Failed in Creating Thread Window\n");
			return 0L;
		    }

		    // This stuff is here for simplicity.  Initialization should
		    //	 be done in StartDemo
		    pNode->ThreadWindow.hwndClient	 = hwndClient;
		    pNode->ThreadWindow.hwndThreadWindow = hwndChildWindow;
		    hHead = (HANDLE)GetWindowLong(hwnd, 0);
		    pNode->hNext = hHead;
		    SetWindowLong(hwnd, 0, (LONG) hNode);

		    if (hThrd = CreateThread(NULL, 0,
				 (LPTHREAD_START_ROUTINE)StartBounce,
				 &pNode->ThreadWindow,
				 STANDARD_RIGHTS_REQUIRED,
				 &pNode->ThreadWindow.lThreadId )) {

			 // I really don't need this info, it won't even be
			 // updated right the way for this thread will be
			 // preempted.
			 pNode->ThreadWindow.hThread = hThrd;
			 iCount++;

		    } else {
			      DbgPrint("Create Thread Failed!\n");
		    }
		    if (LocalUnlock(hNode))
			DbgPrint("7.hNode lock NOT zero - MainWndProc/WM_COMMAND\n");	//<---
		} else {
			    DbgPrint("Failed to Allocate Node!\n");
		}
		return 0L;
	    }

	    case MM_ABOUT:
		if (DialogBox(ghModule, "AboutBox", ghwndMain, About) == -1)
                	DbgPrint("DEMO: About Dialog Creation Error\n");
		return 0L;

	    default:
		return DefFrameProc(hwnd,  hwndClient, message, wParam, lParam);
        }

    default:

	return DefFrameProc(hwnd,  hwndClient, message, wParam, lParam);
    }
}

/********************************************************************\
* ThreadWndProc
*
* History:
* 04-17-91 ScottLu      Created.
* 09-09-91 PetrusW	Rewrote.
\***************************************************************************/

long ThreadWndProc(
    HWND hwnd,
    UINT message,
    DWORD wParam,
    LONG lParam)
{
    switch (message) {
	case WM_SIZE:
	    DbgPrint("WM_SIZE - ThreadWndProc\n");
	case WM_CREATE: {
	    PTHREADBLOCKINFO pThreadBlockInfo;
	    LONG	     lThreadId;
	    PNODE	     pHead;
	    HANDLE	     hHead, hTmp;

	    DbgPrint("WM_CREATE - ThreadWndProc\n");

	    // now find match
	    if (hHead = (HANDLE) GetWindowLong(ghwndMain, 0)) {
		if ((pHead = (PNODE)LocalLock(hHead))==NULL)
		    DbgPrint("Failed in LocalLock\n");

		while ((pHead->ThreadWindow.hwndThreadWindow != hwnd) &&
		      (pHead->hNext != NULL)) {
		   hTmp = hHead;
		   hHead = pHead->hNext;
		   if (LocalUnlock(hTmp))
			DbgPrint("1.hTmp lock count NOT zero - ThreadWndProc/WM_CREATE or SIZE\n");
		   if ((pHead = (PNODE) LocalLock(hHead))==NULL)
			DbgPrint("Failed in LocalLock\n");
		}
		if (pHead->ThreadWindow.hwndThreadWindow == hwnd) {
		    pThreadBlockInfo = &pHead->ThreadWindow;
		    goto Thread_Found;
		} else {
		    DbgPrint("Trouble - Can't find the thread node!\n");
		    goto Thread_Out;
		}

Thread_Found:
		if (!GetClientRect(pThreadBlockInfo->hwndThreadWindow,
				   &pThreadBlockInfo->rcClient))
		    DbgPrint("Failed in GetClientRect\n");
Thread_Out:
		if (LocalUnlock(hHead))
		    DbgPrint("2.hHead lock count NOT zero - ThreadWndProc/WM_CREATE or SIZE\n");   // <--
		return DefMDIChildProc(hwnd, message, wParam, lParam);
	    } else {
			DbgPrint("Can't GetWindowLong(ghwndMain,0) \n");
	    }		return DefMDIChildProc(hwnd, message, wParam, lParam);
	}

	case WM_CLOSE: {
	    PTHREADBLOCKINFO pThreadBlockInfo;
	    LONG	     lThreadId;
	    PNODE	     pHead;
	    HANDLE	     hHead, hTmp;

	    DbgPrint("WM_CLOSE - ThreadWndProc\n");

	    // now find match
	    if (hHead = (HANDLE) GetWindowLong(ghwndMain, 0)) {
		if ((pHead = (PNODE)LocalLock(hHead))==NULL)
		    DbgPrint("Failed in LocalLock\n");

		while ((pHead->ThreadWindow.hwndThreadWindow != hwnd) &&
		      (pHead->hNext != NULL)) {
		   hTmp = hHead;
		   hHead = pHead->hNext;
		   if (LocalUnlock(hTmp))
			DbgPrint("8.hTmp lock count NOT zero - ThreadWndProc/WM_CREATE or SIZE\n");
		   if ((pHead = (PNODE) LocalLock(hHead))==NULL)
			DbgPrint("Failed in LocalLock\n");
		}
		if (pHead->ThreadWindow.hwndThreadWindow == hwnd) {
		    pThreadBlockInfo = &pHead->ThreadWindow;
		    goto Thread_Found1;
		} else {
		    DbgPrint("Trouble - Can't find the thread node! -CLOSE\n");
		    goto Thread_Out1;
		}

Thread_Found1:
		pThreadBlockInfo->bKillThrd = TRUE;
		DbgPrint("bKillThrd = TRUE\n");
Thread_Out1:
		if (LocalUnlock(hHead))
		    DbgPrint("9.hHead lock count NOT zero - ThreadWndProc/WM_CLOSE\n");	 // <--
	    } else {
			DbgPrint("Can't GetWindowLong(ghwndMain,0) \n");
	    }
	    return DefMDIChildProc(hwnd, message, wParam, lParam);
	}

	case WM_DESTROY:
	    DbgPrint("WM_DESTROY - ThreadWndProc\n");
	    //PostQuitMessage(0);
	    return;

	default:
		return DefMDIChildProc(hwnd, message, wParam, lParam);
    }

}


/***************************************************************************\
* About
*
* About dialog proc.
*
* History:
* 04-13-91 ScottLu      Created.
* 09-09-91 PetrusW	Rewrote.
\***************************************************************************/

long About(
    HWND hDlg,
    UINT message,
    DWORD wParam,
    LONG lParam)
{
    switch (message) {
    case WM_INITDIALOG:
        return TRUE;

    case WM_COMMAND:
        if (wParam == IDOK)
            EndDialog(hDlg, wParam);
        break;
    }

    return FALSE;

    lParam;
    hDlg;
}

long StartBounce(PTHREADBLOCKINFO pThreadBlockInfo)
{
	MSG msg;

    if (!StartDemo(MM_BOUNCE, pThreadBlockInfo))
        return(0);

    cmdDemo = MM_BOUNCE;
	/* Here everythings been initialized, ThreadBlockINfo, etc*/
	/* Now loop and call BounceProc to draw and move boxes*/

	/* NOTE NOTE, each thread MUST have its own message loop!*/

    while (TRUE) {
      if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
	  if (GetMessage(&msg, NULL, 0, 0)) {
	      TranslateMessage(&msg);
	      DispatchMessage(&msg);
	  }
      }

      if (!bKillMe && !pThreadBlockInfo->bKillThrd) {
	BounceProc(pThreadBlockInfo);
      } else {
		break;
      }
    }

	DbgPrint("Exiting Thread\n");
	ExitThread(0);
	if (!CloseHandle(pThreadBlockInfo->hThread))
	    DbgPrint("Failed in CloseHandle\n");
}


BOOL StartDemo(
    int cmd,
    PTHREADBLOCKINFO pThreadBlockInfo)
{

    /* Need thread ID into ThreadBlock, so that ThreadWndProc
       can find semaphore*/
    // CR!! Do I need the thread ID to find the semaphore?
    pThreadBlockInfo->lThreadId = GetCurrentThreadId();

    // Initializing the rcClient for the bouncing box
    // Better be initializing here for once CreateThread is called, this
    //	 thread got the time slice; parent thread is preempted.
    if (!GetClientRect(pThreadBlockInfo->hwndThreadWindow,
		       &pThreadBlockInfo->rcClient))
	DbgPrint("Failed in GetClientRect\n");
    pThreadBlockInfo->bKillThrd = FALSE;

    InitializeBoxes(FALSE, pThreadBlockInfo);

    return TRUE;
	UNREFERENCED_PARAMETER(cmd);
}


void InitializeBoxes(
    BOOL fVectorsOnly,
    PTHREADBLOCKINFO pThreadBlockInfo)
{
    int j;
    int i;

    /*
     * Initialize bounce arrays...
     */
    for (i = 0; i < COBJS; i++) {

        if (!fVectorsOnly) {
            for (j = 0; j < CPOINTS; j++) {
                pThreadBlockInfo->gab[i].apt[j].x = 0;
                pThreadBlockInfo->gab[i].apt[j].y = 0;
            }
            pThreadBlockInfo->gab[i].iColor = 0;
        }

        if (GetRandomVector() <= 3) {
            if (GetRandomVector() >= 4) {
                pThreadBlockInfo->gab[i].vx = 4;
            } else {
                pThreadBlockInfo->gab[i].vx = 1;
            }

            pThreadBlockInfo->gab[i].vy = GetRandomVector();
        } else {
            if (GetRandomVector() >= 4) {
                pThreadBlockInfo->gab[i].vy = 4;
            } else {
                pThreadBlockInfo->gab[i].vy = 1;
            }

            pThreadBlockInfo->gab[i].vx = GetRandomVector();
        }

        pThreadBlockInfo->gab[i].dx = pThreadBlockInfo->gab[i].vx;
        pThreadBlockInfo->gab[i].dy = pThreadBlockInfo->gab[i].vy;

        pThreadBlockInfo->gab[i].ax = GetRandomVector() + 3;
        pThreadBlockInfo->gab[i].ay = GetRandomVector() + 3;
    }
}

int GetRandomVector()
{
    /*
     * Returns a number between 1 and 6.
     */
    return (rand() % 6) + 1;
}

void BounceProc( PTHREADBLOCKINFO pThreadBlockInfo)
{
    int i;
	LONG lCurrentThreadId;

#ifdef LATER
// not erasing looks cool
    /*
     * Erase...
     */
    for (i = 0; i < COBJS; i++)
        DrawBox(gab[i].apt[0].x, gab[i].apt[0].y, ghbrWhite);
#endif


    /*
     * Move, and bounce on wall
     */
    for (i = 0; i < COBJS; i++)
        MoveBox((int *)&pThreadBlockInfo->gab[i].apt[0].x, (int *)&pThreadBlockInfo->gab[i].apt[0].y,
                (int *)&pThreadBlockInfo->gab[i].dx, (int *)&pThreadBlockInfo->gab[i].dy,
                (int *)&pThreadBlockInfo->gab[i].vx, (int *)&pThreadBlockInfo->gab[i].vy,
                (int)pThreadBlockInfo->gab[i].ax, (int)pThreadBlockInfo->gab[i].ay, pThreadBlockInfo);

    /*
     * Draw new...
     */
    for (i = 0; i < COBJS; i++) {
        DrawBox(pThreadBlockInfo->gab[i].apt[0].x, pThreadBlockInfo->gab[i].apt[0].y,
                gahbrColor[pThreadBlockInfo->gab[i].iColor++ % CCOLORS], pThreadBlockInfo);
    }

	UNREFERENCED_PARAMETER(lCurrentThreadId);
}

void DrawBox(
    int x,
    int y,
    HBRUSH hbr,
    PTHREADBLOCKINFO pThreadBlockInfo)
{
    HBRUSH hbrOld;

    if (pThreadBlockInfo->hdcThreadWindow = GetDC(pThreadBlockInfo->hwndThreadWindow)) {
       if ((hbrOld = SelectObject(pThreadBlockInfo->hdcThreadWindow, hbr))==0)
	    DbgPrint("Failed in SelectObject\n");
       if (!BitBlt(pThreadBlockInfo->hdcThreadWindow, x, y, 20, 20, NULL, 0, 0, PATCOPY))
	    DbgPrint("Failed in BitBlt\n");
       if (SelectObject(pThreadBlockInfo->hdcThreadWindow, hbrOld)==0)
	    DbgPrint("Failed in SelectObject 2\n");
       if (ReleaseDC(pThreadBlockInfo->hwndThreadWindow, pThreadBlockInfo->hdcThreadWindow)==0)
	    DbgPrint("Failed in ReleaseDC\n");
    } else {
	    DbgPrint("Failed in GetDC\n");
    }
}

void MoveBox(
    int *px,
    int *py,
    int *pdx,
    int *pdy,
    int *pvx,
    int *pvy,
    int ax,
    int ay,
	PTHREADBLOCKINFO pThreadBlockInfo)
{

    (*pdx)--;
    if (*pdx == 0) {
        *px -= ax;
        *pdx = *pvx;
    } else if (*pdx == 3) {
        *px += ax;
        *pdx = *pvx;
    }

    (*pdy)--;
    if (*pdy == 0) {
        *py -= ay;
        *pdy = *pvy;
    } else if (*pdy == 3) {
        *py += ay;
        *pdy = *pvy;
    }

    if (*px < pThreadBlockInfo->rcClient.left) {
	*px = pThreadBlockInfo->rcClient.left;

        if (*pvx >= 4)
            *pvx -= 3;
        else
            *pvx += 3;

        *pdx = *pvx;
    }

    if (*px > (pThreadBlockInfo->rcClient.right - 20)) {
	*px = pThreadBlockInfo->rcClient.right - 20;

        if (*pvx >= 4)
            *pvx -= 3;
        else
            *pvx += 3;

        *pdx = *pvx;
    }

    if (*py < pThreadBlockInfo->rcClient.top) {
	*py = pThreadBlockInfo->rcClient.top;

        if (*pvy >= 4)
            *pvy -= 3;
        else
            *pvy += 3;

        *pdy = *pvy;
    }

    if (*py > (pThreadBlockInfo->rcClient.bottom - 20)) {
	*py = pThreadBlockInfo->rcClient.bottom - 20;

        if (*pvy >= 4)
            *pvy -= 3;
        else
            *pvy += 3;

        *pdy = *pvy;
    }
}
