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