/* Windows GUI Behaviour Tester */
/* by Ove Kåven */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <windows.h>

#include "guitest.rh"

/* checks to include */
#define LOGGING /* can be undefined under Wine and use -debugmsg +message instead */
#define MAIN_STYLE WS_OVERLAPPEDWINDOW|WS_HSCROLL
#define MAIN_EXSTYLE 0
#undef TEST_DESTROY_MAIN
#define SHOW_SUB
#undef TEST_DIALOG
#define RESIZE_DIALOG
#undef TEST_SUBDIALOG
#undef TEST_COMMCTL

/************************/
/*** GLOBAL VARIABLES ***/
/************************/

HINSTANCE hInst;
DWORD StartTime;
HWND hListBox,hMainWnd,hSubWnd;
HWND hButton[4]={0,0,0,0};
HWND hDialog=0,hGroup=0,hSubDlg=0;
WNDPROC wndButton[4],wndDialog,wndGroup,wndSubDlg;
BOOL Clicked=0,Ready=0;
int State=0,Rec=0;
#define STATE_CREATE 0
#define STATE_DESTROY 1
#define STATE_SHOW 2
#define STATE_UPDATE 3
#define STATE_DIALOG 4
#define STATE_TEST 5
#define STATE_DIRECT 6
#define STATE_DISPATCH 7
#define STATE_RECURS 8
char*StateName[]={
 "Creat",
 "Destr",
 "Show ",
 "Updat",
 "Dialg",
 "Test ",
 "Call ",
 "Disp ",
 "RCall"
};

static char wclassname[] = "GUITestClass";
static char wcclassname[] = "GUITestChildClass";
static char winname[] = "GUITest";

/**************************/
/*** LOGGING FACILITIES ***/
/**************************/

struct MSGNAMES {
 int msg;
 char*name;
} MsgNames[]={
#define MSG(x) {x,#x},
#define MSG2(x,y) {y,#x},
#define ENDMSG {0}

/* we get these in CreateWindow */
MSG(WM_GETMINMAXINFO)
MSG(WM_NCCREATE)
MSG(WM_NCCALCSIZE)
MSG(WM_CREATE)
MSG(WM_PARENTNOTIFY)

/* we get these in ShowWindow */
MSG(WM_SHOWWINDOW)
MSG(WM_WINDOWPOSCHANGING)
MSG(WM_QUERYNEWPALETTE)
MSG(WM_ACTIVATEAPP)
MSG(WM_NCACTIVATE)
MSG(WM_GETTEXT)
MSG(WM_ACTIVATE)
MSG(WM_SETFOCUS)
MSG(WM_NCPAINT)
MSG(WM_ERASEBKGND)
MSG(WM_WINDOWPOSCHANGED)
MSG(WM_SIZE)
MSG(WM_MOVE)

/* we get these in DestroyWindow */
MSG(WM_KILLFOCUS)
MSG(WM_DESTROY)
MSG(WM_NCDESTROY)

/* we get these directly sent */
MSG(WM_NCHITTEST)
MSG(WM_SETCURSOR)
MSG(WM_MOUSEACTIVATE)
MSG(WM_CHILDACTIVATE)
MSG(WM_COMMAND)
MSG(WM_SYSCOMMAND)

/* posted events */
MSG(WM_MOUSEMOVE)
MSG(WM_NCMOUSEMOVE)
MSG(WM_PAINT)
MSG(WM_LBUTTONDOWN)
MSG(WM_LBUTTONUP)
MSG(WM_LBUTTONDBLCLK)
MSG(WM_NCLBUTTONDOWN)
MSG(WM_NCLBUTTONUP)
MSG(WM_NCLBUTTONDBLCLK)

MSG(WM_KEYDOWN)
MSG(WM_KEYUP)
MSG(WM_CHAR)

#ifdef WIN32
MSG(WM_CTLCOLORBTN)
MSG(WM_CTLCOLORDLG)
MSG(WM_CTLCOLORSTATIC)
#else
MSG(WM_CTLCOLOR)
#endif

/* moving and sizing */
MSG2(WM_ENTERSIZEMOVE,0x0231)
MSG2(WM_EXITSIZEMOVE,0x0232)
#ifdef WIN32
MSG(WM_SIZING)
#endif

/* menus/dialog boxes */
MSG(WM_CANCELMODE)
MSG(WM_ENABLE)
MSG(WM_SETFONT)
MSG(WM_INITDIALOG)
MSG(WM_GETDLGCODE)
MSG(WM_ENTERIDLE)

/* scroll bars */
MSG(WM_HSCROLL)
MSG(WM_VSCROLL)

/* getting these from Wine but not from Windows */
MSG2(WM_SETVISIBLE,0x0009) /* unheard of in BC++ 4.52 */
#ifdef WIN32
MSG(WM_CAPTURECHANGED)
#endif

ENDMSG};

