1 // SDL interface layer
2 // for the Build Engine
3 // by Jonathon Fowler (jf@jonof.id.au)
4 //
5 // Use SDL2 from http://www.libsdl.org
6 
7 /*
8  * Re: SDL surface blit vs renderer.
9  *
10  * Experiments on a Raspberry Pi 3 Model B+ have demonstrated that
11  * there's merit in both 8-bit mode blitting techniques depending on
12  * the Pi's configuration. With the GL driver disabled, the surface blit
13  * path is significantly faster. With the GL driver enabled, the SDL
14  * renderer interface is slightly quicker. However, faster than both is
15  * the forthcoming GLSL 2 blitter. Also, on a Mac Mini G4 with hampered
16  * AGP under Debian 8, surface blit is fast and the renderer slower.
17  */
18 //#define SDLAYER_USE_RENDERER
19 
20 // have stdio.h declare vasprintf
21 #ifndef _GNU_SOURCE
22 # define _GNU_SOURCE 1
23 #endif
24 
25 #if defined __APPLE__
26 # include <SDL2/SDL.h>
27 #else
28 # include "SDL.h"
29 #endif
30 
31 #if (SDL_MAJOR_VERSION != 2)
32 #  error This must be built with SDL2
33 #endif
34 
35 #include <stdlib.h>
36 #include <math.h>
37 
38 #include "build.h"
39 #include "sdlayer.h"
40 #include "cache1d.h"
41 #include "pragmas.h"
42 #include "a.h"
43 #include "osd.h"
44 #include "glbuild.h"
45 
46 #if defined(__APPLE__)
47 # include "osxbits.h"
48 #elif defined(HAVE_GTK)
49 # include "gtkbits.h"
50 #else
startwin_open(void)51 int startwin_open(void) { return 0; }
startwin_close(void)52 int startwin_close(void) { return 0; }
startwin_puts(const char * UNUSED (s))53 int startwin_puts(const char *UNUSED(s)) { return 0; }
startwin_idle(void * s)54 int startwin_idle(void *s) { return 0; }
startwin_settitle(const char * s)55 int startwin_settitle(const char *s) { s=s; return 0; }
56 #endif
57 
58 // undefine to restrict windowed resolutions to conventional sizes
59 #define ANY_WINDOWED_SIZE
60 
61 int   _buildargc = 1;
62 const char **_buildargv = NULL;
63 
64 char quitevent=0, appactive=1;
65 
66 static char apptitle[256] = "Build Engine";
67 static char wintitle[256] = "";
68 
69 // video
70 static SDL_Window *sdl_window;
71 #ifdef SDLAYER_USE_RENDERER
72 static SDL_Renderer *sdl_renderer;	// For non-GL 8-bit mode output.
73 static SDL_Texture *sdl_texture;	// For non-GL 8-bit mode output.
74 #else
75 static SDL_Surface *sdl_surface;	// For non-GL 8-bit mode output.
76 #endif
77 static unsigned char *frame;
78 int xres=-1, yres=-1, bpp=0, fullscreen=0, bytesperline, imageSize;
79 intptr_t frameplace=0;
80 char modechange=1;
81 char offscreenrendering=0;
82 char videomodereset = 0;
83 extern int gammabrightness;
84 extern float curgamma;
85 
86 #if USE_OPENGL
87 static SDL_GLContext sdl_glcontext;
88 static glbuild8bit gl8bit;
89 static char nogl=0;
90 static int glswapinterval = 1;
91 
92 static int set_glswapinterval(const osdfuncparm_t *parm);
93 #endif
94 
95 // input
96 int inputdevices=0;
97 char keystatus[256];
98 int keyfifo[KEYFIFOSIZ];
99 unsigned char keyasciififo[KEYFIFOSIZ];
100 int keyfifoplc, keyfifoend;
101 int keyasciififoplc, keyasciififoend;
102 static char keynames[256][24];
103 int mousex=0,mousey=0,mouseb=0;
104 int joyaxis[SDL_CONTROLLER_AXIS_MAX], joyb=0;
105 char joynumaxes=0, joynumbuttons=0;
106 static char mouseacquired=0,moustat=0;
107 static SDL_GameController *controller = NULL;
108 
109 void (*keypresscallback)(int,int) = 0;
110 void (*mousepresscallback)(int,int) = 0;
111 void (*joypresscallback)(int,int) = 0;
112 
113 struct keytranslate {
114 	unsigned char normal;
115 	unsigned char controlchar;  // an ASCII control character to insert into the character fifo
116 };
117 #define WITH_CONTROL_KEY 0x80
118 static struct keytranslate keytranslation[SDL_NUM_SCANCODES];
119 static int buildkeytranslationtable(void);
120 
121 static void shutdownvideo(void);
122 
123 #ifndef __APPLE__
124 static SDL_Surface * loadappicon(void);
125 #endif
126 
wm_msgbox(const char * name,const char * fmt,...)127 int wm_msgbox(const char *name, const char *fmt, ...)
128 {
129 	char *buf = NULL;
130 	int rv;
131 	va_list va;
132 
133 	if (!name) {
134 		name = apptitle;
135 	}
136 
137 	va_start(va,fmt);
138 	rv = vasprintf(&buf,fmt,va);
139 	va_end(va);
140 
141 	if (rv < 0) return -1;
142 
143 	do {
144 		rv = 0;
145 
146 #if defined HAVE_GTK
147 		if (wmgtk_msgbox(name, buf) >= 0) {
148 			rv = 1;
149 			break;
150 		}
151 #endif
152 		if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, name, buf, sdl_window) >= 0) {
153 			rv = 1;
154 			break;
155 		}
156 
157 		puts(buf);
158 	} while(0);
159 
160 	free(buf);
161 
162 	return rv;
163 }
164 
wm_ynbox(const char * name,const char * fmt,...)165 int wm_ynbox(const char *name, const char *fmt, ...)
166 {
167 	char *buf = NULL, ch;
168 	int rv;
169 	va_list va;
170 
171 	if (!name) {
172 		name = apptitle;
173 	}
174 
175 	va_start(va,fmt);
176 	rv = vasprintf(&buf,fmt,va);
177 	va_end(va);
178 
179 	if (rv < 0) return -1;
180 
181 	SDL_MessageBoxButtonData buttons[2] = {
182 		{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Yes" },
183 		{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "No" },
184 	};
185 	SDL_MessageBoxData msgbox = {
186 		SDL_MESSAGEBOX_INFORMATION,
187 		sdl_window,
188 		name,
189 		buf,
190 		SDL_arraysize(buttons),
191 		buttons,
192 		NULL
193 	};
194 
195 	do {
196 		rv = 0;
197 
198 #if defined HAVE_GTK
199 		if ((rv = wmgtk_ynbox(name, buf)) >= 0) {
200 			break;
201 		}
202 #endif
203 		if (SDL_ShowMessageBox(&msgbox, &rv) >= 0) {
204 			rv = (rv == 1);
205 			break;
206 		}
207 
208 		puts(buf);
209 		puts("   (assuming 'No')");
210 		rv = 0;
211 	} while(0);
212 
213 	free(buf);
214 
215 	return rv;
216 }
217 
wm_filechooser(const char * initialdir,const char * initialfile,const char * type,int foropen,char ** choice)218 int wm_filechooser(const char *initialdir, const char *initialfile, const char *type, int foropen, char **choice)
219 {
220 #if defined __APPLE__ || defined HAVE_GTK
221     int rv;
222     if (mouseacquired && moustat) {
223         SDL_SetRelativeMouseMode(SDL_FALSE);
224     }
225 #if defined __APPLE__
226     rv = wmosx_filechooser(initialdir, initialfile, type, foropen, choice);
227 #elif defined HAVE_GTK
228     rv = wmgtk_filechooser(initialdir, initialfile, type, foropen, choice);
229 #endif
230     SDL_RaiseWindow(sdl_window);
231     if (mouseacquired && moustat) {
232         SDL_SetRelativeMouseMode(SDL_TRUE);
233     }
234     return rv;
235 #endif
236 	return -1;
237 }
238 
wm_idle(void * ptr)239 int wm_idle(void *ptr)
240 {
241 #if defined HAVE_GTK
242     return wmgtk_idle(ptr);
243 #else
244     (void)ptr;
245     return 0;
246 #endif
247 }
248 
wm_setapptitle(const char * name)249 void wm_setapptitle(const char *name)
250 {
251 	if (name) {
252 		Bstrncpy(apptitle, name, sizeof(apptitle)-1);
253 		apptitle[ sizeof(apptitle)-1 ] = 0;
254 	}
255 #if defined HAVE_GTK
256 	wmgtk_setapptitle(apptitle);
257 #endif
258 }
259 
wm_setwindowtitle(const char * name)260 void wm_setwindowtitle(const char *name)
261 {
262 	if (name) {
263 		Bstrncpy(wintitle, name, sizeof(wintitle)-1);
264 		wintitle[ sizeof(wintitle)-1 ] = 0;
265 	}
266 
267 	if (sdl_window) {
268 		SDL_SetWindowTitle(sdl_window, wintitle);
269 	}
270 }
271 
272 
273 //
274 //
275 // ---------------------------------------
276 //
277 // System
278 //
279 // ---------------------------------------
280 //
281 //
282 
main(int argc,char * argv[])283 int main(int argc, char *argv[])
284 {
285 	int r;
286 
287 	buildkeytranslationtable();
288 
289 	// SDL must be initialised before GTK or else crashing will ensue.
290 	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
291 		buildprintf("Early initialisation of SDL failed! (%s)\n", SDL_GetError());
292 		return 1;
293 	}
294 
295 #ifdef HAVE_GTK
296 	wmgtk_init(&argc, &argv);
297 #endif
298 
299 #ifdef __APPLE__
300 	// consume Xcode's "-NSDocumentRevisionsDebugMode xx" parameter
301 	_buildargv = calloc(argc+1, sizeof(char *));
302 	for (r = _buildargc = 0; r < argc; r++) {
303 		if (strcmp(argv[r], "-NSDocumentRevisionsDebugMode") == 0) {
304 			r++;
305 		} else {
306 			_buildargv[_buildargc++] = argv[r];
307 		}
308 	}
309 	_buildargv[_buildargc] = 0;
310 #else
311 	_buildargc = argc;
312 	_buildargv = (const char **)argv;
313 #endif
314 
315 	startwin_open();
316 	baselayer_init();
317 
318 	// This avoids doubled character input in the (OSX) startup window's edit fields.
319 	SDL_EventState(SDL_TEXTINPUT, SDL_IGNORE);
320 
321 	r = app_main(_buildargc, (char const * const*)_buildargv);
322 
323 #ifdef __APPLE__
324 	free(_buildargv);
325 #endif
326 
327 	startwin_close();
328 #ifdef HAVE_GTK
329 	wmgtk_exit();
330 #endif
331 
332 	SDL_Quit();
333 
334 	return r;
335 }
336 
337 
338 //
339 // initsystem() -- init SDL systems
340 //
initsystem(void)341 int initsystem(void)
342 {
343 	SDL_version linked;
344 	SDL_version compiled;
345 
346 	SDL_GetVersion(&linked);
347 	SDL_VERSION(&compiled);
348 
349 	buildprintf("SDL2 system interface "
350 		  "(compiled with SDL version %d.%d.%d, runtime version %d.%d.%d)\n",
351 		linked.major, linked.minor, linked.patch,
352 		compiled.major, compiled.minor, compiled.patch);
353 
354 	atexit(uninitsystem);
355 
356 #if USE_OPENGL
357 	if (getenv("BUILD_NOGL")) {
358 		buildputs("OpenGL disabled.\n");
359 		nogl = 1;
360 	} else {
361 		nogl = loadgldriver(getenv("BUILD_GLDRV"));
362 		if (nogl) {
363 			buildputs("Failed loading OpenGL driver. GL modes will be unavailable.\n");
364 		}
365 	}
366 
367 	OSD_RegisterFunction("glswapinterval", "glswapinterval: frame swap interval for OpenGL modes (0 = no vsync, max 2)", set_glswapinterval);
368 #endif
369 
370 	return 0;
371 }
372 
373 
374 //
375 // uninitsystem() -- uninit SDL systems
376 //
uninitsystem(void)377 void uninitsystem(void)
378 {
379 	uninitinput();
380 	uninitmouse();
381 	uninittimer();
382 
383 	shutdownvideo();
384 #if USE_OPENGL
385 	glbuild_unloadfunctions();
386 	unloadgldriver();
387 #endif
388 }
389 
390 
391 //
392 // initputs() -- prints a string to the intitialization window
393 //
initputs(const char * str)394 void initputs(const char *str)
395 {
396 	startwin_puts(str);
397 	startwin_idle(NULL);
398 	wm_idle(NULL);
399 }
400 
401 
402 //
403 // debugprintf() -- prints a debug string to stderr
404 //
debugprintf(const char * f,...)405 void debugprintf(const char *f, ...)
406 {
407 #ifdef DEBUGGINGAIDS
408 	va_list va;
409 
410 	va_start(va,f);
411 	Bvfprintf(stderr, f, va);
412 	va_end(va);
413 #endif
414 }
415 
416 
417 //
418 //
419 // ---------------------------------------
420 //
421 // All things Input
422 //
423 // ---------------------------------------
424 //
425 //
426 
427 //
428 // initinput() -- init input system
429 //
initinput(void)430 int initinput(void)
431 {
432 	int i,j;
433 
434 	inputdevices = 1|2; // keyboard (1) and mouse (2)
435 	mouseacquired = 0;
436 
437 	memset(keynames,0,sizeof(keynames));
438 	for (i=0; i<SDL_NUM_SCANCODES; i++) {
439 		if (!keytranslation[i].normal) continue;
440 		strncpy(keynames[ keytranslation[i].normal ], SDL_GetScancodeName(i), sizeof(keynames[i])-1);
441 	}
442 
443 	if (!SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER)) {
444 		int flen, fh;
445 		char *dbuf = NULL;
446 		SDL_RWops *rwops = NULL;
447 
448 		// Load an available controller mappings file.
449 		fh = kopen4load("gamecontrollerdb.txt", 0);
450 		if (fh >= 0) {
451 			flen = kfilelength(fh);
452 			if (flen >= 0) {
453 				dbuf = (char *)malloc(flen + 1);
454 			}
455 		}
456 		if (dbuf) {
457 			flen = kread(fh, dbuf, flen);
458 			dbuf[flen+1] = 0;
459 			kclose(fh);
460 			if (flen >= 0) {
461 				rwops = SDL_RWFromConstMem(dbuf, flen);
462 			}
463 		}
464 		if (rwops) {
465 			i = SDL_GameControllerAddMappingsFromRW(rwops, 0);
466 			buildprintf("Added %d game controller mappings\n", i);
467 			free(dbuf);
468 			SDL_free(rwops);
469 		}
470 
471 		// Enumerate game controllers.
472 		if (SDL_NumJoysticks() < 1) {
473 			buildputs("No game controllers found\n");
474 		} else {
475 			int numjoysticks = SDL_NumJoysticks();
476 			buildputs("Game controllers:\n");
477 			for (i = 0; i < numjoysticks; i++) {
478 				if (SDL_IsGameController(i)) {
479 					buildprintf("  - %s\n", SDL_GameControllerNameForIndex(i));
480 					if (!controller) {
481 						controller = SDL_GameControllerOpen(i);
482 					}
483 				}
484 			}
485 			if (controller) {
486 				buildprintf("Using controller %s\n", SDL_GameControllerName(controller));
487 
488 				inputdevices |= 4;
489 				joynumaxes    = SDL_CONTROLLER_AXIS_MAX;
490 				joynumbuttons = SDL_CONTROLLER_BUTTON_MAX;
491 			} else {
492 				buildprintf("No controllers are usable\n");
493 			}
494 		}
495 	}
496 
497 	return 0;
498 }
499 
500 //
501 // uninitinput() -- uninit input system
502 //
uninitinput(void)503 void uninitinput(void)
504 {
505 	uninitmouse();
506 
507 	if (controller) {
508 		SDL_GameControllerClose(controller);
509 		controller = NULL;
510 	}
511 }
512 
getkeyname(int num)513 const char *getkeyname(int num)
514 {
515 	if ((unsigned)num >= 256) return NULL;
516 	return keynames[num];
517 }
518 
getjoyname(int what,int num)519 const char *getjoyname(int what, int num)
520 {
521 	switch (what) {
522 		case 0: // axis
523 			return SDL_GameControllerGetStringForAxis(num);
524 		case 1: // button
525 			return SDL_GameControllerGetStringForButton(num);
526 		default:
527 			return NULL;
528 	}
529 }
530 
531 //
532 // bgetchar, bkbhit, bflushchars -- character-based input functions
533 //
bgetchar(void)534 unsigned char bgetchar(void)
535 {
536 	unsigned char c;
537 	if (keyasciififoplc == keyasciififoend) return 0;
538 	c = keyasciififo[keyasciififoplc];
539 	keyasciififoplc = ((keyasciififoplc+1)&(KEYFIFOSIZ-1));
540 #ifdef __APPLE__
541 	if (c == SDL_SCANCODE_DELETE) c = SDL_SCANCODE_BACKSPACE;
542 #endif
543 	return c;
544 }
545 
bkbhit(void)546 int bkbhit(void)
547 {
548 	return (keyasciififoplc != keyasciififoend);
549 }
550 
bflushchars(void)551 void bflushchars(void)
552 {
553 	keyasciififoplc = keyasciififoend = 0;
554 }
555 
556 
557 //
558 // set{key|mouse|joy}presscallback() -- sets a callback which gets notified when keys are pressed
559 //
setkeypresscallback(void (* callback)(int,int))560 void setkeypresscallback(void (*callback)(int, int)) { keypresscallback = callback; }
setmousepresscallback(void (* callback)(int,int))561 void setmousepresscallback(void (*callback)(int, int)) { mousepresscallback = callback; }
setjoypresscallback(void (* callback)(int,int))562 void setjoypresscallback(void (*callback)(int, int)) { joypresscallback = callback; }
563 
564 //
565 // initmouse() -- init mouse input
566 //
initmouse(void)567 int initmouse(void)
568 {
569 	moustat=1;
570 	grabmouse(1);
571 	return 0;
572 }
573 
574 //
575 // uninitmouse() -- uninit mouse input
576 //
uninitmouse(void)577 void uninitmouse(void)
578 {
579 	grabmouse(0);
580 	moustat=0;
581 }
582 
583 
584 //
585 // grabmouse() -- show/hide mouse cursor
586 //
grabmouse(int a)587 void grabmouse(int a)
588 {
589 	if (appactive && moustat) {
590 		if (a != mouseacquired) {
591 			SDL_SetRelativeMouseMode(a ? SDL_TRUE : SDL_FALSE);
592 			mouseacquired = a;
593 		}
594 	} else {
595 		mouseacquired = a;
596 	}
597 	mousex = mousey = 0;
598 }
599 
600 
601 //
602 // readmousexy() -- return mouse motion information
603 //
readmousexy(int * x,int * y)604 void readmousexy(int *x, int *y)
605 {
606 	if (!mouseacquired || !appactive || !moustat) { *x = *y = 0; return; }
607 	*x = mousex;
608 	*y = mousey;
609 	mousex = mousey = 0;
610 }
611 
612 //
613 // readmousebstatus() -- return mouse button information
614 //
readmousebstatus(int * b)615 void readmousebstatus(int *b)
616 {
617 	if (!mouseacquired || !appactive || !moustat) *b = 0;
618 	else *b = mouseb;
619 	// clear mousewheel events - the game has them now (in *b)
620 	// the other mousebuttons are cleared when there's a "button released"
621 	// event, but for the mousewheel that doesn't work, as it's released immediately
622 	mouseb &= ~(1<<4 | 1<<5);
623 }
624 
625 //
626 // releaseallbuttons()
627 //
releaseallbuttons(void)628 void releaseallbuttons(void)
629 {
630 }
631 
632 
633 //
634 //
635 // ---------------------------------------
636 //
637 // All things Timer
638 // Ken did this
639 //
640 // ---------------------------------------
641 //
642 //
643 
644 static Uint64 timerfreq=0;
645 static Uint32 timerlastsample=0;
646 static Uint32 timerticspersec=0;
647 static void (*usertimercallback)(void) = NULL;
648 
649 //
650 // inittimer() -- initialise timer
651 //
inittimer(int tickspersecond)652 int inittimer(int tickspersecond)
653 {
654 	if (timerfreq) return 0;    // already installed
655 
656 	buildputs("Initialising timer\n");
657 
658 	timerfreq = SDL_GetPerformanceFrequency();
659 	timerticspersec = tickspersecond;
660 	timerlastsample = (Uint32)(SDL_GetPerformanceCounter() * timerticspersec / timerfreq);
661 
662 	usertimercallback = NULL;
663 
664 	return 0;
665 }
666 
667 //
668 // uninittimer() -- shut down timer
669 //
uninittimer(void)670 void uninittimer(void)
671 {
672 	if (!timerfreq) return;
673 
674 	timerfreq=0;
675 }
676 
677 //
678 // sampletimer() -- update totalclock
679 //
sampletimer(void)680 void sampletimer(void)
681 {
682 	int n;
683 
684 	if (!timerfreq) return;
685 
686 	n = (int)(SDL_GetPerformanceCounter() * timerticspersec / timerfreq) - timerlastsample;
687 	if (n>0) {
688 		totalclock += n;
689 		timerlastsample += n;
690 	}
691 
692 	if (usertimercallback) for (; n>0; n--) usertimercallback();
693 }
694 
695 //
696 // getticks() -- returns a millisecond ticks count
697 //
getticks(void)698 unsigned int getticks(void)
699 {
700 	return (unsigned int)SDL_GetTicks();
701 }
702 
703 //
704 // getusecticks() -- returns a microsecond ticks count
705 //
getusecticks(void)706 unsigned int getusecticks(void)
707 {
708 	return (unsigned int)SDL_GetTicks() * 1000;
709 }
710 
711 
712 //
713 // gettimerfreq() -- returns the number of ticks per second the timer is configured to generate
714 //
gettimerfreq(void)715 int gettimerfreq(void)
716 {
717 	return timerticspersec;
718 }
719 
720 
721 //
722 // installusertimercallback() -- set up a callback function to be called when the timer is fired
723 //
installusertimercallback(void (* callback)(void))724 void (*installusertimercallback(void (*callback)(void)))(void)
725 {
726 	void (*oldtimercallback)(void);
727 
728 	oldtimercallback = usertimercallback;
729 	usertimercallback = callback;
730 
731 	return oldtimercallback;
732 }
733 
734 
735 
736 //
737 //
738 // ---------------------------------------
739 //
740 // All things Video
741 //
742 // ---------------------------------------
743 //
744 //
745 
746 
747 //
748 // getvalidmodes() -- figure out what video modes are available
749 //
sortmodes(const struct validmode_t * a,const struct validmode_t * b)750 static int sortmodes(const struct validmode_t *a, const struct validmode_t *b)
751 {
752 	int x;
753 
754 	if ((x = a->fs   - b->fs)   != 0) return x;
755 	if ((x = a->bpp  - b->bpp)  != 0) return x;
756 	if ((x = a->xdim - b->xdim) != 0) return x;
757 	if ((x = a->ydim - b->ydim) != 0) return x;
758 
759 	return 0;
760 }
761 static char modeschecked=0;
getvalidmodes(void)762 void getvalidmodes(void)
763 {
764 	static int defaultres[][2] = {
765 		{1920,1200},{1920,1080},{1600,1200},{1680,1050},{1600,900},{1400,1050},{1440,900},{1366,768},
766 		{1280,1024},{1280,960},{1280,800},{1280,720},{1152,864},{1024,768},{800,600},{640,480},
767 		{640,400},{512,384},{480,360},{400,300},{320,240},{320,200},{0,0}
768 	};
769 	SDL_DisplayMode mode, desktop;
770 	int i, j, maxx=0, maxy=0;
771 
772 	if (modeschecked) return;
773 
774 	validmodecnt=0;
775 
776 	if (SDL_GetNumVideoDisplays() < 1) {
777 		buildputs("No video displays available!\n");
778 		return;
779 	}
780 
781 	buildputs("Detecting video modes:\n");
782 
783 #define ADDMODE(x,y,c,f) if (validmodecnt<MAXVALIDMODES) { \
784 	int mn; \
785 	for(mn=0;mn<validmodecnt;mn++) \
786 		if (validmode[mn].xdim==x && validmode[mn].ydim==y && \
787 			validmode[mn].bpp==c  && validmode[mn].fs==f) break; \
788 	if (mn==validmodecnt) { \
789 		validmode[validmodecnt].xdim=x; \
790 		validmode[validmodecnt].ydim=y; \
791 		validmode[validmodecnt].bpp=c; \
792 		validmode[validmodecnt].fs=f; \
793 		validmodecnt++; \
794 		buildprintf("  - %dx%d %d-bit %s\n", x, y, c, (f&1)?"fullscreen":"windowed"); \
795 	} \
796 }
797 
798 #define CHECKL(w,h) if ((w < maxx) && (h < maxy))
799 #define CHECKLE(w,h) if ((w <= maxx) && (h <= maxy))
800 
801 	SDL_GetDesktopDisplayMode(0, &desktop);
802 	maxx = desktop.w;
803 	maxy = desktop.h;
804 
805 	// Fullscreen 8-bit modes: upsamples to the desktop mode
806 	for (i=0; defaultres[i][0]; i++)
807 		CHECKLE(defaultres[i][0],defaultres[i][1])
808 			ADDMODE(defaultres[i][0],defaultres[i][1],8,1)
809 
810 #if USE_POLYMOST && USE_OPENGL
811 	// Fullscreen >8-bit modes
812 	if (!nogl) {
813 		for (j = SDL_GetNumDisplayModes(0) - 1; j >= 0; j--) {
814 			SDL_GetDisplayMode(0, j, &mode);
815 			if ((mode.w > MAXXDIM) || (mode.h > MAXYDIM)) continue;
816 			if (SDL_BITSPERPIXEL(mode.format) < 8) continue;
817 			ADDMODE(mode.w, mode.h, SDL_BITSPERPIXEL(mode.format), 1)
818 		}
819 	}
820 #endif
821 
822 	// Windowed 8-bit modes
823 	for (i=0; defaultres[i][0]; i++) {
824 		CHECKL(defaultres[i][0],defaultres[i][1]) {
825 			ADDMODE(defaultres[i][0], defaultres[i][1], 8, 0)
826 		}
827 	}
828 
829 #if USE_POLYMOST && USE_OPENGL
830 	// Windowed >8-bit modes
831 	if (!nogl) {
832 		for (i=0; defaultres[i][0]; i++) {
833 			CHECKL(defaultres[i][0],defaultres[i][1]) {
834 				ADDMODE(defaultres[i][0], defaultres[i][1], SDL_BITSPERPIXEL(desktop.format), 0)
835 			}
836 		}
837 	}
838 #endif
839 
840 #undef CHECK
841 #undef ADDMODE
842 
843 	qsort((void*)validmode, validmodecnt, sizeof(struct validmode_t), (int(*)(const void*,const void*))sortmodes);
844 
845 	modeschecked=1;
846 }
847 
848 
849 //
850 // checkvideomode() -- makes sure the video mode passed is legal
851 //
checkvideomode(int * x,int * y,int c,int fs,int forced)852 int checkvideomode(int *x, int *y, int c, int fs, int forced)
853 {
854 	int i, nearest=-1, dx, dy, odx=9999, ody=9999;
855 
856 	getvalidmodes();
857 
858 #if USE_POLYMOST && USE_OPENGL
859 	if (c > 8 && nogl) return -1;
860 #else
861 	if (c > 8) return -1;
862 #endif
863 
864 	// fix up the passed resolution values to be multiples of 8
865 	// and at least 320x200 or at most MAXXDIMxMAXYDIM
866 	if (*x < 320) *x = 320;
867 	if (*y < 200) *y = 200;
868 	if (*x > MAXXDIM) *x = MAXXDIM;
869 	if (*y > MAXYDIM) *y = MAXYDIM;
870 	*x &= 0xfffffff8l;
871 
872 	for (i=0; i<validmodecnt; i++) {
873 		if (validmode[i].bpp != c) continue;
874 		if (validmode[i].fs != fs) continue;
875 		dx = klabs(validmode[i].xdim - *x);
876 		dy = klabs(validmode[i].ydim - *y);
877 		if (!(dx | dy)) {   // perfect match
878 			nearest = i;
879 			break;
880 		}
881 		if ((dx <= odx) && (dy <= ody)) {
882 			nearest = i;
883 			odx = dx; ody = dy;
884 		}
885 	}
886 
887 #ifdef ANY_WINDOWED_SIZE
888 	if (!forced && (fs&1) == 0 && (nearest < 0 || (validmode[nearest].xdim!=*x || validmode[nearest].ydim!=*y)))
889 		return 0x7fffffffl;
890 #endif
891 
892 	if (nearest < 0) {
893 		// no mode that will match (eg. if no fullscreen modes)
894 		return -1;
895 	}
896 
897 	*x = validmode[nearest].xdim;
898 	*y = validmode[nearest].ydim;
899 
900 	return nearest;     // JBF 20031206: Returns the mode number
901 }
902 
903 
shutdownvideo(void)904 static void shutdownvideo(void)
905 {
906 	if (frame) {
907 		free(frame);
908 		frame = NULL;
909 	}
910 #if USE_OPENGL
911 	if (!nogl) {
912 		glbuild_delete_8bit_shader(&gl8bit);
913 	}
914 	if (sdl_glcontext) {
915 #if USE_POLYMOST
916 		polymost_glreset();
917 #endif
918 
919 		SDL_GL_DeleteContext(sdl_glcontext);
920 		sdl_glcontext = NULL;
921 	}
922 #endif
923 #ifdef SDLAYER_USE_RENDERER
924 	if (sdl_texture) {
925 		SDL_DestroyTexture(sdl_texture);
926 		sdl_texture = NULL;
927 	}
928 	if (sdl_renderer) {
929 		SDL_DestroyRenderer(sdl_renderer);
930 		sdl_renderer = NULL;
931 	}
932 #else
933 	if (sdl_surface) {
934 		SDL_FreeSurface(sdl_surface);
935 		sdl_surface = NULL;
936 	}
937 #endif
938 	if (sdl_window) {
939 		SDL_DestroyWindow(sdl_window);
940 		sdl_window = NULL;
941 	}
942 }
943 
944 //
945 // setvideomode() -- set SDL video mode
946 //
setvideomode(int x,int y,int c,int fs)947 int setvideomode(int x, int y, int c, int fs)
948 {
949 	int modenum, regrab = 0;
950 	int flags;
951 
952 	if ((fs == fullscreen) && (x == xres) && (y == yres) && (c == bpp) &&
953 		!videomodereset) {
954 		OSD_ResizeDisplay(xres,yres);
955 		return 0;
956 	}
957 
958 	if (checkvideomode(&x,&y,c,fs,0) < 0) return -1;	// Will return if GL mode not available.
959 
960 	if (mouseacquired) {
961 		regrab = 1;
962 		grabmouse(0);
963 	}
964 
965 	shutdownvideo();
966 
967 	buildprintf("Setting video mode %dx%d (%d-bpp %s)\n", x,y,c,
968 		(fs & 1) ? "fullscreen" : "windowed");
969 
970 	do {
971 		flags = SDL_WINDOW_HIDDEN;
972 
973 #if USE_OPENGL
974 		if (!nogl) {
975 #if (USE_OPENGL == USE_GLES2)
976 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
977 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
978 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
979 #elif (USE_OPENGL == USE_GL3)
980 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
981 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
982 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
983 #else
984 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
985 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
986 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
987 #endif
988 
989 			SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
990 			SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
991 #if USE_POLYMOST
992 			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, glmultisample > 0);
993 			SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, glmultisample);
994 #endif
995 
996 			flags |= SDL_WINDOW_OPENGL;
997 		}
998 #endif
999 
1000 		if (fs & 1) {
1001 			if (c > 8) flags |= SDL_WINDOW_FULLSCREEN;
1002 			else flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
1003 		}
1004 
1005 		sdl_window = SDL_CreateWindow(wintitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, x, y, flags);
1006 		if (!sdl_window) {
1007 			buildprintf("Error creating window: %s\n", SDL_GetError());
1008 
1009 #if USE_POLYMOST && USE_OPENGL
1010 			if (glmultisample > 0) {
1011 				buildprintf("Retrying without multisampling.\n");
1012 				glmultisample = 0;
1013 				continue;
1014 			}
1015 #endif
1016 
1017 			return -1;
1018 		}
1019 		break;
1020 	} while (1);
1021 
1022 #ifndef __APPLE__
1023 	{
1024 		SDL_Surface *icon = loadappicon();
1025 		SDL_SetWindowIcon(sdl_window, icon);
1026 		SDL_FreeSurface(icon);
1027 	}
1028 #endif
1029 
1030 	if (c == 8) {
1031 		int i, j, pitch;
1032 
1033 		// Round up to a multiple of 4.
1034 		pitch = (((x|1) + 4) & ~3);
1035 
1036 #if USE_OPENGL
1037 		if (nogl) {
1038 #endif
1039 #ifdef SDLAYER_USE_RENDERER
1040 			// 8-bit software with no GL shader blitting goes via the SDL rendering apparatus.
1041 			SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
1042 
1043 			sdl_renderer = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_PRESENTVSYNC);
1044 			if (!sdl_renderer) {
1045 				buildprintf("Error creating renderer: %s\n", SDL_GetError());
1046 				return -1;
1047 			}
1048 			SDL_RenderSetLogicalSize(sdl_renderer, x, y);
1049 			SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, 255);
1050 			SDL_RenderClear(sdl_renderer);
1051 
1052 			sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, x, y);
1053 			if (!sdl_texture) {
1054 				buildprintf("Error creating texture: %s\n", SDL_GetError());
1055 				return -1;
1056 			}
1057 #else
1058 			// 8-bit software with no GL shader blitting goes via the SDL rendering apparatus.
1059 			sdl_surface = SDL_CreateRGBSurface(0, x, y, 8, 0, 0, 0, 0);
1060 			if (!sdl_surface) {
1061 				buildprintf("Error creating surface: %s\n", SDL_GetError());
1062 				return -1;
1063 			}
1064 #endif
1065 #if USE_OPENGL
1066 		} else {
1067 			// Prepare the GLSL shader for 8-bit blitting.
1068 			sdl_glcontext = SDL_GL_CreateContext(sdl_window);
1069 			if (!sdl_glcontext) {
1070 				buildprintf("Error creating OpenGL context: %s\n", SDL_GetError());
1071 				nogl = 1;
1072 			} else if (baselayer_setupopengl()) {
1073 				nogl = 1;
1074 			} else if (glbuild_prepare_8bit_shader(&gl8bit, x, y, pitch) < 0) {
1075 				nogl = 1;
1076 			}
1077 			if (nogl) {
1078 				// Try again but without OpenGL.
1079 				buildputs("Falling back to non-OpenGL render.\n");
1080 				return setvideomode(x, y, c, fs);
1081 			}
1082 		}
1083 #endif
1084 
1085 		frame = (unsigned char *) malloc(pitch * y);
1086 		if (!frame) {
1087 			buildputs("Unable to allocate framebuffer\n");
1088 			return -1;
1089 		}
1090 
1091 		frameplace = (intptr_t) frame;
1092 		bytesperline = pitch;
1093 		imageSize = bytesperline * y;
1094 		numpages = 1;
1095 
1096 		setvlinebpl(bytesperline);
1097 		for (i = j = 0; i <= y; i++) {
1098 			ylookup[i] = j;
1099 			j += bytesperline;
1100 		}
1101 
1102 	} else {
1103 #if USE_OPENGL
1104 		sdl_glcontext = SDL_GL_CreateContext(sdl_window);
1105 		if (!sdl_glcontext) {
1106 			buildprintf("Error creating OpenGL context: %s\n", SDL_GetError());
1107 			return -1;
1108 		}
1109 
1110 		if (baselayer_setupopengl()) {
1111 			shutdownvideo();
1112 			return -1;
1113 		}
1114 #if USE_POLYMOST
1115 		polymost_glreset();
1116 #endif
1117 
1118 		frameplace = 0;
1119 		bytesperline = 0;
1120 		imageSize = 0;
1121 		numpages = 127;
1122 #else
1123 		return -1;
1124 #endif
1125 	}
1126 
1127 	SDL_ShowWindow(sdl_window);
1128 
1129 	xres = x;
1130 	yres = y;
1131 	bpp = c;
1132 	fullscreen = fs;
1133 	modechange = 1;
1134 	videomodereset = 0;
1135 	OSD_ResizeDisplay(xres,yres);
1136 #if USE_OPENGL
1137 	if (sdl_glcontext) {
1138 		if (SDL_GL_SetSwapInterval(glswapinterval) < 0) {
1139 			buildputs("note: OpenGL swap interval could not be changed\n");
1140 		}
1141 	}
1142 #endif
1143 
1144 	gammabrightness = (SDL_SetWindowBrightness(sdl_window, curgamma) == 0);
1145 
1146 	// setpalettefade will set the palette according to whether gamma worked
1147 	setpalettefade(palfadergb.r, palfadergb.g, palfadergb.b, palfadedelta);
1148 
1149 	if (regrab) grabmouse(1);
1150 
1151 	startwin_close();
1152 
1153 	// Start listening for character input.
1154 	SDL_EventState(SDL_TEXTINPUT, SDL_ENABLE);
1155 
1156 	return 0;
1157 }
1158 
1159 
1160 //
1161 // resetvideomode() -- resets the video system
1162 //
resetvideomode(void)1163 void resetvideomode(void)
1164 {
1165 	videomodereset = 1;
1166 	modeschecked = 0;
1167 }
1168 
1169 
1170 //
1171 // begindrawing() -- locks the framebuffer for drawing
1172 //
begindrawing(void)1173 void begindrawing(void)
1174 {
1175 }
1176 
1177 
1178 //
1179 // enddrawing() -- unlocks the framebuffer
1180 //
enddrawing(void)1181 void enddrawing(void)
1182 {
1183 }
1184 
1185 
1186 //
1187 // showframe() -- update the display
1188 //
showframe(void)1189 void showframe(void)
1190 {
1191 	int i,j;
1192 
1193 #if USE_OPENGL
1194 	if (!nogl) {
1195 		if (bpp == 8) {
1196 			glbuild_update_8bit_frame(&gl8bit, frame, xres, yres, bytesperline);
1197 			glbuild_draw_8bit_frame(&gl8bit);
1198 		}
1199 
1200 		SDL_GL_SwapWindow(sdl_window);
1201 		return;
1202 	}
1203 #endif
1204 
1205 	unsigned char *pixels, *in;
1206 	int pitch, y, x;
1207 
1208 #ifdef SDLAYER_USE_RENDERER
1209 	if (SDL_LockTexture(sdl_texture, NULL, (void**)&pixels, &pitch)) {
1210 		debugprintf("Could not lock texture: %s\n", SDL_GetError());
1211 		return;
1212 	}
1213 
1214 	in = frame;
1215 	for (y = yres - 1; y >= 0; y--) {
1216 		for (x = xres - 1; x >= 0; x--) {
1217 #if B_LITTLE_ENDIAN
1218 			// RGBA -> BGRA, ignoring A
1219 			/*
1220 			pixels[(x<<2)+0] = curpalettefaded[in[x]].b;
1221 			pixels[(x<<2)+1] = curpalettefaded[in[x]].g;
1222 			pixels[(x<<2)+2] = curpalettefaded[in[x]].r;
1223 			pixels[(x<<2)+3] = 0;
1224 			*/
1225 			((unsigned int *)pixels)[x] = B_SWAP32(*(unsigned int *)&curpalettefaded[in[x]]) >> 8;
1226 #else
1227 			pixels[(x<<2)+0] = 0;
1228 			pixels[(x<<2)+1] = curpalettefaded[in[x]].r;
1229 			pixels[(x<<2)+2] = curpalettefaded[in[x]].g;
1230 			pixels[(x<<2)+3] = curpalettefaded[in[x]].b;
1231 #endif
1232 		}
1233 		pixels += pitch;
1234 		in += bytesperline;
1235 	}
1236 
1237 	SDL_UnlockTexture(sdl_texture);
1238 	if (SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL)) {
1239 		debugprintf("Could not copy render texture: %s\n", SDL_GetError());
1240 	}
1241 	SDL_RenderPresent(sdl_renderer);
1242 
1243 #else //SDLAYER_USE_RENDERER
1244 	SDL_Surface *winsurface;
1245 
1246 	if (SDL_LockSurface(sdl_surface)) {
1247 		debugprintf("Could not lock surface: %s\n", SDL_GetError());
1248 		return;
1249 	}
1250 	pixels = (unsigned char *)sdl_surface->pixels;
1251 	pitch = sdl_surface->pitch;
1252 
1253 	in = frame;
1254 	for (y = yres - 1; y >= 0; y--) {
1255 		memcpy(pixels, in, xres);
1256 		pixels += pitch;
1257 		in += bytesperline;
1258 	}
1259 
1260 	SDL_UnlockSurface(sdl_surface);
1261 
1262 	winsurface = SDL_GetWindowSurface(sdl_window);
1263 	if (!winsurface) {
1264 		debugprintf("Could not get window surface: %s\n", SDL_GetError());
1265 		return;
1266 	}
1267 	SDL_BlitSurface(sdl_surface, NULL, winsurface, NULL);
1268 	SDL_UpdateWindowSurface(sdl_window);
1269 #endif //SDLAYER_USE_RENDERER
1270 }
1271 
1272 
1273 //
1274 // setpalette() -- set palette values
1275 //
setpalette(int UNUSED (start),int UNUSED (num),unsigned char * UNUSED (dapal))1276 int setpalette(int UNUSED(start), int UNUSED(num), unsigned char * UNUSED(dapal))
1277 {
1278 #if USE_OPENGL
1279 	if (!nogl) {
1280 		glbuild_update_8bit_palette(&gl8bit, curpalettefaded);
1281 	}
1282 #endif
1283 #ifndef SDLAYER_USE_RENDERER
1284 	if (sdl_surface) {
1285 		if (SDL_SetPaletteColors(sdl_surface->format->palette, (const SDL_Color *)curpalettefaded, 0, 256)) {
1286 			debugprintf("Could not set palette: %s\n", SDL_GetError());
1287 		}
1288 	}
1289 #endif
1290 	return 0;
1291 }
1292 
1293 //
1294 // setgamma
1295 //
setgamma(float gamma)1296 int setgamma(float gamma)
1297 {
1298 	if (sdl_window) {
1299 		return SDL_SetWindowBrightness(sdl_window, gamma) == 0;
1300 	}
1301 	return 0;
1302 }
1303 
1304 
1305 #if USE_OPENGL
1306 //
1307 // loadgldriver -- loads an OpenGL DLL
1308 //
loadgldriver(const char * soname)1309 int loadgldriver(const char *soname)
1310 {
1311 	const char *name = soname;
1312 	if (!name) {
1313 		name = "system OpenGL library";
1314 	}
1315 
1316 	buildprintf("Loading %s\n", name);
1317 	if (SDL_GL_LoadLibrary(soname)) return -1;
1318 	return 0;
1319 }
1320 
unloadgldriver(void)1321 int unloadgldriver(void)
1322 {
1323 	SDL_GL_UnloadLibrary();
1324 	return 0;
1325 }
1326 
1327 //
1328 // getglprocaddress
1329 //
getglprocaddress(const char * name,int UNUSED (ext))1330 void *getglprocaddress(const char *name, int UNUSED(ext))
1331 {
1332 	return (void*)SDL_GL_GetProcAddress(name);
1333 }
1334 #endif
1335 
1336 
1337 #ifndef __APPLE__
1338 extern struct sdlappicon sdlappicon;
loadappicon(void)1339 static SDL_Surface * loadappicon(void)
1340 {
1341 	SDL_Surface *surf;
1342 
1343 	surf = SDL_CreateRGBSurfaceFrom((void*)sdlappicon.pixels,
1344 			sdlappicon.width, sdlappicon.height, 32, sdlappicon.width*4,
1345 			0xffl,0xff00l,0xff0000l,0xff000000l);
1346 
1347 	return surf;
1348 }
1349 #endif
1350 
1351 //
1352 //
1353 // ---------------------------------------
1354 //
1355 // Miscellany
1356 //
1357 // ---------------------------------------
1358 //
1359 //
1360 
1361 
1362 //
1363 // handleevents() -- process the SDL message queue
1364 //   returns !0 if there was an important event worth checking (like quitting)
1365 //
handleevents(void)1366 int handleevents(void)
1367 {
1368 	int code, rv=0, j, control;
1369 	SDL_Event ev;
1370 	static int firstcall = 1;
1371 	int eattextinput = 0;
1372 
1373 #define SetKey(key,state) { \
1374 	keystatus[key] = state; \
1375 		if (state) { \
1376 	keyfifo[keyfifoend] = key; \
1377 	keyfifo[(keyfifoend+1)&(KEYFIFOSIZ-1)] = state; \
1378 	keyfifoend = ((keyfifoend+2)&(KEYFIFOSIZ-1)); \
1379 		} \
1380 }
1381 
1382 	while (SDL_PollEvent(&ev)) {
1383 		switch (ev.type) {
1384 			case SDL_TEXTINPUT:
1385 				if (eattextinput) {
1386 					eattextinput = 0;
1387 					break;
1388 				}
1389 				for (j = 0; j < SDL_TEXTINPUTEVENT_TEXT_SIZE && ev.text.text[j]; j++) {
1390 					if (ev.text.text[j] & 0x80) {
1391 						continue;   // UTF8 character byte
1392 					}
1393 					code = ev.text.text[j];
1394 					if (OSD_HandleChar(code)) {
1395 						if (((keyasciififoend+1)&(KEYFIFOSIZ-1)) != keyasciififoplc) {
1396 							keyasciififo[keyasciififoend] = code;
1397 							keyasciififoend = ((keyasciififoend+1)&(KEYFIFOSIZ-1));
1398 						}
1399 					}
1400 				}
1401 				break;
1402 
1403 			case SDL_KEYUP:
1404 				// (un)grab mouse with ctrl-g
1405 				if (ev.key.keysym.sym == SDLK_g
1406 					&& (ev.key.keysym.mod & KMOD_CTRL)) {
1407 					grabmouse(!mouseacquired);
1408 					break;
1409 				}
1410 				// else: fallthrough
1411 			case SDL_KEYDOWN:
1412 				code = keytranslation[ev.key.keysym.scancode].normal;
1413 				control = keytranslation[ev.key.keysym.scancode].controlchar;
1414 
1415 				if (control && ev.key.type == SDL_KEYDOWN) {
1416 					int needcontrol = (control & WITH_CONTROL_KEY) == WITH_CONTROL_KEY;
1417 					int mod = ev.key.keysym.mod & ~(KMOD_CAPS|KMOD_NUM);
1418 					control &= ~WITH_CONTROL_KEY;
1419 
1420 					// May need to insert a control character into the ascii input
1421 					// FIFO depending on what the state of the control keys are.
1422 					if ((needcontrol  && mod && (mod & KMOD_CTRL) == mod) ||
1423 						(!needcontrol && (mod == KMOD_NONE))) {
1424 						if (OSD_HandleChar(control)) {
1425 							if (((keyasciififoend+1)&(KEYFIFOSIZ-1)) != keyasciififoplc) {
1426 								keyasciififo[keyasciififoend] = control;
1427 								keyasciififoend = ((keyasciififoend+1)&(KEYFIFOSIZ-1));
1428 							}
1429 						}
1430 					}
1431 				}
1432 
1433 				// hook in the osd
1434 				if (code == OSD_CaptureKey(-1)) {
1435 					if (ev.key.type == SDL_KEYDOWN) {
1436 						// The character produced by the OSD toggle key needs to be ignored.
1437 						eattextinput = 1;
1438 
1439 						OSD_ShowDisplay(-1);
1440 					}
1441 					break;
1442 				} else if (OSD_HandleKey(code, (ev.key.type == SDL_KEYDOWN)) == 0)
1443 					break;
1444 
1445 				if (ev.key.type == SDL_KEYDOWN) {
1446 					if (!keystatus[code]) {
1447 						SetKey(code, 1);
1448 						if (keypresscallback)
1449 							keypresscallback(code, 1);
1450 					}
1451 				} else {
1452 					SetKey(code, 0);
1453 					if (keypresscallback)
1454 						keypresscallback(code, 0);
1455 				}
1456 				break;
1457 
1458 			case SDL_WINDOWEVENT:
1459 				if (ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED ||
1460 						ev.window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
1461 					appactive = ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED;
1462 					if (mouseacquired && moustat) {
1463 						SDL_SetRelativeMouseMode(appactive ? SDL_TRUE : SDL_FALSE);
1464 					}
1465 					rv=-1;
1466 				}
1467 				break;
1468 
1469 			case SDL_MOUSEBUTTONDOWN:
1470 			case SDL_MOUSEBUTTONUP:
1471 				switch (ev.button.button) {
1472 					case SDL_BUTTON_LEFT: j = 0; break;
1473 					case SDL_BUTTON_RIGHT: j = 1; break;
1474 					case SDL_BUTTON_MIDDLE: j = 2; break;
1475 					default: j = -1; break;
1476 				}
1477 				if (j<0) break;
1478 
1479 				if (ev.button.state == SDL_PRESSED)
1480 					mouseb |= (1<<j);
1481 				else
1482 					mouseb &= ~(1<<j);
1483 
1484 				if (mousepresscallback)
1485 					mousepresscallback(j+1, ev.button.state == SDL_PRESSED);
1486 				break;
1487 
1488 			case SDL_MOUSEWHEEL:
1489 				if (ev.wheel.y > 0) {   // Up
1490 					j = 5;
1491 				} else if (ev.wheel.y < 0) {    // Down
1492 					j = 4;
1493 				} else {
1494 					break;
1495 				}
1496 				mouseb |= 1<<j;
1497 				// 'release' is done in readmousebstatus()
1498 				if (mousepresscallback) {
1499 					mousepresscallback(j+1, 1);
1500 					mousepresscallback(j+1, 0);
1501 				}
1502 				break;
1503 
1504 			case SDL_MOUSEMOTION:
1505 				if (!firstcall) {
1506 					if (appactive) {
1507 						mousex += ev.motion.xrel;
1508 						mousey += ev.motion.yrel;
1509 					}
1510 				}
1511 				break;
1512 
1513 			case SDL_CONTROLLERAXISMOTION:
1514 				if (appactive) {
1515 					joyaxis[ ev.caxis.axis ] = ev.caxis.value;
1516 				}
1517 				break;
1518 
1519 			case SDL_CONTROLLERBUTTONDOWN:
1520 			case SDL_CONTROLLERBUTTONUP:
1521 				if (appactive) {
1522 					if (ev.cbutton.state == SDL_PRESSED)
1523 						joyb |= 1 << ev.cbutton.button;
1524 					else
1525 						joyb &= ~(1 << ev.cbutton.button);
1526 				}
1527 				break;
1528 
1529 			case SDL_QUIT:
1530 				quitevent = 1;
1531 				rv=-1;
1532 				break;
1533 
1534 			default:
1535 				//buildprintf("Got event (%d)\n", ev.type);
1536 				break;
1537 		}
1538 	}
1539 
1540 	sampletimer();
1541 	startwin_idle(NULL);
1542 	wm_idle(NULL);
1543 #undef SetKey
1544 
1545 	firstcall = 0;
1546 
1547 	return rv;
1548 }
1549 
1550 
buildkeytranslationtable(void)1551 static int buildkeytranslationtable(void)
1552 {
1553 	memset(keytranslation,0,sizeof(keytranslation));
1554 
1555 #define MAP(x,y) keytranslation[x].normal = y
1556 #define MAPC(x,y,c) keytranslation[x].normal = y, keytranslation[x].controlchar = c
1557 
1558 	MAPC(SDL_SCANCODE_BACKSPACE, 0xe, 0x8);
1559 	MAPC(SDL_SCANCODE_TAB,       0xf, 0x9);
1560 	MAPC(SDL_SCANCODE_RETURN,    0x1c, 0xd);
1561 	MAP(SDL_SCANCODE_PAUSE,     0x59);  // 0x1d + 0x45 + 0x9d + 0xc5
1562 	MAPC(SDL_SCANCODE_ESCAPE,    0x1, 0x1b);
1563 	MAP(SDL_SCANCODE_SPACE,     0x39);
1564 	MAP(SDL_SCANCODE_APOSTROPHE,     0x28);
1565 	MAP(SDL_SCANCODE_COMMA,     0x33);
1566 	MAP(SDL_SCANCODE_MINUS,     0xc);
1567 	MAP(SDL_SCANCODE_PERIOD,    0x34);
1568 	MAP(SDL_SCANCODE_SLASH,     0x35);
1569 	MAP(SDL_SCANCODE_0,     0xb);
1570 	MAP(SDL_SCANCODE_1,     0x2);
1571 	MAP(SDL_SCANCODE_2,     0x3);
1572 	MAP(SDL_SCANCODE_3,     0x4);
1573 	MAP(SDL_SCANCODE_4,     0x5);
1574 	MAP(SDL_SCANCODE_5,     0x6);
1575 	MAP(SDL_SCANCODE_6,     0x7);
1576 	MAP(SDL_SCANCODE_7,     0x8);
1577 	MAP(SDL_SCANCODE_8,     0x9);
1578 	MAP(SDL_SCANCODE_9,     0xa);
1579 	MAP(SDL_SCANCODE_SEMICOLON, 0x27);
1580 	MAP(SDL_SCANCODE_EQUALS,    0xd);
1581 	MAPC(SDL_SCANCODE_LEFTBRACKET,   0x1a, 0x1b | WITH_CONTROL_KEY);
1582 	MAPC(SDL_SCANCODE_BACKSLASH, 0x2b, 0x1c | WITH_CONTROL_KEY);
1583 	MAPC(SDL_SCANCODE_RIGHTBRACKET,  0x1b, 0x1d | WITH_CONTROL_KEY);
1584 	MAP(SDL_SCANCODE_GRAVE, 0x29);
1585 	MAPC(SDL_SCANCODE_A,     0x1e, 0x1 | WITH_CONTROL_KEY);
1586 	MAPC(SDL_SCANCODE_B,     0x30, 0x2 | WITH_CONTROL_KEY);
1587 	MAPC(SDL_SCANCODE_C,     0x2e, 0x3 | WITH_CONTROL_KEY);
1588 	MAPC(SDL_SCANCODE_D,     0x20, 0x4 | WITH_CONTROL_KEY);
1589 	MAPC(SDL_SCANCODE_E,     0x12, 0x5 | WITH_CONTROL_KEY);
1590 	MAPC(SDL_SCANCODE_F,     0x21, 0x6 | WITH_CONTROL_KEY);
1591 	MAPC(SDL_SCANCODE_G,     0x22, 0x7 | WITH_CONTROL_KEY);
1592 	MAPC(SDL_SCANCODE_H,     0x23, 0x8 | WITH_CONTROL_KEY);
1593 	MAPC(SDL_SCANCODE_I,     0x17, 0x9 | WITH_CONTROL_KEY);
1594 	MAPC(SDL_SCANCODE_J,     0x24, 0xa | WITH_CONTROL_KEY);
1595 	MAPC(SDL_SCANCODE_K,     0x25, 0xb | WITH_CONTROL_KEY);
1596 	MAPC(SDL_SCANCODE_L,     0x26, 0xc | WITH_CONTROL_KEY);
1597 	MAPC(SDL_SCANCODE_M,     0x32, 0xd | WITH_CONTROL_KEY);
1598 	MAPC(SDL_SCANCODE_N,     0x31, 0xe | WITH_CONTROL_KEY);
1599 	MAPC(SDL_SCANCODE_O,     0x18, 0xf | WITH_CONTROL_KEY);
1600 	MAPC(SDL_SCANCODE_P,     0x19, 0x10 | WITH_CONTROL_KEY);
1601 	MAPC(SDL_SCANCODE_Q,     0x10, 0x11 | WITH_CONTROL_KEY);
1602 	MAPC(SDL_SCANCODE_R,     0x13, 0x12 | WITH_CONTROL_KEY);
1603 	MAPC(SDL_SCANCODE_S,     0x1f, 0x13 | WITH_CONTROL_KEY);
1604 	MAPC(SDL_SCANCODE_T,     0x14, 0x14 | WITH_CONTROL_KEY);
1605 	MAPC(SDL_SCANCODE_U,     0x16, 0x15 | WITH_CONTROL_KEY);
1606 	MAPC(SDL_SCANCODE_V,     0x2f, 0x16 | WITH_CONTROL_KEY);
1607 	MAPC(SDL_SCANCODE_W,     0x11, 0x17 | WITH_CONTROL_KEY);
1608 	MAPC(SDL_SCANCODE_X,     0x2d, 0x18 | WITH_CONTROL_KEY);
1609 	MAPC(SDL_SCANCODE_Y,     0x15, 0x19 | WITH_CONTROL_KEY);
1610 	MAPC(SDL_SCANCODE_Z,     0x2c, 0x1a | WITH_CONTROL_KEY);
1611 	MAP(SDL_SCANCODE_DELETE,    0xd3);
1612 	MAP(SDL_SCANCODE_KP_0,       0x52);
1613 	MAP(SDL_SCANCODE_KP_1,       0x4f);
1614 	MAP(SDL_SCANCODE_KP_2,       0x50);
1615 	MAP(SDL_SCANCODE_KP_3,       0x51);
1616 	MAP(SDL_SCANCODE_KP_4,       0x4b);
1617 	MAP(SDL_SCANCODE_KP_5,       0x4c);
1618 	MAP(SDL_SCANCODE_KP_6,       0x4d);
1619 	MAP(SDL_SCANCODE_KP_7,       0x47);
1620 	MAP(SDL_SCANCODE_KP_8,       0x48);
1621 	MAP(SDL_SCANCODE_KP_9,       0x49);
1622 	MAP(SDL_SCANCODE_KP_PERIOD, 0x53);
1623 	MAP(SDL_SCANCODE_KP_DIVIDE, 0xb5);
1624 	MAP(SDL_SCANCODE_KP_MULTIPLY,   0x37);
1625 	MAP(SDL_SCANCODE_KP_MINUS,  0x4a);
1626 	MAP(SDL_SCANCODE_KP_PLUS,   0x4e);
1627 	MAPC(SDL_SCANCODE_KP_ENTER,  0x9c, 0xd);
1628 	MAP(SDL_SCANCODE_UP,        0xc8);
1629 	MAP(SDL_SCANCODE_DOWN,      0xd0);
1630 	MAP(SDL_SCANCODE_RIGHT,     0xcd);
1631 	MAP(SDL_SCANCODE_LEFT,      0xcb);
1632 	MAP(SDL_SCANCODE_INSERT,    0xd2);
1633 	MAP(SDL_SCANCODE_HOME,      0xc7);
1634 	MAP(SDL_SCANCODE_END,       0xcf);
1635 	MAP(SDL_SCANCODE_PAGEUP,    0xc9);
1636 	MAP(SDL_SCANCODE_PAGEDOWN,  0xd1);
1637 	MAP(SDL_SCANCODE_F1,        0x3b);
1638 	MAP(SDL_SCANCODE_F2,        0x3c);
1639 	MAP(SDL_SCANCODE_F3,        0x3d);
1640 	MAP(SDL_SCANCODE_F4,        0x3e);
1641 	MAP(SDL_SCANCODE_F5,        0x3f);
1642 	MAP(SDL_SCANCODE_F6,        0x40);
1643 	MAP(SDL_SCANCODE_F7,        0x41);
1644 	MAP(SDL_SCANCODE_F8,        0x42);
1645 	MAP(SDL_SCANCODE_F9,        0x43);
1646 	MAP(SDL_SCANCODE_F10,       0x44);
1647 	MAP(SDL_SCANCODE_F11,       0x57);
1648 	MAP(SDL_SCANCODE_F12,       0x58);
1649 	MAP(SDL_SCANCODE_NUMLOCKCLEAR,   0x45);
1650 	MAP(SDL_SCANCODE_CAPSLOCK,  0x3a);
1651 	MAP(SDL_SCANCODE_SCROLLLOCK, 0x46);
1652 	MAP(SDL_SCANCODE_RSHIFT,    0x36);
1653 	MAP(SDL_SCANCODE_LSHIFT,    0x2a);
1654 	MAP(SDL_SCANCODE_RCTRL,     0x9d);
1655 	MAP(SDL_SCANCODE_LCTRL,     0x1d);
1656 	MAP(SDL_SCANCODE_RALT,      0xb8);
1657 	MAP(SDL_SCANCODE_LALT,      0x38);
1658 	MAP(SDL_SCANCODE_LGUI,    0xdb);  // win l
1659 	MAP(SDL_SCANCODE_RGUI,    0xdc);  // win r
1660 	MAP(SDL_SCANCODE_PRINTSCREEN,     -2);    // 0xaa + 0xb7
1661 	MAP(SDL_SCANCODE_SYSREQ,    0x54);  // alt+printscr
1662 	MAP(SDL_SCANCODE_APPLICATION,      0xdd);  // win menu?
1663 
1664 	return 0;
1665 }
1666 
1667 #if USE_OPENGL
set_glswapinterval(const osdfuncparm_t * parm)1668 static int set_glswapinterval(const osdfuncparm_t *parm)
1669 {
1670 	int interval;
1671 
1672 	if (nogl) {
1673 		buildputs("glswapinterval is not adjustable\n");
1674 		return OSDCMD_OK;
1675 	}
1676 	if (parm->numparms == 0) {
1677 		buildprintf("glswapinterval = %d\n", glswapinterval);
1678 		return OSDCMD_OK;
1679 	}
1680 	if (parm->numparms != 1) return OSDCMD_SHOWHELP;
1681 
1682 	interval = Batol(parm->parms[0]);
1683 	if (interval < 0 || interval > 2) return OSDCMD_SHOWHELP;
1684 
1685 	if (SDL_GL_SetSwapInterval(interval) < 0) {
1686 		buildputs("note: OpenGL swap interval could not be changed\n");
1687 	} else {
1688 		glswapinterval = interval;
1689 	}
1690 	return OSDCMD_OK;
1691 }
1692 #endif
1693