1 /*  Sarien - A Sierra AGI resource interpreter engine
2  *  Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
3  *
4  *  $Id: win32.c,v 1.35 2001/09/13 02:25:53 cmatsuoka Exp $
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; see docs/COPYING for further details.
9  */
10 
11 /* Win32 port by Felipe Rosinha <rosinha@helllabs.org>
12  * Fixes and hacks by Igor Nesterov <nest@rtsnet.ru>
13  * Mouse support by Ryan Gordon <icculus@clutteredmind.org>
14  * Extra fixes and hacks by Matt Hargett <matt@use.net>
15  * Misc. mess by Claudio Matsuoka <claudio@helllabs.org>
16  */
17 #include <ctype.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <windows.h>
21 #include <windowsx.h>
22 #include <mmsystem.h>
23 #include <stdio.h>
24 #include <process.h>
25 
26 #include "sarien.h"
27 #include "graphics.h"
28 #include "keyboard.h"
29 #include "console.h"
30 #include "win32.h"
31 
32 #define TICK_SECONDS		18
33 #define TICK_IN_MSEC		(1000 / (TICK_SECONDS))
34 #define REPEATED_KEYMASK	(1<<30)
35 #define EXTENDED_KEYMASK	(1<<24)
36 #define KEY_QUEUE_SIZE		16
37 
38 #define key_enqueue(k) do {				\
39 	EnterCriticalSection(&g_key_queue.cs);		\
40 	g_key_queue.queue[g_key_queue.end++] = (k);	\
41 	g_key_queue.end %= KEY_QUEUE_SIZE;		\
42 	LeaveCriticalSection(&g_key_queue.cs);		\
43 } while (0)
44 
45 #define key_dequeue(k) do {				\
46 	EnterCriticalSection(&g_key_queue.cs);		\
47 	(k) = g_key_queue.queue[g_key_queue.start++];	\
48 	g_key_queue.start %= KEY_QUEUE_SIZE;		\
49 	LeaveCriticalSection(&g_key_queue.cs);		\
50 } while (0)
51 
52 
53 typedef struct {
54 	UINT16 x1, y1;
55 	UINT16 x2, y2;
56 } xyxy;
57 
58 enum {
59 	WM_PUT_BLOCK = WM_USER + 1
60 };
61 static UINT16 g_err = err_OK;
62 static HPALETTE g_hPalette = NULL;
63 static const char g_szMainWndClass[] = "SarienWin";
64 static int scale = 2;
65 
66 static struct{
67 	HBITMAP    screen_bmp;
68 	CRITICAL_SECTION  cs;
69 	BITMAPINFO *binfo;
70 	void       *screen_pixels;
71 } g_screen;
72 
73 static struct{
74 	int start;
75 	int end;
76 	int queue[KEY_QUEUE_SIZE];
77 	CRITICAL_SECTION cs;
78 } g_key_queue = { 0, 0 };
79 
80 
81 static int	init_vidmode		(void);
82 static int	deinit_vidmode		(void);
83 static void	win32_put_block		(int, int, int, int);
84 static int	win32_keypress		(void);
85 static int	win32_get_key		(void);
86 static void	win32_new_timer		(void);
87 
88 static int	set_palette		(UINT8 *, int, int);
89 
90 
91 static struct gfx_driver gfx_win32 = {
92 	init_vidmode,
93 	deinit_vidmode,
94 	win32_put_block,
95 	NULL,
96 	win32_new_timer,
97 	win32_keypress,
98 	win32_get_key
99 };
100 
101 
102 extern struct sarien_options opt;
103 extern struct gfx_driver *gfx;
104 
105 static char *apptext = TITLE " " VERSION;
106 static HDC  hDC;
107 static WNDCLASS wndclass;
108 static int xsize, ysize;
109 
110 
111 #define ASPECT_RATIO(x) ((x) * 6 / 5)
112 
113 /* ====================================================================*/
114 
115 /* Some optimized put_pixel routines for the most common cases */
116 
_putpixels_scale1(int x,int y,int w,BYTE * p)117 static void _putpixels_scale1 (int x, int y, int w, BYTE *p)
118 {
119 	BYTE *p0 = g_screen.screen_pixels; /* Word aligned! */
120 
121 	y = GFX_HEIGHT - y - 1;
122 	p0 += x + y * xsize;
123 
124 	EnterCriticalSection(&g_screen.cs);
125 	while (w--) *p0++ = *p++;
126 	LeaveCriticalSection(&g_screen.cs);
127 }
128 
_putpixels_scale2(int x,int y,int w,BYTE * p)129 static void _putpixels_scale2 (int x, int y, int w, BYTE *p)
130 {
131 	BYTE *p0 = g_screen.screen_pixels, *p1; /* Word aligned! */
132 
133 	y = GFX_HEIGHT - y - 1;
134 	x <<= 1; y <<= 1;
135 	p0 += x + y * xsize;
136 	p1 = p0 + xsize;
137 
138 	EnterCriticalSection(&g_screen.cs);
139 	while (w--) {
140 		*p0++ = *p; *p0++ = *p;
141 		*p1++ = *p; *p1++ = *p;
142 		p++;
143 	}
144 	LeaveCriticalSection (&g_screen.cs);
145 }
146 
147 
148 /* ====================================================================*/
149 
150 /* Aspect ratio correcting put pixels handlers */
151 
_putpixels_fixratio_scale1(int x,int y,int w,UINT8 * p)152 static void _putpixels_fixratio_scale1 (int x, int y, int w, UINT8 *p)
153 {
154 	if (y > 0 && ASPECT_RATIO (y) - 1 != ASPECT_RATIO (y - 1))
155 		_putpixels_scale1 (x, ASPECT_RATIO(y) - 1, w, p);
156 	_putpixels_scale1 (x, ASPECT_RATIO(y), w, p);
157 }
158 
_putpixels_fixratio_scale2(int x,int y,int w,BYTE * p)159 static void _putpixels_fixratio_scale2 (int x, int y, int w, BYTE *p)
160 {
161 	BYTE *p0 = g_screen.screen_pixels, *p1, *p2, *_p; /* Word aligned! */
162 	int extra = 0;
163 
164 	if (0 == w)
165 		return;
166 
167 	y = GFX_HEIGHT - y - 1;
168 	x <<= 1; y <<= 1;
169 
170 	if (y < ((GFX_WIDTH - 1) << 2) && ASPECT_RATIO (y) + 2 != ASPECT_RATIO (y + 2)) {
171 		extra = w;
172 	}
173 
174 	y = ASPECT_RATIO(y);
175 
176 	p0 += x + y * xsize;
177 	p1 = p0 + xsize;
178 	p2 = p1 + xsize;
179 
180 	EnterCriticalSection(&g_screen.cs);
181 	for (_p = p; w--; p++) {
182 		*p0++ = *p;
183 		*p0++ = *p;
184 		*p1++ = *p;
185 		*p1++ = *p;
186 	}
187 
188 	for (p = _p; extra--; p++) {
189 		*p2++ = *p;
190 		*p2++ = *p;
191 	}
192 	LeaveCriticalSection (&g_screen.cs);
193 }
194 
195 /* ====================================================================*/
196 
update_mouse_pos(int x,int y)197 static void update_mouse_pos(int x, int y)
198 {
199 	mouse.x = x;
200 	mouse.y = y;
201 	if (opt.scale != 0) {
202 		mouse.x /= opt.scale;
203 		mouse.y /= opt.scale;
204 	}
205 
206 	/* for mouse we make the inverse transform of ASPECT_RATIO */
207 	if (opt.fixratio)
208 		mouse.y = mouse.y * 5 / 6;
209 }
210 
211 
212 LRESULT CALLBACK
MainWndProc(HWND hwnd,UINT nMsg,WPARAM wParam,LPARAM lParam)213 MainWndProc (HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
214 {
215 	HDC          hDC;
216 	PAINTSTRUCT  ps;
217 	int          h, w, key = 0;
218 	xyxy         *p = (xyxy *)lParam;
219 
220 	switch (nMsg) {
221 	case WM_PUT_BLOCK:
222 		hDC = GetDC (hwndMain);
223 		w = p->x2 - p->x1 + 1;
224 		h = p->y2 - p->y1 + 1;
225 		EnterCriticalSection (&g_screen.cs);
226 		StretchDIBits (
227 			hDC,
228 			p->x1, p->y1, w, h,
229 			p->x1, ysize - p->y2 - 1, w, h,
230 			g_screen.screen_pixels,
231 			g_screen.binfo,
232 			DIB_RGB_COLORS,
233 			SRCCOPY);
234 		LeaveCriticalSection (&g_screen.cs);
235 		ReleaseDC (hwndMain, hDC);
236 		break;
237 
238 	case WM_DESTROY:
239 		deinit_vidmode ();
240 		exit (-1);
241 		return 0;
242 
243 	case WM_PAINT:
244 		hDC = BeginPaint (hwndMain, &ps);
245 		EnterCriticalSection(&g_screen.cs);
246 		StretchDIBits(
247 			hDC,
248 			0, 0, xsize, ysize,
249 			0, 0, xsize, ysize,
250 			g_screen.screen_pixels,
251 			g_screen.binfo,
252 			DIB_RGB_COLORS,
253 			SRCCOPY);
254 		EndPaint (hwndMain, &ps);
255 		LeaveCriticalSection(&g_screen.cs);
256 		return 0;
257 
258 	/* Multimedia functions
259 	 * (Damn! The CALLBACK_FUNCTION parameter doesn't work!)
260 	 */
261 	case MM_WOM_DONE:
262 		flush_sound ((PWAVEHDR) lParam);
263 		return 0;
264 
265 	case WM_LBUTTONDOWN:
266 		key = BUTTON_LEFT;
267 		mouse.button = TRUE;
268 		update_mouse_pos(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
269 		break;
270 
271 	case WM_RBUTTONDOWN:
272 		key = BUTTON_RIGHT;
273 		mouse.button = TRUE;
274 		update_mouse_pos(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
275 		break;
276 
277 	case WM_LBUTTONUP:
278 	case WM_RBUTTONUP:
279 		mouse.button = FALSE;
280 		return 0;
281 
282 	case WM_MOUSEMOVE:
283 		update_mouse_pos(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
284 		return 0;
285 
286 	case WM_SYSKEYDOWN:
287 	case WM_KEYDOWN:
288 		/*  report ("%02x\n", (int)wParam); */
289 		switch (key = (int)wParam) {
290 		case VK_SHIFT:
291 			key = 0;
292 			break;
293 		case VK_CONTROL:
294 			key = 0;
295 			break;
296 		case VK_UP:
297 		case VK_NUMPAD8:
298 			if (lParam & REPEATED_KEYMASK)
299 				return 0;
300 			key = KEY_UP;
301 			break;
302 		case VK_LEFT:
303 		case VK_NUMPAD4:
304 			if (lParam & REPEATED_KEYMASK)
305 				return 0;
306 			key = KEY_LEFT;
307 			break;
308 		case VK_DOWN:
309 		case VK_NUMPAD2:
310 			if (lParam & REPEATED_KEYMASK)
311 				return 0;
312 			key = KEY_DOWN;
313 			break;
314 		case VK_RIGHT:
315 		case VK_NUMPAD6:
316 			if (lParam & REPEATED_KEYMASK)
317 				return 0;
318 			key = KEY_RIGHT;
319 			break;
320 		case VK_HOME:
321 		case VK_NUMPAD7:
322 			if (lParam & REPEATED_KEYMASK)
323 				return 0;
324 			key = KEY_UP_LEFT;
325 			break;
326 		case VK_PRIOR:
327 		case VK_NUMPAD9:
328 			if (lParam & REPEATED_KEYMASK)
329 				return 0;
330 			key = KEY_UP_RIGHT;
331 			break;
332 		case VK_NEXT:
333 		case VK_NUMPAD3:
334 			if (lParam & REPEATED_KEYMASK)
335 				return 0;
336 			key = KEY_DOWN_RIGHT;
337 			break;
338 		case VK_END:
339 		case VK_NUMPAD1:
340 			if (lParam & REPEATED_KEYMASK)
341 				return 0;
342 			key = KEY_DOWN_LEFT;
343 			break;
344 		case VK_CLEAR:
345 		case VK_NUMPAD5:
346 			key = KEY_STATIONARY;
347 			break;
348 		case VK_RETURN:
349 			key = KEY_ENTER;
350 			break;
351 		case VK_ADD:
352 			key = '+';
353 			break;
354 		case VK_SUBTRACT:
355 			key = '-';
356 			break;
357 		case VK_TAB:
358 			key = 0x0009;
359 			break;
360 		case VK_F1:
361 			key = 0x3b00;
362 			break;
363 		case VK_F2:
364 			key = 0x3c00;
365 			break;
366 		case VK_F3:
367 			key = 0x3d00;
368 			break;
369 		case VK_F4:
370 			key = 0x3e00;
371 			break;
372 		case VK_F5:
373 			key = 0x3f00;
374 			break;
375 		case VK_F6:
376 			key = 0x4000;
377 			break;
378 		case VK_F7:
379 			key = 0x4100;
380 			break;
381 		case VK_F8:
382 			key = 0x4200;
383 			break;
384 		case VK_F9:
385 			key = 0x4300;
386 			break;
387 		case VK_F10:
388 			key = 0x4400;
389 			break;
390 		case VK_F11:
391 			key = KEY_STATUSLN;
392 			break;
393 		case VK_F12:
394 			key = KEY_PRIORITY;
395 			break;
396 		case VK_ESCAPE:
397 			key = 0x1b;
398 			break;
399 		case 0xba:
400 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? ':' : ';';
401 			break;
402 		case 0xbb:
403 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? '+' : '=';
404 			break;
405 		case 0xbc:
406 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? '<' : ',';
407 			break;
408 		case 0xbd:
409 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? '_' : '-';
410 			break;
411 		case 0xbe:
412 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? '>' : '.';
413 			break;
414 		case 0xbf:
415 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? '?' : '/';
416 			break;
417 		case 0xdb:
418 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? '{' : '[';
419 			break;
420 		case 0xdc:
421 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? '|' : '\\';
422 			break;
423 		case 0xdd:
424 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? '}' : ']';
425 			break;
426 		case 0xde:
427 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? '"' : '\'';
428 			break;
429 		case 192:
430 			key = GetAsyncKeyState (VK_SHIFT) & 0x8000 ? '~' : '`';
431 			break;
432 		default:
433 			if (!isalpha (key))
434 				break;
435 
436 			/* Must exist a better way to do that! */
437 			if (GetKeyState (VK_CAPITAL) & 0x1) {
438 				if (GetAsyncKeyState (VK_SHIFT) & 0x8000)
439 					key = key + 32;
440 			} else {
441 				if (!(GetAsyncKeyState (VK_SHIFT) & 0x8000))
442 					key = key + 32;
443 			}
444 
445 			/* Control and Alt modifier */
446 			if (GetAsyncKeyState (VK_CONTROL) & 0x8000)
447 				key = (key & ~0x20) - 0x40;
448 			else
449 				if (GetAsyncKeyState (VK_MENU) & 0x8000)
450 					key = scancode_table[(key & ~0x20) - 0x41] << 8;
451 
452 			break;
453 
454 		};
455 
456 		_D (": key = 0x%02x ('%c')", key, isprint(key) ? key : '?');
457 
458 		/* Cancel "alt" keybind to toggle.monitor (huh?) */
459 		if (key == 0x12)
460 			key = 0;
461 
462 		break;
463 	};
464 
465 	/* Keyboard message handled */
466 	if (key) {
467 		key_enqueue (key);
468 		return 0;
469 	}
470 
471 	return DefWindowProc (hwnd, nMsg, wParam, lParam);
472 }
473 
474 
init_machine(int argc,char ** argv)475 int init_machine (int argc, char **argv)
476 {
477 	InitializeCriticalSection (&g_screen.cs);
478 	InitializeCriticalSection (&g_key_queue.cs);
479 
480 	gfx = &gfx_win32;
481 	scale = opt.scale;
482 
483 	return err_OK;
484 }
485 
deinit_machine()486 int deinit_machine ()
487 {
488 	DeleteCriticalSection(&g_key_queue.cs);
489 	DeleteCriticalSection(&g_screen.cs);
490 	return err_OK;
491 }
492 
init_vidmode()493 static int init_vidmode ()
494 {
495 	int i;
496 
497 #if 0
498 	/* FIXME: place this in an "About" box or something... */
499 	fprintf (stderr,
500 	"win32: Win32 DIB support by rosinha@dexter.damec.cefetpr.br\n");
501 #endif
502 
503 	xsize = GFX_WIDTH * scale;
504 	ysize = (opt.fixratio ? ASPECT_RATIO(GFX_HEIGHT) : GFX_HEIGHT) * scale;
505 
506 	memset (&wndclass, 0, sizeof(WNDCLASS));
507 	wndclass.lpszClassName = g_szMainWndClass;
508 	wndclass.style         = CS_HREDRAW | CS_VREDRAW;
509 	wndclass.lpfnWndProc   = MainWndProc;
510 	wndclass.hInstance     = GetModuleHandle(NULL);
511 	wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
512 	wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
513 	wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
514 
515 	if (!RegisterClass(&wndclass))
516 	{
517 		OutputDebugString("win32.c: init_vidmode(): can't register class");
518 		g_err = err_Unk;
519 		goto exx;
520 	}
521 
522 	hwndMain = CreateWindow (
523 		g_szMainWndClass,
524 		apptext,
525 		WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX,
526 		CW_USEDEFAULT,
527 		CW_USEDEFAULT,
528 		xsize + GetSystemMetrics (SM_CXFRAME),
529 		ysize + GetSystemMetrics (SM_CYCAPTION) +
530 			GetSystemMetrics (SM_CYFRAME),
531 		NULL,
532 		NULL,
533 		NULL,
534 		NULL
535 	);
536 
537 	if (NULL == hwndMain)
538 	{
539 		OutputDebugString("win32.c: init_vidmode(): can't register class");
540 		g_err = err_Unk;
541 		goto exx;
542 	}
543 
544 	/* First create the palete */
545 	set_palette (palette, 0, 16);
546 
547 	/* Fill in the bitmap info header */
548 	g_screen.binfo = (BITMAPINFO *)malloc(sizeof(*g_screen.binfo) +
549 		256 * sizeof(RGBQUAD));
550 
551 	if (g_screen.binfo == NULL) {
552 		OutputDebugString("win32.c: init_vidmode(): malloc of g_screen.binfo failed");
553 		g_err =  err_Unk;
554 		goto exx;
555 	}
556 
557 	g_screen.binfo->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
558 	g_screen.binfo->bmiHeader.biWidth         = xsize;
559 	g_screen.binfo->bmiHeader.biHeight        = ysize;
560 	g_screen.binfo->bmiHeader.biPlanes        = 1;
561 	g_screen.binfo->bmiHeader.biBitCount      = 8;   /* should be fine */
562 	g_screen.binfo->bmiHeader.biCompression   = BI_RGB;
563 	g_screen.binfo->bmiHeader.biSizeImage     = 0;
564 	g_screen.binfo->bmiHeader.biXPelsPerMeter = 0;
565 	g_screen.binfo->bmiHeader.biYPelsPerMeter = 0;
566 	g_screen.binfo->bmiHeader.biClrUsed       = 32;
567 	g_screen.binfo->bmiHeader.biClrImportant  = 0;
568 
569 	for (i = 0; i < 32; i ++) {
570 		g_screen.binfo->bmiColors[i].rgbRed   = (palette[i*3    ]) << 2;
571 		g_screen.binfo->bmiColors[i].rgbGreen = (palette[i*3 + 1]) << 2;
572 		g_screen.binfo->bmiColors[i].rgbBlue  = (palette[i*3 + 2]) << 2;
573 		g_screen.binfo->bmiColors[i].rgbReserved = 0;
574 	}
575 
576 	/* Create the offscreen bitmap buffer */
577 	hDC = GetDC (hwndMain);
578 	g_screen.screen_bmp = CreateDIBSection (hDC, g_screen.binfo,
579 		DIB_RGB_COLORS, (void **)(&g_screen.screen_pixels), NULL, 0);
580 	ReleaseDC (hwndMain, hDC);
581 
582 	if (g_screen.screen_bmp == NULL || g_screen.screen_pixels == NULL) {
583 		OutputDebugString ("win32.c: init_vidmode(): "
584 			"CreateDIBSection failed");
585 		g_err = err_Unk;
586 	} else {
587 		ShowWindow (hwndMain, TRUE);
588 		UpdateWindow (hwndMain);
589 		g_err = err_OK;
590 	}
591 
592 	if (!opt.fixratio) {
593 		switch (scale) {
594 		case 1:
595 			gfx_win32.put_pixels = _putpixels_scale1;
596 			break;
597 		case 2:
598 			gfx_win32.put_pixels = _putpixels_scale2;
599 			break;
600 		}
601 	} else {
602 		switch (scale) {
603 		case 1:
604 			gfx_win32.put_pixels = _putpixels_fixratio_scale1;
605 			break;
606 		case 2:
607 			gfx_win32.put_pixels = _putpixels_fixratio_scale2;
608 			break;
609 		}
610 	}
611 exx:
612 
613 	return g_err;
614 }
615 
process_events()616 static void INLINE process_events ()
617 {
618 	MSG msg;
619 
620 	while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
621 		GetMessage (&msg, NULL, 0, 0);
622 		TranslateMessage (&msg);
623 		DispatchMessage (&msg);
624 	}
625 }
626 
deinit_vidmode(void)627 static int deinit_vidmode (void)
628 {
629 	PostMessage (hwndMain, WM_QUIT, 0, 0);
630 	DeleteObject (g_screen.screen_bmp);
631 
632 	return err_OK;
633 }
634 
635 /* put a block onto the screen */
win32_put_block(int x1,int y1,int x2,int y2)636 static void win32_put_block (int x1, int y1, int x2, int y2)
637 {
638 	xyxy *p;
639 
640 	if ((p = malloc (sizeof(xyxy))) == NULL)
641 		return;
642 
643 	if (x1 >= GFX_WIDTH)  x1 = GFX_WIDTH - 1;
644 	if (y1 >= GFX_HEIGHT) y1 = GFX_HEIGHT - 1;
645 	if (x2 >= GFX_WIDTH)  x2 = GFX_WIDTH - 1;
646 	if (y2 >= GFX_HEIGHT) y2 = GFX_HEIGHT - 1;
647 
648 	p->x1 = x1 * scale;
649 	p->y1 = y1 * scale;
650 	p->x2 = (x2 + 1) * scale - 1;
651 	p->y2 = (y2 + 1) * scale - 1;
652 
653 	if (opt.fixratio) {
654 		p->y1 = ASPECT_RATIO(p->y1);
655 		p->y2 = ASPECT_RATIO(p->y2 + 1);
656 	}
657 
658 	PostMessage (hwndMain, WM_PUT_BLOCK, 0, (LPARAM)p);
659 }
660 
win32_keypress(void)661 static int win32_keypress (void)
662 {
663 	int b;
664 
665 	process_events ();
666 	EnterCriticalSection(&g_key_queue.cs);
667 	b = (g_key_queue.start != g_key_queue.end);
668 	LeaveCriticalSection(&g_key_queue.cs);
669 
670 	return b;
671 }
672 
win32_get_key(void)673 static int win32_get_key (void)
674 {
675 	int k;
676 
677 	while (!win32_keypress())
678 		win32_new_timer ();
679 
680 	key_dequeue (k);
681 
682 	return k;
683 }
684 
win32_new_timer()685 static void win32_new_timer ()
686 {
687 	DWORD	now;
688 	static DWORD last = 0;
689 
690 	now = GetTickCount();
691 
692 	while (now - last < TICK_IN_MSEC) {
693 		Sleep (TICK_IN_MSEC - (now - last));
694 		now = GetTickCount ();
695 	}
696 	last = now;
697 
698 	process_events ();
699 }
700 
701 /* Primitive palette functions */
set_palette(UINT8 * pal,int scol,int numcols)702 static int set_palette (UINT8 *pal, int scol, int numcols)
703 {
704 	int          i, j;
705 	HDC          hDC;
706 	LOGPALETTE   *palette;
707 	PALETTEENTRY *entries;
708 
709 	hDC = GetDC(hwndMain);
710 
711 	if (GetDeviceCaps(hDC, PLANES) * GetDeviceCaps(hDC, BITSPIXEL) <= 8 ) {
712 		palette = malloc(sizeof(*palette) + 16 * sizeof(PALETTEENTRY));
713 		if (NULL == palette) {
714 			OutputDebugString("malloc failed for palette");
715 			return err_Unk;
716 		}
717 
718 		palette->palVersion    = 0x300;
719 		palette->palNumEntries = 256;   /* Yikes! */
720 
721 		GetSystemPaletteEntries(hDC, 0, 16, palette->palPalEntry);
722 
723 		g_hPalette = CreatePalette(palette);
724 
725 		entries = (PALETTEENTRY *)malloc(256 * sizeof(PALETTEENTRY));
726 
727 		for (i = 0, j = 0; j < 256; j++) {
728 			entries[j].peRed   = pal[i*3    ] << 2;
729 			entries[j].peGreen = pal[i*3 + 1] << 2;
730 			entries[j].peBlue  = pal[i*3 + 2] << 2;
731 			entries[j].peFlags = PC_NOCOLLAPSE;
732 
733 			i ++;
734 			if (i >= 32)
735 				i = 0;
736 		}
737 
738 		SetPaletteEntries(g_hPalette, 0, 256, entries);
739 		SelectPalette(hDC, g_hPalette, FALSE);
740 		RealizePalette(hDC);
741 	}
742 
743 	ReleaseDC( hwndMain, hDC );
744 
745 	return err_OK;
746 }
747 
748 
749