struct MSGNAMES ButMsgs[]={
MSG(BM_SETSTATE)
MSG(BM_SETSTYLE)

ENDMSG};

char*MsgName(UINT msg,HWND hWnd)
{
 int i;
 static char buffer[64],wclass[64];
 GetClassName(hWnd,wclass,sizeof(wclass));

#define MSGSEARCH(msgs) { \
  for (i=0; msgs[i].name&&msgs[i].msg!=msg; i++); \
  if (msgs[i].name) return msgs[i].name; \
 }

 if (!stricmp(wclass,"Button")) MSGSEARCH(ButMsgs);
 MSGSEARCH(MsgNames);
 /* WM_USER */
 if (msg>=WM_USER) {
  sprintf(buffer,"WM_USER+%04x{%s}",msg-WM_USER,wclass);
  return buffer;
 }
 /* message not found */
 sprintf(buffer,"%04x{%s}",msg,wclass);
 return buffer;
}

char*WndName(HWND hWnd,int state)
{
 static char buffer[16];
 if (!hWnd) return "0000";
 if (hWnd==hMainWnd || (state==STATE_CREATE && !hMainWnd)) return "main";
 if (hWnd==hSubWnd || (state==STATE_CREATE && !hSubWnd)) return "chld";
 if (hWnd==hDialog || (state==STATE_DIALOG && !hDialog)) return "tdlg";
 if (hWnd==hGroup) return "tgrp";
 if (hWnd==hButton[0]) return "but1";
 if (hWnd==hButton[1]) return "but2";
 if (hWnd==hButton[2]) return "but3";
 if (hWnd==hButton[3]) return "but4";
 if (hWnd==hSubDlg || (state==STATE_CREATE && !hSubDlg)) return "sdlg";
 if (hDialog) {
  int id=GetDlgCtrlID(hWnd);
  if (id) {
   sprintf(buffer,"dlgitem(%d)",id);
   return buffer;
  }
 }
 sprintf(buffer,"%04x",hWnd);
 return buffer;
}

void Log(const char*fmt)
{
#ifdef LOGGING
 if (!Clicked) SendMessage(hListBox,LB_ADDSTRING,0,(LPARAM)fmt);
#endif
}

void Logf(const char*fmt,...)
{
 va_list par;
 static char buffer[256];

 va_start(par,fmt);
 vsprintf(buffer,fmt,par);
 va_end(par);
 Log(buffer);
}

void LogChildOrder(HWND hWnd)
{
 HWND hWndChild = GetWindow(hWnd,GW_CHILD);
 static char buffer[256];

 strcpy(buffer,"child list:");
 while (hWndChild) {
  strcat(strcat(buffer," "),WndName(hWndChild,State));
  hWndChild=GetWindow(hWndChild,GW_HWNDNEXT);
 }
 Log(buffer);
}

