1 /*  Sarien - A Sierra AGI resource interpreter engine
2  *  Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
3  *
4  *  $Id: wince.cpp,v 1.3.2.1 2001/11/10 12:13:54 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 /*
12  * Win32 port by Felipe Rosinha <rosinha@helllabs.org>
13  */
14 
15 /*
16  * Massively modified by Vasyl Tsvirkunov <vasyl@pacbell.net> for
17  * Pocket PC/WinCE port
18  */
19 
20 #include <ctype.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <windows.h>
24 #include <mmsystem.h>
25 #include <stdio.h>
26 #include <commctrl.h>
27 #include <aygshell.h>
28 #include "gx.h"
29 
30 #include "sarien.h"
31 #include "graphics.h"
32 #include "keyboard.h"
33 #include "console.h"
34 #include "text.h"
35 /* Prevent name conflict */
36 #undef snprintf
37 #include "win32.h"
38 
39 #include "resource.h"
40 
41 #define USE_F_KEYS
42 #define SMOOTH
43 
44 static LPTSTR szAppName = TEXT("Pocket Sarien");
45 static GXDisplayProperties gxdp;
46 static GXKeyList gxkl;
47 static bool active = true;
48 
49 static HWND hwndMB = NULL;
50 
51 typedef unsigned char UBYTE;
52 static UBYTE* screen;
53 #ifdef SMOOTH
54 static UBYTE* palRed;
55 static UBYTE* palGreen;
56 static UBYTE* palBlue;
57 #else
58 static unsigned short* pal;
59 #endif
60 
61 static bool bmono;
62 static bool b565;
63 static bool b555;
64 
65 static UBYTE invert = 0;
66 static int colorscale = 0;
67 
68 #define COLORCONV565(r,g,b) (((r&0xf8)<<(11-3))|((g&0xfc)<<(5-2))|((b&0xf8)>>3))
69 #define COLORCONV555(r,g,b) (((r&0xf8)<<(10-3))|((g&0xf8)<<(5-2))|((b&0xf8)>>3))
70 #define COLORCONVMONO(r,g,b) ((((3*r>>3)+(g>>1)+(b>>3))>>colorscale)^invert)
71 
72 static int	wince_init_vidmode	(void);
73 static int	wince_deinit_vidmode(void);
74 static void	wince_put_block		(int, int, int, int);
75 static void	wince_put_pixels	(int, int, int, BYTE *);
76 static int	wince_keypress		(void);
77 static int	wince_get_key		(void);
78 static void	wince_new_timer		(void);
79 
80 LRESULT CALLBACK WindowProc(HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam);
81 
82 #ifdef USE_F_KEYS
83 void setup_extra_windows(HWND hwndTop);
84 void adjust_extra_windows();
85 HWND hwndFKeys;
86 #endif
87 
88 static struct gfx_driver gfx_wince =
89 {
90 	wince_init_vidmode,
91 	wince_deinit_vidmode,
92 	wince_put_block,
93 	wince_put_pixels,
94 	wince_new_timer,
95 	wince_keypress,
96 	wince_get_key
97 };
98 
99 
100 extern "C" struct gfx_driver *gfx;
101 
init_machine(int argc,char ** argv)102 int init_machine(int argc, char **argv)
103 {
104 	gfx = &gfx_wince;
105 	return err_OK;
106 }
107 
deinit_machine()108 int deinit_machine()
109 {
110 	return err_OK;
111 }
112 
set_palette(int ent,UBYTE r,UBYTE g,UBYTE b)113 void set_palette(int ent, UBYTE r, UBYTE g, UBYTE b)
114 {
115 	if (ent >= 256)
116 		return;
117 #ifdef SMOOTH
118 	palRed[ent] = r;
119 	palGreen[ent] = g;
120 	palBlue[ent] = b;
121 #else
122 	if(b565)
123 		pal[ent] = COLORCONV565(r,g,b);
124 	else if(b555)
125 		pal[ent] = COLORCONV555(r,g,b);
126 	else if(bmono)
127 		pal[ent] = COLORCONVMONO(r,g,b);
128 #endif
129 }
130 
131 
wince_init_vidmode()132 static int wince_init_vidmode()
133 {
134 	WNDCLASS wc;
135 
136 	wc.style = CS_HREDRAW | CS_VREDRAW;
137 	wc.lpfnWndProc = WindowProc;
138 	wc.cbClsExtra = 0;
139 	wc.cbWndExtra = 0;
140 	wc.hInstance = GetModuleHandle(NULL);
141 	wc.hIcon = (HICON)LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_SARIEN));
142 	wc.hCursor = NULL;
143 	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
144 	wc.lpszMenuName = NULL;
145 	wc.lpszClassName = szAppName;
146 	RegisterClass(&wc);
147 
148 	hwndMain = CreateWindow(szAppName,
149 		szAppName,
150 		WS_VISIBLE,
151 		0,
152 		0,
153 		GetSystemMetrics(SM_CXSCREEN),
154 		GetSystemMetrics(SM_CYSCREEN),
155 		NULL,
156 		NULL,
157 		GetModuleHandle(NULL),
158 		NULL);
159 
160 	if(!hwndMain)
161 		return err_Unk;
162 
163 	SHMENUBARINFO smbi;
164 	smbi.cbSize = sizeof(smbi);
165 	smbi.hwndParent = hwndMain;
166 	smbi.dwFlags = 0;
167 	smbi.nToolBarId = IDM_MENU;
168 	smbi.hInstRes = GetModuleHandle(NULL);
169 	smbi.nBmpId = 0;
170 	smbi.cBmpImages = 0;
171 	smbi.hwndMB = NULL;
172 	BOOL res = SHCreateMenuBar(&smbi);
173 	hwndMB = smbi.hwndMB;
174 
175 	screen = new UBYTE[GFX_WIDTH*GFX_HEIGHT];
176 #ifdef SMOOTH
177 	palRed = new UBYTE[256];
178 	palGreen = new UBYTE[256];
179 	palBlue = new UBYTE[256];
180 	if(!palRed || !palGreen || !palBlue)
181 		return err_Unk;
182 #else
183 	pal = new unsigned short[256];
184 	if(!pal)
185 		return err_Unk;
186 #endif
187 
188 	if(!screen)
189 		return err_Unk;
190 
191 	memset(screen, 0, GFX_WIDTH*GFX_HEIGHT);
192 
193 	GXOpenDisplay(hwndMain, GX_FULLSCREEN);
194 	GXOpenInput();
195 
196 	SetWindowPos(hwndMain, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
197 	SetForegroundWindow(hwndMain);
198 	SHFullScreen(hwndMain, SHFS_SHOWSIPBUTTON);
199 	SHFullScreen(hwndMain, SHFS_HIDETASKBAR);
200 
201 	gxdp = GXGetDisplayProperties();
202 	gxkl = GXGetDefaultKeys(GX_NORMALKEYS);
203 
204 
205 	bmono = (gxdp.ffFormat & kfDirect) && (gxdp.cBPP <= 8);
206 	b565 = (gxdp.ffFormat & kfDirect565) != 0;
207 	b555 = (gxdp.ffFormat & kfDirect555) != 0;
208 	if(bmono)
209 	{
210 		if(gxdp.ffFormat & kfDirectInverted)
211 			invert = (1<<gxdp.cBPP)-1;
212 		colorscale = gxdp.cBPP < 8 ? 8-gxdp.cBPP : 0;
213 	}
214 
215 	int i;
216 	for(i=0; i<16; i++)
217 		set_palette(i, palette[i*3+0]<<2, palette[i*3+1]<<2, palette[i*3+2]<<2);
218 	for(i=17; i<256; i++)
219 		set_palette(i, 0, 255, 0);
220 
221 #ifdef USE_F_KEYS
222 	setup_extra_windows(hwndMain);
223 #endif
224 
225 	return err_OK;
226 }
227 
process_events()228 static void INLINE process_events()
229 {
230 	MSG msg;
231 	while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
232 	{
233 		TranslateMessage(&msg);
234 		DispatchMessage(&msg);
235 	}
236 }
237 
wince_deinit_vidmode(void)238 static int wince_deinit_vidmode(void)
239 {
240 	GXCloseInput();
241 	GXCloseDisplay();
242 
243 	delete[] screen;
244 #ifdef SMOOTH
245 	delete[] palRed;
246 	delete[] palGreen;
247 	delete[] palBlue;
248 #else
249 	delete[] pal;
250 #endif
251 
252 
253 	PostMessage(hwndMain, WM_QUIT, 0, 0);
254 	return err_OK;
255 }
256 
257 
258 #define TICK_SECONDS		18
259 #define TICK_IN_MSEC		(1000 / (TICK_SECONDS))
260 
wince_new_timer()261 static void wince_new_timer()
262 {
263 	DWORD	now;
264 	static DWORD last = 0;
265 
266 	now = GetTickCount();
267 
268 	while (now - last < TICK_IN_MSEC)
269 	{
270 		Sleep (TICK_IN_MSEC - (now - last));
271 		now = GetTickCount ();
272 	}
273 	last = now;
274 
275 	process_events ();
276 }
277 
278 #define REPEATED_KEYMASK	(1<<30)
279 #define RELEASED_KEYMASK	(1<<31)
280 #define KEY_QUEUE_SIZE		16
281 
282 static struct{
283 	int start;
284 	int end;
285 	int queue[KEY_QUEUE_SIZE];
286 } g_key_queue = { 0, 0 };
287 
288 
289 #define key_enqueue(k) do {				\
290 	g_key_queue.queue[g_key_queue.end++] = (k);	\
291 	g_key_queue.end %= KEY_QUEUE_SIZE;		\
292 } while (0)
293 
294 #define key_dequeue(k) do {				\
295 	(k) = g_key_queue.queue[g_key_queue.start++];	\
296 	g_key_queue.start %= KEY_QUEUE_SIZE;		\
297 } while (0)
298 
299 
wince_keypress(void)300 static int wince_keypress(void)
301 {
302 	process_events();
303 	return (g_key_queue.start != g_key_queue.end);
304 }
305 
wince_get_key(void)306 static int wince_get_key(void)
307 {
308 	int k;
309 	while (!wince_keypress())
310 		wince_new_timer();
311 	key_dequeue(k);
312 	return k;
313 }
314 
315 
wince_put_pixels(int x,int y,int w,BYTE * p)316 static void wince_put_pixels(int x, int y, int w, BYTE *p)
317 {
318 	memcpy(screen+x+y*GFX_WIDTH, p, w);
319 }
320 
wince_put_block(int x1,int y1,int x2,int y2)321 static void wince_put_block(int x1, int y1, int x2, int y2)
322 {
323 	if(!active)
324 		return;
325 
326 /* Somehow this function gets called with unchecked bounds causing visual artefacts */
327 	if(x2 > 319)
328 		x2 = 319;
329 	if(y2 > 199)
330 		y2 = 199;
331 
332 	static UBYTE *src;
333 	static UBYTE *dst;
334 
335 	static UBYTE *scraddr;
336 	static UBYTE *scr_ptr;
337 
338 	static UBYTE *scr_ptr_limit;
339 	static UBYTE *src_limit;
340 
341 	static long pixelstep;
342 	static long linestep;
343 
344 // Special code is used to deal with packed pixels in monochrome mode
345 	static UBYTE bitmask;
346 	static int   bitshift;
347 
348 #ifdef SMOOTH
349 	x1 &= ~3;
350 #endif
351 
352 	scr_ptr = screen + x1 + y1*GFX_WIDTH;
353 	scr_ptr_limit = screen + x2 + y2*GFX_WIDTH;
354 
355 	linestep = gxdp.cbyPitch;
356 	pixelstep = gxdp.cbxPitch;
357 
358 	if(bmono)
359 	{
360 		if(pixelstep == 0)
361 			return; // unsupported screen geometry
362 	// this will work on mono iPAQ and @migo, don't know about any others
363 		bitshift = 0;
364 		bitmask = (1<<gxdp.cBPP)-1;
365 		linestep = (pixelstep > 0) ? -1 : 1;
366 	}
367 
368 
369 	scraddr = (UBYTE*)GXBeginDraw();
370 	if(scraddr)
371 	{
372 		if(bmono)
373 		{
374 		// Some partial pixel voodoo. I don't know if this works in all cases.
375 			scraddr += (x1*3/4)*pixelstep;
376 			int full = (y1*gxdp.cBPP)>>3;
377 			int partial = (y1*gxdp.cBPP)&7;
378 			scraddr += full*linestep;
379 			bitshift += gxdp.cBPP*partial;
380 			bitmask <<= gxdp.cBPP*partial;
381 			if(linestep < 0)
382 				scraddr += (pixelstep-1);
383 		}
384 		else
385 			scraddr += (x1*3/4)*pixelstep + y1*linestep;
386 		src_limit = scr_ptr + x2-x1+1;
387 
388 		while(scr_ptr < scr_ptr_limit)
389 		{
390 			src = scr_ptr;
391 			dst = scraddr;
392 			while(src < src_limit)
393 			{
394 #ifdef SMOOTH
395 			/* Let's see how fast that CPU is */
396 				UBYTE r, g, b;
397 				r = (3*palRed[*(src+0)] + palRed[*(src+1)])>>2;
398 				g = (3*palGreen[*(src+0)] + palGreen[*(src+1)])>>2;
399 				b = (3*palBlue[*(src+0)] + palBlue[*(src+1)])>>2;
400 
401 				if(b565)
402 					*(unsigned short*)dst = COLORCONV565(r,g,b);
403 				else if(b555)
404 					*(unsigned short*)dst = COLORCONV555(r,g,b);
405 				else if(bmono)
406 					*dst = (*dst & ~bitmask) | (COLORCONVMONO(r,g,b)<<bitshift);
407 
408 				dst += pixelstep;
409 
410 				r = (palRed[*(src+1)] + palRed[*(src+2)])>>1;
411 				g = (palGreen[*(src+1)] + palGreen[*(src+2)])>>1;
412 				b = (palBlue[*(src+1)] + palBlue[*(src+2)])>>1;
413 
414 				if(b565)
415 					*(unsigned short*)dst = COLORCONV565(r,g,b);
416 				else if(b555)
417 					*(unsigned short*)dst = COLORCONV555(r,g,b);
418 				else if(bmono)
419 					*dst = (*dst & ~bitmask) | (COLORCONVMONO(r,g,b)<<bitshift);
420 
421 				dst += pixelstep;
422 
423 				r = (palRed[*(src+2)] + 3*palRed[*(src+3)])>>2;
424 				g = (palGreen[*(src+2)] + 3*palGreen[*(src+3)])>>2;
425 				b = (palBlue[*(src+2)] + 3*palBlue[*(src+3)])>>2;
426 
427 				if(b565)
428 					*(unsigned short*)dst = COLORCONV565(r,g,b);
429 				else if(b555)
430 					*(unsigned short*)dst = COLORCONV555(r,g,b);
431 				else if(bmono)
432 					*dst = (*dst & ~bitmask) | (COLORCONVMONO(r,g,b)<<bitshift);
433 
434 				dst += pixelstep;
435 
436 				src += 4;
437 #else
438 				if((unsigned long)src & 3)
439 				{
440 					if(bmono)
441 						*dst = ((*dst)&~bitmask)|(pal[*src]<<bitshift);
442 					else
443 						*(unsigned short*)dst = pal[*src];
444 					dst += pixelstep;
445 				}
446 				src ++;
447 #endif
448 			}
449 			if(bmono)
450 			{
451 				bitshift += gxdp.cBPP;
452 				if(bitshift >= 8)
453 				{
454 					bitshift = 0;
455 					bitmask = (1<<gxdp.cBPP)-1;
456 					scraddr += linestep;
457 				}
458 				else
459 					bitmask <<= gxdp.cBPP;
460 			}
461 			else
462 				scraddr += linestep;
463 			scr_ptr += GFX_WIDTH;
464 			src_limit += GFX_WIDTH;
465 		}
466 
467 		GXEndDraw();
468 	}
469 }
470 
471 
WindowProc(HWND hwnd,UINT nMsg,WPARAM wParam,LPARAM lParam)472 LRESULT CALLBACK WindowProc(HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
473 {
474 	HDC          hDC;
475 	PAINTSTRUCT  ps;
476 	int          key = 0;
477 	static		 SHACTIVATEINFO sai;
478 	int x, y;
479 	RECT		 rc;
480 
481 	switch (nMsg)
482 	{
483 	case WM_CREATE:
484 		memset(&sai, 0, sizeof(sai));
485 		SHSipPreference(hwnd, SIP_INPUTDIALOG);
486 		return 0;
487 	case WM_DESTROY:
488 		wince_deinit_vidmode ();
489 		exit (-1);
490 		return 0;
491 	case WM_ERASEBKGND:
492 		{
493 			GetClientRect(hwnd, &rc);
494 			rc.top = 200;
495 			hDC = GetDC(hwnd);
496 			if(rc.top < rc.bottom)
497 				FillRect(hDC, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
498 			ReleaseDC(hwnd, hDC);
499 		}
500 		return 1;
501 	case WM_PAINT:
502 		hDC = BeginPaint (hwndMain, &ps);
503 		EndPaint (hwndMain, &ps);
504 		SHSipPreference(hwndMain, SIP_UP); /* Hack! */
505 #ifdef USE_F_KEYS
506 		adjust_extra_windows();
507 #endif
508 		/* It does not happen often but I don't want to see tooltip traces */
509 		wince_put_block(0, 0, 319, 199);
510 		return 0;
511 	case WM_ACTIVATE:
512 		if(!active)
513 		{
514 			active = true;
515 			GXResume();
516 		}
517 		SHHandleWMActivate(hwnd, wParam, lParam, &sai, SHA_INPUTDIALOG);
518 #ifdef USE_F_KEYS
519 		adjust_extra_windows();
520 #endif
521 		return 0;
522 	case WM_HIBERNATE:
523 		if(active)
524 		{
525 			active = false;
526 			GXSuspend();
527 		}
528 		return 0;
529 	case WM_SETTINGCHANGE:
530 		SHHandleWMSettingChange(hwnd, wParam, lParam, &sai);
531 #ifdef USE_F_KEYS
532 		adjust_extra_windows();
533 #endif
534 		return 0;
535 	case WM_COMMAND:
536 		switch(wParam)
537 		{
538 		case IDC_ABOUT:
539 			message_box("Pocket Sarien R4 (" __DATE__ ")\n\n"
540 						"Ported by Vasyl Tsvirkunov\n"
541 						"http://pocketatari.\n"
542 						"               retrogames.com\n\n"
543 						"Visit Sarien homepage at\n"
544 						"http://sarien.sourceforge.net");
545 			break;
546 		case IDC_EXIT:
547 			DestroyWindow(hwndMain);
548 			break;
549 		}
550 		return 0;
551 
552 	/* Multimedia functions
553 	 * (Damn! The CALLBACK_FUNCTION parameter doesn't work!)
554 	 */
555 	/* VT: I think it works but I don't care to test at this moment */
556 	case MM_WOM_DONE:
557 		flush_sound ((PWAVEHDR) lParam);
558 		return 0;
559 
560 	case WM_LBUTTONDOWN:
561 		if(HIWORD(lParam)<200)
562 		{
563 			key = BUTTON_LEFT;
564 			mouse.button = TRUE;
565 			mouse.x = LOWORD(lParam)*4/3;
566 			mouse.y = HIWORD(lParam);
567 		}
568 		break;
569 
570 	case WM_RBUTTONDOWN:
571 		if(HIWORD(lParam)<200)
572 		{
573 			key = BUTTON_RIGHT;
574 			mouse.button = TRUE;
575 			mouse.x = LOWORD(lParam)*4/3;
576 			mouse.y = HIWORD(lParam);
577 		}
578 		break;
579 
580 	case WM_LBUTTONUP:
581 	case WM_RBUTTONUP:
582 		mouse.button = FALSE;
583 		return 0;
584 
585 	case WM_MOUSEMOVE:
586 		if(HIWORD(lParam)<200)
587 		{
588 			mouse.x = LOWORD(lParam)*4/3;
589 			mouse.y = HIWORD(lParam);
590 		}
591 		return 0;
592 
593 	case WM_KEYDOWN:
594 		switch(wParam)
595 		{
596 		case VK_UP:
597 			if(!(lParam & REPEATED_KEYMASK))
598 				key = KEY_UP;
599 			break;
600 		case VK_LEFT:
601 			if(!(lParam & REPEATED_KEYMASK))
602 				key = KEY_LEFT;
603 			break;
604 		case VK_DOWN:
605 			if(!(lParam & REPEATED_KEYMASK))
606 				key = KEY_DOWN;
607 			break;
608 		case VK_RIGHT:
609 			if(!(lParam & REPEATED_KEYMASK))
610 				key = KEY_RIGHT;
611 			break;
612 		case VK_HOME:
613 			if(!(lParam & REPEATED_KEYMASK))
614 			key = KEY_UP_LEFT;
615 				break;
616 		case VK_PRIOR:
617 			if(!(lParam & REPEATED_KEYMASK))
618 				key = KEY_UP_RIGHT;
619 			break;
620 		case VK_NEXT:
621 			if(!(lParam & REPEATED_KEYMASK))
622 				key = KEY_DOWN_RIGHT;
623 			break;
624 		case VK_END:
625 			if(!(lParam & REPEATED_KEYMASK))
626 				key = KEY_DOWN_LEFT;
627 			break;
628 		case VK_F1:
629 			key = 0x3b00;
630 			break;
631 		case VK_F2:
632 			key = 0x3c00;
633 			break;
634 		case VK_F3:
635 			key = 0x3d00;
636 			break;
637 		case VK_F4:
638 			key = 0x3e00;
639 			break;
640 		case VK_F5:
641 			key = 0x3f00;
642 			break;
643 		case VK_F6:
644 			key = 0x4000;
645 			break;
646 		case VK_F7:
647 			key = 0x4100;
648 			break;
649 		case VK_F8:
650 			key = 0x4200;
651 			break;
652 		case VK_F9:
653 			key = 0x4300;
654 			break;
655 		case VK_F10:
656 			key = 0x4400;
657 			break;
658 		case VK_SNAPSHOT:
659 			key = KEY_PRIORITY;
660 			break;
661 		default:
662 			if(key == gxkl.vkA)
663 			{
664 				key = KEY_ENTER;
665 				break;
666 			}
667 			else if(key == gxkl.vkB)
668 			{
669 				key = KEY_ESCAPE;
670 				break;
671 			}
672 #ifdef USE_CONSOLE
673 			else if(key == gxkl.vkC)
674 			{
675 				key = CONSOLE_ACTIVATE_KEY;
676 				break;
677 			}
678 #endif
679 		};
680 		break;
681 
682 	case WM_CHAR:
683 		if(!(lParam & RELEASED_KEYMASK)) /* pressed? */
684 		{
685 			key = (int)wParam;
686 			if(key == '\\')
687 				key = KEY_ESCAPE; /* menu */
688 		}
689 		else
690 			return 0;
691 		break;
692 	};
693 
694 	/* Keyboard message handled */
695 	if (key) {
696 		key_enqueue (key);
697 		return 0;
698 	}
699 
700 	return DefWindowProc (hwnd, nMsg, wParam, lParam);
701 }
702 
703 #ifdef USE_F_KEYS
704 /* Window for F-key input. Hack but some people asked for it. */
FWindowProc(HWND hwnd,UINT nMsg,WPARAM wParam,LPARAM lParam)705 LRESULT CALLBACK FWindowProc(HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
706 {
707 	switch (nMsg)
708 	{
709 	case WM_CREATE:
710 		return 0;
711 	case WM_PAINT:
712 		{
713 			HDC          hDC;
714 			PAINTSTRUCT  ps;
715 			RECT		 rc;
716 			hDC = BeginPaint(hwnd, &ps);
717 			GetClientRect(hwnd, &rc);
718 			HGDIOBJ oldPen = SelectObject(hDC, (HGDIOBJ)GetStockObject(BLACK_PEN));
719 			HGDIOBJ oldBr = SelectObject(hDC, (HGDIOBJ)GetStockObject(WHITE_BRUSH));
720 			HGDIOBJ oldFont = SelectObject(hDC, (HGDIOBJ)GetStockObject(SYSTEM_FONT));
721 			int rcWidth = rc.right-rc.left;
722 			RECT rcItem;
723 			rcItem.top = rc.top;
724 			rcItem.bottom = rc.bottom;
725 			POINT pts[2];
726 			pts[0].y = rc.top;
727 			pts[1].y = rc.bottom;
728 			TCHAR text[4];
729 			for(int i=0; i<10; i++)
730 			{
731 				wsprintf(text, TEXT("F%d"), i+1);
732 				rcItem.left = rc.left+rcWidth*i/10;
733 				rcItem.right = rc.left+rcWidth*(i+1)/10;
734 				pts[0].x = pts[1].x = rcItem.right;
735 				Polyline(hDC, pts, 2);
736 				DrawText(hDC, text, -1, &rcItem, DT_CENTER|DT_VCENTER);
737 			}
738 			SelectObject(hDC, oldPen);
739 			SelectObject(hDC, oldBr);
740 			SelectObject(hDC, oldFont);
741 			EndPaint(hwnd, &ps);
742 		}
743 		return 0;
744 	case WM_LBUTTONDOWN:
745 		{
746 			int x = LOWORD(lParam);
747 			RECT rc; GetWindowRect(hwnd, &rc);
748 			int fnum = x*10/(rc.right-rc.left);
749 			PostMessage(hwndMain, WM_KEYDOWN, VK_F1+fnum, 0);
750 		}
751 		return 0;
752 	}
753 
754 	return DefWindowProc(hwnd, nMsg, wParam, lParam);
755 }
756 
setup_extra_windows(HWND hwndTop)757 void setup_extra_windows(HWND hwndTop)
758 {
759 	LPTSTR fkeyname = TEXT("fkeys");
760 
761 	WNDCLASS wc;
762 	wc.style = CS_HREDRAW | CS_VREDRAW;
763 	wc.lpfnWndProc = FWindowProc;
764 	wc.cbClsExtra = 0;
765 	wc.cbWndExtra = 0;
766 	wc.hInstance = GetModuleHandle(NULL);
767 	wc.hIcon = NULL;
768 	wc.hCursor = NULL;
769 	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
770 	wc.lpszMenuName = NULL;
771 	wc.lpszClassName = fkeyname;
772 	RegisterClass(&wc);
773 
774 	hwndFKeys = CreateWindow(fkeyname,
775 		fkeyname,
776 		WS_VISIBLE|WS_CHILD,
777 		0,
778 		200,
779 		GetSystemMetrics(SM_CXSCREEN),
780 		20,
781 		hwndTop,
782 		(HMENU)100,
783 		GetModuleHandle(NULL),
784 		NULL);
785 }
786 
adjust_extra_windows()787 void adjust_extra_windows()
788 {
789 	SIPINFO si;
790 	si.cbSize = sizeof(SIPINFO);
791 	SHSipInfo(SPI_GETSIPINFO, 0, &si, 0);
792 	if(si.fdwFlags & SIPF_ON)
793 	{
794 		int bottom = si.rcVisibleDesktop.bottom;
795 		SetWindowPos(hwndFKeys, 0, 0, 200, GetSystemMetrics(SM_CXSCREEN), bottom-200,
796 			SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER);
797 	}
798 }
799 #endif
800