1 /**
2  ** vd_win32.c ---- the standard Win32-API driver
3  **
4  ** Author:	Gernot Graeff
5  ** E-mail:	gernot.graeff@t-online.de
6  ** Date:	13.11.98
7  **
8  ** This file is part of the GRX graphics library.
9  **
10  ** The GRX graphics library is free software; you can redistribute it
11  ** and/or modify it under some conditions; see the "copying.grx" file
12  ** for details.
13  **
14  ** This library is distributed in the hope that it will be useful,
15  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17  **
18  ** Changes by Josu Onandia (jonandia@fagorautomation.es) 21/02/2001
19  **   - The colors loaded in the ColorList are guaranteed to be actually used
20  **     by Windows (GetNearestColor), for the GR_frameWin32 to work.
21  **   - When the window is created, it gets the maximum size allowed by the
22  **     current mode. Indeed this size is stored (maxWindowWidth/
23  **     maxWindowHeight).
24  **     When the window is going to be resized (WM_GETMINMAXINFO) it's not
25  **     allowed to grow bigger than this maximum size (it makes nosense).
26  **   - Added some modes for 24bpp colors.
27  **   - When changed to text-mode, the graphics window is hidden. If the
28  **     application has a console (linked with -mconsole) it can use
29  **     printf/scanf and the like.
30  **     When changed again into graphics mode, the window reappears.
31  **   - Inter-task synchronization. In some cases the two threads are
32  **     manipulating at the same time the main window, and the on-memory
33  **     bitmap. I guess this is causing trouble, so in some cases the main
34  **     thread suspends the worker thread, make its operation, and then
35  **     resumes it.
36  **   - The window title is selectable with a define, at compile time.
37  **     If not defined, it defaults to "GRX".
38  **
39  ** Changes by M.Alvarez (malfer@telefonica.net) 02/01/2002
40  **   - Go to full screen if the framemode dimensions are equal to
41  **     the screen dimensions (setting the client start area at 0,0).
42  **
43  ** Changes by M.Alvarez (malfer@telefonica.net) 02/02/2002
44  **   - The w32 imput queue implemented as a circular queue.
45  **   - All the input related code moved to w32inp.c
46  **   - The main window is created in WinMain, so the grx program
47  **     can use other videodrivers like the memory one.
48  **
49  ** Changes by M.Alvarez (malfer@telefonica.net) 11/02/2002
50  **   - Now the GRX window is properly closed, so the previous app
51  **     gets the focus.
52  **
53  ** Changes by M.Alvarez (malfer@telefonica.net) 31/03/2002
54  **   - Accepts arbitrary (user defined) resolution.
55  **
56  ** Changes by Thomas Demmer (TDemmer@krafteurope.com)
57  **   - Instead of begin with WinMain and start a thread with the main
58  **     GRX program, do it backward: begin in main and start a thread to
59  **     handle the Windows window. With this change we get rid of the
60  **     awful GRXMain special entry point.
61  **
62  ** Changes by M.Alvarez (malfer@telefonica.net) 12/02/2003
63  **   - Sanitize the Thomas changes.
64  **
65  ** Changes by Thomas Demmer (TDemmer@krafteurope.com) 18/03/2003
66  **   - Use a DIB for the hDCMem.
67  **
68  ** Changes by Josu Onandia (jonandia@fagorautomation.es) 19/03/2003
69  **   - With the Thomas idea of using a DIB, we can now use the DIB like
70  **     a linear frame buffer, so the new win32 framedrivers can take
71  **     advantage of the standard GRX frame drivers.
72  **
73  ** Changes by Peter Gerwinski 19/06/2004
74  **   - more W32 events handling
75  **
76  ** Changes by Maurice Lombardi 21/08/2007
77  **   - Corrections to WM_PAINT
78  **     1 - revert the previous change: was saturating GrMouseInfo->queue
79  **         for fast paintings
80  **     2 - GetUpdateRect() gave wrong UpdateRect !!!
81  **
82  ** Changes by Peter Schauer <peterschauer@gmx.net> 12/05/2008
83  **   - vdrivers/vd_win32.c has a race condition with the loadcolor
84  **     SetDIBColorTable function call, which happens sometimes on
85  **     fast multiprocessor machines. This affects only 8 bpp modes,
86  **     as loadcolor is not called in 32 bpp modes.
87  **     If the WndThread is currently executing its BitBlt during WM_PAINT
88  **     processing and the GRX user thread is calling GrAllocColor, the
89  **     SetDIBColorTable function call fails, as the DC is locked by the BitBlt.
90  **     This results in the color not being set, which could also happen
91  **     during the initial setting of the VGA colors in _GrResetColors.
92  **     My proposed fix delays the SetDIBColorTable call and moves it to
93  **     the WM_PAINT processing, making it synchronous with the BitBlt call.
94  **
95  ** Changes by M.Alvarez (malfer@telefonica.net) 01/12/2007
96  **   - Added videomodes for wide monitors
97  **
98  **/
99 
100 #include "libwin32.h"
101 #include "libgrx.h"
102 #include "grdriver.h"
103 #include "arith.h"
104 
105 #ifndef GRXWINDOW_TITLE
106 #define GRXWINDOW_TITLE "GRX"
107 #endif
108 
109 HWND hGRXWnd = NULL;
110 HWND hPrvWnd = NULL;
111 HDC hDCMem = NULL;
112 
113 CRITICAL_SECTION _csEventQueue;
114 W32Event *_W32EventQueue = NULL;
115 volatile int _W32EventQueueSize = 0;
116 volatile int _W32EventQueueRead = 0;
117 volatile int _W32EventQueueWrite = 0;
118 volatile int _W32EventQueueLength = 0;
119 
120 static HBITMAP hBmpDIB = NULL;
121 
122 static int maxScreenWidth, maxScreenHeight;
123 static int maxWindowWidth, maxWindowHeight;
124 
125 HANDLE windowThread = INVALID_HANDLE_VALUE;
126 static HANDLE mainThread   = INVALID_HANDLE_VALUE;
127 
128 static volatile int isWindowThreadRunning = 0;
129 static volatile int isMainWaitingTermination = 0;
130 
131 struct _GR_modifiedColors {
132     int modified;
133     RGBQUAD color;
134 };
135 static struct _GR_modifiedColors modifiedColors[256];
136 static volatile int isColorModified = 0;
137 
138 static DWORD WINAPI WndThread(void *param);
139 static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
140 				LPARAM lParam);
141 
loadcolor(int c,int r,int g,int b)142 static void loadcolor(int c, int r, int g, int b)
143 {
144     RGBQUAD color;
145     color.rgbBlue = b;
146     color.rgbGreen = g;
147     color.rgbRed = r;
148     color.rgbReserved = 0;
149     if (c >= 0 && c <= 256) {
150 	modifiedColors[c].color = color;
151 	modifiedColors[c].modified = 1;
152 	isColorModified = 1;
153 	InvalidateRect(hGRXWnd, NULL, FALSE);
154     }
155 }
156 
CreateDIB8(HDC hdc,int w,int h,char ** pBits)157 static HBITMAP CreateDIB8(HDC hdc, int w, int h, char **pBits)
158 {
159     BITMAPINFO *pbmInfo;
160     HBITMAP hBmp;
161 
162     pbmInfo = malloc(sizeof(BITMAPINFO) +256*sizeof(RGBQUAD));
163     pbmInfo->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
164     pbmInfo->bmiHeader.biWidth = w;
165     pbmInfo->bmiHeader.biHeight = -h;
166     pbmInfo->bmiHeader.biPlanes = 1;
167     pbmInfo->bmiHeader.biBitCount = 8;
168     pbmInfo->bmiHeader.biCompression = BI_RGB;
169     pbmInfo->bmiHeader.biSizeImage = 0;
170     pbmInfo->bmiHeader.biXPelsPerMeter = 0;
171     pbmInfo->bmiHeader.biYPelsPerMeter = 0;
172     pbmInfo->bmiHeader.biClrUsed = 0;
173     pbmInfo->bmiHeader.biClrImportant = 0;
174     hBmp = CreateDIBSection(0, pbmInfo, DIB_RGB_COLORS, (void*)pBits, 0, 0);
175     free(pbmInfo);
176     return(hBmp);
177 }
178 
CreateDIB24(HDC hdc,int w,int h,char ** pBits)179 static HBITMAP CreateDIB24(HDC hdc, int w, int h, char **pBits)
180 {
181     BITMAPINFO *pbmInfo;
182     HBITMAP hBmp;
183 
184     pbmInfo = malloc(sizeof(BITMAPINFO));
185     pbmInfo->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
186     pbmInfo->bmiHeader.biWidth = w;
187     pbmInfo->bmiHeader.biHeight = -h;
188     pbmInfo->bmiHeader.biPlanes = 1;
189     pbmInfo->bmiHeader.biBitCount = 24;
190     pbmInfo->bmiHeader.biCompression = BI_RGB;
191     pbmInfo->bmiHeader.biSizeImage = 0;
192     pbmInfo->bmiHeader.biXPelsPerMeter = 0;
193     pbmInfo->bmiHeader.biYPelsPerMeter = 0;
194     pbmInfo->bmiHeader.biClrUsed = 0;
195     pbmInfo->bmiHeader.biClrImportant = 0;
196     hBmp = CreateDIBSection(0, pbmInfo, DIB_RGB_COLORS, (void*)pBits, 0, 0);
197     free(pbmInfo);
198     return(hBmp);
199 }
200 
setmode(GrVideoMode * mp,int noclear)201 static int setmode(GrVideoMode * mp, int noclear)
202 {
203     RECT Rect;
204     HDC hDC;
205     HBRUSH hBrush;
206     RGBQUAD color;
207     int inipos;
208 
209     if (mp->extinfo->mode != GR_frameText) {
210 	inipos = 50;
211 	if (mp->width == maxScreenWidth && mp->height == maxScreenHeight)
212 	    inipos = 0;
213 	Rect.left = inipos;
214 	Rect.top = inipos;
215 	Rect.right = mp->width + inipos;
216 	Rect.bottom = mp->height + inipos;
217 	AdjustWindowRect(&Rect, WS_OVERLAPPEDWINDOW, FALSE);
218 	maxWindowWidth = Rect.right - Rect.left;
219 	maxWindowHeight = Rect.bottom - Rect.top;
220 	SetWindowPos(hGRXWnd, NULL,
221 		     Rect.left, Rect.top,
222 		     maxWindowWidth, maxWindowHeight,
223 		     SWP_DRAWFRAME | SWP_NOZORDER | SWP_SHOWWINDOW);
224 
225         if (hBmpDIB != NULL) {
226             DeleteObject(hBmpDIB);
227             hBmpDIB = NULL;
228         }
229 	hDC = GetDC(hGRXWnd);
230 	if (hDCMem == NULL)
231 	    hDCMem = CreateCompatibleDC(hDC);
232         if (mp->bpp == 8) {
233             hBmpDIB = CreateDIB8(hDC, mp->width, mp->height,
234                                  &mp->extinfo->frame);
235         } else {
236             hBmpDIB = CreateDIB24(hDC, mp->width, mp->height,
237                                   &mp->extinfo->frame);
238         }
239 	SelectObject(hDCMem, hBmpDIB);
240 	if (mp->bpp == 8) {
241 	    color.rgbBlue = color.rgbGreen = color.rgbRed =
242 	                    color.rgbReserved = 0;
243 	    SetDIBColorTable(hDCMem, 0, 1, &color);
244 	}
245 	SetRect(&Rect, 0, 0, mp->width, mp->height);
246 	hBrush = CreateSolidBrush(0);
247         FillRect(hDCMem, &Rect, hBrush);
248 	if (mp->bpp == 8)
249 	    BitBlt(hDC, 0, 0, mp->width, mp->height, hDCMem, 0, 0, SRCCOPY);
250 	else
251 	    FillRect(hDC, &Rect, hBrush);
252 	ReleaseDC(hGRXWnd, hDC);
253 	DeleteObject(hBrush);
254         UpdateWindow(hGRXWnd);
255         SetForegroundWindow(hGRXWnd);
256     } else {
257         /* If changing to text-mode, hide the graphics window. */
258 	if (hGRXWnd != NULL) {
259 	    ShowWindow(hGRXWnd, SW_HIDE);
260 	    SetForegroundWindow(hPrvWnd);
261 	}
262     }
263     return (TRUE);
264 }
265 
setbank_dummy(int bk)266 static void setbank_dummy(int bk)
267 {
268     bk = bk;
269 }
270 
271 GrVideoModeExt grtextext = {
272     GR_frameText,		/* frame driver */
273     NULL,			/* frame driver override */
274     NULL,			/* frame buffer address */
275     {0, 0, 0},			/* color precisions */
276     {0, 0, 0},			/* color component bit positions */
277     0,				/* mode flag bits */
278     setmode,			/* mode set */
279     NULL,			/* virtual size set */
280     NULL,			/* virtual scroll */
281     NULL,			/* bank set function */
282     NULL,			/* double bank set function */
283     NULL			/* color loader */
284 };
285 
286 static GrVideoModeExt grxwinext8 = {
287     GR_frameWIN32_8,		/* frame driver */
288     NULL,			/* frame driver override */
289     NULL,			/* frame buffer address */
290     {8, 8, 8},			/* color precisions */
291     {0, 8, 16},                 /* color component bit positions */
292     0,				/* mode flag bits */
293     setmode,			/* mode set */
294     NULL,			/* virtual size set */
295     NULL,			/* virtual scroll */
296     setbank_dummy,		/* bank set function */
297     NULL,			/* double bank set function */
298     loadcolor			/* color loader */
299 };
300 
301 static GrVideoModeExt grxwinext24 = {
302     GR_frameWIN32_24,		/* frame driver */
303     NULL,			/* frame driver override */
304     NULL,			/* frame buffer address */
305     {8, 8, 8},			/* color precisions */
306     {16, 8, 0},                 /* color component bit positions */
307     0,				/* mode flag bits */
308     setmode,			/* mode set */
309     NULL,			/* virtual size set */
310     NULL,			/* virtual scroll */
311     setbank_dummy,		/* bank set function */
312     NULL,			/* double bank set function */
313     NULL			/* color loader */
314 };
315 
316 static GrVideoMode modes[] = {
317     /* pres.  bpp wdt   hgt   BIOS   scan  priv. &ext  */
318     {TRUE, 8, 80, 25, 0x00, 80, 1, &grtextext},
319 
320     {TRUE, 8, 320, 240, 0x00, 320, 0, &grxwinext8},
321     {TRUE, 8, 640, 480, 0x00, 640, 0, &grxwinext8},
322     {TRUE, 8, 800, 600, 0x00, 800, 0, &grxwinext8},
323     {TRUE, 8, 1024, 768, 0x00, 1024, 0, &grxwinext8},
324     {TRUE, 8, 1280, 1024, 0x00, 1280, 0, &grxwinext8},
325     {TRUE, 8, 1600, 1200, 0x00, 1600, 0, &grxwinext8},
326     {TRUE, 8, 1440, 900, 0x00, 1440, 0, &grxwinext8},
327     {TRUE, 8, 1680, 1050, 0x00, 1680, 0, &grxwinext8},
328     {TRUE, 8, 1920, 1200, 0x00, 1920, 0, &grxwinext8},
329     {TRUE, 8, 2560, 1600, 0x00, 2560, 0, &grxwinext8},
330 
331     {TRUE, 24, 320, 240, 0x00, 960, 0, &grxwinext24},
332     {TRUE, 24, 640, 480, 0x00, 1920, 0, &grxwinext24},
333     {TRUE, 24, 800, 600, 0x00, 2400, 0, &grxwinext24},
334     {TRUE, 24, 1024, 768, 0x00, 3072, 0, &grxwinext24},
335     {TRUE, 24, 1280, 1024, 0x00, 3840, 0, &grxwinext24},
336     {TRUE, 24, 1600, 1200, 0x00, 4800, 0, &grxwinext24},
337     {TRUE, 24, 1440, 900, 0x00, 4320, 0, &grxwinext24},
338     {TRUE, 24, 1680, 1050, 0x00, 5040, 0, &grxwinext24},
339     {TRUE, 24, 1920, 1200, 0x00, 5760, 0, &grxwinext24},
340     {TRUE, 24, 2560, 1600, 0x00, 7680, 0, &grxwinext24},
341 
342     {FALSE, 0, 9999, 9999, 0x00, 0, 0, NULL}
343 };
344 
detect(void)345 static int detect(void)
346 {
347     static int inited = 0;
348     WNDCLASSEX wndclass;
349 
350     if (!inited) {
351         wndclass.cbSize = sizeof(wndclass);
352         wndclass.style = CS_HREDRAW | CS_VREDRAW;
353         wndclass.lpfnWndProc = WndProc;
354         wndclass.cbClsExtra = 0;
355         wndclass.cbWndExtra = 0;
356         wndclass.hInstance = GetModuleHandle(NULL);
357         wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
358         wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
359         wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
360         wndclass.lpszMenuName = NULL;
361         wndclass.lpszClassName = "GRXCLASS";
362         wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
363         if (RegisterClassEx(&wndclass)== 0) return FALSE;
364         inited = 1;
365     }
366 
367     return TRUE;
368 }
369 
init(char * options)370 static int init(char *options)
371 {
372     int i;
373     DWORD thread_id;
374 
375     if (!detect()) return FALSE;
376 
377     /* WARNING: mainThread can not be used in the windowThread */
378     mainThread = GetCurrentThread();
379 
380     hPrvWnd = GetForegroundWindow();
381 
382     InitializeCriticalSection(&_csEventQueue);
383 
384     /* The modes not compatible width the configuration */
385     /* of Windows are made 'non-present'                */
386     maxScreenWidth = GetSystemMetrics(SM_CXSCREEN);
387     for (i = 1; i < itemsof(modes); i++) {
388         if (modes[i].width > maxScreenWidth)
389             modes[i].present = FALSE;
390     }
391     maxScreenHeight = GetSystemMetrics(SM_CYSCREEN);
392     for (i = 1; i < itemsof(modes); i++) {
393 	if (modes[i].height > maxScreenHeight)
394             modes[i].present = FALSE;
395     }
396 
397     windowThread = CreateThread(NULL, 0, WndThread, NULL, 0, &thread_id);
398 
399     /* Wait for thread creating the window. This is a busy */
400     /* waiting loop (bad), but we Sleep to yield (good)    */
401     while (isWindowThreadRunning == 0)
402 	Sleep(1);
403 
404     return TRUE;
405 }
406 
reset(void)407 static void reset(void)
408 {
409     isMainWaitingTermination = 1;
410     PostMessage(hGRXWnd, WM_CLOSE, 0, 0);
411 
412     while (isWindowThreadRunning == 1)
413 	Sleep(1);
414 
415     isMainWaitingTermination = 0;
416     DeleteCriticalSection(&_csEventQueue);
417 
418     if(hBmpDIB != NULL)
419     {
420         DeleteObject(hBmpDIB);
421         hBmpDIB = NULL;
422     }
423     if (hDCMem != NULL) {
424         DeleteDC(hDCMem);
425         hDCMem = NULL;
426     }
427 }
428 
_w32_selectmode(GrVideoDriver * drv,int w,int h,int bpp,int txt,unsigned int * ep)429 static GrVideoMode * _w32_selectmode(GrVideoDriver * drv, int w, int h,
430                                      int bpp, int txt, unsigned int * ep)
431 {
432     GrVideoMode *mp, *res;
433     long resto;
434 
435     if (txt) {
436         res = _gr_selectmode(drv, w, h, bpp, txt, ep);
437         goto done;
438     }
439     for (mp = &modes[1]; mp < &modes[itemsof(modes)-1]; mp++) {
440         if (mp->present && mp->width == w && mp->height == h) {
441             res = _gr_selectmode(drv, w, h, bpp, txt, ep);
442             goto done;
443         }
444     }
445     /* no predefined mode found. Create a new mode if we can*/
446     if (w <= maxScreenWidth && h <= maxScreenHeight) {
447         mp->present = TRUE;
448         mp->width = w;
449         mp->height = h;
450         if (bpp <= 8) {
451             mp->bpp = 8;
452             mp->extinfo = &grxwinext8;
453             resto = mp->width % 4;
454             if (resto) resto = 4 - resto;
455             mp->lineoffset = mp->width + resto;
456         }
457         else {
458             mp->bpp = 24;
459             mp->extinfo = &grxwinext24;
460             resto = (mp->width * 3) % 4;
461             if (resto) resto = 4 - resto;
462             mp->lineoffset = mp->width * 3 + resto;
463         }
464     }
465     res = _gr_selectmode(drv, w, h, bpp, txt, ep);
466 done:
467     return res;
468 }
469 
470 GrVideoDriver _GrVideoDriverWIN32 = {
471     "win32",			/* name */
472     GR_WIN32,			/* adapter type */
473     NULL,			/* inherit modes from this driver */
474     modes,			/* mode table */
475     itemsof(modes),		/* # of modes */
476     detect,			/* detection routine */
477     init,			/* initialization routine */
478     reset,			/* reset routine */
479     _w32_selectmode,            /* special mode select routine */
480     GR_DRIVERF_USER_RESOLUTION  /* arbitrary resolution possible */
481 };
482 
WndThread(void * param)483 static DWORD WINAPI WndThread(void *param)
484 {
485     MSG msg;
486 
487     hGRXWnd = CreateWindow("GRXCLASS", GRXWINDOW_TITLE,
488                            WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU
489                            | WS_THICKFRAME | WS_MINIMIZEBOX, 0, 0,
490                            CW_USEDEFAULT, CW_USEDEFAULT, NULL,
491                            NULL, GetModuleHandle(NULL), NULL);
492     ShowWindow(hGRXWnd, SW_HIDE);
493 
494     isWindowThreadRunning = 1;
495 
496     while (GetMessage(&msg, NULL, 0, 0)) {
497         TranslateMessage(&msg);
498         DispatchMessage(&msg);
499     }
500 
501     isWindowThreadRunning = 0;
502 
503     ExitThread(0);
504     return 0;
505 }
506 
convertwin32keystate(void)507 static int convertwin32keystate(void)
508 {
509     int fkbState = 0;
510 
511     if (GetKeyState(VK_SHIFT) < 0)
512 	fkbState |= GR_KB_SHIFT;
513     if (GetKeyState(VK_CONTROL) < 0)
514 	fkbState |= GR_KB_CTRL;
515     if (GetKeyState(VK_MENU) < 0)
516 	fkbState |= GR_KB_ALT;
517     if (GetKeyState(VK_SCROLL) < 0)
518 	fkbState |= GR_KB_SCROLLOCK;
519     if (GetKeyState(VK_NUMLOCK) < 0)
520 	fkbState |= GR_KB_NUMLOCK;
521     if (GetKeyState(VK_CAPITAL) < 0)
522 	fkbState |= GR_KB_CAPSLOCK;
523     if (GetKeyState(VK_INSERT) < 0)
524 	fkbState |= GR_KB_INSERT;
525     return fkbState;
526 }
527 
EnqueueW32Event(UINT uMsg,WPARAM wParam,LPARAM lParam,int kbstat)528 static void EnqueueW32Event(UINT uMsg, WPARAM wParam, LPARAM lParam,
529 			    int kbstat)
530 {
531     if (_W32EventQueue == NULL)
532 	return;
533 
534     EnterCriticalSection(&_csEventQueue);
535     _W32EventQueue[_W32EventQueueWrite].uMsg = uMsg;
536     _W32EventQueue[_W32EventQueueWrite].wParam = wParam;
537     _W32EventQueue[_W32EventQueueWrite].lParam = lParam;
538     _W32EventQueue[_W32EventQueueWrite].kbstat = kbstat;
539     if (++_W32EventQueueWrite == _W32EventQueueSize)
540 	_W32EventQueueWrite = 0;
541     if (++_W32EventQueueLength > _W32EventQueueSize) {
542 	_W32EventQueueLength--;
543 	if (++_W32EventQueueRead == _W32EventQueueSize)
544 	    _W32EventQueueRead = 0;
545     }
546     LeaveCriticalSection(&_csEventQueue);
547 }
548 
WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)549 static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
550 				LPARAM lParam)
551 {
552     static int cursorOn = 1;
553     int kbstat;
554     BOOL fInsert;
555 
556     switch (uMsg) {
557 
558     case WM_NCHITTEST:
559 	{
560 	    LRESULT res = DefWindowProc(hWnd, uMsg, wParam, lParam);
561 	    if (res == HTCLIENT) {
562 		if (cursorOn) {
563 		    ShowCursor(FALSE);
564 		    cursorOn = 0;
565 		}
566 	    } else {
567 		if (!cursorOn) {
568 		    ShowCursor(TRUE);
569 		    cursorOn = 1;
570 		}
571 	    }
572 	    return res;
573 	}
574 
575     case WM_CLOSE:
576         if (!isMainWaitingTermination && MessageBox(hWnd,
577             "This will abort the program\nare you sure?", "Abort",
578              MB_APPLMODAL | MB_ICONQUESTION | MB_YESNO ) != IDYES)
579             return 0;
580         DestroyWindow(hWnd);
581         if (!isMainWaitingTermination)
582         {
583             isWindowThreadRunning = 0;
584             ExitProcess(1);
585         }
586         break;
587 
588     case WM_DESTROY:
589         PostQuitMessage(0);
590         break;
591 
592     case WM_GETMINMAXINFO:
593 	{
594 	    LPMINMAXINFO lpmmi = (LPMINMAXINFO) lParam;
595 
596 	    lpmmi->ptMaxSize.x = lpmmi->ptMaxTrackSize.x = maxWindowWidth;
597 	    lpmmi->ptMaxSize.y = lpmmi->ptMaxTrackSize.y = maxWindowHeight;
598 	}
599         return 0;
600 
601     case WM_SYSCHAR:
602 	fInsert = FALSE;
603 	kbstat = convertwin32keystate();
604 	if (kbstat & GR_KB_ALT) {
605 	    if (wParam >= 'a' && wParam <= 'z')
606 		fInsert = TRUE;
607 	    if (wParam >= 'A' && wParam <= 'Z')
608 		fInsert = TRUE;
609 	    if (wParam >= '0' && wParam <= '9')
610 		fInsert = TRUE;
611 	}
612 	if (!fInsert)
613 	    break;
614 	EnqueueW32Event(uMsg, wParam, lParam, kbstat);
615 	return 0;
616 
617     case WM_COMMAND:
618     case WM_CHAR:
619     case WM_KEYDOWN:
620     case WM_SYSKEYDOWN:
621     case WM_LBUTTONDOWN:
622     case WM_MBUTTONDOWN:
623     case WM_RBUTTONDOWN:
624     case WM_LBUTTONUP:
625     case WM_MBUTTONUP:
626     case WM_RBUTTONUP:
627     case WM_MOUSEMOVE:
628           {
629 	    kbstat = convertwin32keystate();
630 	    EnqueueW32Event(uMsg, wParam, lParam, kbstat);
631           }
632 	return 0;
633 
634     case WM_MOUSEWHEEL:
635           {
636 	    kbstat = convertwin32keystate();
637 	    /* twice to simulate down up */
638 	    EnqueueW32Event(uMsg, wParam, lParam, kbstat);
639 	    EnqueueW32Event(uMsg, wParam, lParam, kbstat);
640           }
641 	return 0;
642 
643 
644     case WM_PAINT:
645 	{
646 	    HDC hDC;
647 	    PAINTSTRUCT ps;
648 
649 	    if (isColorModified) {
650 		int c;
651 
652 		isColorModified = 0;
653 		for (c = 0; c < 256; c++) {
654 		    if (modifiedColors[c].modified) {
655 			int res;
656 
657 			modifiedColors[c].modified = 0;
658     			if ((res = SetDIBColorTable(hDCMem, c, 1, &modifiedColors[c].color)) != 1)
659 			    DBGPRINTF(DBG_DRIVER,("SetDIBColorTable returned %d (%ld) color %d\n", res, GetLastError(), c));
660 		    }
661 		}
662 	    }
663 
664 	    hDC = BeginPaint(hWnd, &ps);
665 	    BitBlt(hDC,
666 		   ps.rcPaint.left, ps.rcPaint.top,
667 		   ps.rcPaint.right - ps.rcPaint.left + 1,
668 		   ps.rcPaint.bottom - ps.rcPaint.top + 1,
669 		   hDCMem, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
670 	    EndPaint(hWnd, &ps);
671 	}
672 	return 0;
673 
674     case WM_SYSCOMMAND:
675     case WM_NCCREATE:
676     case WM_NCPAINT:
677     case WM_NCMOUSEMOVE:
678     case WM_PALETTEISCHANGING:
679     case WM_ACTIVATEAPP:
680     case WM_NCCALCSIZE:
681     case WM_ACTIVATE:
682     case WM_NCACTIVATE:
683     case WM_SHOWWINDOW:
684     case WM_WINDOWPOSCHANGING:
685     case WM_GETTEXT:
686     case WM_SETFOCUS:
687     case WM_KILLFOCUS:
688     case WM_GETICON:
689     case WM_ERASEBKGND:
690     case WM_QUERYNEWPALETTE:
691     case WM_WINDOWPOSCHANGED:
692     case WM_GETDLGCODE:
693     case WM_MOVE:
694     case WM_SIZE:
695     case WM_SETCURSOR:
696     case WM_HELP:
697     case WM_KEYUP:
698     case WM_SYSKEYUP:
699 	break;
700 
701     default:
702 /*
703         char szMsg[255];
704         sprintf(szMsg, "Msg %x, wParam %d, lParam %d",
705                 uMsg, wParam, lParam);
706         MessageBox(NULL, szMsg, "Msg", MB_OK);
707 */
708 	break;
709 
710     }
711 
712     return DefWindowProc(hWnd, uMsg, wParam, lParam);
713 }
714