void LogMessage(int state,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam,char*name)
{
 static char buffer[256];
 DWORD tick=GetTickCount()-StartTime;
 char*msgname=MsgName(msg,hWnd);
 if (!name) name=WndName(hWnd,state);
 switch (msg) {
  case WM_SETFOCUS:
  case WM_KILLFOCUS:
  case WM_SETCURSOR:
   Logf("%04d[%s(%d):%s]%s(%s,%08x)",tick,StateName[state],Rec,
        name,msgname,WndName((HWND)wParam,State),lParam);
   break;
#ifdef WIN32
  case WM_ENTERIDLE:
  case WM_CTLCOLORBTN:
  case WM_CTLCOLORDLG:
   Logf("%04d[%s(%d):%s]%s(%08x,%s)",tick,StateName[state],Rec,
        name,msgname,wParam,WndName((HWND)lParam,State));
   break;
#else
  case WM_ENTERIDLE:
  case WM_CTLCOLOR:
   Logf("%04d[%s(%d):%s]%s(%08x,%04x:%s)",tick,StateName[state],Rec,
        name,msgname,wParam,HIWORD(lParam),WndName((HWND)LOWORD(lParam),State));
   break;
#endif
  case WM_WINDOWPOSCHANGING:
  case WM_WINDOWPOSCHANGED:
   {
    WINDOWPOS*pos=(WINDOWPOS*)lParam;
#ifdef WIN32
	 Logf("%04d[%s(%d):%s]%s(%08x,%p)",tick,StateName[state],Rec,
			name,msgname,wParam,pos);
#else
	 Logf("%04d[%s(%d):%s]%s(%04x,%p)",tick,StateName[state],Rec,
			name,msgname,wParam,pos);
#endif
	 strcpy(buffer,"FLAGS:");
	 if (pos->flags&SWP_DRAWFRAME) strcat(buffer," DRAWFRAME");
	 if (pos->flags&SWP_HIDEWINDOW) strcat(buffer," HIDEWINDOW");
	 if (pos->flags&SWP_NOACTIVATE) strcat(buffer," NOACTIVATE");
	 if (pos->flags&SWP_NOCOPYBITS) strcat(buffer," NOCOPYBITS");
	 if (pos->flags&SWP_NOMOVE) strcat(buffer," NOMOVE");
	 if (pos->flags&SWP_NOOWNERZORDER) strcat(buffer," NOOWNERZORDER");
	 if (pos->flags&SWP_NOSIZE) strcat(buffer," NOSIZE");
	 if (pos->flags&SWP_NOREDRAW) strcat(buffer," NOREDRAW");
	 if (pos->flags&SWP_NOZORDER) strcat(buffer," NOZORDER");
	 if (pos->flags&SWP_SHOWWINDOW) strcat(buffer," SHOWWINDOW");
	 Log(buffer);
	}
	break;
  case WM_SYSCOMMAND:
	{
	 char*cmd=NULL;
	 switch (wParam&0xFFF0) {
#define CASE(x) case SC_##x: cmd=#x; break;
	  CASE(CLOSE)
	  CASE(DEFAULT)
	  CASE(HOTKEY)
	  CASE(HSCROLL)
	  CASE(KEYMENU)
	  CASE(MAXIMIZE)
	  CASE(MINIMIZE)
	  CASE(MOUSEMENU)
	  CASE(MOVE)
	  CASE(NEXTWINDOW)
	  CASE(PREVWINDOW)
	  CASE(RESTORE)
	  CASE(SCREENSAVE)
	  CASE(SIZE)
	  CASE(TASKLIST)
	  CASE(VSCROLL)
#undef CASE
	 }
	 if (cmd) {
	  Logf("%04d[%s(%d):%s]%s(%s+%x,%08x)",tick,StateName[state],Rec,
			 name,msgname,cmd,wParam&0xF,lParam);
	 } else goto GENERIC_MSG;
	}
	break;
  case WM_HSCROLL:
  case WM_VSCROLL:
	{
	 char*cmd=NULL;
	 switch (LOWORD(wParam)) {
#define CASE(x) case SB_##x: cmd=#x; break;
#define CASE2(h,v) case SB_##h: if (msg==WM_HSCROLL) cmd=#h; else cmd=#v; break;
	  CASE(BOTTOM)
	  CASE(ENDSCROLL)
	  CASE2(LINELEFT,LINEUP)
	  CASE2(LINERIGHT,LINEDOWN)
	  CASE2(PAGELEFT,PAGEUP)
	  CASE2(PAGERIGHT,PAGEDOWN)
	  CASE(THUMBPOSITION)
	  CASE(THUMBTRACK)
     CASE(TOP)
#undef CASE
	 }
	 if (cmd) {
#ifdef WIN32
	  Logf("%04d[%s(%d):%s]%s(%s,%04x,%s)",tick,StateName[state],Rec,
			 name,msgname,cmd,HIWORD(wParam),WndName((HWND)lParam,State));
#else
	  Logf("%04d[%s(%d):%s]%s(%04x,%04x,%s)",tick,StateName[state],Rec,
			 name,msgname,cmd,LOWORD(lParam),WndName((HWND)HIWORD(lParam),State));
#endif
	 } else goto GENERIC_MSG;
	}
	break;
  default:
GENERIC_MSG:
#ifdef WIN32
	Logf("%04d[%s(%d):%s]%s(%08x,%08x)",tick,StateName[state],Rec,
		  name,msgname,wParam,lParam);
#else
	Logf("%04d[%s(%d):%s]%s(%04x,%08x)",tick,StateName[state],Rec,
		  name,msgname,wParam,lParam);
#endif
 }
}

