1 // Windows interface layer
2 // for the Build Engine
3 // by Jonathon Fowler (jf@jonof.id.au)
4 //
5 // This is all very ugly.
6
7 #ifndef _WIN32
8 #error winlayer.c is for Windows only.
9 #endif
10
11 #define WINVER 0x0600
12 #define _WIN32_WINNT 0x0600
13
14 #define WIN32_LEAN_AND_MEAN
15 #include <windows.h>
16 #include <xinput.h>
17 #include <math.h>
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <signal.h>
22 #include <stdarg.h>
23 #include <commdlg.h>
24
25 #include "build.h"
26
27 #if USE_OPENGL
28 #include "glbuild.h"
29 #include "wglext.h"
30 #endif
31
32 #include "winlayer.h"
33 #include "pragmas.h"
34 #include "a.h"
35 #include "osd.h"
36
37
38 // undefine to restrict windowed resolutions to conventional sizes
39 #define ANY_WINDOWED_SIZE
40
41 int _buildargc = 0;
42 const char **_buildargv = NULL;
43 static char *argvbuf = NULL;
44
45 // Windows crud
46 static HINSTANCE hInstance = 0;
47 static HWND hWindow = 0;
48 static HDC hDCWindow = NULL;
49 #define WINDOW_CLASS "buildapp"
50 static BOOL window_class_registered = FALSE;
51 static HANDLE instanceflag = NULL;
52
53 int backgroundidle = 1;
54 static char apptitle[256] = "Build Engine";
55 static char wintitle[256] = "";
56
57 static WORD sysgamma[3][256];
58 extern int gammabrightness;
59 extern float curgamma;
60
61 #if USE_OPENGL
62 // OpenGL stuff
63 static HGLRC hGLRC = 0;
64 static HANDLE hGLDLL;
65 static glbuild8bit gl8bit;
66 static unsigned char nogl=0;
67 static unsigned char *frame = NULL;
68
69 static HWND hGLWindow = NULL;
70 static HWND dummyhGLwindow = NULL;
71 static HDC hDCGLWindow = NULL;
72
73 static struct winlayer_glfuncs {
74 HGLRC (WINAPI * wglCreateContext)(HDC);
75 BOOL (WINAPI * wglDeleteContext)(HGLRC);
76 PROC (WINAPI * wglGetProcAddress)(LPCSTR);
77 BOOL (WINAPI * wglMakeCurrent)(HDC,HGLRC);
78 BOOL (WINAPI * wglSwapBuffers)(HDC);
79
80 const char * (WINAPI * wglGetExtensionsStringARB)(HDC hdc);
81 BOOL (WINAPI * wglChoosePixelFormatARB)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
82 HGLRC (WINAPI * wglCreateContextAttribsARB)(HDC hDC, HGLRC hShareContext, const int *attribList);
83 BOOL (WINAPI * wglSwapIntervalEXT)(int interval);
84 int (WINAPI * wglGetSwapIntervalEXT)(void);
85
86 int have_ARB_create_context_profile;
87 } wglfunc;
88 #endif
89
90 static LPTSTR GetWindowsErrorMsg(DWORD code);
91 static const char * getwindowserrorstr(DWORD code);
92 static void ShowErrorBox(const char *m);
93 static BOOL CheckWinVersion(void);
94 static LRESULT CALLBACK WndProcCallback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
95 static void fetchkeynames(void);
96 static void updatemouse(void);
97 static void updatejoystick(void);
98 static void UninitDIB(void);
99 static int SetupDIB(int width, int height);
100 static void UninitOpenGL(void);
101 static int SetupOpenGL(int width, int height, int bitspp, int cover);
102 static BOOL RegisterWindowClass(void);
103 static BOOL CreateAppWindow(int width, int height, int bitspp, int fs, int refresh);
104 static void DestroyAppWindow(void);
105 static void UpdateAppWindowTitle(void);
106
107 static void shutdownvideo(void);
108
109 // video
110 static int desktopxdim=0,desktopydim=0,desktopbpp=0, desktopmodeset=0;
111 int xres=-1, yres=-1, fullscreen=0, bpp=0, bytesperline=0, imageSize=0;
112 intptr_t frameplace=0;
113 static int windowposx, windowposy;
114 static unsigned modeschecked=0;
115 unsigned maxrefreshfreq=60;
116 char modechange=1;
117 char offscreenrendering=0;
118 int glswapinterval = 1;
119 int glcolourdepth=32;
120 char videomodereset = 0;
121
122 // input and events
123 int inputdevices=0;
124 char quitevent=0, appactive=1;
125 int mousex=0, mousey=0, mouseb=0;
126 static unsigned int mousewheel[2] = { 0,0 };
127 #define MouseWheelFakePressTime (100) // getticks() is a 1000Hz timer, and the button press is faked for 100ms
128 int joyaxis[8], joyb=0;
129 char joynumaxes=0, joynumbuttons=0;
130
131 static char taskswitching=1;
132
133 char keystatus[256];
134 int keyfifo[KEYFIFOSIZ];
135 unsigned char keyasciififo[KEYFIFOSIZ];
136 int keyfifoplc, keyfifoend;
137 int keyasciififoplc, keyasciififoend;
138 static char keynames[256][24];
139 static const int wscantable[256], wxscantable[256];
140
141 void (*keypresscallback)(int,int) = 0;
142 void (*mousepresscallback)(int,int) = 0;
143 void (*joypresscallback)(int,int) = 0;
144
145
146
147
148 //-------------------------------------------------------------------------------------------------
149 // MAIN CRAP
150 //=================================================================================================
151
152
153 //
154 // win_gethwnd() -- gets the window handle
155 //
win_gethwnd(void)156 intptr_t win_gethwnd(void)
157 {
158 return (intptr_t)hWindow;
159 }
160
161
162 //
163 // win_gethinstance() -- gets the application instance
164 //
win_gethinstance(void)165 intptr_t win_gethinstance(void)
166 {
167 return (intptr_t)hInstance;
168 }
169
170
171 //
172 // win_allowtaskswitching() -- captures/releases alt+tab hotkeys
173 //
win_allowtaskswitching(int onf)174 void win_allowtaskswitching(int onf)
175 {
176 if (onf == taskswitching) return;
177
178 if (onf) {
179 UnregisterHotKey(0,0);
180 UnregisterHotKey(0,1);
181 } else {
182 RegisterHotKey(0,0,MOD_ALT,VK_TAB);
183 RegisterHotKey(0,1,MOD_ALT|MOD_SHIFT,VK_TAB);
184 }
185
186 taskswitching = onf;
187 }
188
189
190 //
191 // win_checkinstance() -- looks for another instance of a Build app
192 //
win_checkinstance(void)193 int win_checkinstance(void)
194 {
195 if (!instanceflag) return 0;
196 return (WaitForSingleObject(instanceflag,0) == WAIT_TIMEOUT);
197 }
198
199
200 //
201 // wm_msgbox/wm_ynbox() -- window-manager-provided message boxes
202 //
wm_msgbox(const char * name,const char * fmt,...)203 int wm_msgbox(const char *name, const char *fmt, ...)
204 {
205 char buf[1000];
206 va_list va;
207
208 va_start(va,fmt);
209 vsprintf(buf,fmt,va);
210 va_end(va);
211
212 MessageBox(hWindow,buf,name,MB_OK|MB_TASKMODAL);
213 return 0;
214 }
wm_ynbox(const char * name,const char * fmt,...)215 int wm_ynbox(const char *name, const char *fmt, ...)
216 {
217 char buf[1000];
218 va_list va;
219 int r;
220
221 va_start(va,fmt);
222 vsprintf(buf,fmt,va);
223 va_end(va);
224
225 r = MessageBox((HWND)win_gethwnd(),buf,name,MB_YESNO|MB_TASKMODAL);
226 if (r==IDYES) return 1;
227 return 0;
228 }
229
230 //
231 // wm_filechooser() -- display a file selector dialogue box
232 //
wm_filechooser(const char * initialdir,const char * initialfile,const char * type,int foropen,char ** choice)233 int wm_filechooser(const char *initialdir, const char *initialfile, const char *type, int foropen, char **choice)
234 {
235 OPENFILENAME ofn;
236 char filter[100], *filterp = filter;
237 char filename[BMAX_PATH+1] = "";
238
239 *choice = NULL;
240
241 if (!foropen && initialfile) {
242 strcpy(filename, initialfile);
243 }
244
245 // ext Files\0*.ext\0\0
246 memset(filter, 0, sizeof(filter));
247 sprintf(filterp, "%s Files", type);
248 filterp += strlen(filterp) + 1;
249 sprintf(filterp, "*.%s", type);
250
251 ZeroMemory(&ofn, sizeof(ofn));
252 ofn.lStructSize = sizeof(OPENFILENAME);
253 ofn.hwndOwner = hWindow;
254 ofn.lpstrFilter = filter;
255 ofn.nFilterIndex = 1;
256 ofn.lpstrFile = filename;
257 ofn.nMaxFile = sizeof(filename);
258 ofn.lpstrInitialDir = initialdir;
259 ofn.Flags = OFN_DONTADDTORECENT | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
260 ofn.lpstrDefExt = type;
261
262 if (foropen ? GetOpenFileName(&ofn) : GetSaveFileName(&ofn)) {
263 *choice = strdup(filename);
264 return 1;
265 } else {
266 return 0;
267 }
268 }
269
270 //
271 // wm_setapptitle() -- changes the application title
272 //
wm_setapptitle(const char * name)273 void wm_setapptitle(const char *name)
274 {
275 if (name) {
276 Bstrncpy(apptitle, name, sizeof(apptitle)-1);
277 apptitle[ sizeof(apptitle)-1 ] = 0;
278 }
279
280 UpdateAppWindowTitle();
281 startwin_settitle(apptitle);
282 }
283
284 //
285 // wm_setwindowtitle() -- changes the rendering window title
286 //
wm_setwindowtitle(const char * name)287 void wm_setwindowtitle(const char *name)
288 {
289 if (name) {
290 Bstrncpy(wintitle, name, sizeof(wintitle)-1);
291 wintitle[ sizeof(wintitle)-1 ] = 0;
292 }
293
294 UpdateAppWindowTitle();
295 }
296
297 //
298 // SignalHandler() -- called when we've sprung a leak
299 //
SignalHandler(int signum)300 static void SignalHandler(int signum)
301 {
302 switch (signum) {
303 case SIGSEGV:
304 buildputs("Fatal Signal caught: SIGSEGV. Bailing out.\n");
305 uninitsystem();
306 if (stdout) fclose(stdout);
307 break;
308 default:
309 break;
310 }
311 }
312
313
314 //
315 // WinMain() -- main Windows entry point
316 //
WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nCmdShow)317 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
318 {
319 int r;
320 char *argp;
321 FILE *fp;
322 HDC hdc;
323
324 hInstance = hInst;
325
326 if (CheckWinVersion() || hPrevInst) {
327 MessageBox(0, "This application must be run under Windows Vista or newer.",
328 apptitle, MB_OK|MB_ICONSTOP);
329 return -1;
330 }
331
332 hdc = GetDC(NULL);
333 r = GetDeviceCaps(hdc, BITSPIXEL);
334 ReleaseDC(NULL, hdc);
335 if (r <= 8) {
336 MessageBox(0, "This application requires a desktop colour depth of 65536-colours or more.",
337 apptitle, MB_OK|MB_ICONSTOP);
338 return -1;
339 }
340
341 // carve up the commandline into more recognizable pieces
342 argvbuf = strdup(GetCommandLine());
343 _buildargc = 0;
344 if (argvbuf) {
345 char quoted = 0, instring = 0, swallownext = 0;
346 char *p,*wp; int i;
347 for (p=wp=argvbuf; *p; p++) {
348 if (*p == ' ') {
349 if (instring && !quoted) {
350 // end of a string
351 *(wp++) = 0;
352 instring = 0;
353 } else if (instring) {
354 *(wp++) = *p;
355 }
356 } else if (*p == '"' && !swallownext) {
357 if (instring && quoted) {
358 // end of a string
359 if (p[1] == ' ') {
360 *(wp++) = 0;
361 instring = 0;
362 quoted = 0;
363 } else {
364 quoted = 0;
365 }
366 } else if (instring && !quoted) {
367 quoted = 1;
368 } else if (!instring) {
369 instring = 1;
370 quoted = 1;
371 _buildargc++;
372 }
373 } else if (*p == '\\' && p[1] == '"' && !swallownext) {
374 swallownext = 1;
375 } else {
376 if (!instring) _buildargc++;
377 instring = 1;
378 *(wp++) = *p;
379 swallownext = 0;
380 }
381 }
382 *wp = 0;
383
384 _buildargv = malloc(sizeof(char*)*_buildargc);
385 wp = argvbuf;
386 for (i=0; i<_buildargc; i++,wp++) {
387 _buildargv[i] = wp;
388 while (*wp) wp++;
389 }
390 }
391
392 // pipe standard outputs to files
393 if ((argp = Bgetenv("BUILD_LOGSTDOUT")) != NULL)
394 if (!Bstrcasecmp(argp, "TRUE")) {
395 fp = freopen("stdout.txt", "w", stdout);
396 if (!fp) {
397 fp = fopen("stdout.txt", "w");
398 }
399 if (fp) setvbuf(fp, 0, _IONBF, 0);
400 *stdout = *fp;
401 *stderr = *fp;
402 }
403
404 // install signal handlers
405 signal(SIGSEGV, SignalHandler);
406
407 if (RegisterWindowClass()) return -1;
408
409 atexit(uninitsystem);
410
411 instanceflag = CreateSemaphore(NULL, 1,1, WINDOW_CLASS);
412
413 startwin_open();
414 baselayer_init();
415 r = app_main(_buildargc, _buildargv);
416
417 fclose(stdout);
418
419 startwin_close();
420 if (instanceflag) CloseHandle(instanceflag);
421
422 if (argvbuf) free(argvbuf);
423
424 return r;
425 }
426
427
set_maxrefreshfreq(const osdfuncparm_t * parm)428 static int set_maxrefreshfreq(const osdfuncparm_t *parm)
429 {
430 int freq;
431 if (parm->numparms == 0) {
432 if (maxrefreshfreq == 0)
433 buildputs("maxrefreshfreq = No maximum\n");
434 else
435 buildprintf("maxrefreshfreq = %d Hz\n",maxrefreshfreq);
436 return OSDCMD_OK;
437 }
438 if (parm->numparms != 1) return OSDCMD_SHOWHELP;
439
440 freq = Batol(parm->parms[0]);
441 if (freq < 0) return OSDCMD_SHOWHELP;
442
443 maxrefreshfreq = (unsigned)freq;
444 modeschecked = 0;
445
446 return OSDCMD_OK;
447 }
448
449 #if USE_OPENGL
set_glswapinterval(const osdfuncparm_t * parm)450 static int set_glswapinterval(const osdfuncparm_t *parm)
451 {
452 int interval;
453
454 if (!wglfunc.wglSwapIntervalEXT || nogl) {
455 buildputs("glswapinterval is not adjustable\n");
456 return OSDCMD_OK;
457 }
458 if (parm->numparms == 0) {
459 buildprintf("glswapinterval = %d\n", glswapinterval);
460 return OSDCMD_OK;
461 }
462 if (parm->numparms != 1) return OSDCMD_SHOWHELP;
463
464 interval = Batol(parm->parms[0]);
465 if (interval < 0 || interval > 2) return OSDCMD_SHOWHELP;
466
467 glswapinterval = interval;
468 wglfunc.wglSwapIntervalEXT(interval);
469
470 return OSDCMD_OK;
471 }
472 #endif
473
474 //
475 // initsystem() -- init systems
476 //
initsystem(void)477 int initsystem(void)
478 {
479 DEVMODE desktopmode;
480
481 buildputs("Initialising Windows system interface\n");
482
483 // get the desktop dimensions before anything changes them
484 ZeroMemory(&desktopmode, sizeof(DEVMODE));
485 desktopmode.dmSize = sizeof(DEVMODE);
486 EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&desktopmode);
487
488 desktopxdim = desktopmode.dmPelsWidth;
489 desktopydim = desktopmode.dmPelsHeight;
490 desktopbpp = desktopmode.dmBitsPerPel;
491
492 memset(curpalette, 0, sizeof(palette_t) * 256);
493
494 atexit(uninitsystem);
495
496 frameplace=0;
497
498 #if USE_OPENGL
499 memset(&wglfunc, 0, sizeof(wglfunc));
500 nogl = loadgldriver(getenv("BUILD_GLDRV"));
501 if (!nogl) {
502 // Load the core WGL functions.
503 wglfunc.wglGetProcAddress = getglprocaddress("wglGetProcAddress", 0);
504 wglfunc.wglCreateContext = getglprocaddress("wglCreateContext", 0);
505 wglfunc.wglDeleteContext = getglprocaddress("wglDeleteContext", 0);
506 wglfunc.wglMakeCurrent = getglprocaddress("wglMakeCurrent", 0);
507 wglfunc.wglSwapBuffers = getglprocaddress("wglSwapBuffers", 0);
508 nogl = !wglfunc.wglGetProcAddress ||
509 !wglfunc.wglCreateContext ||
510 !wglfunc.wglDeleteContext ||
511 !wglfunc.wglMakeCurrent ||
512 !wglfunc.wglSwapBuffers;
513 }
514 if (nogl) {
515 buildputs("Failed loading OpenGL driver. GL modes will be unavailable.\n");
516 memset(&wglfunc, 0, sizeof(wglfunc));
517 } else {
518 OSD_RegisterFunction("glswapinterval", "glswapinterval: frame swap interval for OpenGL modes (0 = no vsync, max 2)", set_glswapinterval);
519 }
520 #endif
521
522 OSD_RegisterFunction("maxrefreshfreq", "maxrefreshfreq: maximum display frequency to set for OpenGL Polymost modes (0=no maximum)", set_maxrefreshfreq);
523 return 0;
524 }
525
526
527 //
528 // uninitsystem() -- uninit systems
529 //
uninitsystem(void)530 void uninitsystem(void)
531 {
532 DestroyAppWindow();
533
534 startwin_close();
535
536 uninitinput();
537 uninittimer();
538
539 win_allowtaskswitching(1);
540
541 shutdownvideo();
542 #if USE_OPENGL
543 glbuild_unloadfunctions();
544 memset(&wglfunc, 0, sizeof(wglfunc));
545 unloadgldriver();
546 #endif
547 }
548
549
550 //
551 // initputs() -- prints a string to the intitialization window
552 //
initputs(const char * buf)553 void initputs(const char *buf)
554 {
555 startwin_puts(buf);
556 }
557
558
559 //
560 // debugprintf() -- sends a debug string to the debugger
561 //
debugprintf(const char * f,...)562 void debugprintf(const char *f, ...)
563 {
564 #ifdef DEBUGGINGAIDS
565 va_list va;
566 char buf[1024];
567
568 va_start(va,f);
569 Bvsnprintf(buf, 1024, f, va);
570 va_end(va);
571
572 if (IsDebuggerPresent()) {
573 OutputDebugString(buf);
574 } else {
575 fputs(buf, stdout);
576 }
577 #endif
578 }
579
580
581 //
582 // handleevents() -- process the Windows message queue
583 // returns !0 if there was an important event worth checking (like quitting)
584 //
585 static int eatosdinput = 0;
handleevents(void)586 int handleevents(void)
587 {
588 int rv=0;
589 MSG msg;
590
591 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
592 if (msg.message == WM_QUIT)
593 quitevent = 1;
594
595 if (startwin_idle((void*)&msg) > 0) continue;
596
597 TranslateMessage(&msg);
598 DispatchMessage(&msg);
599 }
600
601 eatosdinput = 0;
602 updatemouse();
603 updatejoystick();
604
605 if (!appactive || quitevent) rv = -1;
606
607 sampletimer();
608
609 return rv;
610 }
611
612
613
614
615 //-------------------------------------------------------------------------------------------------
616 // INPUT (MOUSE/KEYBOARD)
617 //=================================================================================================
618
619 static char moustat = 0, mousegrab = 0;
620 static int joyblast=0;
621
622 static int xinputusernum = -1;
623
624 // I don't see any pressing need to store the key-up events yet
625 #define SetKey(key,state) { \
626 keystatus[key] = state; \
627 if (state) { \
628 keyfifo[keyfifoend] = key; \
629 keyfifo[(keyfifoend+1)&(KEYFIFOSIZ-1)] = state; \
630 keyfifoend = ((keyfifoend+2)&(KEYFIFOSIZ-1)); \
631 } \
632 }
633
634
635 //
636 // initinput() -- init input system
637 //
initinput(void)638 int initinput(void)
639 {
640 moustat=0;
641 memset(keystatus, 0, sizeof(keystatus));
642 keyfifoplc = keyfifoend = 0;
643 keyasciififoplc = keyasciififoend = 0;
644
645 inputdevices = 1;
646 joynumaxes=0;
647 joynumbuttons=0;
648
649 fetchkeynames();
650
651 {
652 DWORD usernum, result;
653 XINPUT_CAPABILITIES caps;
654
655 buildputs("Initialising game controllers\n");
656
657 for (usernum = 0; usernum < XUSER_MAX_COUNT; usernum++) {
658 result = XInputGetCapabilities(usernum, XINPUT_FLAG_GAMEPAD, &caps);
659 if (result == ERROR_SUCCESS && xinputusernum < 0) {
660 xinputusernum = (int)usernum;
661 inputdevices |= 4;
662
663 joynumbuttons = 15;
664 joynumaxes = 6;
665 }
666 }
667 if (xinputusernum >= 0) {
668 buildprintf(" - Using controller in port %d\n", xinputusernum);
669 } else {
670 buildputs(" - No usable controller found\n");
671 }
672 }
673
674 return 0;
675 }
676
677
678 //
679 // uninitinput() -- uninit input system
680 //
uninitinput(void)681 void uninitinput(void)
682 {
683 uninitmouse();
684
685 xinputusernum = -1;
686 inputdevices &= ~4;
687 }
688
689
690 //
691 // bgetchar, bkbhit, bflushchars -- character-based input functions
692 //
bgetchar(void)693 unsigned char bgetchar(void)
694 {
695 unsigned char c;
696 if (keyasciififoplc == keyasciififoend) return 0;
697 c = keyasciififo[keyasciififoplc];
698 keyasciififoplc = ((keyasciififoplc+1)&(KEYFIFOSIZ-1));
699 return c;
700 }
701
bkbhit(void)702 int bkbhit(void)
703 {
704 return (keyasciififoplc != keyasciififoend);
705 }
706
bflushchars(void)707 void bflushchars(void)
708 {
709 keyasciififoplc = keyasciififoend = 0;
710 }
711
712
713 //
714 // set{key|mouse|joy}presscallback() -- sets a callback which gets notified when keys are pressed
715 //
setkeypresscallback(void (* callback)(int,int))716 void setkeypresscallback(void (*callback)(int, int)) { keypresscallback = callback; }
setmousepresscallback(void (* callback)(int,int))717 void setmousepresscallback(void (*callback)(int, int)) { mousepresscallback = callback; }
setjoypresscallback(void (* callback)(int,int))718 void setjoypresscallback(void (*callback)(int, int)) { joypresscallback = callback; }
719
720
721 //
722 // initmouse() -- init mouse input
723 //
initmouse(void)724 int initmouse(void)
725 {
726 RAWINPUTDEVICE rid;
727
728 if (moustat) return 0;
729
730 buildputs("Initialising mouse\n");
731
732 // Register for mouse raw input.
733 rid.usUsagePage = 0x01;
734 rid.usUsage = 0x02;
735 rid.dwFlags = 0; // We want legacy events when the mouse is not grabbed, so no RIDEV_NOLEGACY.
736 rid.hwndTarget = NULL;
737 if (RegisterRawInputDevices(&rid, 1, sizeof(rid)) == FALSE) {
738 buildprintf("initinput: could not register for raw mouse input (%s)\n",
739 getwindowserrorstr(GetLastError()));
740 return -1;
741 }
742
743 // grab input
744 moustat=1;
745 inputdevices |= 2;
746 grabmouse(1);
747
748 return 0;
749 }
750
751
752 //
753 // uninitmouse() -- uninit mouse input
754 //
uninitmouse(void)755 void uninitmouse(void)
756 {
757 RAWINPUTDEVICE rid;
758
759 if (!moustat) return;
760
761 grabmouse(0);
762 moustat=mousegrab=0;
763
764 // Unregister for mouse raw input.
765 rid.usUsagePage = 0x01;
766 rid.usUsage = 0x02;
767 rid.dwFlags = RIDEV_REMOVE;
768 rid.hwndTarget = NULL;
769 if (RegisterRawInputDevices(&rid, 1, sizeof(rid)) == FALSE) {
770 buildprintf("initinput: could not unregister for raw mouse input (%s)\n",
771 getwindowserrorstr(GetLastError()));
772 }
773 }
774
775
constrainmouse(int a)776 static void constrainmouse(int a)
777 {
778 RECT rect;
779 LONG x, y;
780
781 if (!hWindow) return;
782 if (a) {
783 GetWindowRect(hWindow, &rect);
784
785 x = rect.left + (rect.right - rect.left) / 2;
786 y = rect.top + (rect.bottom - rect.top) / 2;
787 rect.left = x - 1;
788 rect.right = x + 1;
789 rect.top = y - 1;
790 rect.bottom = y + 1;
791
792 ClipCursor(&rect);
793 ShowCursor(FALSE);
794 } else {
795 ClipCursor(NULL);
796 ShowCursor(TRUE);
797 }
798 }
799
updatemouse(void)800 static void updatemouse(void)
801 {
802 unsigned t = getticks();
803
804 if (!mousegrab) return;
805
806 // we only want the wheel to signal once, but hold the state for a moment
807 if (mousewheel[0] > 0 && t - mousewheel[0] > MouseWheelFakePressTime) {
808 if (mousepresscallback) mousepresscallback(5,0);
809 mousewheel[0] = 0; mouseb &= ~16;
810 }
811 if (mousewheel[1] > 0 && t - mousewheel[1] > MouseWheelFakePressTime) {
812 if (mousepresscallback) mousepresscallback(6,0);
813 mousewheel[1] = 0; mouseb &= ~32;
814 }
815 }
816
817 //
818 // grabmouse() -- show/hide mouse cursor
819 //
grabmouse(int a)820 void grabmouse(int a)
821 {
822 if (!moustat) return;
823
824 mousegrab = a;
825
826 constrainmouse(a);
827 mousex = 0;
828 mousey = 0;
829 mouseb = 0;
830 }
831
832
833 //
834 // readmousexy() -- return mouse motion information
835 //
readmousexy(int * x,int * y)836 void readmousexy(int *x, int *y)
837 {
838 if (!moustat || !mousegrab) { *x = *y = 0; return; }
839 *x = mousex;
840 *y = mousey;
841 mousex = 0;
842 mousey = 0;
843 }
844
845
846 //
847 // readmousebstatus() -- return mouse button information
848 //
readmousebstatus(int * b)849 void readmousebstatus(int *b)
850 {
851 if (!moustat || !mousegrab) *b = 0;
852 else *b = mouseb;
853 }
854
855
updatejoystick(void)856 static void updatejoystick(void)
857 {
858 XINPUT_STATE state;
859
860 if (xinputusernum < 0) return;
861
862 ZeroMemory(&state, sizeof(state));
863 if (XInputGetState(xinputusernum, &state) != ERROR_SUCCESS) {
864 buildputs("Joystick error, disabling.\n");
865 joyb = 0;
866 memset(joyaxis, 0, sizeof(joyaxis));
867 xinputusernum = -1;
868 return;
869 }
870
871 // We use SDL's game controller button order for BUILD:
872 // A, B, X, Y, Back, (Guide), Start, LThumb, RThumb,
873 // LShoulder, RShoulder, DPUp, DPDown, DPLeft, DPRight
874 // So we must shuffle XInput around.
875 joyb = ((state.Gamepad.wButtons & 0xF000) >> 12) | // A,B,X,Y
876 ((state.Gamepad.wButtons & 0x0020) >> 1) | // Back
877 ((state.Gamepad.wButtons & 0x0010) << 2) | // Start
878 ((state.Gamepad.wButtons & 0x03C0) << 1) | // LThumb,RThumb,LShoulder,RShoulder
879 ((state.Gamepad.wButtons & 0x000F) << 8); // DPadUp,Down,Left,Right
880
881 joyaxis[0] = state.Gamepad.sThumbLX;
882 joyaxis[1] = -state.Gamepad.sThumbLY;
883 joyaxis[2] = state.Gamepad.sThumbRX;
884 joyaxis[3] = -state.Gamepad.sThumbRY;
885 joyaxis[4] = (state.Gamepad.bLeftTrigger >> 1) | ((int)state.Gamepad.bLeftTrigger << 7); // Extend to 0-32767
886 joyaxis[5] = (state.Gamepad.bRightTrigger >> 1) | ((int)state.Gamepad.bRightTrigger << 7);
887 }
888
889
releaseallbuttons(void)890 void releaseallbuttons(void)
891 {
892 int i;
893
894 if (mousepresscallback) {
895 if (mouseb & 1) mousepresscallback(1, 0);
896 if (mouseb & 2) mousepresscallback(2, 0);
897 if (mouseb & 4) mousepresscallback(3, 0);
898 if (mouseb & 8) mousepresscallback(4, 0);
899 if (mousewheel[0]>0) mousepresscallback(5,0);
900 if (mousewheel[1]>0) mousepresscallback(6,0);
901 }
902 mousewheel[0]=mousewheel[1]=0;
903 mouseb = 0;
904
905 if (joypresscallback) {
906 for (i=0;i<32;i++)
907 if (joyb & (1<<i)) joypresscallback(i+1, 0);
908 }
909 joyb = joyblast = 0;
910
911 for (i=0;i<256;i++) {
912 //if (!keystatus[i]) continue;
913 //if (OSD_HandleKey(i, 0) != 0) {
914 OSD_HandleKey(i, 0);
915 SetKey(i, 0);
916 if (keypresscallback) keypresscallback(i, 0);
917 //}
918 }
919 }
920
921
922 //
923 // fetchkeynames() -- retrieves the names for all the keys on the keyboard
924 //
putkeyname(int vsc,int ex,int scan)925 static void putkeyname(int vsc, int ex, int scan) {
926 TCHAR tbuf[24];
927
928 vsc <<= 16;
929 vsc |= ex << 24;
930 if (GetKeyNameText(vsc, tbuf, 24) == 0) return;
931 CharToOemBuff(tbuf, keynames[scan], 24-1);
932
933 //buildprintf("VSC %8x scan %-2x = %s\n", vsc, scan, keynames[scan]);
934 }
935
fetchkeynames(void)936 static void fetchkeynames(void)
937 {
938 int scan, ex;
939 unsigned i;
940
941 memset(keynames,0,sizeof(keynames));
942 for (i=0; i < 256; i++) {
943 scan = wscantable[i];
944 if (scan != 0) {
945 putkeyname(i, 0, scan);
946 }
947 scan = wxscantable[i];
948 if (scan != 0) {
949 putkeyname(i, 1, scan);
950 }
951 }
952 }
953
getkeyname(int num)954 const char *getkeyname(int num)
955 {
956 if ((unsigned)num >= 256) return NULL;
957 return keynames[num];
958 }
959
getjoyname(int what,int num)960 const char *getjoyname(int what, int num)
961 {
962 static const char * axisnames[6] = {
963 "Left Stick X",
964 "Left Stick Y",
965 "Right Stick X",
966 "Right Stick Y",
967 "Left Trigger",
968 "Right Trigger",
969 };
970 static const char * buttonnames[15] = {
971 "A",
972 "B",
973 "X",
974 "Y",
975 "Start",
976 "Guide",
977 "Back",
978 "Left Thumb",
979 "Right Thumb",
980 "Left Shoulder",
981 "Right Shoulder",
982 "DPad Up",
983 "DPad Down",
984 "DPad Left",
985 "DPad Right",
986 };
987 switch (what) {
988 case 0: // axis
989 if ((unsigned)num > (unsigned)6) return NULL;
990 return axisnames[num];
991
992 case 1: // button
993 if ((unsigned)num > (unsigned)15) return NULL;
994 return buttonnames[num];
995
996 default:
997 return NULL;
998 }
999 }
1000
1001
1002
1003
1004
1005 //-------------------------------------------------------------------------------------------------
1006 // TIMER
1007 //=================================================================================================
1008
1009 static int64_t timerfreq=0;
1010 static int timerlastsample=0;
1011 static int timerticspersec=0;
1012 static void (*usertimercallback)(void) = NULL;
1013
1014 // This timer stuff is all Ken's idea.
1015
1016 //
1017 // installusertimercallback() -- set up a callback function to be called when the timer is fired
1018 //
installusertimercallback(void (* callback)(void))1019 void (*installusertimercallback(void (*callback)(void)))(void)
1020 {
1021 void (*oldtimercallback)(void);
1022
1023 oldtimercallback = usertimercallback;
1024 usertimercallback = callback;
1025
1026 return oldtimercallback;
1027 }
1028
1029
1030 //
1031 // inittimer() -- initialise timer
1032 //
inittimer(int tickspersecond)1033 int inittimer(int tickspersecond)
1034 {
1035 int64_t t;
1036
1037 if (timerfreq) return 0; // already installed
1038
1039 buildputs("Initialising timer\n");
1040
1041 // OpenWatcom seems to want us to query the value into a local variable
1042 // instead of the global 'timerfreq' or else it gets pissed with an
1043 // access violation
1044 if (!QueryPerformanceFrequency((LARGE_INTEGER*)&t)) {
1045 ShowErrorBox("Failed fetching timer frequency");
1046 return -1;
1047 }
1048 timerfreq = t;
1049 timerticspersec = tickspersecond;
1050 QueryPerformanceCounter((LARGE_INTEGER*)&t);
1051 timerlastsample = (int)(t*timerticspersec / timerfreq);
1052
1053 usertimercallback = NULL;
1054
1055 return 0;
1056 }
1057
1058 //
1059 // uninittimer() -- shut down timer
1060 //
uninittimer(void)1061 void uninittimer(void)
1062 {
1063 if (!timerfreq) return;
1064
1065 timerfreq=0;
1066 timerticspersec = 0;
1067 }
1068
1069 //
1070 // sampletimer() -- update totalclock
1071 //
sampletimer(void)1072 void sampletimer(void)
1073 {
1074 int64_t i;
1075 int n;
1076
1077 if (!timerfreq) return;
1078
1079 QueryPerformanceCounter((LARGE_INTEGER*)&i);
1080 n = (int)(i*timerticspersec / timerfreq) - timerlastsample;
1081 if (n>0) {
1082 totalclock += n;
1083 timerlastsample += n;
1084 }
1085
1086 if (usertimercallback) for (; n>0; n--) usertimercallback();
1087 }
1088
1089
1090 //
1091 // getticks() -- returns the millisecond ticks count
1092 //
getticks(void)1093 unsigned int getticks(void)
1094 {
1095 int64_t i;
1096 if (timerfreq == 0) return 0;
1097 QueryPerformanceCounter((LARGE_INTEGER*)&i);
1098 return (unsigned int)(i*INT64_C(1000)/timerfreq);
1099 }
1100
1101
1102 //
1103 // getusecticks() -- returns the microsecond ticks count
1104 //
getusecticks(void)1105 unsigned int getusecticks(void)
1106 {
1107 int64_t i;
1108 if (timerfreq == 0) return 0;
1109 QueryPerformanceCounter((LARGE_INTEGER*)&i);
1110 return (unsigned int)(i*INT64_C(1000000)/timerfreq);
1111 }
1112
1113
1114 //
1115 // gettimerfreq() -- returns the number of ticks per second the timer is configured to generate
1116 //
gettimerfreq(void)1117 int gettimerfreq(void)
1118 {
1119 return timerticspersec;
1120 }
1121
1122
1123
1124
1125 //-------------------------------------------------------------------------------------------------
1126 // VIDEO
1127 //=================================================================================================
1128
1129 // DIB stuff
1130 static HDC hDCSection = NULL;
1131 static HBITMAP hDIBSection = NULL;
1132 static HPALETTE hPalette = NULL;
1133 static VOID *lpPixels = NULL;
1134
1135 static int setgammaramp(WORD gt[3][256]);
1136 static int getgammaramp(WORD gt[3][256]);
1137
1138 //
1139 // checkvideomode() -- makes sure the video mode passed is legal
1140 //
checkvideomode(int * x,int * y,int c,int fs,int forced)1141 int checkvideomode(int *x, int *y, int c, int fs, int forced)
1142 {
1143 int i, nearest=-1, dx, dy, odx=9999, ody=9999;
1144
1145 getvalidmodes();
1146
1147 #if USE_OPENGL
1148 if (c > 8 && nogl) return -1;
1149 #else
1150 if (c > 8) return -1;
1151 #endif
1152
1153 // fix up the passed resolution values to be multiples of 8
1154 // and at least 320x200 or at most MAXXDIMxMAXYDIM
1155 if (*x < 320) *x = 320;
1156 if (*y < 200) *y = 200;
1157 if (*x > MAXXDIM) *x = MAXXDIM;
1158 if (*y > MAXYDIM) *y = MAXYDIM;
1159 *x &= 0xfffffff8l;
1160
1161 for (i=0; i<validmodecnt; i++) {
1162 if (validmode[i].bpp != c) continue;
1163 if (validmode[i].fs != fs) continue;
1164 dx = klabs(validmode[i].xdim - *x);
1165 dy = klabs(validmode[i].ydim - *y);
1166 if (!(dx | dy)) { // perfect match
1167 nearest = i;
1168 break;
1169 }
1170 if ((dx <= odx) && (dy <= ody)) {
1171 nearest = i;
1172 odx = dx; ody = dy;
1173 }
1174 }
1175
1176 #ifdef ANY_WINDOWED_SIZE
1177 if (!forced && (fs&1) == 0 && (nearest < 0 || validmode[nearest].xdim!=*x || validmode[nearest].ydim!=*y)) {
1178 // check the colour depth is recognised at the very least
1179 for (i=0;i<validmodecnt;i++)
1180 if (validmode[i].bpp == c)
1181 return 0x7fffffffl;
1182 return -1; // strange colour depth
1183 }
1184 #endif
1185
1186 if (nearest < 0) {
1187 // no mode that will match (eg. if no fullscreen modes)
1188 return -1;
1189 }
1190
1191 *x = validmode[nearest].xdim;
1192 *y = validmode[nearest].ydim;
1193
1194 return nearest; // JBF 20031206: Returns the mode number
1195 }
1196
shutdownvideo(void)1197 static void shutdownvideo(void)
1198 {
1199 #if USE_OPENGL
1200 if (frame) {
1201 free(frame);
1202 frame = NULL;
1203 }
1204 glbuild_delete_8bit_shader(&gl8bit);
1205 UninitOpenGL();
1206 #endif
1207 UninitDIB();
1208
1209 if (desktopmodeset) {
1210 ChangeDisplaySettings(NULL, 0);
1211 desktopmodeset = 0;
1212 }
1213 }
1214
1215 //
1216 // setvideomode() -- set the video mode
1217 //
setvideomode(int x,int y,int c,int fs)1218 int setvideomode(int x, int y, int c, int fs)
1219 {
1220 int i, modenum, refresh=-1;
1221
1222 if ((fs == fullscreen) && (x == xres) && (y == yres) && (c == bpp) && !videomodereset) {
1223 OSD_ResizeDisplay(xres,yres);
1224 return 0;
1225 }
1226
1227 modenum = checkvideomode(&x,&y,c,fs,0);
1228 if (modenum < 0) return -1;
1229 if (modenum != 0x7fffffff) {
1230 refresh = validmode[modenum].extra;
1231 }
1232
1233 if (hWindow && gammabrightness) {
1234 setgammaramp(sysgamma);
1235 gammabrightness = 0;
1236 }
1237
1238 shutdownvideo();
1239
1240 buildprintf("Setting video mode %dx%d (%d-bit %s)\n",
1241 x,y,c, ((fs&1) ? "fullscreen" : "windowed"));
1242
1243 if (CreateAppWindow(x, y, c, fs, refresh)) return -1;
1244
1245 if (!gammabrightness) {
1246 if (getgammaramp(sysgamma) >= 0) gammabrightness = 1;
1247 if (gammabrightness && setgamma(curgamma) < 0) gammabrightness = 0;
1248 }
1249
1250 modechange=1;
1251 videomodereset = 0;
1252 //baselayer_onvideomodechange(c>8);
1253
1254 return 0;
1255 }
1256
1257
1258 //
1259 // getvalidmodes() -- figure out what video modes are available
1260 //
1261 #define ADDMODE(x,y,c,f,n) if (validmodecnt<MAXVALIDMODES) { \
1262 validmode[validmodecnt].xdim=x; \
1263 validmode[validmodecnt].ydim=y; \
1264 validmode[validmodecnt].bpp=c; \
1265 validmode[validmodecnt].fs=f; \
1266 validmode[validmodecnt].extra=n; \
1267 validmodecnt++; \
1268 buildprintf(" - %dx%d %d-bit %s\n", x, y, c, (f&1)?"fullscreen":"windowed"); \
1269 }
1270
1271 #define CHECKL(w,h) if ((w < maxx) && (h < maxy))
1272 #define CHECKLE(w,h) if ((w <= maxx) && (h <= maxy))
1273
1274 #if USE_OPENGL
cdsenummodes(void)1275 static void cdsenummodes(void)
1276 {
1277 DEVMODE dm;
1278 int i = 0, j = 0;
1279
1280 struct { unsigned x,y,bpp,freq; } modes[MAXVALIDMODES];
1281 int nmodes=0;
1282 unsigned maxx = MAXXDIM, maxy = MAXYDIM;
1283
1284 // Enumerate display modes.
1285 ZeroMemory(&dm,sizeof(DEVMODE));
1286 dm.dmSize = sizeof(DEVMODE);
1287 while (nmodes < MAXVALIDMODES && EnumDisplaySettings(NULL, j, &dm)) {
1288 // Identify the same resolution and bit depth in the existing set.
1289 for (i=0;i<nmodes;i++) {
1290 if (modes[i].x == dm.dmPelsWidth
1291 && modes[i].y == dm.dmPelsHeight
1292 && modes[i].bpp == dm.dmBitsPerPel)
1293 break;
1294 }
1295 // A new mode, or a same format mode with a better refresh rate match.
1296 if ((i==nmodes) ||
1297 (dm.dmDisplayFrequency <= maxrefreshfreq && dm.dmDisplayFrequency > modes[i].freq && maxrefreshfreq > 0) ||
1298 (dm.dmDisplayFrequency > modes[i].freq && maxrefreshfreq == 0)) {
1299 if (i==nmodes) nmodes++;
1300
1301 modes[i].x = dm.dmPelsWidth;
1302 modes[i].y = dm.dmPelsHeight;
1303 modes[i].bpp = dm.dmBitsPerPel;
1304 modes[i].freq = dm.dmDisplayFrequency;
1305 }
1306
1307 j++;
1308 ZeroMemory(&dm,sizeof(DEVMODE));
1309 dm.dmSize = sizeof(DEVMODE);
1310 }
1311
1312 // Add what was found to the list.
1313 for (i=0;i<nmodes;i++) {
1314 CHECKLE(modes[i].x, modes[i].y) {
1315 ADDMODE(modes[i].x, modes[i].y, modes[i].bpp, 1, modes[i].freq);
1316 }
1317 }
1318 }
1319 #endif
1320
sortmodes(const struct validmode_t * a,const struct validmode_t * b)1321 static int sortmodes(const struct validmode_t *a, const struct validmode_t *b)
1322 {
1323 int x;
1324
1325 if ((x = a->fs - b->fs) != 0) return x;
1326 if ((x = a->bpp - b->bpp) != 0) return x;
1327 if ((x = a->xdim - b->xdim) != 0) return x;
1328 if ((x = a->ydim - b->ydim) != 0) return x;
1329
1330 return 0;
1331 }
getvalidmodes(void)1332 void getvalidmodes(void)
1333 {
1334 static int defaultres[][2] = {
1335 {1920,1200},{1920,1080},{1600,1200},{1680,1050},{1600,900},{1400,1050},{1440,900},{1366,768},
1336 {1280,1024},{1280,960},{1280,800},{1280,720},{1152,864},{1024,768},{800,600},{640,480},
1337 {640,400},{512,384},{480,360},{400,300},{320,240},{320,200},{0,0}
1338 };
1339 int i, j, maxx=0, maxy=0;
1340
1341 if (modeschecked) return;
1342
1343 validmodecnt=0;
1344 buildputs("Detecting video modes:\n");
1345
1346 // Fullscreen 8-bit modes: upsamples to the desktop mode.
1347 maxx = desktopxdim;
1348 maxy = desktopydim;
1349 for (i=0; defaultres[i][0]; i++) {
1350 CHECKLE(defaultres[i][0],defaultres[i][1]) {
1351 ADDMODE(defaultres[i][0], defaultres[i][1], 8, 1, -1);
1352 }
1353 }
1354
1355 #if USE_POLYMOST && USE_OPENGL
1356 // Fullscreen >8-bit modes.
1357 if (!nogl) cdsenummodes();
1358 #endif
1359
1360 // Windowed modes can't be bigger than the current desktop resolution.
1361 maxx = desktopxdim-1;
1362 maxy = desktopydim-1;
1363
1364 // Windows 8-bit modes
1365 for (i=0; defaultres[i][0]; i++) {
1366 CHECKL(defaultres[i][0],defaultres[i][1]) {
1367 ADDMODE(defaultres[i][0], defaultres[i][1], 8, 0, -1);
1368 }
1369 }
1370
1371 #if USE_POLYMOST && USE_OPENGL
1372 // Windowed >8-bit modes
1373 if (!nogl) {
1374 for (i=0; defaultres[i][0]; i++) {
1375 CHECKL(defaultres[i][0],defaultres[i][1]) {
1376 ADDMODE(defaultres[i][0], defaultres[i][1], desktopbpp, 0, -1);
1377 }
1378 }
1379 }
1380 #endif
1381
1382 qsort((void*)validmode, validmodecnt, sizeof(struct validmode_t), (int(*)(const void*,const void*))sortmodes);
1383
1384 modeschecked=1;
1385 }
1386
1387 #undef CHECK
1388 #undef ADDMODE
1389
1390
1391 //
1392 // resetvideomode() -- resets the video system
1393 //
resetvideomode(void)1394 void resetvideomode(void)
1395 {
1396 videomodereset = 1;
1397 modeschecked = 0;
1398 }
1399
1400
1401 //
1402 // begindrawing() -- locks the framebuffer for drawing
1403 //
begindrawing(void)1404 void begindrawing(void)
1405 {
1406 }
1407
1408
1409 //
1410 // enddrawing() -- unlocks the framebuffer
1411 //
enddrawing(void)1412 void enddrawing(void)
1413 {
1414 }
1415
1416
1417 //
1418 // showframe() -- update the display
1419 //
showframe(void)1420 void showframe(void)
1421 {
1422 HRESULT result;
1423 char *p,*q;
1424 int i,j;
1425
1426 #if USE_OPENGL
1427 if (!nogl) {
1428 if (bpp == 8) {
1429 glbuild_update_8bit_frame(&gl8bit, frame, xres, yres, bytesperline);
1430 glbuild_draw_8bit_frame(&gl8bit);
1431 }
1432
1433 wglfunc.wglSwapBuffers(hDCGLWindow);
1434 return;
1435 }
1436 #endif
1437
1438 {
1439 if ((xres == desktopxdim && yres == desktopydim) || !fullscreen) {
1440 BitBlt(hDCWindow, 0, 0, xres, yres, hDCSection, 0, 0, SRCCOPY);
1441 } else {
1442 int xpos, ypos, xscl, yscl;
1443 int desktopaspect = divscale16(desktopxdim, desktopydim);
1444 int frameaspect = divscale16(xres, yres);
1445
1446 if (desktopaspect >= frameaspect) {
1447 // Desktop is at least as wide as the frame. We maximise frame height and centre on width.
1448 ypos = 0;
1449 yscl = desktopydim;
1450 xscl = mulscale16(desktopydim, frameaspect);
1451 xpos = (desktopxdim - xscl) >> 1;
1452 } else {
1453 // Desktop is narrower than the frame. We maximise frame width and centre on height.
1454 xpos = 0;
1455 xscl = desktopxdim;
1456 yscl = divscale16(desktopxdim, frameaspect);
1457 ypos = (desktopydim - yscl) >> 1;
1458 }
1459
1460 StretchBlt(hDCWindow, xpos, ypos, xscl, yscl, hDCSection, 0, 0, xres, yres, SRCCOPY);
1461 }
1462 }
1463 }
1464
1465
1466 //
1467 // setpalette() -- set palette values
1468 //
setpalette(int UNUSED (start),int UNUSED (num),unsigned char * UNUSED (dapal))1469 int setpalette(int UNUSED(start), int UNUSED(num), unsigned char * UNUSED(dapal))
1470 {
1471 #if USE_OPENGL
1472 if (!nogl && bpp == 8) {
1473 glbuild_update_8bit_palette(&gl8bit, curpalettefaded);
1474 return 0;
1475 }
1476 #endif
1477 if (hDCSection) {
1478 RGBQUAD rgb[256];
1479 int i;
1480
1481 for (i = 0; i < 256; i++) {
1482 rgb[i].rgbBlue = curpalettefaded[i].b;
1483 rgb[i].rgbGreen = curpalettefaded[i].g;
1484 rgb[i].rgbRed = curpalettefaded[i].r;
1485 rgb[i].rgbReserved = 0;
1486 }
1487
1488 SetDIBColorTable(hDCSection, 0, 256, rgb);
1489 }
1490
1491 return 0;
1492 }
1493
1494
1495 //
1496 // setgamma
1497 //
setgammaramp(WORD gt[3][256])1498 static int setgammaramp(WORD gt[3][256])
1499 {
1500 int i;
1501 i = SetDeviceGammaRamp(hDCWindow, gt) ? 0 : -1;
1502 return i;
1503 }
1504
setgamma(float gamma)1505 int setgamma(float gamma)
1506 {
1507 int i;
1508 WORD gt[3][256];
1509
1510 if (!hWindow) return -1;
1511
1512 gamma = 1.0 / gamma;
1513 for (i=0;i<256;i++) {
1514 gt[0][i] =
1515 gt[1][i] =
1516 gt[2][i] = (WORD)min(65535, max(0, (int)(pow((double)i / 256.0, gamma) * 65535.0 + 0.5)));
1517 }
1518
1519 return setgammaramp(gt);
1520 }
1521
getgammaramp(WORD gt[3][256])1522 static int getgammaramp(WORD gt[3][256])
1523 {
1524 int i;
1525
1526 if (!hWindow) return -1;
1527
1528 i = GetDeviceGammaRamp(hDCWindow, gt) ? 0 : -1;
1529
1530 return i;
1531 }
1532
1533
1534 //
1535 // UninitDIB() -- clean up the DIB renderer
1536 //
UninitDIB(void)1537 static void UninitDIB(void)
1538 {
1539 if (hPalette) {
1540 DeleteObject(hPalette);
1541 hPalette = NULL;
1542 }
1543
1544 if (hDCSection) {
1545 DeleteDC(hDCSection);
1546 hDCSection = NULL;
1547 }
1548
1549 if (hDIBSection) {
1550 DeleteObject(hDIBSection);
1551 hDIBSection = NULL;
1552 }
1553 }
1554
1555
1556 //
1557 // SetupDIB() -- sets up DIB rendering
1558 //
SetupDIB(int width,int height)1559 static int SetupDIB(int width, int height)
1560 {
1561 struct binfo {
1562 BITMAPINFOHEADER header;
1563 RGBQUAD colours[256];
1564 } dibsect;
1565 int i, bpl;
1566
1567 // create the new DIB section
1568 memset(&dibsect, 0, sizeof(dibsect));
1569 numpages = 1; // KJS 20031225
1570 dibsect.header.biSize = sizeof(dibsect.header);
1571 dibsect.header.biWidth = width|1; // Ken did this
1572 dibsect.header.biHeight = -height;
1573 dibsect.header.biPlanes = 1;
1574 dibsect.header.biBitCount = 8;
1575 dibsect.header.biCompression = BI_RGB;
1576 dibsect.header.biClrUsed = 256;
1577 dibsect.header.biClrImportant = 256;
1578 for (i=0; i<256; i++) {
1579 dibsect.colours[i].rgbBlue = curpalettefaded[i].b;
1580 dibsect.colours[i].rgbGreen = curpalettefaded[i].g;
1581 dibsect.colours[i].rgbRed = curpalettefaded[i].r;
1582 }
1583
1584 hDIBSection = CreateDIBSection(hDCWindow, (BITMAPINFO *)&dibsect, DIB_RGB_COLORS, &lpPixels, NULL, 0);
1585 if (!hDIBSection || lpPixels == NULL) {
1586 UninitDIB();
1587 ShowErrorBox("Error creating DIB section");
1588 return TRUE;
1589 }
1590
1591 memset(lpPixels, 0, (((width|1) + 4) & ~3)*height);
1592
1593 // create a compatible memory DC
1594 hDCSection = CreateCompatibleDC(hDCWindow);
1595 if (!hDCSection) {
1596 UninitDIB();
1597 ShowErrorBox("Error creating compatible DC");
1598 return TRUE;
1599 }
1600
1601 // select the DIB section into the memory DC
1602 if (!SelectObject(hDCSection, hDIBSection)) {
1603 UninitDIB();
1604 ShowErrorBox("Error creating compatible DC");
1605 return TRUE;
1606 }
1607
1608 return FALSE;
1609 }
1610
1611 #if USE_OPENGL
1612
1613 //
1614 // loadgldriver -- loads an OpenGL DLL
1615 //
loadgldriver(const char * dll)1616 int loadgldriver(const char *dll)
1617 {
1618 if (hGLDLL) return 0; // Already loaded
1619
1620 if (!dll) {
1621 dll = "OPENGL32.DLL";
1622 }
1623
1624 buildprintf("Loading %s\n", dll);
1625
1626 hGLDLL = LoadLibrary(dll);
1627 if (!hGLDLL) return -1;
1628
1629 return 0;
1630 }
1631
unloadgldriver(void)1632 int unloadgldriver(void)
1633 {
1634 if (!hGLDLL) return 0;
1635 FreeLibrary(hGLDLL);
1636 hGLDLL = NULL;
1637 return 0;
1638 }
1639
1640 //
1641 // getglprocaddress
1642 //
getglprocaddress(const char * name,int ext)1643 void *getglprocaddress(const char *name, int ext)
1644 {
1645 void *func = NULL;
1646 if (!hGLDLL) return NULL;
1647 if (ext && wglfunc.wglGetProcAddress) {
1648 func = wglfunc.wglGetProcAddress(name);
1649 }
1650 if (!func) {
1651 func = GetProcAddress(hGLDLL, name);
1652 }
1653 return func;
1654 }
1655
1656
1657 //
1658 // UninitOpenGL() -- cleans up OpenGL rendering
1659 //
1660
UninitOpenGL(void)1661 static void UninitOpenGL(void)
1662 {
1663 if (hGLRC) {
1664 #if USE_POLYMOST
1665 polymost_glreset();
1666 #endif
1667 if (!wglfunc.wglMakeCurrent(0,0)) { }
1668 if (!wglfunc.wglDeleteContext(hGLRC)) { }
1669 hGLRC = NULL;
1670 }
1671 if (hGLWindow) {
1672 if (hDCGLWindow) {
1673 ReleaseDC(hGLWindow, hDCGLWindow);
1674 hDCGLWindow = NULL;
1675 }
1676
1677 DestroyWindow(hGLWindow);
1678 hGLWindow = NULL;
1679 }
1680 }
1681
1682 // Enumerate the WGL interface extensions.
EnumWGLExts(HDC hdc)1683 static void EnumWGLExts(HDC hdc)
1684 {
1685 const GLchar *extstr;
1686 char *workstr, *workptr, *nextptr = NULL, *ext = NULL;
1687 int ack;
1688
1689 wglfunc.wglGetExtensionsStringARB = getglprocaddress("wglGetExtensionsStringARB", 1);
1690 if (!wglfunc.wglGetExtensionsStringARB) {
1691 debugprintf("Note: OpenGL does not provide WGL_ARB_extensions_string extension.\n");
1692 return;
1693 }
1694
1695 extstr = wglfunc.wglGetExtensionsStringARB(hdc);
1696
1697 debugprintf("WGL extensions supported:\n");
1698 workstr = workptr = strdup(extstr);
1699 while ((ext = Bstrtoken(workptr, " ", &nextptr, 1)) != NULL) {
1700 if (!strcmp(ext, "WGL_ARB_pixel_format")) {
1701 wglfunc.wglChoosePixelFormatARB = getglprocaddress("wglChoosePixelFormatARB", 1);
1702 ack = !wglfunc.wglChoosePixelFormatARB ? '!' : '+';
1703 } else if (!strcmp(ext, "WGL_ARB_create_context")) {
1704 wglfunc.wglCreateContextAttribsARB = getglprocaddress("wglCreateContextAttribsARB", 1);
1705 ack = !wglfunc.wglCreateContextAttribsARB ? '!' : '+';
1706 } else if (!strcmp(ext, "WGL_ARB_create_context_profile")) {
1707 wglfunc.have_ARB_create_context_profile = 1;
1708 ack = '+';
1709 } else if (!strcmp(ext, "WGL_EXT_swap_control")) {
1710 wglfunc.wglSwapIntervalEXT = getglprocaddress("wglSwapIntervalEXT", 1);
1711 wglfunc.wglGetSwapIntervalEXT = getglprocaddress("wglGetSwapIntervalEXT", 1);
1712 ack = (!wglfunc.wglSwapIntervalEXT || !wglfunc.wglGetSwapIntervalEXT) ? '!' : '+';
1713 } else {
1714 ack = ' ';
1715 }
1716 debugprintf(" %s %c\n", ext, ack);
1717 workptr = NULL;
1718 }
1719 free(workstr);
1720 }
1721
1722 //
1723 // SetupOpenGL() -- sets up opengl rendering
1724 //
SetupOpenGL(int width,int height,int bitspp,int cover)1725 static int SetupOpenGL(int width, int height, int bitspp, int cover)
1726 {
1727 int err, pixelformat;
1728
1729 // Step 1. Create a fake context with a safe pixel format descriptor.
1730 GLuint dummyPixelFormat;
1731 PIXELFORMATDESCRIPTOR dummyPfd = {
1732 sizeof(PIXELFORMATDESCRIPTOR),
1733 1, //Version Number
1734 PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, //Must Support these
1735 PFD_TYPE_RGBA, //Request An RGBA Format
1736 32, //Color Depth
1737 0,0,0,0,0,0, //Color Bits Ignored
1738 0, //No Alpha Buffer
1739 0, //Shift Bit Ignored
1740 0, //No Accumulation Buffer
1741 0,0,0,0, //Accumulation Bits Ignored
1742 24, //16/24/32 Z-Buffer depth
1743 8, //Stencil Buffer
1744 0, //No Auxiliary Buffer
1745 PFD_MAIN_PLANE, //Main Drawing Layer
1746 0, //Reserved
1747 0,0,0 //Layer Masks Ignored
1748 };
1749 HDC dummyhDC = 0;
1750 HGLRC dummyhGLRC = 0;
1751 const char *errmsg = NULL;
1752
1753 dummyhGLwindow = CreateWindow(
1754 WINDOW_CLASS,
1755 "OpenGL Window",
1756 WS_CHILD,
1757 0,0,
1758 1,1,
1759 hWindow,
1760 (HMENU)0,
1761 hInstance,
1762 NULL);
1763 if (!dummyhGLwindow) {
1764 errmsg = "Error creating dummy OpenGL child window.";
1765 goto fail;
1766 }
1767
1768 dummyhDC = GetDC(dummyhGLwindow);
1769 if (!dummyhDC) {
1770 errmsg = "Error getting dummy device context";
1771 goto fail;
1772 }
1773
1774 dummyPixelFormat = ChoosePixelFormat(dummyhDC, &dummyPfd);
1775 if (!dummyPixelFormat) {
1776 errmsg = "Can't choose dummy pixel format";
1777 goto fail;
1778 }
1779
1780 err = SetPixelFormat(dummyhDC, dummyPixelFormat, &dummyPfd);
1781 if (!err) {
1782 errmsg = "Can't set dummy pixel format";
1783 goto fail;
1784 }
1785
1786 dummyhGLRC = wglfunc.wglCreateContext(dummyhDC);
1787 if (!dummyhGLRC) {
1788 errmsg = "Can't create dummy GL context";
1789 goto fail;
1790 }
1791
1792 if (!wglfunc.wglMakeCurrent(dummyhDC, dummyhGLRC)) {
1793 errmsg = "Can't activate dummy GL context";
1794 goto fail;
1795 }
1796
1797 // Step 2. Check the WGL extensions.
1798 EnumWGLExts(dummyhDC);
1799
1800 // Step 3. Create the actual window we will use going forward.
1801 {
1802 int xpos, ypos, xscl, yscl;
1803
1804 if (cover) {
1805 // The desktop resolution is set to the target. Fill the screen.
1806 xpos = ypos = 0;
1807 xscl = width;
1808 yscl = height;
1809 } else {
1810 // The desktop resolution remains the same and we stretch to fit.
1811 int desktopaspect = divscale16(desktopxdim, desktopydim);
1812 int frameaspect = divscale16(width, height);
1813 if (desktopaspect >= frameaspect) {
1814 // Desktop is at least as wide as the frame. We maximise frame height and centre on width.
1815 ypos = 0;
1816 yscl = desktopydim;
1817 xscl = mulscale16(desktopydim, frameaspect);
1818 xpos = (desktopxdim - xscl) >> 1;
1819 } else {
1820 // Desktop is narrower than the frame. We maximise frame width and centre on height.
1821 xpos = 0;
1822 xscl = desktopxdim;
1823 yscl = divscale16(desktopxdim, frameaspect);
1824 ypos = (desktopydim - yscl) >> 1;
1825 }
1826 }
1827
1828 hGLWindow = CreateWindow(
1829 WINDOW_CLASS,
1830 "OpenGL Window",
1831 WS_CHILD|WS_VISIBLE,
1832 xpos, ypos,
1833 xscl, yscl,
1834 hWindow,
1835 (HMENU)0,
1836 hInstance,
1837 NULL);
1838 if (!hGLWindow) {
1839 errmsg = "Error creating OpenGL child window.";
1840 goto fail;
1841 }
1842 }
1843
1844 hDCGLWindow = GetDC(hGLWindow);
1845 if (!hDCGLWindow) {
1846 errmsg = "Error getting device context.";
1847 goto fail;
1848 }
1849
1850 // Step 3. Find and set a suitable pixel format.
1851 if (wglfunc.wglChoosePixelFormatARB) {
1852 UINT numformats;
1853 int pformatattribs[] = {
1854 WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
1855 WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
1856 WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
1857 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
1858 WGL_COLOR_BITS_ARB, bitspp,
1859 WGL_DEPTH_BITS_ARB, 24,
1860 WGL_STENCIL_BITS_ARB, 0,
1861 WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
1862 0,
1863 };
1864 if (!wglfunc.wglChoosePixelFormatARB(hDCGLWindow, pformatattribs, NULL, 1, &pixelformat, &numformats)) {
1865 errmsg = "Can't choose pixel format.";
1866 goto fail;
1867 } else if (numformats < 1) {
1868 errmsg = "No suitable pixel format available.";
1869 goto fail;
1870 }
1871 } else {
1872 PIXELFORMATDESCRIPTOR pfd = {
1873 sizeof(PIXELFORMATDESCRIPTOR),
1874 1, //Version Number
1875 PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, //Must Support these
1876 PFD_TYPE_RGBA, //Request An RGBA Format
1877 bitspp, //Color Depth
1878 0,0,0,0,0,0, //Color Bits Ignored
1879 0, //No Alpha Buffer
1880 0, //Shift Bit Ignored
1881 0, //No Accumulation Buffer
1882 0,0,0,0, //Accumulation Bits Ignored
1883 24, //16/24/32 Z-Buffer depth
1884 0, //Stencil Buffer
1885 0, //No Auxiliary Buffer
1886 PFD_MAIN_PLANE, //Main Drawing Layer
1887 0, //Reserved
1888 0,0,0 //Layer Masks Ignored
1889 };
1890 pixelformat = ChoosePixelFormat(hDCGLWindow, &pfd);
1891 if (!pixelformat) {
1892 errmsg = "Can't choose pixel format";
1893 goto fail;
1894 }
1895 }
1896
1897 DescribePixelFormat(hDCGLWindow, pixelformat, sizeof(PIXELFORMATDESCRIPTOR), &dummyPfd);
1898 err = SetPixelFormat(hDCGLWindow, pixelformat, &dummyPfd);
1899 if (!err) {
1900 errmsg = "Can't set pixel format.";
1901 goto fail;
1902 }
1903
1904 // Step 4. Create a context with the needed profile.
1905 if (wglfunc.wglCreateContextAttribsARB) {
1906 int contextattribs[] = {
1907 #if (USE_OPENGL == USE_GL3)
1908 WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
1909 WGL_CONTEXT_MINOR_VERSION_ARB, 2,
1910 WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
1911 #else
1912 WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
1913 WGL_CONTEXT_MINOR_VERSION_ARB, 1,
1914 WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
1915 #endif
1916 0,
1917 };
1918 if (!wglfunc.have_ARB_create_context_profile) {
1919 contextattribs[4] = 0; //WGL_CONTEXT_PROFILE_MASK_ARB
1920 }
1921 hGLRC = wglfunc.wglCreateContextAttribsARB(hDCGLWindow, 0, contextattribs);
1922 if (!hGLRC) {
1923 errmsg = "Can't create GL context.";
1924 goto fail;
1925 }
1926 } else {
1927 hGLRC = wglfunc.wglCreateContext(hDCGLWindow);
1928 if (!hGLRC) {
1929 errmsg = "Can't create GL context.";
1930 goto fail;
1931 }
1932 }
1933
1934 // Scrap the dummy stuff.
1935 if (!wglfunc.wglMakeCurrent(NULL, NULL)) { }
1936 if (!wglfunc.wglDeleteContext(dummyhGLRC)) { }
1937 ReleaseDC(dummyhGLwindow, dummyhDC);
1938 DestroyWindow(dummyhGLwindow);
1939 dummyhGLwindow = NULL;
1940 dummyhGLRC = NULL;
1941 dummyhDC = NULL;
1942
1943 if (!wglfunc.wglMakeCurrent(hDCGLWindow, hGLRC)) {
1944 errmsg = "Can't activate GL context";
1945 goto fail;
1946 }
1947
1948 // Step 5. Done.
1949 switch (baselayer_setupopengl()) {
1950 case 0:
1951 break;
1952 case -1:
1953 errmsg = "Can't load required OpenGL function pointers.";
1954 goto fail;
1955 case -2:
1956 errmsg = "Minimum OpenGL requirements are not met.";
1957 goto fail;
1958 default:
1959 errmsg = "Other OpenGL initialisation error.";
1960 goto fail;
1961 }
1962
1963 if (wglfunc.wglSwapIntervalEXT) {
1964 wglfunc.wglSwapIntervalEXT(glswapinterval);
1965 }
1966 numpages = 127;
1967
1968 return FALSE;
1969
1970 fail:
1971 if (bpp > 8) {
1972 ShowErrorBox(errmsg);
1973 }
1974 shutdownvideo();
1975
1976 if (!wglfunc.wglMakeCurrent(NULL, NULL)) { }
1977
1978 if (hGLRC) {
1979 if (!wglfunc.wglDeleteContext(hGLRC)) { }
1980 }
1981 if (hGLWindow) {
1982 if (hDCGLWindow) {
1983 ReleaseDC(hGLWindow, hDCGLWindow);
1984 }
1985 }
1986 hDCGLWindow = NULL;
1987 hGLRC = NULL;
1988 hGLWindow = NULL;
1989
1990 if (dummyhGLRC) {
1991 if (!wglfunc.wglDeleteContext(dummyhGLRC)) { }
1992 }
1993 if (dummyhGLwindow) {
1994 if (dummyhDC) {
1995 ReleaseDC(dummyhGLwindow, dummyhDC);
1996 }
1997 DestroyWindow(dummyhGLwindow);
1998 }
1999
2000 return TRUE;
2001 }
2002
2003 #endif //USE_OPENGL
2004
2005 //
2006 // CreateAppWindow() -- create the application window
2007 //
CreateAppWindow(int width,int height,int bitspp,int fs,int refresh)2008 static BOOL CreateAppWindow(int width, int height, int bitspp, int fs, int refresh)
2009 {
2010 RECT rect;
2011 int w, h, x, y, stylebits = 0, stylebitsex = 0;
2012 HRESULT result;
2013
2014 if (width == xres && height == yres && fs == fullscreen && bitspp == bpp && !videomodereset) return FALSE;
2015
2016 if (hWindow) {
2017 ShowWindow(hWindow, SW_HIDE); // so Windows redraws what's behind if the window shrinks
2018 }
2019
2020 if (fs) {
2021 stylebitsex = WS_EX_TOPMOST;
2022 stylebits = WS_POPUP;
2023 } else {
2024 stylebitsex = 0;
2025 stylebits = (WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX);
2026 }
2027
2028 if (!hWindow) {
2029 hWindow = CreateWindowEx(
2030 stylebitsex,
2031 "buildapp",
2032 apptitle,
2033 stylebits,
2034 CW_USEDEFAULT,
2035 CW_USEDEFAULT,
2036 320,
2037 200,
2038 NULL,
2039 NULL,
2040 hInstance,
2041 0);
2042 if (!hWindow) {
2043 ShowErrorBox("Unable to create window");
2044 return TRUE;
2045 }
2046
2047 hDCWindow = GetDC(hWindow);
2048 if (!hDCWindow) {
2049 ShowErrorBox("Error getting device context");
2050 return TRUE;
2051 }
2052
2053 startwin_close();
2054 } else {
2055 SetWindowLong(hWindow,GWL_EXSTYLE,stylebitsex);
2056 SetWindowLong(hWindow,GWL_STYLE,stylebits);
2057 }
2058
2059 // resize the window
2060 if (!fs) {
2061 rect.left = 0;
2062 rect.top = 0;
2063 rect.right = width-1;
2064 rect.bottom = height-1;
2065 AdjustWindowRect(&rect, stylebits, FALSE);
2066
2067 w = (rect.right - rect.left);
2068 h = (rect.bottom - rect.top);
2069 x = (desktopxdim - w) / 2;
2070 y = (desktopydim - h) / 2;
2071 } else {
2072 x=y=0;
2073 w=desktopxdim;
2074 h=desktopydim;
2075 }
2076 SetWindowPos(hWindow, HWND_TOP, x, y, w, h, 0);
2077
2078 UpdateAppWindowTitle();
2079 ShowWindow(hWindow, SW_SHOWNORMAL);
2080 SetForegroundWindow(hWindow);
2081 SetFocus(hWindow);
2082
2083 if (bitspp == 8) {
2084 int i, j;
2085
2086 #if USE_OPENGL
2087 if (nogl) {
2088 #endif
2089 // 8-bit software with no GL shader uses classic Windows DIB blitting.
2090 if (SetupDIB(width, height)) {
2091 return TRUE;
2092 }
2093
2094 frameplace = (intptr_t)lpPixels;
2095 bytesperline = (((width|1) + 4) & ~3);
2096 #if USE_OPENGL
2097 } else {
2098 // Prepare the GLSL shader for 8-bit blitting.
2099 if (SetupOpenGL(width, height, bitspp, !fs)) {
2100 // No luck. Write off OpenGL and try DIB.
2101 buildputs("OpenGL initialisation failed. Falling back to DIB mode.\n");
2102 nogl = 1;
2103 return CreateAppWindow(width, height, bitspp, fs, refresh);
2104 }
2105
2106 bytesperline = (((width|1) + 4) & ~3);
2107
2108 if (glbuild_prepare_8bit_shader(&gl8bit, width, height, bytesperline) < 0) {
2109 shutdownvideo();
2110 return -1;
2111 }
2112
2113 frame = (unsigned char *) malloc(bytesperline * height);
2114 if (!frame) {
2115 shutdownvideo();
2116 buildputs("Unable to allocate framebuffer\n");
2117 return FALSE;
2118 }
2119
2120 frameplace = (intptr_t)frame;
2121 }
2122 #endif
2123
2124 imageSize = bytesperline*height;
2125 setvlinebpl(bytesperline);
2126
2127 for(i=j=0; i<=height; i++) ylookup[i] = j, j += bytesperline;
2128 modechange=0;
2129
2130 numpages = 1;
2131 } else {
2132 #if USE_OPENGL
2133 if (fs) {
2134 DEVMODE dmScreenSettings;
2135
2136 ZeroMemory(&dmScreenSettings, sizeof(DEVMODE));
2137 dmScreenSettings.dmSize = sizeof(DEVMODE);
2138 dmScreenSettings.dmPelsWidth = width;
2139 dmScreenSettings.dmPelsHeight = height;
2140 dmScreenSettings.dmBitsPerPel = bitspp;
2141 dmScreenSettings.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
2142 if (refresh > 0) {
2143 dmScreenSettings.dmDisplayFrequency = refresh;
2144 dmScreenSettings.dmFields |= DM_DISPLAYFREQUENCY;
2145 }
2146
2147 if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
2148 ShowErrorBox("Video mode not supported");
2149 return TRUE;
2150 }
2151 desktopmodeset = 1;
2152 }
2153
2154 ShowWindow(hWindow, SW_SHOWNORMAL);
2155 SetForegroundWindow(hWindow);
2156 SetFocus(hWindow);
2157
2158 if (SetupOpenGL(width, height, bitspp, !desktopmodeset)) {
2159 return TRUE;
2160 }
2161
2162 frameplace = 0;
2163 bytesperline = 0;
2164 imageSize = 0;
2165 #else
2166 return FALSE;
2167 #endif
2168 }
2169
2170 xres = width;
2171 yres = height;
2172 bpp = bitspp;
2173 fullscreen = fs;
2174
2175 modechange = 1;
2176 OSD_ResizeDisplay(xres,yres);
2177
2178 UpdateWindow(hWindow);
2179
2180 return FALSE;
2181 }
2182
2183
2184 //
2185 // DestroyAppWindow() -- destroys the application window
2186 //
DestroyAppWindow(void)2187 static void DestroyAppWindow(void)
2188 {
2189 if (hWindow && gammabrightness) {
2190 setgammaramp(sysgamma);
2191 gammabrightness = 0;
2192 }
2193
2194 shutdownvideo();
2195
2196 if (hDCWindow) {
2197 ReleaseDC(hWindow, hDCWindow);
2198 hDCWindow = NULL;
2199 }
2200
2201 if (hWindow) {
2202 DestroyWindow(hWindow);
2203 hWindow = NULL;
2204 }
2205 }
2206
2207 //
2208 // UpdateAppWindowTitle() -- sets the title of the application window
2209 //
UpdateAppWindowTitle(void)2210 static void UpdateAppWindowTitle(void)
2211 {
2212 char tmp[256+3+256+1]; //sizeof(wintitle) + " - " + sizeof(apptitle) + '\0'
2213
2214 if (!hWindow) return;
2215
2216 if (wintitle[0]) {
2217 snprintf(tmp, sizeof(tmp), "%s - %s", wintitle, apptitle);
2218 tmp[sizeof(tmp)-1] = 0;
2219 SetWindowText(hWindow, tmp);
2220 } else {
2221 SetWindowText(hWindow, apptitle);
2222 }
2223 }
2224
2225
2226
2227
2228
2229
2230 //-------------------------------------------------------------------------------------------------
2231 // MOSTLY STATIC INTERNAL WINDOWS THINGS
2232 //=================================================================================================
2233
2234 //
2235 // ShowErrorBox() -- shows an error message box
2236 //
ShowErrorBox(const char * m)2237 static void ShowErrorBox(const char *m)
2238 {
2239 TCHAR msg[1024];
2240
2241 wsprintf(msg, "%s: %s", m, GetWindowsErrorMsg(GetLastError()));
2242 MessageBox(0, msg, apptitle, MB_OK|MB_ICONSTOP);
2243 }
2244
2245
2246 //
2247 // CheckWinVersion() -- check to see what version of Windows we happen to be running under
2248 //
CheckWinVersion(void)2249 static BOOL CheckWinVersion(void)
2250 {
2251 OSVERSIONINFO osv;
2252
2253 ZeroMemory(&osv, sizeof(osv));
2254 osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
2255 if (!GetVersionEx(&osv)) return TRUE;
2256
2257 // At least Windows Vista
2258 if (osv.dwPlatformId != VER_PLATFORM_WIN32_NT) return TRUE;
2259 if (osv.dwMajorVersion < 6) return TRUE;
2260
2261 return FALSE;
2262 }
2263
2264
2265 static const int wscantable[256] = {
2266 /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
2267 /* 0y */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
2268 /* 1y */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
2269 /* 2y */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
2270 /* 3y */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
2271 /* 4y */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x59, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
2272 /* 5y */ 0x50, 0x51, 0x52, 0x53, 0, 0, 0, 0x57, 0x58, 0, 0, 0, 0, 0, 0, 0,
2273 /* 6y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2274 /* 7y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2275 /* 8y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2276 /* 9y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2277 /* Ay */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2278 /* By */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2279 /* Cy */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2280 /* Dy */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2281 /* Ey */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2282 /* Fy */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2283 };
2284
2285 static const int wxscantable[256] = {
2286 /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
2287 /* 0y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2288 /* 1y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x9c, 0x9d, 0, 0,
2289 /* 2y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2290 /* 3y */ 0, 0, 0, 0, 0, 0xb5, 0, 0, 0xb8, 0, 0, 0, 0, 0xb8, 0, 0,
2291 /* 4y */ 0, 0, 0, 0, 0, 0x45, 0, 0xc7, 0xc8, 0xc9, 0, 0xcb, 0, 0xcd, 0, 0xcf,
2292 /* 5y */ 0xd0, 0xd1, 0xd2, 0xd3, 0, 0, 0, 0, 0, 0, 0, 0x5b, 0x5c, 0x5d, 0, 0,
2293 /* 6y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2294 /* 7y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2295 /* 8y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2296 /* 9y */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x9d, 0, 0,
2297 /* Ay */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2298 /* By */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2299 /* Cy */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2300 /* Dy */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2301 /* Ey */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2302 /* Fy */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2303 };
2304
2305
2306 //
2307 // WndProcCallback() -- the Windows window callback
2308 //
WndProcCallback(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)2309 static LRESULT CALLBACK WndProcCallback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2310 {
2311 RECT rect;
2312 POINT pt;
2313 HRESULT result;
2314
2315 #if USE_OPENGL
2316 if (hGLWindow && hWnd == hGLWindow) return DefWindowProc(hWnd,uMsg,wParam,lParam);
2317 if (dummyhGLwindow && hWnd == dummyhGLwindow) return DefWindowProc(hWnd,uMsg,wParam,lParam);
2318 #endif
2319
2320 switch (uMsg) {
2321 case WM_SYSCOMMAND:
2322 // don't let the monitor fall asleep or let the screensaver activate
2323 if (wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER) return 0;
2324
2325 // Since DirectInput won't let me set an exclusive-foreground
2326 // keyboard for some unknown reason I just have to tell Windows to
2327 // rack off with its keyboard accelerators.
2328 if (wParam == SC_KEYMENU || wParam == SC_HOTKEY) return 0;
2329 break;
2330
2331 case WM_ACTIVATEAPP:
2332 appactive = wParam;
2333 if (backgroundidle)
2334 SetPriorityClass( GetCurrentProcess(),
2335 appactive ? NORMAL_PRIORITY_CLASS : IDLE_PRIORITY_CLASS );
2336 break;
2337
2338 case WM_ACTIVATE:
2339 // AcquireInputDevices(appactive);
2340 constrainmouse(LOWORD(wParam) != WA_INACTIVE && HIWORD(wParam) == 0);
2341 break;
2342
2343 case WM_SIZE:
2344 if (wParam == SIZE_MAXHIDE || wParam == SIZE_MINIMIZED) appactive = 0;
2345 else appactive = 1;
2346 // AcquireInputDevices(appactive);
2347 break;
2348
2349 case WM_DISPLAYCHANGE:
2350 // desktop settings changed so adjust our world-view accordingly
2351 desktopxdim = LOWORD(lParam);
2352 desktopydim = HIWORD(lParam);
2353 desktopbpp = wParam;
2354 getvalidmodes();
2355 break;
2356
2357 case WM_PAINT:
2358 break;
2359
2360 // don't draw the frame if fullscreen
2361 //case WM_NCPAINT:
2362 //if (!fullscreen) break;
2363 //return 0;
2364
2365 case WM_ERASEBKGND:
2366 break;//return TRUE;
2367
2368 case WM_MOVE:
2369 windowposx = LOWORD(lParam);
2370 windowposy = HIWORD(lParam);
2371 return 0;
2372
2373 case WM_CLOSE:
2374 quitevent = 1;
2375 return 0;
2376
2377 case WM_SYSKEYDOWN:
2378 case WM_SYSKEYUP:
2379 case WM_KEYDOWN:
2380 case WM_KEYUP:
2381 {
2382 int press = (lParam & 0x80000000l) == 0;
2383 int wscan = (lParam >> 16) & 0xff;
2384 int scan = 0;
2385
2386 if (lParam & (1<<24)) {
2387 scan = wxscantable[wscan];
2388 } else {
2389 scan = wscantable[wscan];
2390 }
2391
2392 //buildprintf("VK %-2x VSC %8x scan %-2x = %s\n", wParam, (UINT)lParam, scan, keynames[scan]);
2393
2394 if (scan == 0) {
2395 // Not a key we want, so give it to the OS to handle.
2396 break;
2397 } else if (scan == OSD_CaptureKey(-1)) {
2398 if (press) {
2399 OSD_ShowDisplay(-1);
2400 eatosdinput = 1;
2401 }
2402 } else if (OSD_HandleKey(scan, press) != 0) {
2403 if (!keystatus[scan] || !press) {
2404 SetKey(scan, press);
2405 if (keypresscallback) keypresscallback(scan, press);
2406 }
2407 }
2408 }
2409 return 0;
2410
2411 case WM_CHAR:
2412 if (eatosdinput) {
2413 eatosdinput = 0;
2414 } else if (OSD_HandleChar((unsigned char)wParam)) {
2415 if (((keyasciififoend+1)&(KEYFIFOSIZ-1)) != keyasciififoplc) {
2416 keyasciififo[keyasciififoend] = (unsigned char)wParam;
2417 keyasciififoend = ((keyasciififoend+1)&(KEYFIFOSIZ-1));
2418 //buildprintf("Char %d, %d-%d\n",wParam,keyasciififoplc,keyasciififoend);
2419 }
2420 }
2421 return 0;
2422
2423 case WM_HOTKEY:
2424 return 0;
2425
2426 case WM_INPUT:
2427 {
2428 RAWINPUT raw;
2429 UINT dwSize = sizeof(RAWINPUT);
2430
2431 GetRawInputData((HRAWINPUT)lParam, RID_INPUT, (LPVOID)&raw, &dwSize, sizeof(RAWINPUTHEADER));
2432
2433 if (raw.header.dwType == RIM_TYPEMOUSE) {
2434 int but;
2435
2436 if (!mousegrab) {
2437 return 0;
2438 }
2439
2440 for (but = 0; but < 4; but++) { // Sorry XBUTTON2, I didn't plan for you.
2441 switch ((raw.data.mouse.usButtonFlags >> (but << 1)) & 3) {
2442 case 1: // press
2443 mouseb |= (1 << but);
2444 if (mousepresscallback) {
2445 mousepresscallback(but, 1);
2446 }
2447 break;
2448 case 2: // release
2449 mouseb &= ~(1 << but);
2450 if (mousepresscallback) {
2451 mousepresscallback(but, 0);
2452 }
2453 break;
2454 default: break; // no change
2455 }
2456 }
2457
2458 if (raw.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) {
2459 int direction = (short)raw.data.mouse.usButtonData < 0; // 1 = down (-ve values), 0 = up
2460
2461 // Repeated events before the fake button release timer
2462 // expires need to trigger a release and a new press.
2463 if (mousewheel[direction] > 0 && mousepresscallback) {
2464 mousepresscallback(5 + direction, 0);
2465 }
2466
2467 mousewheel[direction] = getticks();
2468 mouseb |= (16 << direction);
2469 if (mousepresscallback) {
2470 mousepresscallback(5 + direction, 1);
2471 }
2472 }
2473
2474 if (raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
2475 static LONG absx = 0, absy = 0;
2476 static char first = 1;
2477
2478 if (!first) {
2479 mousex += raw.data.mouse.lLastX - absx;
2480 mousey += raw.data.mouse.lLastY - absy;
2481 } else {
2482 first = 0;
2483 }
2484 absx = raw.data.mouse.lLastX;
2485 absy = raw.data.mouse.lLastY;
2486 } else {
2487 mousex += raw.data.mouse.lLastX;
2488 mousey += raw.data.mouse.lLastY;
2489 }
2490 }
2491 }
2492 return 0;
2493
2494 case WM_ENTERMENULOOP:
2495 case WM_ENTERSIZEMOVE:
2496 // AcquireInputDevices(0);
2497 return 0;
2498 case WM_EXITMENULOOP:
2499 case WM_EXITSIZEMOVE:
2500 // AcquireInputDevices(1);
2501 return 0;
2502
2503 case WM_DESTROY:
2504 hWindow = 0;
2505 //PostQuitMessage(0); // JBF 20040115: not anymore
2506 return 0;
2507
2508 default:
2509 break;
2510 }
2511
2512 return DefWindowProc(hWnd, uMsg, wParam, lParam);
2513 }
2514
2515
2516 //
2517 // RegisterWindowClass() -- register the window class
2518 //
RegisterWindowClass(void)2519 static BOOL RegisterWindowClass(void)
2520 {
2521 WNDCLASSEX wcx;
2522
2523 if (window_class_registered) return FALSE;
2524
2525 //buildputs("Registering window class\n");
2526
2527 wcx.cbSize = sizeof(wcx);
2528 wcx.style = CS_OWNDC;
2529 wcx.lpfnWndProc = WndProcCallback;
2530 wcx.cbClsExtra = 0;
2531 wcx.cbWndExtra = 0;
2532 wcx.hInstance = hInstance;
2533 wcx.hIcon = LoadImage(hInstance, MAKEINTRESOURCE(100), IMAGE_ICON,
2534 GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
2535 wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
2536 wcx.hbrBackground = CreateSolidBrush(RGB(0,0,0));
2537 wcx.lpszMenuName = NULL;
2538 wcx.lpszClassName = WINDOW_CLASS;
2539 wcx.hIconSm = LoadImage(hInstance, MAKEINTRESOURCE(100), IMAGE_ICON,
2540 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
2541 if (!RegisterClassEx(&wcx)) {
2542 ShowErrorBox("Failed to register window class");
2543 return TRUE;
2544 }
2545
2546 window_class_registered = TRUE;
2547
2548 return FALSE;
2549 }
2550
2551
2552 //
2553 // GetWindowsErrorMsg() -- gives a pointer to a static buffer containing the Windows error message
2554 //
GetWindowsErrorMsg(DWORD code)2555 static LPTSTR GetWindowsErrorMsg(DWORD code)
2556 {
2557 static TCHAR lpMsgBuf[1024];
2558
2559 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
2560 NULL, code,
2561 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2562 (LPTSTR)lpMsgBuf, 1024, NULL);
2563
2564 return lpMsgBuf;
2565 }
2566
getwindowserrorstr(DWORD code)2567 static const char *getwindowserrorstr(DWORD code)
2568 {
2569 static char msg[1024];
2570 memset(msg, 0, sizeof(msg));
2571 OemToCharBuff(GetWindowsErrorMsg(code), msg, sizeof(msg)-1);
2572 return msg;
2573 }
2574