/***************************/
/*** GRAPHICS FACILITIES ***/
/***************************/

void Paint(HWND hWnd)
{
 HDC dc;
 PAINTSTRUCT ps;
 dc=BeginPaint(hWnd,&ps);
 EndPaint(hWnd,&ps);
}

void FillPattern(HWND hWnd,HDC pdc)
{
 HDC dc=pdc?pdc:GetDC(hWnd);
 HBRUSH oldbrush;
 RECT rect;
 if (!dc) {
  Logf("failed to acquire DC for window %s",WndName(hWnd,State));
  return;
 } else {
  Logf("acquired DC for %s window %s, painting",
       IsWindowVisible(hWnd)?"visible":"invisible",WndName(hWnd,State));
 }
 GetClientRect(hWnd,&rect);
 oldbrush=SelectObject(dc,GetStockObject(LTGRAY_BRUSH));
 PatBlt(dc,0,0,rect.right,rect.bottom,PATCOPY);
 SelectObject(dc,oldbrush);
 if (!pdc) ReleaseDC(hWnd,dc);
}

void PaintPattern(HWND hWnd)
{
 HDC dc;
 PAINTSTRUCT ps;
 dc=BeginPaint(hWnd,&ps);
 FillPattern(hWnd,dc);
 EndPaint(hWnd,&ps);
}

/*************************/
/*** WINDOW PROCEDURES ***/
/*************************/

/* MAIN WINDOW */
LRESULT FAR CALLBACK _export MainWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 LRESULT lResult=0;
 RECT rect;
 int OldState=State;

 State=STATE_RECURS; Rec++;
 if (!Clicked) LogMessage(OldState,hWnd,msg,wParam,lParam,NULL);
 switch (msg) {
  case WM_NCHITTEST:
   lResult=DefWindowProc(hWnd,msg,wParam,lParam);
   break;
  case WM_LBUTTONDOWN:
  case WM_CHAR:
   if (!Clicked) {
    SetParent(hListBox,hWnd);
    GetClientRect(hWnd,&rect);
    MoveWindow(hListBox,0,0,rect.right,rect.bottom,TRUE);
    ShowWindow(hListBox,SW_SHOW);
    SetFocus(hListBox);
    Clicked=TRUE;
   }
   break;
  case WM_SIZE:
   GetClientRect(hWnd,&rect);
   if (Clicked) {
    MoveWindow(hListBox,0,0,rect.right,rect.bottom,TRUE);
   }
   MoveWindow(hSubWnd,0,rect.bottom/2,rect.right,rect.bottom-(rect.bottom/2),TRUE);
   break;
  case WM_PAINT:
   Paint(hWnd);
   break;
  case WM_DESTROY:
   PostQuitMessage(0);
   break;
  default:
   lResult=DefWindowProc(hWnd,msg,wParam,lParam);
 }
 State=OldState; Rec--;
 return lResult;
}

/* CHILD WINDOW */
LRESULT FAR CALLBACK _export SubWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 LRESULT lResult=0;
 RECT rect;
 int OldState=State;

 State=STATE_RECURS; Rec++;
 if (!Clicked) LogMessage(OldState,hWnd,msg,wParam,lParam,NULL);
 switch (msg) {
  case WM_PAINT:
   Paint(hWnd);
   break;
  default:
   lResult=DefWindowProc(hWnd,msg,wParam,lParam);
 }
 State=OldState; Rec--;
 return lResult;
}

BOOL FAR CALLBACK _export SubDialogProc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam);

/* SUBCLASSED CONTROLS */
LRESULT FAR CALLBACK _export SubClassWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 LRESULT lResult=0;
 RECT rect;
 int OldState=State;
 int But=-1;

 if (hWnd==hButton[0]) But=0; else
 if (hWnd==hButton[1]) But=1; else
 if (hWnd==hButton[2]) But=2; else
 if (hWnd==hButton[3]) But=3;

 State=STATE_RECURS; Rec++;
 if (!Clicked) {
  LogMessage(OldState,hWnd,msg,wParam,lParam,NULL);
  if (But!=-1) {
   lResult=CallWindowProc((FARPROC)wndButton[But],hWnd,msg,wParam,lParam);
   if (msg==WM_LBUTTONUP) {
    LogChildOrder(GetParent(hWnd));
   }
  }
  else if (hWnd==hDialog) {
   lResult=CallWindowProc((FARPROC)wndDialog,hWnd,msg,wParam,lParam);
  }
  else if (hWnd==hSubDlg) {
   lResult=CallWindowProc((FARPROC)wndSubDlg,hWnd,msg,wParam,lParam);
  }
  else if (hWnd==hGroup) {
   lResult=CallWindowProc((FARPROC)wndGroup,hWnd,msg,wParam,lParam);
   if (msg==WM_SETFOCUS) {
    /* create subdialog */
    if (hSubDlg) {
#if 0
     SetRect(&rect,0,0,1,1);
     InvalidateRect(hWnd,&rect,FALSE);
#endif
    } else {
#ifdef TEST_SUBDIALOG
     State=STATE_CREATE;
     hSubDlg=CreateDialog(hInst,MAKEINTRESOURCE(2),hWnd,(FARPROC)SubDialogProc);
     State=STATE_RECURS;
#else
#ifdef RESIZE_DIALOG
     GetWindowRect(GetParent(hWnd),&rect);
     rect.right++;
     SetWindowPos(GetParent(hWnd),0,0,0,
                  rect.right-rect.left,rect.bottom-rect.top,
                  SWP_NOMOVE|SWP_NOZORDER);
#endif
#endif
    }
   }
  }
 }
 State=OldState; Rec--;
 return lResult;
}

/* MAIN DIALOG PROCEDURE */
BOOL FAR CALLBACK _export TestDialogProc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
 BOOL bResult=0;
 RECT rect;
 int OldState=State;
 int But=-1;

 State=STATE_RECURS; Rec++;
 if (!Clicked) LogMessage(OldState,hWndDlg,msg,wParam,lParam,"dlgp");
 switch (msg) {
  case WM_INITDIALOG:
   hDialog = hWndDlg;
   /* subclass dialog window proc */
   wndDialog = (WNDPROC)SetWindowLong(hDialog,GWL_WNDPROC,(LONG)SubClassWindowProc);
   Logf("dialog visible=%s",IsWindowVisible(hWndDlg)?"TRUE":"FALSE");
   /* subclass OK button */
   hButton[3] = GetDlgItem(hWndDlg,IDOK);
   wndButton[3] = (WNDPROC)SetWindowLong(hButton[3],GWL_WNDPROC,(LONG)SubClassWindowProc);
   /* subclass group box */
   hGroup = GetDlgItem(hWndDlg,IDC_GROUPBOX1);
   wndGroup = (WNDPROC)SetWindowLong(hGroup,GWL_WNDPROC,(LONG)SubClassWindowProc);

#ifdef RESIZE_DIALOG
   GetWindowRect(hWndDlg,&rect);
   rect.right--;
   SetWindowPos(hWndDlg,0,0,0,
                rect.right-rect.left,rect.bottom-rect.top,
                SWP_NOMOVE|SWP_NOZORDER);
//   ShowWindow(GetDlgItem(hWndDlg,IDCANCEL),SW_HIDE);
#endif

   bResult=TRUE; /* we don't do SetFocus */
   break;
  case WM_PAINT:
   PaintPattern(hWndDlg);
   bResult=TRUE;
   break;
  case WM_COMMAND:
   EndDialog(hWndDlg,LOWORD(wParam));
   bResult=TRUE;
   break;
  case WM_CLOSE:
   EndDialog(hWndDlg,IDCANCEL);
   bResult=TRUE;
   break;
  case WM_NCDESTROY:
   hDialog = 0;
   break;
 }
 State=OldState; Rec--;
 return bResult;
}

/* SUBDIALOG PROCEDURE */
BOOL FAR CALLBACK _export SubDialogProc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
 BOOL bResult=0;
 RECT rect;
 int OldState=State;
 int But=-1;

 State=STATE_RECURS; Rec++;
 if (!Clicked) LogMessage(OldState,hWndDlg,msg,wParam,lParam,NULL);
 switch (msg) {
  case WM_INITDIALOG:
   hSubDlg = hWndDlg;
   /* subclass dialog window proc */
   wndSubDlg = (WNDPROC)SetWindowLong(hDialog,GWL_WNDPROC,(LONG)SubClassWindowProc);

   bResult=TRUE; /* we don't do SetFocus */
   break;
  case WM_NCDESTROY:
   hSubDlg = 0;
   break;
 }
 State=OldState; Rec--;
 return bResult;
}

/********************/
/*** MAIN PROGRAM ***/
/********************/

BOOL AppInit(void)
{
 WNDCLASS wclass;

 wclass.style = CS_HREDRAW|CS_VREDRAW;
 wclass.lpfnWndProc = MainWindowProc;
 wclass.cbClsExtra = 0;
 wclass.cbWndExtra = 0;
 wclass.hInstance = hInst;
 wclass.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(1));
 wclass.hCursor = LoadCursor(0,IDC_ARROW);
 wclass.hbrBackground = GetStockObject(WHITE_BRUSH);
 wclass.lpszMenuName = NULL;
 wclass.lpszClassName = wclassname;
 if (!RegisterClass(&wclass)) return FALSE;
 wclass.lpfnWndProc = SubWindowProc;
 wclass.lpszClassName = wcclassname;
 if (!RegisterClass(&wclass)) return FALSE;
 return TRUE;
}

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpszCmdLine, int nCmdShow)
{
 MSG msg;
 RECT rect;

 hInst = hInstance;
 if (!hPrevInstance)
  if (!AppInit())
   return 0;

 StartTime=GetTickCount();
 hListBox = CreateWindow("LISTBOX","Messages",WS_BORDER|WS_VSCROLL|WS_CHILD|
                                              LBS_HASSTRINGS|LBS_NOTIFY|LBS_WANTKEYBOARDINPUT,
                         0,0,0,0,GetDesktopWindow(),0,hInst,0);
 if (!hListBox) {
  MessageBox(0,"Could not create list box","Error",MB_OK);
 }

 State=STATE_CREATE;
 hMainWnd = CreateWindowEx(MAIN_EXSTYLE,wclassname,winname,MAIN_STYLE,
                           CW_USEDEFAULT,0,400,300,0,0,hInst,0);
 if (!hMainWnd) return 0;
 State=STATE_SHOW;
 ShowWindow(hMainWnd,nCmdShow);
#ifdef TEST_DESTROY_MAIN
 State=STATE_DESTROY;
 DestroyWindow(hMainWnd);
 State=STATE_DIRECT;
 while (GetMessage(&msg,0,0,0)) {
  TranslateMessage(&msg);
  State=STATE_DISPATCH;
  DispatchMessage(&msg);
  State=STATE_DIRECT;
 }
 State=STATE_CREATE;
 hMainWnd = CreateWindowEx(MAIN_EXSTYLE,wclassname,winname,MAIN_STYLE,
                           CW_USEDEFAULT,0,400,300,0,0,hInst,0);
 if (!hMainWnd) return 0;
 State=STATE_SHOW;
 ShowWindow(hMainWnd,nCmdShow);
#endif
/* update, so no WM_PAINTs are pending */
 State=STATE_UPDATE;
// UpdateWindow(hMainWnd);
 Ready=TRUE;
/* fill client area with a pattern */
 FillPattern(hMainWnd,0);
/* create subwindow */
 State=STATE_CREATE;
 GetClientRect(hMainWnd,&rect);
 hSubWnd = CreateWindow(wcclassname,winname,WS_CHILD|WS_BORDER|WS_CLIPSIBLINGS,
                        0,rect.bottom/2,rect.right,rect.bottom-(rect.bottom/2),hMainWnd,0,hInst,0);
 if (!hSubWnd) return 0;
/* create buttons */
 hButton[0] = CreateWindow("BUTTON","1",WS_CHILD|WS_CLIPSIBLINGS|WS_VISIBLE,
                           8,8,48,20,hMainWnd,0,hInst,0);
 hButton[1] = CreateWindow("BUTTON","2",WS_CHILD|WS_CLIPSIBLINGS|WS_VISIBLE,
                           32,12,48,20,hMainWnd,0,hInst,0);
 hButton[2] = CreateWindow("BUTTON","3",WS_CHILD|WS_CLIPSIBLINGS|WS_VISIBLE,
                           56,16,48,20,hMainWnd,0,hInst,0);
/* subclass them */
 wndButton[0] = (WNDPROC)SetWindowLong(hButton[0],GWL_WNDPROC,(LONG)SubClassWindowProc);
 wndButton[1] = (WNDPROC)SetWindowLong(hButton[1],GWL_WNDPROC,(LONG)SubClassWindowProc);
 wndButton[2] = (WNDPROC)SetWindowLong(hButton[2],GWL_WNDPROC,(LONG)SubClassWindowProc);
/* show them */
 State=STATE_UPDATE;
 UpdateWindow(hButton[0]);
 LogChildOrder(hMainWnd);
 Logf("but1 visible=%d",IsWindowVisible(hButton[0]));

/* now reparent the button to our (invisible) subwindow */
 State=STATE_TEST;
 /* in different order, seeing who gets topmost */
 SetParent(hButton[0],hSubWnd);
 SetParent(hButton[2],hSubWnd);
 SetParent(hButton[1],hSubWnd);
 LogChildOrder(hSubWnd);
/* the button should now be invisible */
 Logf("but1 visible=%d",IsWindowVisible(hButton[0]));
/* see if we can draw on them */
 FillPattern(hButton[0],0);

#ifdef SHOW_SUB
 State=STATE_SHOW;
 ShowWindow(hSubWnd,SW_SHOWNORMAL);
 State=STATE_UPDATE;
 UpdateWindow(hSubWnd);
 FillPattern(hSubWnd,0);
// InvalidateRect(hMainWnd,NULL,TRUE);
 Logf("but1 visible=%d",IsWindowVisible(hButton[0]));
#endif

#ifdef TEST_DIALOG
 State=STATE_DIALOG;
 DialogBox(hInst,MAKEINTRESOURCE(1),hMainWnd,(FARPROC)TestDialogProc);
#endif
#ifdef TEST_COMMCTL
 {
  DWORD arr[16];
  CHOOSECOLOR cc={sizeof(cc),0,hInst,0,arr,0};
  ChooseColor(&cc);
 }
#endif

 State=STATE_DIRECT;
 while (GetMessage(&msg,0,0,0)) {
  TranslateMessage(&msg);
  State=STATE_DISPATCH;
  DispatchMessage(&msg);
  State=STATE_DIRECT;
 }
 return 0;
}


