1 // SDL interface layer for the Build Engine
2 // Use SDL 1.2 or 2.0 from http://www.libsdl.org
3 
4 #include <signal.h>
5 
6 #include "a.h"
7 #include "build.h"
8 #include "cache1d.h"
9 #include "compat.h"
10 #include "build_cpuid.h"
11 #include "engine_priv.h"
12 #include "osd.h"
13 #include "palette.h"
14 #include "renderlayer.h"
15 #include "sdl_inc.h"
16 #include "softsurface.h"
17 
18 #ifdef USE_OPENGL
19 # include "glad/glad.h"
20 # include "glbuild.h"
21 # include "glsurface.h"
22 #endif
23 
24 #if defined HAVE_GTK2
25 # include "gtkbits.h"
26 #endif
27 
28 #ifdef __ANDROID__
29 # include <android/log.h>
30 #elif defined __APPLE__
31 # include "osxbits.h"
32 # include <mach/mach.h>
33 # include <mach/mach_time.h>
34 #elif defined GEKKO
35 # include "wiibits.h"
36 # include <ogc/lwp.h>
37 # include <ogc/lwp_watchdog.h>
38 #elif defined _WIN32
39 # include "winbits.h"
40 #endif
41 
42 #include "vfs.h"
43 #include "communityapi.h"
44 
45 #define MICROPROFILE_IMPL
46 #include "microprofile.h"
47 
48 #if SDL_MAJOR_VERSION >= 2
49 static SDL_version linked;
50 #else
51 #define SDL_JoystickNameForIndex(x) SDL_JoystickName(x)
52 #endif
53 
54 #if !defined STARTUP_SETUP_WINDOW
startwin_open(void)55 int32_t startwin_open(void) { return 0; }
startwin_close(void)56 int32_t startwin_close(void) { return 0; }
startwin_puts(const char * s)57 int32_t startwin_puts(const char *s) { UNREFERENCED_PARAMETER(s); return 0; }
startwin_idle(void * s)58 int32_t startwin_idle(void *s) { UNREFERENCED_PARAMETER(s); return 0; }
startwin_settitle(const char * s)59 int32_t startwin_settitle(const char *s) { UNREFERENCED_PARAMETER(s); return 0; }
startwin_run(void)60 int32_t startwin_run(void) { return 0; }
61 #endif
62 
63 /// These can be useful for debugging sometimes...
64 //#define SDL_WM_GrabInput(x) SDL_WM_GrabInput(SDL_GRAB_OFF)
65 //#define SDL_ShowCursor(x) SDL_ShowCursor(SDL_ENABLE)
66 
67 #define SURFACE_FLAGS	(SDL_SWSURFACE|SDL_HWPALETTE|SDL_HWACCEL)
68 
69 // undefine to restrict windowed resolutions to conventional sizes
70 #define ANY_WINDOWED_SIZE
71 
72 // fix for mousewheel
73 int32_t inputchecked = 0;
74 
75 char quitevent=0, appactive=1, novideo=0;
76 
77 // video
78 static SDL_Surface *sdl_surface/*=NULL*/;
79 
80 #if SDL_MAJOR_VERSION >= 2
81 static SDL_Window *sdl_window=NULL;
82 static SDL_GLContext sdl_context=NULL;
83 #endif
84 
85 int32_t xres=-1, yres=-1, bpp=0, fullscreen=0, bytesperline;
86 double refreshfreq = 59.0;
87 intptr_t frameplace=0;
88 int32_t lockcount=0;
89 char modechange=1;
90 char offscreenrendering=0;
91 char videomodereset = 0;
92 int32_t nofog=0;
93 #ifndef EDUKE32_GLES
94 static uint16_t sysgamma[3][256];
95 #endif
96 #ifdef USE_OPENGL
97 // OpenGL stuff
98 char nogl=0;
99 #endif
100 static int32_t vsync_renderlayer;
101 static int vsync_unsupported;
102 int32_t maxrefreshfreq=0;
103 // last gamma, contrast, brightness
104 static float lastvidgcb[3];
105 
106 //#define KEY_PRINT_DEBUG
107 
108 #include "sdlkeytrans.cpp"
109 
110 static SDL_Surface *appicon = NULL;
111 #if !defined __APPLE__ && !defined EDUKE32_TOUCH_DEVICES
112 static SDL_Surface *loadappicon(void);
113 #endif
114 
115 static mutex_t m_initprintf;
116 
117 // Joystick dead and saturation zones
118 uint16_t joydead[9], joysatur[9];
119 
120 #ifdef _WIN32
121 # if SDL_MAJOR_VERSION >= 2
122 //
123 // win_gethwnd() -- gets the window handle
124 //
win_gethwnd(void)125 HWND win_gethwnd(void)
126 {
127     struct SDL_SysWMinfo wmInfo;
128     SDL_VERSION(&wmInfo.version);
129 
130     if (SDL_GetWindowWMInfo(sdl_window, &wmInfo) != SDL_TRUE)
131         return 0;
132 
133     if (wmInfo.subsystem == SDL_SYSWM_WINDOWS)
134         return wmInfo.info.win.window;
135 
136     initprintf("win_gethwnd: Unknown WM subsystem?!\n");
137 
138     return 0;
139 }
140 # endif
141 //
142 // win_gethinstance() -- gets the application instance
143 //
win_gethinstance(void)144 HINSTANCE win_gethinstance(void)
145 {
146     return (HINSTANCE)GetModuleHandle(NULL);
147 }
148 #endif
149 
150 
wm_msgbox(const char * name,const char * fmt,...)151 int32_t wm_msgbox(const char *name, const char *fmt, ...)
152 {
153     char buf[2048];
154     va_list va;
155 
156     UNREFERENCED_PARAMETER(name);
157 
158     va_start(va,fmt);
159     Bvsnprintf(buf,sizeof(buf),fmt,va);
160     va_end(va);
161 
162 #if defined EDUKE32_OSX
163     return osx_msgbox(name, buf);
164 #elif defined _WIN32
165     MessageBox(win_gethwnd(),buf,name,MB_OK|MB_TASKMODAL);
166     return 0;
167 #elif defined EDUKE32_TOUCH_DEVICES
168     initprintf("wm_msgbox called. Message: %s: %s",name,buf);
169     return 0;
170 #elif defined GEKKO
171     puts(buf);
172     return 0;
173 #else
174 # if defined HAVE_GTK2
175     if (gtkbuild_msgbox(name, buf) >= 0)
176         return 0;
177 # endif
178 # if SDL_MAJOR_VERSION >= 2
179 #  if !defined _WIN32
180     // Replace all tab chars with spaces because the hand-rolled SDL message
181     // box diplays the former as N/L instead of whitespace.
182     for (size_t i=0; i<sizeof(buf); i++)
183         if (buf[i] == '\t')
184             buf[i] = ' ';
185 #  endif
186     return SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, name, buf, NULL);
187 # else
188     puts(buf);
189     puts("   (press Return or Enter to continue)");
190     getchar();
191 
192     return 0;
193 # endif
194 #endif
195 }
196 
wm_ynbox(const char * name,const char * fmt,...)197 int32_t wm_ynbox(const char *name, const char *fmt, ...)
198 {
199     char buf[2048];
200     va_list va;
201 
202     UNREFERENCED_PARAMETER(name);
203 
204     va_start(va,fmt);
205     Bvsnprintf(buf,sizeof(buf),fmt,va);
206     va_end(va);
207 
208 #if defined EDUKE32_OSX
209     return osx_ynbox(name, buf);
210 #elif defined _WIN32
211     return (MessageBox(win_gethwnd(),buf,name,MB_YESNO|MB_ICONQUESTION|MB_TASKMODAL) == IDYES);
212 #elif defined EDUKE32_TOUCH_DEVICES
213     initprintf("wm_ynbox called, this is bad! Message: %s: %s",name,buf);
214     initprintf("Returning false..");
215     return 0;
216 #elif defined GEKKO
217     puts(buf);
218     puts("Assuming yes...");
219     return 1;
220 #else
221 # if defined HAVE_GTK2
222     int ret = gtkbuild_ynbox(name, buf);
223     if (ret >= 0)
224         return ret;
225 # endif
226 # if SDL_MAJOR_VERSION >= 2
227     int r = -1;
228 
229     const SDL_MessageBoxButtonData buttons[] = {
230         {
231             SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
232             0,
233             "No"
234         },{
235             SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
236             1,
237             "Yes"
238         },
239     };
240 
241     SDL_MessageBoxData data = {
242         SDL_MESSAGEBOX_INFORMATION,
243         NULL, /* no parent window */
244         name,
245         buf,
246         2,
247         buttons,
248         NULL /* Default color scheme */
249     };
250 
251     SDL_ShowMessageBox(&data, &r);
252 
253     return r;
254 # else
255     char c;
256 
257     puts(buf);
258     puts("   (type 'Y' or 'N', and press Return or Enter to continue)");
259     do c = getchar(); while (c != 'Y' && c != 'y' && c != 'N' && c != 'n');
260     return c == 'Y' || c == 'y';
261 # endif
262 #endif
263 }
264 
wm_setapptitle(const char * name)265 void wm_setapptitle(const char *name)
266 {
267 #ifndef EDUKE32_TOUCH_DEVICES
268     if (name != apptitle)
269         Bstrncpyz(apptitle, name, sizeof(apptitle));
270 
271 #if !defined(__APPLE__)
272     if (!appicon)
273         appicon = loadappicon();
274 #endif
275 
276 #if SDL_MAJOR_VERSION >= 2
277     if (sdl_window)
278     {
279         SDL_SetWindowTitle(sdl_window, apptitle);
280 
281         if (appicon)
282         {
283 #if defined _WIN32
284         if (!EDUKE32_SDL_LINKED_PREREQ(linked, 2, 0, 5))
285 #endif
286             SDL_SetWindowIcon(sdl_window, appicon);
287         }
288     }
289 #else
290     SDL_WM_SetCaption(apptitle, NULL);
291 
292     if (appicon && sdl_surface)
293         SDL_WM_SetIcon(appicon, 0);
294 #endif
295 
296     startwin_settitle(apptitle);
297 #else
298     UNREFERENCED_PARAMETER(name);
299 #endif
300 }
301 
302 //
303 //
304 // ---------------------------------------
305 //
306 // System
307 //
308 // ---------------------------------------
309 //
310 //
311 
312 /* XXX: libexecinfo could be used on systems without gnu libc. */
313 #if !defined _WIN32 && defined __GNUC__ && !defined __OpenBSD__ && !(defined __APPLE__ && defined __BIG_ENDIAN__) && !defined GEKKO && !defined EDUKE32_TOUCH_DEVICES && !defined __OPENDINGUX__
314 # define PRINTSTACKONSEGV 1
315 # include <execinfo.h>
316 #endif
317 
318 static inline char grabmouse_low(char a);
319 
320 #ifndef __ANDROID__
attach_debugger_here(void)321 static void attach_debugger_here(void)
322 {
323 #ifdef DEBUGGINGAIDS
324     debug_break();
325 #endif
326 }
327 
sighandler(int signum)328 static void sighandler(int signum)
329 {
330     UNREFERENCED_PARAMETER(signum);
331     //    if (signum==SIGSEGV)
332     {
333         grabmouse_low(0);
334 #if PRINTSTACKONSEGV
335         {
336             void *addr[32];
337             int32_t errfd = fileno(stderr);
338             int32_t n=backtrace(addr, ARRAY_SIZE(addr));
339             backtrace_symbols_fd(addr, n, errfd);
340         }
341         // This is useful for attaching the debugger post-mortem. For those pesky
342         // cases where the program runs through happily when inspected from the start.
343         //        usleep(15000000);
344 #endif
345         attach_debugger_here();
346         app_crashhandler();
347         Bexit(EXIT_FAILURE);
348     }
349 }
350 #endif
351 
352 #ifdef __ANDROID__
353 int mobile_halted = 0;
354 #ifdef __cplusplus
355 extern "C"
356 {
357 #endif
358 void G_Shutdown(void);
359 #ifdef __cplusplus
360 }
361 #endif
362 
sdlayer_mobilefilter(void * userdata,SDL_Event * event)363 int sdlayer_mobilefilter(void *userdata, SDL_Event *event)
364 {
365     switch (event->type)
366     {
367         case SDL_APP_TERMINATING:
368             // yes, this calls into the game, ugh
369             if (mobile_halted == 1)
370                 G_Shutdown();
371 
372             mobile_halted = 1;
373             return 0;
374         case SDL_APP_LOWMEMORY:
375             gltexinvalidatetype(INVALIDATE_ALL);
376             return 0;
377         case SDL_APP_WILLENTERBACKGROUND:
378             mobile_halted = 1;
379             return 0;
380         case SDL_APP_DIDENTERBACKGROUND:
381             gltexinvalidatetype(INVALIDATE_ALL);
382             // tear down video?
383             return 0;
384         case SDL_APP_WILLENTERFOREGROUND:
385             // restore video?
386             return 0;
387         case SDL_APP_DIDENTERFOREGROUND:
388             mobile_halted = 0;
389             return 0;
390         default:
391             return 1;//!halt;
392     }
393 
394     UNREFERENCED_PARAMETER(userdata);
395 }
396 
397 # include <setjmp.h>
398 static jmp_buf eduke32_exit_jmp_buf;
399 static int eduke32_return_value;
400 
eduke32_exit_return(int retval)401 void eduke32_exit_return(int retval)
402 {
403     eduke32_return_value = retval;
404     longjmp(eduke32_exit_jmp_buf, 1);
405     EDUKE32_UNREACHABLE_SECTION(return);
406 }
407 #endif
408 
sdlayer_sethints()409 void sdlayer_sethints()
410 {
411 #if defined _WIN32
412     // Thread naming interferes with debugging using MinGW-w64's GDB.
413 #if defined SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING
414     SDL_SetHint(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, "1");
415 #endif
416 #if defined SDL_HINT_XINPUT_ENABLED
417     if (!Bgetenv("EDUKE32_NO_XINPUT"))
418         SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "0");
419 #endif
420 #endif
421 
422 #if defined SDL_HINT_NO_SIGNAL_HANDLERS
423     SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
424 #endif
425 #if defined SDL_HINT_VIDEO_HIGHDPI_DISABLED
426     SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "1");
427 #endif
428 #if defined SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
429     SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "0");
430 #endif
431 #if defined SDL_HINT_AUDIO_RESAMPLING_MODE
432     SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "3");
433 #endif
434 #if defined SDL_HINT_MOUSE_RELATIVE_SCALING
435     SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_SCALING, "0");
436 #endif
437 }
438 
439 #ifdef _WIN32
WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nCmdShow)440 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
441 #elif defined __ANDROID__
442 # ifdef __cplusplus
443 extern "C" int eduke32_android_main(int argc, char const *argv[]);
444 # endif
445 int eduke32_android_main(int argc, char const *argv[])
446 #elif defined GEKKO
447 int SDL_main(int argc, char *argv[])
448 #else
449 int main(int argc, char *argv[])
450 #endif
451 {
452     MicroProfileOnThreadCreate("Main");
453     MicroProfileSetForceEnable(true);
454     MicroProfileSetEnableAllGroups(true);
455     MicroProfileSetForceMetaCounters(true);
456 
457 #ifdef __ANDROID__
458     if (setjmp(eduke32_exit_jmp_buf))
459     {
460         return eduke32_return_value;
461     }
462 #endif
463 
464     sdlayer_sethints();
465 
466 #ifdef USE_OPENGL
467     char *argp;
468 
469     if ((argp = Bgetenv("EDUKE32_NO_OPENGL_FOG")) != NULL)
470         nofog = Batol(argp);
471 
472 #if defined __linux__ || defined EDUKE32_BSD
473     if (!Bgetenv("EDUKE32_NO_GL_THREADED_OPTIMIZATIONS"))
474     {
475         if (!Bgetenv("EDUKE32_NO_NVIDIA_THREADED_OPTIMIZATIONS"))
476             setenv("__GL_THREADED_OPTIMIZATIONS", "1", 0);
477 
478         if (!Bgetenv("EDUKE32_NO_MESA_THREADED_OPTIMIZATIONS"))
479             setenv("mesa_glthread", "true", 0);
480     }
481 #endif
482 #endif
483 
484     buildkeytranslationtable();
485 
486 #ifndef __ANDROID__
487     signal(SIGSEGV, sighandler);
488     signal(SIGILL, sighandler);  /* clang -fcatch-undefined-behavior uses an ill. insn */
489     signal(SIGABRT, sighandler);
490     signal(SIGFPE, sighandler);
491 #else
492     SDL_SetEventFilter(sdlayer_mobilefilter, NULL);
493 #endif
494 
495 #ifdef _WIN32
496     UNREFERENCED_PARAMETER(hInst);
497     UNREFERENCED_PARAMETER(hPrevInst);
498     UNREFERENCED_PARAMETER(lpCmdLine);
499     UNREFERENCED_PARAMETER(nCmdShow);
500 
501     if (windowsPreInit())
502         return -1;
503 
504 #elif defined(GEKKO)
505     wii_open();
506 #elif defined(HAVE_GTK2)
507     // Pre-initialize SDL video system in order to make sure XInitThreads() is called
508     // before GTK starts talking to X11.
509     if (SDL_WasInit(SDL_INIT_VIDEO) != SDL_INIT_VIDEO)
510         SDL_InitSubSystem(SDL_INIT_VIDEO);
511 
512     gtkbuild_init(&argc, &argv);
513 #endif
514 
515 #ifdef EDUKE32_OSX
516     osx_preopen();
517 #endif
518     startwin_open();
519 #ifdef EDUKE32_OSX
520     osx_postopen();
521 #endif
522     maybe_redirect_outputs();
523 
524 #ifdef _WIN32
525     char *argvbuf;
526     int buildargc = windowsGetCommandLine(&argvbuf);
527     const char **buildargv = (const char **) Xmalloc(sizeof(char *)*(buildargc+1));
528     char *wp = argvbuf;
529 
530     for (bssize_t i=0; i<buildargc; i++, wp++)
531     {
532         buildargv[i] = wp;
533         while (*wp) wp++;
534     }
535     buildargv[buildargc] = NULL;
536 
537 #ifdef USE_PHYSFS
538     PHYSFS_init(buildargv[0]);
539     PHYSFS_setWriteDir(PHYSFS_getBaseDir());
540 #endif
541     int const r = app_main(buildargc, (const char **)buildargv);
542 #else
543 #ifdef USE_PHYSFS
544     int pfsi = PHYSFS_init(argv[0]);
545     assert(pfsi != 0);
546     PHYSFS_setWriteDir(PHYSFS_getUserDir());
547 #endif
548     int const r = app_main(argc, (char const * const *)argv);
549 #endif
550 
551     startwin_close();
552 
553 #if defined(HAVE_GTK2)
554     gtkbuild_exit(r);
555 #endif
556 
557     return r;
558 }
559 
560 
561 #if SDL_MAJOR_VERSION >= 2
sdlayer_getswapinterval(int const syncMode)562 static int sdlayer_getswapinterval(int const syncMode)
563 {
564     static int intervals[] = { -1, 0, 1, 0};
565     Bassert((unsigned)(syncMode + 1) < ARRAY_SIZE(intervals));
566     return intervals[syncMode + 1];
567 }
568 
sdlayer_checkvsync(int checkSync)569 static int sdlayer_checkvsync(int checkSync)
570 {
571     int const actualSync = SDL_GL_GetSwapInterval();
572     if (actualSync != sdlayer_getswapinterval(checkSync))
573     {
574         OSD_Printf("GL: driver enforcing SwapInterval %d, unable to configure VSync!\n", actualSync);
575         checkSync = actualSync;
576         vsync_unsupported = true;
577     }
578     return checkSync;
579 }
580 
videoSetVsync(int32_t newSync)581 int32_t videoSetVsync(int32_t newSync)
582 {
583     if (vsync_unsupported)
584     {
585         OSD_Printf("GL: VSync configuration locked by driver.\n");
586         return vsync_renderlayer;
587     }
588 
589     if (vsync_renderlayer == newSync)
590         return newSync;
591 
592 #ifdef USE_OPENGL
593     if (sdl_context)
594     {
595         int result = SDL_GL_SetSwapInterval(sdlayer_getswapinterval(newSync));
596 
597         if (result == -1)
598         {
599             if (newSync == -1)
600             {
601                 OSD_Printf("GL: driver rejected SwapInterval %d, unable to configure adaptive VSync!\n", sdlayer_getswapinterval(newSync));
602 
603                 newSync = 1;
604                 result  = SDL_GL_SetSwapInterval(sdlayer_getswapinterval(newSync));
605             }
606 
607             if (result == -1)
608             {
609                 OSD_Printf("GL: driver rejected SwapInterval %d, unable to configure VSync!\n", sdlayer_getswapinterval(newSync));
610                 newSync = 0;
611             }
612         }
613 
614         vsync_renderlayer = sdlayer_checkvsync(newSync);
615     }
616     else
617 #endif
618     {
619         vsync_renderlayer = newSync;
620 
621         videoResetMode();
622 
623         if (videoSetGameMode(fullscreen, xres, yres, bpp, upscalefactor))
624             OSD_Printf("restartvid: Reset failed...\n");
625     }
626 
627     return newSync;
628 }
629 #endif
630 
631 int32_t sdlayer_checkversion(void);
632 #if SDL_MAJOR_VERSION >= 2
sdlayer_checkversion(void)633 int32_t sdlayer_checkversion(void)
634 {
635     SDL_version compiled;
636 
637     SDL_GetVersion(&linked);
638     SDL_VERSION(&compiled);
639 
640     if (!Bmemcmp(&compiled, &linked, sizeof(SDL_version)))
641         initprintf("Initializing SDL %d.%d.%d\n",
642             compiled.major, compiled.minor, compiled.patch);
643     else
644     initprintf("Initializing SDL %d.%d.%d"
645                " (built against SDL version %d.%d.%d)\n",
646                linked.major, linked.minor, linked.patch, compiled.major, compiled.minor, compiled.patch);
647 
648     if (SDL_VERSIONNUM(linked.major, linked.minor, linked.patch) < SDL2_REQUIREDVERSION)
649     {
650         /*reject running under SDL versions older than what is stated in sdl_inc.h */
651         initprintf("You need at least v%d.%d.%d of SDL to run this game\n",SDL2_MIN_X,SDL2_MIN_Y,SDL2_MIN_Z);
652         return -1;
653     }
654 
655     return 0;
656 }
657 
658 //
659 // initsystem() -- init SDL systems
660 //
initsystem(void)661 int32_t initsystem(void)
662 {
663     mutex_init(&m_initprintf);
664 
665 #ifdef _WIN32
666     windowsPlatformInit();
667 #endif
668     sysReadCPUID();
669 
670     if (sdlayer_checkversion())
671         return -1;
672 
673     int32_t err = 0;
674 
675     if (SDL_WasInit(SDL_INIT_VIDEO) != SDL_INIT_VIDEO)
676         err = SDL_InitSubSystem(SDL_INIT_VIDEO);
677 
678     if (err)
679     {
680         initprintf("SDL initialization failed! (%s)\nNon-interactive mode enabled.  This is probably not what you want.\n", SDL_GetError());
681         novideo = 1;
682 #ifdef USE_OPENGL
683         nogl = 1;
684 #endif
685     }
686 
687 #if SDL_MAJOR_VERSION >= 2
688     SDL_StopTextInput();
689     SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
690 #endif
691 
692     timerInit(CLOCKTICKSPERSECOND);
693 
694     frameplace = 0;
695     lockcount = 0;
696 
697     if (!novideo)
698     {
699 #ifdef USE_OPENGL
700         if (SDL_GL_LoadLibrary(0))
701         {
702             initprintf("Failed loading OpenGL Driver.  GL modes will be unavailable. Error: %s\n", SDL_GetError());
703             nogl = 1;
704         }
705 #ifdef POLYMER
706         if (loadglulibrary(getenv("BUILD_GLULIB")))
707         {
708             initprintf("Failed loading GLU.  GL modes will be unavailable.\n");
709             nogl = 1;
710         }
711 #endif
712 #endif
713 
714 #ifndef _WIN32
715         const char *drvname = SDL_GetVideoDriver(0);
716 
717         if (drvname)
718             initprintf("Using \"%s\" video driver\n", drvname);
719 #endif
720         wm_setapptitle(apptitle);
721     }
722 
723     return 0;
724 }
725 #endif
726 
727 
728 //
729 // uninitsystem() -- uninit SDL systems
730 //
uninitsystem(void)731 void uninitsystem(void)
732 {
733     uninitinput();
734     timerUninit();
735 
736     if (appicon)
737     {
738         SDL_FreeSurface(appicon);
739         appicon = NULL;
740     }
741 
742 #ifdef _WIN32
743     windowsPlatformCleanup();
744 #endif
745 
746     SDL_Quit();
747 
748 #ifdef USE_OPENGL
749 # if SDL_MAJOR_VERSION >= 2
750     SDL_GL_UnloadLibrary();
751 # endif
752 # ifdef POLYMER
753     unloadglulibrary();
754 # endif
755 #endif
756 }
757 
758 
759 //
760 // system_getcvars() -- propagate any cvars that are read post-initialization
761 //
system_getcvars(void)762 void system_getcvars(void)
763 {
764 # ifdef _WIN32
765     windowsDwmSetupComposition(false);
766 # endif
767 
768     vsync = videoSetVsync(vsync);
769 }
770 
771 //
772 // initprintf() -- prints a formatted string to the initialization window
773 //
initprintf(const char * f,...)774 int initprintf(const char *f, ...)
775 {
776     va_list va;
777     char buf[2048];
778 
779     va_start(va, f);
780     int len = Bvsnprintf(buf, sizeof(buf), f, va);
781     va_end(va);
782 
783     osdstrings.append(Xstrdup(buf));
784     initputs(buf);
785 
786     return len;
787 }
788 
789 
790 //
791 // initputs() -- prints a string to the initialization window
792 //
initputs(const char * buf)793 void initputs(const char *buf)
794 {
795     static char dabuf[2048];
796 
797 #ifdef __ANDROID__
798     __android_log_print(ANDROID_LOG_INFO,"DUKE", "%s",buf);
799 #endif
800     OSD_Puts(buf);
801 //    Bprintf("%s", buf);
802 
803     mutex_lock(&m_initprintf);
804     if (Bstrlen(dabuf) + Bstrlen(buf) > 1022)
805     {
806         startwin_puts(dabuf);
807         Bmemset(dabuf, 0, sizeof(dabuf));
808     }
809 
810     Bstrcat(dabuf,buf);
811 
812     if (g_logFlushWindow || Bstrlen(dabuf) > 768)
813     {
814         startwin_puts(dabuf);
815 #ifndef _WIN32
816         startwin_idle(NULL);
817 #else
818         if (sdl_window)
819             handleevents();
820 #endif
821         Bmemset(dabuf, 0, sizeof(dabuf));
822     }
823     mutex_unlock(&m_initprintf);
824 }
825 
826 //
827 // debugprintf() -- prints a formatted debug string to stderr
828 //
debugprintf(const char * f,...)829 int debugprintf(const char *f, ...)
830 {
831 #if defined DEBUGGINGAIDS && !(defined __APPLE__ && defined __BIG_ENDIAN__)
832     va_list va;
833 
834     va_start(va,f);
835     int len = Bvfprintf(stderr, f, va);
836     va_end(va);
837     return len;
838 #else
839     UNREFERENCED_PARAMETER(f);
840     return 0;
841 #endif
842 }
843 
844 
845 //
846 //
847 // ---------------------------------------
848 //
849 // All things Input
850 //
851 // ---------------------------------------
852 //
853 //
854 
855 // static int32_t joyblast=0;
856 static SDL_Joystick *joydev = NULL;
857 #if SDL_MAJOR_VERSION >= 2
858 static SDL_GameController *controller = NULL;
859 
LoadSDLControllerDB()860 static void LoadSDLControllerDB()
861 {
862     buildvfs_kfd fh = kopen4load("gamecontrollerdb.txt", 0);
863     if (fh == buildvfs_kfd_invalid)
864         return;
865 
866     int const flen = kfilelength(fh);
867     if (flen <= 0)
868     {
869         kclose(fh);
870         return;
871     }
872 
873     char * dbuf = (char *)Xaligned_alloc(16, flen + 1);
874     if (!dbuf)
875     {
876         kclose(fh);
877         return;
878     }
879 
880     if (kread_and_test(fh, dbuf, flen))
881     {
882         Xaligned_free(dbuf);
883         kclose(fh);
884         return;
885     }
886 
887     dbuf[flen] = '\0';
888     kclose(fh);
889 
890     SDL_RWops * rwops = SDL_RWFromConstMem(dbuf, flen);
891     if (!rwops)
892     {
893         Xaligned_free(dbuf);
894         return;
895     }
896 
897     int i = SDL_GameControllerAddMappingsFromRW(rwops, 1);
898 
899     if (i == -1)
900         buildprintf("Failed loading game controller database: %s\n", SDL_GetError());
901     else
902         buildputs("Loaded game controller database\n");
903 
904     Xaligned_free(dbuf);
905 }
906 #endif
907 
joyScanDevices()908 void joyScanDevices()
909 {
910     inputdevices &= ~4;
911 
912 #if SDL_MAJOR_VERSION >= 2
913     if (controller)
914     {
915         SDL_GameControllerClose(controller);
916         controller = nullptr;
917     }
918 #endif
919     if (joydev)
920     {
921         SDL_JoystickClose(joydev);
922         joydev = nullptr;
923     }
924 
925     int numjoysticks = SDL_NumJoysticks();
926     if (numjoysticks < 1)
927     {
928         buildputs("No game controllers found\n");
929     }
930     else
931     {
932         buildputs("Game controllers:\n");
933         for (int i = 0; i < numjoysticks; i++)
934         {
935             const char * name;
936 #if SDL_MAJOR_VERSION >= 2
937             if (SDL_IsGameController(i))
938                 name = SDL_GameControllerNameForIndex(i);
939             else
940 #endif
941                 name = SDL_JoystickNameForIndex(i);
942 
943             buildprintf("  %d. %s\n", i+1, name);
944         }
945 
946 #if SDL_MAJOR_VERSION >= 2
947         for (int i = 0; i < numjoysticks; i++)
948         {
949             if ((controller = SDL_GameControllerOpen(i)))
950             {
951                 buildprintf("Using controller %s\n", SDL_GameControllerName(controller));
952 
953                 joystick.numAxes    = SDL_CONTROLLER_AXIS_MAX;
954                 joystick.numBalls   = 0;
955                 joystick.numButtons = SDL_CONTROLLER_BUTTON_MAX;
956                 joystick.numHats    = 0;
957 
958                 joystick.isGameController = 1;
959 
960                 Xfree(joystick.pAxis);
961                 joystick.pAxis = (int32_t *)Xcalloc(joystick.numAxes, sizeof(int32_t));
962                 Xfree(joystick.pHat);
963                 joystick.pHat = nullptr;
964 
965                 inputdevices |= 4;
966 
967                 return;
968             }
969         }
970 #endif
971 
972         for (int i = 0; i < numjoysticks; i++)
973         {
974             if ((joydev = SDL_JoystickOpen(i)))
975             {
976                 buildprintf("Using joystick %s\n", SDL_JoystickNameForIndex(i));
977 
978                 // KEEPINSYNC duke3d/src/gamedefs.h, mact/include/_control.h
979                 joystick.numAxes    = min(9, SDL_JoystickNumAxes(joydev));
980                 joystick.numBalls   = SDL_JoystickNumBalls(joydev);
981                 joystick.numButtons = min(32, SDL_JoystickNumButtons(joydev));
982                 joystick.numHats    = min((36 - joystick.numButtons) / 4, SDL_JoystickNumHats(joydev));
983 
984                 joystick.isGameController = 0;
985 
986                 buildprint("Joystick ", i+1, " has ", joystick.numAxes, " axes, ", joystick.numButtons, " buttons, ");
987                 if (joystick.numHats) buildprint(joystick.numHats); else buildprint("no");
988                 buildprint(" hats, and ");
989                 if (joystick.numBalls) buildprint(joystick.numBalls); else buildprint("no");
990                 buildprint(" balls.\n");
991 
992                 Xfree(joystick.pAxis);
993                 joystick.pAxis = (int32_t *)Xcalloc(joystick.numAxes, sizeof(int32_t));
994 
995                 Xfree(joystick.pHat);
996                 if (joystick.numHats)
997                     joystick.pHat = (int32_t *)Xcalloc(joystick.numHats, sizeof(int32_t));
998                 else
999                     joystick.pHat = nullptr;
1000 
1001                 for (int j = 0; j < joystick.numHats; j++)
1002                     joystick.pHat[j] = -1; // center
1003 
1004                 SDL_JoystickEventState(SDL_ENABLE);
1005                 inputdevices |= 4;
1006 
1007                 return;
1008             }
1009         }
1010 
1011         buildputs("No controllers are usable\n");
1012     }
1013 }
1014 
1015 //
1016 // initinput() -- init input system
1017 //
initinput(void)1018 int32_t initinput(void)
1019 {
1020     int32_t i;
1021 
1022 #if defined EDUKE32_OSX
1023     // force OS X to operate in >1 button mouse mode so that LMB isn't adulterated
1024     if (!getenv("SDL_HAS3BUTTONMOUSE"))
1025     {
1026         static char sdl_has3buttonmouse[] = "SDL_HAS3BUTTONMOUSE=1";
1027         putenv(sdl_has3buttonmouse);
1028     }
1029 #endif
1030 
1031     inputdevices = 1 | 2;  // keyboard (1) and mouse (2)
1032     g_mouseGrabbed = 0;
1033 
1034     memset(g_keyNameTable, 0, sizeof(g_keyNameTable));
1035 
1036 #if SDL_MAJOR_VERSION < 2
1037 #define SDL_SCANCODE_TO_KEYCODE(x) (SDLKey)(x)
1038 #define SDL_NUM_SCANCODES SDLK_LAST
1039     if (SDL_EnableKeyRepeat(250, 30))
1040         initprintf("Error enabling keyboard repeat.\n");
1041     SDL_EnableUNICODE(1);  // let's hope this doesn't hit us too hard
1042 #endif
1043 
1044     for (i = SDL_NUM_SCANCODES - 1; i >= 0; i--)
1045     {
1046         if (!keytranslation[i])
1047             continue;
1048 
1049         Bstrncpyz(g_keyNameTable[keytranslation[i]], SDL_GetKeyName(SDL_SCANCODE_TO_KEYCODE(i)), sizeof(g_keyNameTable[0]));
1050     }
1051 
1052 #if SDL_MAJOR_VERSION >= 2
1053     if (!SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER))
1054 #else
1055     if (!SDL_InitSubSystem(SDL_INIT_JOYSTICK))
1056 #endif
1057     {
1058 #if SDL_MAJOR_VERSION >= 2
1059         LoadSDLControllerDB();
1060 #endif
1061 
1062         joyScanDevices();
1063     }
1064 
1065     return 0;
1066 }
1067 
1068 //
1069 // uninitinput() -- uninit input system
1070 //
uninitinput(void)1071 void uninitinput(void)
1072 {
1073     mouseUninit();
1074 
1075 #if SDL_MAJOR_VERSION >= 2
1076     if (controller)
1077     {
1078         SDL_GameControllerClose(controller);
1079         controller = NULL;
1080     }
1081 #endif
1082 
1083     if (joydev)
1084     {
1085         SDL_JoystickClose(joydev);
1086         joydev = NULL;
1087     }
1088 }
1089 
1090 #ifndef GEKKO
joyGetName(int32_t what,int32_t num)1091 const char *joyGetName(int32_t what, int32_t num)
1092 {
1093     static char tmp[64];
1094 
1095     switch (what)
1096     {
1097         case 0:  // axis
1098             if ((unsigned)num > (unsigned)joystick.numAxes)
1099                 return NULL;
1100 
1101 #if SDL_MAJOR_VERSION >= 2
1102             if (controller)
1103             {
1104 # if 0
1105                 // Use this if SDL's provided strings ever become user-friendly.
1106                 return SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)num);
1107 # else
1108                 static char const * axisStrings[] =
1109                 {
1110                     "Left Stick X-Axis",
1111                     "Left Stick Y-Axis",
1112                     "Right Stick X-Axis",
1113                     "Right Stick Y-Axis",
1114                     "Left Trigger",
1115                     "Right Trigger",
1116                     NULL
1117                 };
1118                 return axisStrings[num];
1119 # endif
1120             }
1121 #endif
1122 
1123             Bsprintf(tmp, "Axis %d", num);
1124             return (char *)tmp;
1125 
1126         case 1:  // button
1127             if ((unsigned)num > (unsigned)joystick.numButtons)
1128                 return NULL;
1129 
1130 #if SDL_MAJOR_VERSION >= 2
1131             if (controller)
1132             {
1133 # if 0
1134                 // See above.
1135                 return SDL_GameControllerGetStringForButton((SDL_GameControllerButton)num);
1136 # else
1137                 static char const * buttonStrings[] =
1138                 {
1139                     "A",
1140                     "B",
1141                     "X",
1142                     "Y",
1143                     "Back",
1144                     "Guide",
1145                     "Start",
1146                     "Left Stick",
1147                     "Right Stick",
1148                     "Left Shoulder",
1149                     "Right Shoulder",
1150                     "D-Pad Up",
1151                     "D-Pad Down",
1152                     "D-Pad Left",
1153                     "D-Pad Right",
1154                     NULL
1155                 };
1156                 return buttonStrings[num];
1157 # endif
1158             }
1159 #endif
1160 
1161             Bsprintf(tmp, "Button %d", num);
1162             return (char *)tmp;
1163 
1164         case 2:  // hat
1165             if ((unsigned)num > (unsigned)joystick.numHats)
1166                 return NULL;
1167             Bsprintf(tmp, "Hat %d", num);
1168             return (char *)tmp;
1169 
1170         default: return NULL;
1171     }
1172 }
1173 #endif
1174 
1175 
1176 //
1177 // initmouse() -- init mouse input
1178 //
mouseInit(void)1179 void mouseInit(void)
1180 {
1181     mouseGrabInput(g_mouseEnabled = g_mouseLockedToWindow);  // FIXME - SA
1182 }
1183 
1184 //
1185 // uninitmouse() -- uninit mouse input
1186 //
mouseUninit(void)1187 void mouseUninit(void)
1188 {
1189     mouseGrabInput(0);
1190     g_mouseEnabled = 0;
1191 }
1192 
1193 
1194 #if SDL_MAJOR_VERSION >= 2
1195 //
1196 // grabmouse_low() -- show/hide mouse cursor, lower level (doesn't check state).
1197 //                    furthermore return 0 if successful.
1198 //
1199 
1200 #if defined _WIN32 && SDL_MINOR_VERSION == 0 && SDL_PATCHLEVEL < 13
1201 // bypass SDL_SetWindowGrab--see https://bugzilla.libsdl.org/show_bug.cgi?id=4748
SetWindowGrab(SDL_Window * pWindow,int const clipToWindow)1202 static void SetWindowGrab(SDL_Window *pWindow, int const clipToWindow)
1203 {
1204     UNREFERENCED_PARAMETER(pWindow);
1205     RECT rect { windowx, windowy, windowx + xdim, windowy + ydim };
1206     ClipCursor(clipToWindow ? &rect : nullptr);
1207 }
1208 #define SDL_SetWindowGrab SetWindowGrab
1209 #endif
grabmouse_low(char a)1210 static inline char grabmouse_low(char a)
1211 {
1212 #if !defined EDUKE32_TOUCH_DEVICES
1213     /* FIXME: Maybe it's better to make sure that grabmouse_low
1214        is called only when a window is ready?                */
1215     if (sdl_window)
1216         SDL_SetWindowGrab(sdl_window, a ? SDL_TRUE : SDL_FALSE);
1217     return SDL_SetRelativeMouseMode(a ? SDL_TRUE : SDL_FALSE);
1218 #else
1219     UNREFERENCED_PARAMETER(a);
1220     return 0;
1221 #endif
1222 }
1223 #undef SDL_SetWindowGrab
1224 #endif
1225 
1226 //
1227 // grabmouse() -- show/hide mouse cursor
1228 //
mouseGrabInput(bool grab)1229 void mouseGrabInput(bool grab)
1230 {
1231     if (appactive && g_mouseEnabled)
1232     {
1233 #if !defined EDUKE32_TOUCH_DEVICES
1234         if ((grab != g_mouseGrabbed) && !grabmouse_low(grab))
1235 #endif
1236             g_mouseGrabbed = grab;
1237     }
1238     else
1239         g_mouseGrabbed = grab;
1240 
1241     g_mousePos.x = g_mousePos.y = 0;
1242 }
1243 
mouseLockToWindow(char a)1244 void mouseLockToWindow(char a)
1245 {
1246     if (!(a & 2))
1247     {
1248         mouseGrabInput(a);
1249         g_mouseLockedToWindow = g_mouseGrabbed;
1250     }
1251 
1252     SDL_ShowCursor((osd && osd->flags & OSD_CAPTURE) ? SDL_ENABLE : SDL_DISABLE);
1253 }
1254 
mouseMoveToCenter(void)1255 void mouseMoveToCenter(void)
1256 {
1257 #if SDL_MAJOR_VERSION != 1
1258     if (sdl_window)
1259     {
1260         g_mouseAbs = { xres >> 1, yres >> 1 };
1261         SDL_WarpMouseInWindow(sdl_window, g_mouseAbs.x, g_mouseAbs.y);
1262     }
1263 #endif
1264 }
1265 
1266 //
1267 // setjoydeadzone() -- sets the dead and saturation zones for the joystick
1268 //
joySetDeadZone(int32_t axis,uint16_t dead,uint16_t satur)1269 void joySetDeadZone(int32_t axis, uint16_t dead, uint16_t satur)
1270 {
1271     joydead[axis] = dead;
1272     joysatur[axis] = satur;
1273 }
1274 
1275 
1276 //
1277 // getjoydeadzone() -- gets the dead and saturation zones for the joystick
1278 //
joyGetDeadZone(int32_t axis,uint16_t * dead,uint16_t * satur)1279 void joyGetDeadZone(int32_t axis, uint16_t *dead, uint16_t *satur)
1280 {
1281     *dead = joydead[axis];
1282     *satur = joysatur[axis];
1283 }
1284 
1285 
1286 //
1287 //
1288 // ---------------------------------------
1289 //
1290 // All things Video
1291 //
1292 // ---------------------------------------
1293 //
1294 //
1295 
1296 
1297 //
1298 // getvalidmodes() -- figure out what video modes are available
1299 //
sortmodes(const void * a_,const void * b_)1300 static int sortmodes(const void *a_, const void *b_)
1301 {
1302     auto a = (const struct validmode_t *)b_;
1303     auto b = (const struct validmode_t *)a_;
1304 
1305     int x;
1306 
1307     if ((x = a->fs   - b->fs)   != 0) return x;
1308     if ((x = a->bpp  - b->bpp)  != 0) return x;
1309     if ((x = a->xdim - b->xdim) != 0) return x;
1310     if ((x = a->ydim - b->ydim) != 0) return x;
1311 
1312     return 0;
1313 }
1314 
1315 static char modeschecked=0;
1316 
1317 #if SDL_MAJOR_VERSION >= 2
videoGetModes(void)1318 void videoGetModes(void)
1319 {
1320     int32_t i, maxx = 0, maxy = 0;
1321     SDL_DisplayMode dispmode;
1322     int const display = r_displayindex < SDL_GetNumVideoDisplays() ? r_displayindex : 0;
1323 
1324     if (modeschecked || novideo)
1325         return;
1326 
1327     validmodecnt = 0;
1328     //    initprintf("Detecting video modes:\n");
1329 
1330     // do fullscreen modes first
1331     for (i = 0; i < SDL_GetNumDisplayModes(display); i++)
1332     {
1333         SDL_GetDisplayMode(display, i, &dispmode);
1334 
1335         if (!SDL_CHECKMODE(dispmode.w, dispmode.h) ||
1336             (maxrefreshfreq && (dispmode.refresh_rate > maxrefreshfreq)))
1337             continue;
1338 
1339         // HACK: 8-bit == Software, 32-bit == OpenGL
1340         SDL_ADDMODE(dispmode.w, dispmode.h, 8, 1);
1341 #ifdef USE_OPENGL
1342         if (!nogl)
1343             SDL_ADDMODE(dispmode.w, dispmode.h, 32, 1);
1344 #endif
1345         if ((dispmode.w > maxx) || (dispmode.h > maxy))
1346         {
1347             maxx = dispmode.w;
1348             maxy = dispmode.h;
1349         }
1350     }
1351 
1352     SDL_CHECKFSMODES(maxx, maxy);
1353 
1354     // add windowed modes next
1355     // SDL sorts display modes largest to smallest, so we can just compare with mode 0
1356     // to make sure we aren't adding modes that are larger than the actual screen res
1357     SDL_GetDisplayMode(display, 0, &dispmode);
1358 
1359     for (i = 0; g_defaultVideoModes[i].x; i++)
1360     {
1361         auto const &mode = g_defaultVideoModes[i];
1362 
1363         if (mode.x > dispmode.w || mode.y > dispmode.h || !SDL_CHECKMODE(mode.x, mode.y))
1364             continue;
1365 
1366         // 8-bit == Software, 32-bit == OpenGL
1367         SDL_ADDMODE(mode.x, mode.y, 8, 0);
1368 
1369 #ifdef USE_OPENGL
1370         if (nogl)
1371             continue;
1372 
1373         SDL_ADDMODE(mode.x, mode.y, 32, 0);
1374 #endif
1375     }
1376 
1377     qsort((void *)validmode, validmodecnt, sizeof(struct validmode_t), &sortmodes);
1378 
1379     modeschecked = 1;
1380 }
1381 #endif
1382 
1383 //
1384 // checkvideomode() -- makes sure the video mode passed is legal
1385 //
videoCheckMode(int32_t * x,int32_t * y,int32_t c,int32_t fs,int32_t forced)1386 int32_t videoCheckMode(int32_t *x, int32_t *y, int32_t c, int32_t fs, int32_t forced)
1387 {
1388     int32_t i, nearest=-1, dx, dy, odx=9999, ody=9999;
1389 
1390     videoGetModes();
1391 
1392     if (c>8
1393 #ifdef USE_OPENGL
1394             && nogl
1395 #endif
1396        ) return -1;
1397 
1398     // fix up the passed resolution values to be multiples of 8
1399     // and at least 320x200 or at most MAXXDIMxMAXYDIM
1400     *x = clamp(*x, 320, MAXXDIM);
1401     *y = clamp(*y, 200, MAXYDIM);
1402 
1403     for (i = 0; i < validmodecnt; i++)
1404     {
1405         if (validmode[i].bpp != c || validmode[i].fs != fs)
1406             continue;
1407 
1408         dx = klabs(validmode[i].xdim - *x);
1409         dy = klabs(validmode[i].ydim - *y);
1410 
1411         if (!(dx | dy))
1412         {
1413             // perfect match
1414             nearest = i;
1415             break;
1416         }
1417 
1418         if ((dx <= odx) && (dy <= ody))
1419         {
1420             nearest = i;
1421             odx = dx;
1422             ody = dy;
1423         }
1424     }
1425 
1426 #ifdef ANY_WINDOWED_SIZE
1427     if (!forced && (fs&1) == 0 && (nearest < 0 || (validmode[nearest].xdim!=*x || validmode[nearest].ydim!=*y)))
1428         return 0x7fffffffl;
1429 #endif
1430 
1431     if (nearest < 0)
1432         return -1;
1433 
1434     *x = validmode[nearest].xdim;
1435     *y = validmode[nearest].ydim;
1436 
1437     return nearest;
1438 }
1439 
destroy_window_resources()1440 static void destroy_window_resources()
1441 {
1442 /* We should NOT destroy the window surface. This is done automatically
1443    when SDL_DestroyWindow or SDL_SetVideoMode is called.             */
1444 
1445 #if MICROPROFILE_ENABLED != 0
1446     MicroProfileGpuShutdown();
1447 #endif
1448 
1449 #if SDL_MAJOR_VERSION >= 2
1450     if (sdl_context)
1451         SDL_GL_DeleteContext(sdl_context);
1452     sdl_context = NULL;
1453     if (sdl_window)
1454         SDL_DestroyWindow(sdl_window);
1455     sdl_window = NULL;
1456 #endif
1457 }
1458 
1459 #ifdef USE_OPENGL
sdlayer_setvideomode_opengl(void)1460 void sdlayer_setvideomode_opengl(void)
1461 {
1462     glsurface_destroy();
1463     polymost_glreset();
1464 
1465     glShadeModel(GL_SMOOTH);  // GL_FLAT
1466     glClearColor(0, 0, 0, 1.0);  // Black Background
1467     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // Use FASTEST for ortho!
1468 //    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
1469 
1470 #ifndef EDUKE32_GLES
1471     glDisable(GL_DITHER);
1472 #endif
1473 
1474     fill_glinfo();
1475 
1476 }
1477 #endif  // defined USE_OPENGL
1478 
1479 //
1480 // setvideomode() -- set SDL video mode
1481 //
1482 
setvideomode_sdlcommon(int32_t * x,int32_t * y,int32_t c,int32_t fs,int32_t * regrab)1483 int32_t setvideomode_sdlcommon(int32_t *x, int32_t *y, int32_t c, int32_t fs, int32_t *regrab)
1484 {
1485     if ((fs == fullscreen) && (*x == xres) && (*y == yres) && (c == bpp) && !videomodereset)
1486         return 0;
1487 
1488     if (videoCheckMode(x, y, c, fs, 0) < 0)
1489         return -1;
1490 
1491 #ifdef GEKKO
1492     if (!sdl_surface) // only run this the first time we set a video mode
1493         wii_initgamevideo();
1494 #endif
1495 
1496     startwin_close();
1497 
1498     if (g_mouseGrabbed)
1499     {
1500         *regrab = 1;
1501         mouseGrabInput(0);
1502     }
1503 
1504     while (lockcount) videoEndDrawing();
1505 
1506 #ifdef USE_OPENGL
1507     if (sdl_surface)
1508     {
1509         if (bpp > 8)
1510             polymost_glreset();
1511     }
1512     if (!nogl)
1513     {
1514         if (bpp == 8)
1515             glsurface_destroy();
1516         if ((fs == fullscreen) && (*x == xres) && (*y == yres) && (bpp != 0) && !videomodereset)
1517             return 0;
1518     }
1519     else
1520 #endif
1521     {
1522        softsurface_destroy();
1523     }
1524 
1525     // clear last gamma/contrast/brightness so that it will be set anew
1526     lastvidgcb[0] = lastvidgcb[1] = lastvidgcb[2] = 0.0f;
1527 
1528     return 1;
1529 }
1530 
setvideomode_sdlcommonpost(int32_t x,int32_t y,int32_t c,int32_t fs,int32_t regrab)1531 void setvideomode_sdlcommonpost(int32_t x, int32_t y, int32_t c, int32_t fs, int32_t regrab)
1532 {
1533     wm_setapptitle(apptitle);
1534 
1535 #ifdef USE_OPENGL
1536     if (!nogl)
1537         sdlayer_setvideomode_opengl();
1538 #endif
1539 
1540     xres = x;
1541     yres = y;
1542     bpp = c;
1543     fullscreen = fs;
1544     // bytesperline = sdl_surface->pitch;
1545     numpages = c > 8 ? 2 : 1;
1546     frameplace = 0;
1547     lockcount = 0;
1548     modechange = 1;
1549     videomodereset = 0;
1550 
1551     // save the current system gamma to determine if gamma is available
1552 #ifndef EDUKE32_GLES
1553     if (!gammabrightness)
1554     {
1555         //        float f = 1.0 + ((float)curbrightness / 10.0);
1556 #if SDL_MAJOR_VERSION >= 2
1557         if (SDL_GetWindowGammaRamp(sdl_window, sysgamma[0], sysgamma[1], sysgamma[2]) == 0)
1558 #else
1559         if (SDL_GetGammaRamp(sysgamma[0], sysgamma[1], sysgamma[2]) >= 0)
1560 #endif
1561             gammabrightness = 1;
1562 
1563         // see if gamma really is working by trying to set the brightness
1564         if (gammabrightness && videoSetGamma() < 0)
1565             gammabrightness = 0;  // nope
1566     }
1567 #endif
1568 
1569     videoFadePalette(palfadergb.r, palfadergb.g, palfadergb.b, palfadedelta);
1570 
1571     if (regrab)
1572         mouseGrabInput(g_mouseLockedToWindow);
1573 }
1574 
1575 #if SDL_MAJOR_VERSION >= 2
setrefreshrate(void)1576 void setrefreshrate(void)
1577 {
1578     int const display = r_displayindex < SDL_GetNumVideoDisplays() ? r_displayindex : 0;
1579 
1580     SDL_DisplayMode dispmode;
1581     SDL_GetCurrentDisplayMode(display, &dispmode);
1582 
1583     dispmode.refresh_rate = maxrefreshfreq;
1584 
1585     SDL_DisplayMode newmode;
1586     SDL_GetClosestDisplayMode(display, &dispmode, &newmode);
1587 
1588     char error = 0;
1589 
1590     if (dispmode.refresh_rate != newmode.refresh_rate)
1591         error = SDL_SetWindowDisplayMode(sdl_window, &newmode);
1592 
1593     if (!newmode.refresh_rate || error)
1594         newmode.refresh_rate = 59;
1595 
1596 #ifdef _WIN32
1597     if (timingInfo.rateRefresh.uiNumerator)
1598         refreshfreq = (double)timingInfo.rateRefresh.uiNumerator / timingInfo.rateRefresh.uiDenominator;
1599     else
1600 #endif
1601         refreshfreq = newmode.refresh_rate;
1602 
1603     initprintf("Refresh rate: %.2fHz\n", refreshfreq);
1604 }
1605 
videoSetMode(int32_t x,int32_t y,int32_t c,int32_t fs)1606 int32_t videoSetMode(int32_t x, int32_t y, int32_t c, int32_t fs)
1607 {
1608     int32_t regrab = 0, ret;
1609 
1610     ret = setvideomode_sdlcommon(&x, &y, c, fs, &regrab);
1611 
1612     if (ret != 1)
1613     {
1614         if (ret == 0)
1615         {
1616             setvideomode_sdlcommonpost(x, y, c, fs, regrab);
1617         }
1618         return ret;
1619     }
1620 
1621     // deinit
1622     destroy_window_resources();
1623 
1624     initprintf("Setting video mode %dx%d (%d-bpp %s)\n", x, y, c, ((fs & 1) ? "fullscreen" : "windowed"));
1625 
1626     int const display = r_displayindex < SDL_GetNumVideoDisplays() ? r_displayindex : 0;
1627 
1628     SDL_DisplayMode desktopmode;
1629     SDL_GetDesktopDisplayMode(display, &desktopmode);
1630 
1631     int const matchedResolution = (desktopmode.w == x && desktopmode.h == y);
1632     int const borderless = (r_borderless == 1 || (r_borderless == 2 && matchedResolution)) ? SDL_WINDOW_BORDERLESS : 0;
1633 #ifdef USE_OPENGL
1634     if (c > 8 || !nogl)
1635     {
1636         int32_t i;
1637 
1638         if (nogl)
1639             return -1;
1640 
1641         struct glattribs
1642         {
1643             SDL_GLattr attr;
1644             int32_t value;
1645         } sdlayer_gl_attributes[] =
1646         {
1647 #ifdef EDUKE32_GLES
1648               { SDL_GL_CONTEXT_MAJOR_VERSION, 1 },
1649               { SDL_GL_CONTEXT_MINOR_VERSION, 1 },
1650 #endif
1651               { SDL_GL_DOUBLEBUFFER, 1 },
1652 
1653               { SDL_GL_STENCIL_SIZE, 1 },
1654               { SDL_GL_ACCELERATED_VISUAL, 1 },
1655           };
1656 
1657         SDL_GL_ATTRIBUTES(i, sdlayer_gl_attributes);
1658 
1659         /* HACK: changing SDL GL attribs only works before surface creation,
1660             so we have to create a new surface in a different format first
1661             to force the surface we WANT to be recreated instead of reused. */
1662 
1663 
1664         sdl_window = SDL_CreateWindow("", windowpos ? windowx : (int)SDL_WINDOWPOS_CENTERED_DISPLAY(display),
1665                                         windowpos ? windowy : (int)SDL_WINDOWPOS_CENTERED_DISPLAY(display), x, y,
1666                                         SDL_WINDOW_OPENGL | borderless);
1667 
1668         if (sdl_window)
1669             sdl_context = SDL_GL_CreateContext(sdl_window);
1670 
1671         if (!sdl_window || !sdl_context)
1672         {
1673             initprintf("Unable to set video mode: %s failed: %s\n", sdl_window ? "SDL_GL_CreateContext" : "SDL_GL_CreateWindow",  SDL_GetError());
1674             nogl = 1;
1675             destroy_window_resources();
1676             return -1;
1677         }
1678 
1679         gladLoadGLLoader(SDL_GL_GetProcAddress);
1680         if (GLVersion.major < 2)
1681         {
1682             initprintf("Your computer does not support OpenGL version 2 or greater. GL modes are unavailable.\n");
1683             nogl = 1;
1684             destroy_window_resources();
1685             return -1;
1686         }
1687 
1688         SDL_SetWindowFullscreen(sdl_window, ((fs & 1) ? (matchedResolution ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN) : 0));
1689         SDL_GL_SetSwapInterval(sdlayer_getswapinterval(vsync_renderlayer));
1690         vsync_renderlayer = sdlayer_checkvsync(vsync_renderlayer);
1691 
1692         setrefreshrate();
1693     }
1694     else
1695 #endif  // defined USE_OPENGL
1696     {
1697         // init
1698         sdl_window = SDL_CreateWindow("", windowpos ? windowx : (int)SDL_WINDOWPOS_CENTERED_DISPLAY(display),
1699                                       windowpos ? windowy : (int)SDL_WINDOWPOS_CENTERED_DISPLAY(display), x, y,
1700                                       borderless);
1701         if (!sdl_window)
1702             SDL2_VIDEO_ERR("SDL_CreateWindow");
1703 
1704         setrefreshrate();
1705 
1706         sdl_surface = SDL_GetWindowSurface(sdl_window);
1707         if (!sdl_surface)
1708             SDL2_VIDEO_ERR("SDL_GetWindowSurface");
1709 
1710         SDL_SetWindowFullscreen(sdl_window, ((fs & 1) ? (matchedResolution ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN) : 0));
1711     }
1712 
1713     setvideomode_sdlcommonpost(x, y, c, fs, regrab);
1714 
1715 #if MICROPROFILE_ENABLED != 0
1716     MicroProfileGpuInitGL();
1717 #endif
1718 
1719     return 0;
1720 }
1721 #endif
1722 
1723 //
1724 // resetvideomode() -- resets the video system
1725 //
videoResetMode(void)1726 void videoResetMode(void)
1727 {
1728     videomodereset = 1;
1729     modeschecked = 0;
1730 }
1731 
1732 //
1733 // begindrawing() -- locks the framebuffer for drawing
1734 //
1735 
1736 #ifdef DEBUG_FRAME_LOCKING
1737 uint32_t begindrawing_line[BEGINDRAWING_SIZE];
1738 const char *begindrawing_file[BEGINDRAWING_SIZE];
begindrawing_real(void)1739 void begindrawing_real(void)
1740 #else
1741 void videoBeginDrawing(void)
1742 #endif
1743 {
1744     if (bpp > 8)
1745     {
1746         if (offscreenrendering) return;
1747         frameplace = 0;
1748         bytesperline = 0;
1749         modechange = 0;
1750         return;
1751     }
1752 
1753     // lock the frame
1754     if (lockcount++ > 0)
1755         return;
1756 
1757     static intptr_t backupFrameplace = 0;
1758 
1759     if (inpreparemirror)
1760     {
1761         //POGO: if we are offscreenrendering and we need to render a mirror
1762         //      or we are rendering a mirror and we start offscreenrendering,
1763         //      backup our offscreen target so we can restore it later
1764         //      (but only allow one level deep,
1765         //       i.e. no viewscreen showing a camera showing a mirror that reflects the same viewscreen and recursing)
1766         if (offscreenrendering)
1767         {
1768             if (!backupFrameplace)
1769                 backupFrameplace = frameplace;
1770             else if (frameplace != (intptr_t)mirrorBuffer &&
1771                      frameplace != backupFrameplace)
1772                 return;
1773         }
1774 
1775         frameplace = (intptr_t)mirrorBuffer;
1776 
1777         if (offscreenrendering)
1778             return;
1779     }
1780     else if (offscreenrendering)
1781     {
1782         if (backupFrameplace)
1783         {
1784             frameplace = backupFrameplace;
1785             backupFrameplace = 0;
1786         }
1787         return;
1788     }
1789     else
1790 #ifdef USE_OPENGL
1791     if (!nogl)
1792     {
1793         frameplace = (intptr_t)glsurface_getBuffer();
1794     }
1795     else
1796 #endif
1797     {
1798         frameplace = (intptr_t)softsurface_getBuffer();
1799     }
1800 
1801     if (modechange)
1802     {
1803         bytesperline = xdim;
1804         calc_ylookup(bytesperline, ydim);
1805         modechange=0;
1806     }
1807 }
1808 
1809 
1810 //
1811 // enddrawing() -- unlocks the framebuffer
1812 //
videoEndDrawing(void)1813 void videoEndDrawing(void)
1814 {
1815     if (bpp > 8)
1816     {
1817         if (!offscreenrendering) frameplace = 0;
1818         return;
1819     }
1820 
1821     if (!frameplace) return;
1822     if (lockcount > 1) { lockcount--; return; }
1823     if (!offscreenrendering) frameplace = 0;
1824     if (lockcount == 0) return;
1825     lockcount = 0;
1826 }
1827 
1828 //
1829 // showframe() -- update the display
1830 //
1831 #if SDL_MAJOR_VERSION >= 2
1832 
1833 #ifdef __ANDROID__
1834 extern "C" void AndroidDrawControls();
1835 #endif
1836 
videoShowFrame(int32_t w)1837 void videoShowFrame(int32_t w)
1838 {
1839     UNREFERENCED_PARAMETER(w);
1840 
1841 #ifdef __ANDROID__
1842     if (mobile_halted) return;
1843 #endif
1844 
1845 #ifdef USE_OPENGL
1846     if (!nogl)
1847     {
1848         if (bpp > 8)
1849         {
1850             if (palfadedelta)
1851                 fullscreen_tint_gl(palfadergb.r, palfadergb.g, palfadergb.b, palfadedelta);
1852             fullscreen_tint_gl_blood();
1853 
1854 #ifdef __ANDROID__
1855             AndroidDrawControls();
1856 #endif
1857         }
1858         else
1859         {
1860             glsurface_blitBuffer();
1861         }
1862 
1863         if ((r_glfinish == 1 && r_finishbeforeswap == 1) || vsync_renderlayer == 2)
1864         {
1865             MICROPROFILE_SCOPEI("Engine", "glFinish", MP_GREEN);
1866             glFinish();
1867         }
1868 
1869 #ifdef _WIN32
1870         if (vsync_renderlayer == 2)
1871         {
1872             static uint64_t nextSwapTime = timerGetPerformanceCounter();
1873             uint64_t const  swapInterval = (timerGetPerformanceFrequency() / refreshfreq);
1874             uint64_t const  swapTime     = timerGetPerformanceCounter();
1875 
1876             // TODO: use timing information to determine swap time and just busy loop ourselves for more timing control
1877             if (swapTime < nextSwapTime)
1878             {
1879                 MICROPROFILE_SCOPEI("Engine", "waitForVBlank", MP_GREEN2);
1880                 windowsWaitForVBlank();
1881             }
1882             else if (swapTime - nextSwapTime >= swapInterval)
1883                 nextSwapTime += swapInterval;
1884 
1885             nextSwapTime += swapInterval;
1886         }
1887 #endif
1888 
1889         {
1890             MICROPROFILE_SCOPEI("Engine", "SDL_GL_SwapWindow", MP_GREEN3);
1891             SDL_GL_SwapWindow(sdl_window);
1892         }
1893 
1894         if (r_glfinish == 1 && r_finishbeforeswap == 0 && vsync_renderlayer != 2)
1895         {
1896             MICROPROFILE_SCOPEI("Engine", "glFinish2", MP_GREEN4);
1897             glFinish();
1898         }
1899 
1900         MicroProfileFlip();
1901 
1902         return;
1903     }
1904 #endif
1905 
1906     if (offscreenrendering) return;
1907 
1908     if (lockcount)
1909     {
1910         OSD_Printf("Frame still locked %d times when showframe() called.\n", lockcount);
1911         while (lockcount) videoEndDrawing();
1912     }
1913 
1914     if (SDL_MUSTLOCK(sdl_surface)) SDL_LockSurface(sdl_surface);
1915     softsurface_blitBuffer((uint32_t*) sdl_surface->pixels, sdl_surface->format->BitsPerPixel);
1916     if (SDL_MUSTLOCK(sdl_surface)) SDL_UnlockSurface(sdl_surface);
1917 
1918     if (SDL_UpdateWindowSurface(sdl_window))
1919     {
1920         // If a fullscreen X11 window is minimized then this may be required.
1921         // FIXME: What to do if this fails...
1922         sdl_surface = SDL_GetWindowSurface(sdl_window);
1923         SDL_UpdateWindowSurface(sdl_window);
1924     }
1925 
1926     MicroProfileFlip();
1927 }
1928 #endif
1929 //
1930 // setpalette() -- set palette values
1931 //
videoUpdatePalette(int32_t start,int32_t num)1932 int32_t videoUpdatePalette(int32_t start, int32_t num)
1933 {
1934     UNREFERENCED_PARAMETER(start);
1935     UNREFERENCED_PARAMETER(num);
1936 
1937     if (bpp > 8)
1938         return 0;  // no palette in opengl
1939 
1940 #ifdef USE_OPENGL
1941     if (!nogl)
1942         glsurface_setPalette(curpalettefaded);
1943     else
1944 #endif
1945     {
1946         if (sdl_surface)
1947             softsurface_setPalette(curpalettefaded,
1948                                    sdl_surface->format->Rmask,
1949                                    sdl_surface->format->Gmask,
1950                                    sdl_surface->format->Bmask);
1951     }
1952 
1953     return 0;
1954 }
1955 
1956 //
1957 // setgamma
1958 //
videoSetGamma(void)1959 int32_t videoSetGamma(void)
1960 {
1961     if (novideo)
1962         return 0;
1963 
1964     int32_t i;
1965     uint16_t gammaTable[768];
1966     float gamma = max(0.1f, min(4.f, g_videoGamma));
1967     float contrast = max(0.1f, min(3.f, g_videoContrast));
1968     float bright = max(-0.8f, min(0.8f, g_videoBrightness));
1969 
1970     float invgamma = 1.f / gamma;
1971     float norm = powf(255.f, invgamma - 1.f);
1972 
1973     if (lastvidgcb[0] == gamma && lastvidgcb[1] == contrast && lastvidgcb[2] == bright)
1974         return 0;
1975 
1976     // This formula is taken from Doomsday
1977 
1978     for (i = 0; i < 256; i++)
1979     {
1980         float val = i * contrast - (contrast - 1.f) * 127.f;
1981         if (gamma != 1.f)
1982             val = powf(val, invgamma) / norm;
1983 
1984         val += bright * 128.f;
1985 
1986         gammaTable[i] = gammaTable[i + 256] = gammaTable[i + 512] = (uint16_t)max(0.f, min(65535.f, val * 256.f));
1987     }
1988 
1989 #if SDL_MAJOR_VERSION >= 2
1990     i = INT32_MIN;
1991 
1992     if (sdl_window)
1993         i = SDL_SetWindowGammaRamp(sdl_window, &gammaTable[0], &gammaTable[256], &gammaTable[512]);
1994 #else
1995     i = SDL_SetGammaRamp(&gammaTable[0], &gammaTable[256], &gammaTable[512]);
1996     if (i != -1)
1997 #endif
1998 
1999     if (i < 0)
2000     {
2001         if (i != INT32_MIN)
2002             OSD_Printf("videoSetGamma(): %s\n", SDL_GetError());
2003 
2004 #ifndef EDUKE32_GLES
2005 #if SDL_MAJOR_VERSION >= 2
2006         if (sdl_window)
2007             SDL_SetWindowGammaRamp(sdl_window, &sysgamma[0][0], &sysgamma[1][0], &sysgamma[2][0]);
2008 #else
2009         SDL_SetGammaRamp(&sysgamma[0][0], &sysgamma[1][0], &sysgamma[2][0]);
2010 #endif
2011         gammabrightness = 0;
2012 #endif
2013     }
2014     else
2015     {
2016         lastvidgcb[0] = gamma;
2017         lastvidgcb[1] = contrast;
2018         lastvidgcb[2] = bright;
2019 
2020         gammabrightness = 1;
2021     }
2022 
2023     return i;
2024 }
2025 
2026 #if !defined __APPLE__ && !defined EDUKE32_TOUCH_DEVICES
2027 extern "C" struct sdlappicon sdlappicon;
loadappicon(void)2028 static inline SDL_Surface *loadappicon(void)
2029 {
2030     SDL_Surface *surf = SDL_CreateRGBSurfaceFrom((void *)sdlappicon.pixels, sdlappicon.width, sdlappicon.height, 32,
2031                                                  sdlappicon.width * 4, 0xffl, 0xff00l, 0xff0000l, 0xff000000l);
2032     return surf;
2033 }
2034 #endif
2035 
2036 //
2037 //
2038 // ---------------------------------------
2039 //
2040 // Miscellany
2041 //
2042 // ---------------------------------------
2043 //
2044 //
2045 
handleevents_peekkeys(void)2046 int32_t handleevents_peekkeys(void)
2047 {
2048     SDL_PumpEvents();
2049 
2050 #if SDL_MAJOR_VERSION >= 2
2051     return SDL_PeepEvents(NULL, 1, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_KEYDOWN);
2052 #else
2053     return SDL_PeepEvents(NULL, 1, SDL_PEEKEVENT, SDL_EVENTMASK(SDL_KEYDOWN));
2054 #endif
2055 }
2056 
handleevents_updatemousestate(uint8_t state)2057 void handleevents_updatemousestate(uint8_t state)
2058 {
2059     g_mouseClickState = state == SDL_RELEASED ? MOUSE_RELEASED : MOUSE_PRESSED;
2060 }
2061 
2062 
2063 //
2064 // handleevents() -- process the SDL message queue
2065 //   returns !0 if there was an important event worth checking (like quitting)
2066 //
2067 
handleevents_sdlcommon(SDL_Event * ev)2068 int32_t handleevents_sdlcommon(SDL_Event *ev)
2069 {
2070     switch (ev->type)
2071     {
2072 #if !defined EDUKE32_IOS
2073         case SDL_MOUSEMOTION:
2074 #ifndef GEKKO
2075             g_mouseAbs.x = ev->motion.x;
2076             g_mouseAbs.y = ev->motion.y;
2077             fallthrough__;
2078 #endif
2079         case SDL_JOYBALLMOTION:
2080             // SDL <VER> doesn't handle relative mouse movement correctly yet as the cursor still clips to the
2081             // screen edges
2082             // so, we call SDL_WarpMouse() to center the cursor and ignore the resulting motion event that occurs
2083             //  <VER> is 1.3 for PK, 1.2 for tueidj
2084             if (appactive && g_mouseGrabbed)
2085             {
2086 # if SDL_MAJOR_VERSION < 2
2087                 if (ev->motion.x != xdim >> 1 || ev->motion.y != ydim >> 1)
2088 # endif
2089                 {
2090                     g_mousePos.x += ev->motion.xrel;
2091                     g_mousePos.y += ev->motion.yrel;
2092 # if SDL_MAJOR_VERSION < 2
2093                     SDL_WarpMouse(xdim>>1, ydim>>1);
2094 # endif
2095                 }
2096             }
2097             break;
2098 
2099         case SDL_MOUSEBUTTONDOWN:
2100         case SDL_MOUSEBUTTONUP:
2101         {
2102             int32_t j;
2103 
2104             // some of these get reordered to match winlayer
2105             switch (ev->button.button)
2106             {
2107                 default: j = -1; break;
2108                 case SDL_BUTTON_LEFT: j = 0; handleevents_updatemousestate(ev->button.state); break;
2109                 case SDL_BUTTON_RIGHT: j = 1; break;
2110                 case SDL_BUTTON_MIDDLE: j = 2; break;
2111 
2112 #if SDL_MAJOR_VERSION < 2
2113                 case SDL_BUTTON_WHEELUP:    // 4
2114                 case SDL_BUTTON_WHEELDOWN:  // 5
2115                     j = ev->button.button;
2116                     break;
2117 #endif
2118                 /* Thumb buttons. */
2119 #if SDL_MAJOR_VERSION < 2
2120                 // NOTE: SDL1 does have SDL_BUTTON_X1, but that's not what is
2121                 // generated. (Only tested on Linux and Windows.)
2122                 case 8: j = 3; break;
2123                 case 9: j = 6; break;
2124 #else
2125                 // On SDL2/Windows and SDL >= 2.0.?/Linux, everything is as it should be.
2126                 // If anyone cares about old versions of SDL2 on Linux, patches welcome.
2127                 case SDL_BUTTON_X1: j = 3; break;
2128                 case SDL_BUTTON_X2: j = 6; break;
2129 #endif
2130             }
2131 
2132             if (j < 0)
2133                 break;
2134 
2135             if (ev->button.state == SDL_PRESSED)
2136                 g_mouseBits |= (1 << j);
2137             else
2138 #if SDL_MAJOR_VERSION < 2
2139                 if (j != SDL_BUTTON_WHEELUP && j != SDL_BUTTON_WHEELDOWN)
2140 #endif
2141                 g_mouseBits &= ~(1 << j);
2142 
2143             if (g_mouseCallback)
2144                 g_mouseCallback(j+1, ev->button.state == SDL_PRESSED);
2145             break;
2146         }
2147 #else
2148 # if SDL_MAJOR_VERSION >= 2
2149         case SDL_FINGERUP:
2150             g_mouseClickState = MOUSE_RELEASED;
2151             break;
2152         case SDL_FINGERDOWN:
2153             g_mouseClickState = MOUSE_PRESSED;
2154         case SDL_FINGERMOTION:
2155             g_mouseAbs.x = Blrintf(ev->tfinger.x * xdim);
2156             g_mouseAbs.y = Blrintf(ev->tfinger.y * ydim);
2157             break;
2158 # endif
2159 #endif
2160 
2161         case SDL_JOYAXISMOTION:
2162 #if SDL_MAJOR_VERSION >= 2
2163             if (joystick.isGameController)
2164                 break;
2165             fallthrough__;
2166         case SDL_CONTROLLERAXISMOTION:
2167 #endif
2168             if (appactive && ev->jaxis.axis < joystick.numAxes)
2169             {
2170                 joystick.pAxis[ev->jaxis.axis] = ev->jaxis.value;
2171                 int32_t const scaledValue = ev->jaxis.value * 10000 / 32767;
2172                 if ((scaledValue < joydead[ev->jaxis.axis]) &&
2173                     (scaledValue > -joydead[ev->jaxis.axis]))
2174                     joystick.pAxis[ev->jaxis.axis] = 0;
2175                 else if (scaledValue >= joysatur[ev->jaxis.axis])
2176                     joystick.pAxis[ev->jaxis.axis] = 32767;
2177                 else if (scaledValue <= -joysatur[ev->jaxis.axis])
2178                     joystick.pAxis[ev->jaxis.axis] = -32767;
2179                 else
2180                     joystick.pAxis[ev->jaxis.axis] = joystick.pAxis[ev->jaxis.axis] * 10000 / joysatur[ev->jaxis.axis];
2181             }
2182             break;
2183 
2184         case SDL_JOYHATMOTION:
2185         {
2186             int32_t hatvals[16] = {
2187                 -1,     // centre
2188                 0,      // up 1
2189                 9000,   // right 2
2190                 4500,   // up+right 3
2191                 18000,  // down 4
2192                 -1,     // down+up!! 5
2193                 13500,  // down+right 6
2194                 -1,     // down+right+up!! 7
2195                 27000,  // left 8
2196                 27500,  // left+up 9
2197                 -1,     // left+right!! 10
2198                 -1,     // left+right+up!! 11
2199                 22500,  // left+down 12
2200                 -1,     // left+down+up!! 13
2201                 -1,     // left+down+right!! 14
2202                 -1,     // left+down+right+up!! 15
2203             };
2204             if (appactive && ev->jhat.hat < joystick.numHats)
2205                 joystick.pHat[ev->jhat.hat] = hatvals[ev->jhat.value & 15];
2206             break;
2207         }
2208 
2209         case SDL_JOYBUTTONDOWN:
2210         case SDL_JOYBUTTONUP:
2211 #if SDL_MAJOR_VERSION >= 2
2212             if (joystick.isGameController)
2213                 break;
2214             fallthrough__;
2215         case SDL_CONTROLLERBUTTONDOWN:
2216         case SDL_CONTROLLERBUTTONUP:
2217 #endif
2218             if (appactive && ev->jbutton.button < joystick.numButtons)
2219             {
2220                 if (ev->jbutton.state == SDL_PRESSED)
2221                     joystick.bits |= 1 << ev->jbutton.button;
2222                 else
2223                     joystick.bits &= ~(1 << ev->jbutton.button);
2224 
2225 #ifdef GEKKO
2226                 if (ev->jbutton.button == 0) // WII_A
2227                     handleevents_updatemousestate(ev->jbutton.state);
2228 #endif
2229             }
2230             break;
2231 
2232         case SDL_QUIT:
2233             quitevent = 1;
2234             return -1;
2235     }
2236 
2237     return 0;
2238 }
2239 
2240 int32_t handleevents_pollsdl(void);
2241 #if SDL_MAJOR_VERSION >= 2
2242 // SDL 2.0 specific event handling
handleevents_pollsdl(void)2243 int32_t handleevents_pollsdl(void)
2244 {
2245     int32_t code, rv=0, j;
2246     SDL_Event ev;
2247 
2248     while (SDL_PollEvent(&ev))
2249     {
2250         switch (ev.type)
2251         {
2252             case SDL_TEXTINPUT:
2253                 j = 0;
2254                 do
2255                 {
2256                     code = ev.text.text[j];
2257 
2258                     if (code != g_keyAsciiTable[OSD_OSDKey()] && !keyBufferFull())
2259                     {
2260                         if (OSD_HandleChar(code))
2261                             keyBufferInsert(code);
2262                     }
2263                 } while (j < SDL_TEXTINPUTEVENT_TEXT_SIZE-1 && ev.text.text[++j]);
2264                 break;
2265 
2266             case SDL_KEYDOWN:
2267             case SDL_KEYUP:
2268             {
2269                 auto const &sc = ev.key.keysym.scancode;
2270                 code = keytranslation[sc];
2271 
2272                 // Modifiers that have to be held down to be effective
2273                 // (excludes KMOD_NUM, for example).
2274                 static const int MODIFIERS =
2275                     KMOD_LSHIFT|KMOD_RSHIFT|KMOD_LCTRL|KMOD_RCTRL|
2276                     KMOD_LALT|KMOD_RALT|KMOD_LGUI|KMOD_RGUI;
2277 
2278                 // XXX: see osd.c, OSD_HandleChar(), there are more...
2279                 if (ev.key.type == SDL_KEYDOWN && !keyBufferFull() &&
2280                     (sc == SDL_SCANCODE_RETURN || sc == SDL_SCANCODE_KP_ENTER ||
2281                      sc == SDL_SCANCODE_ESCAPE ||
2282                      sc == SDL_SCANCODE_BACKSPACE ||
2283                      sc == SDL_SCANCODE_TAB ||
2284                      (((ev.key.keysym.mod) & MODIFIERS) == KMOD_LCTRL &&
2285                       (sc >= SDL_SCANCODE_A && sc <= SDL_SCANCODE_Z))))
2286                 {
2287                     char keyvalue;
2288                     switch (sc)
2289                     {
2290                         case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: keyvalue = '\r'; break;
2291                         case SDL_SCANCODE_ESCAPE: keyvalue = 27; break;
2292                         case SDL_SCANCODE_BACKSPACE: keyvalue = '\b'; break;
2293                         case SDL_SCANCODE_TAB: keyvalue = '\t'; break;
2294                         default: keyvalue = sc - SDL_SCANCODE_A + 1; break;  // Ctrl+A --> 1, etc.
2295                     }
2296                     if (OSD_HandleChar(keyvalue))
2297                         keyBufferInsert(keyvalue);
2298                 }
2299                 else if (ev.key.type == SDL_KEYDOWN &&
2300                          ev.key.keysym.sym != g_keyAsciiTable[OSD_OSDKey()] && !keyBufferFull() &&
2301                          !SDL_IsTextInputActive())
2302                 {
2303                     /*
2304                     Necessary for Duke 3D's method of entering cheats to work without showing IMEs.
2305                     SDL_TEXTINPUT is preferable overall, but with bitmap fonts it has no advantage.
2306                     */
2307                     SDL_Keycode keyvalue = ev.key.keysym.sym;
2308 
2309                     if ('a' <= keyvalue && keyvalue <= 'z')
2310                     {
2311                         if (!!(ev.key.keysym.mod & KMOD_SHIFT) ^ !!(ev.key.keysym.mod & KMOD_CAPS))
2312                             keyvalue -= 'a'-'A';
2313                     }
2314                     else if (ev.key.keysym.mod & KMOD_SHIFT)
2315                     {
2316                         switch (keyvalue)
2317                         {
2318                             case '\'': keyvalue = '"'; break;
2319 
2320                             case ',': keyvalue = '<'; break;
2321                             case '-': keyvalue = '_'; break;
2322                             case '.': keyvalue = '>'; break;
2323                             case '/': keyvalue = '?'; break;
2324                             case '0': keyvalue = ')'; break;
2325                             case '1': keyvalue = '!'; break;
2326                             case '2': keyvalue = '@'; break;
2327                             case '3': keyvalue = '#'; break;
2328                             case '4': keyvalue = '$'; break;
2329                             case '5': keyvalue = '%'; break;
2330                             case '6': keyvalue = '^'; break;
2331                             case '7': keyvalue = '&'; break;
2332                             case '8': keyvalue = '*'; break;
2333                             case '9': keyvalue = '('; break;
2334 
2335                             case ';': keyvalue = ':'; break;
2336 
2337                             case '=': keyvalue = '+'; break;
2338 
2339                             case '[': keyvalue = '{'; break;
2340                             case '\\': keyvalue = '|'; break;
2341                             case ']': keyvalue = '}'; break;
2342 
2343                             case '`': keyvalue = '~'; break;
2344                         }
2345                     }
2346                     else if (ev.key.keysym.mod & KMOD_NUM) // && !(ev.key.keysym.mod & KMOD_SHIFT)
2347                     {
2348                         switch (keyvalue)
2349                         {
2350                             case SDLK_KP_1: keyvalue = '1'; break;
2351                             case SDLK_KP_2: keyvalue = '2'; break;
2352                             case SDLK_KP_3: keyvalue = '3'; break;
2353                             case SDLK_KP_4: keyvalue = '4'; break;
2354                             case SDLK_KP_5: keyvalue = '5'; break;
2355                             case SDLK_KP_6: keyvalue = '6'; break;
2356                             case SDLK_KP_7: keyvalue = '7'; break;
2357                             case SDLK_KP_8: keyvalue = '8'; break;
2358                             case SDLK_KP_9: keyvalue = '9'; break;
2359                             case SDLK_KP_0: keyvalue = '0'; break;
2360                             case SDLK_KP_PERIOD: keyvalue = '.'; break;
2361                             case SDLK_KP_COMMA: keyvalue = ','; break;
2362                         }
2363                     }
2364 
2365                     switch (keyvalue)
2366                     {
2367                         case SDLK_KP_DIVIDE: keyvalue = '/'; break;
2368                         case SDLK_KP_MULTIPLY: keyvalue = '*'; break;
2369                         case SDLK_KP_MINUS: keyvalue = '-'; break;
2370                         case SDLK_KP_PLUS: keyvalue = '+'; break;
2371                     }
2372 
2373                     if ((unsigned)keyvalue <= 0x7Fu)
2374                     {
2375                         if (OSD_HandleChar(keyvalue))
2376                             keyBufferInsert(keyvalue);
2377                     }
2378                 }
2379 
2380                 // initprintf("SDL2: got key %d, %d, %u\n", ev.key.keysym.scancode, code, ev.key.type);
2381 
2382                 // hook in the osd
2383                 if ((j = OSD_HandleScanCode(code, (ev.key.type == SDL_KEYDOWN))) <= 0)
2384                 {
2385                     if (j == -1)  // osdkey
2386                     {
2387                         for (j = 0; j < NUMKEYS; ++j)
2388                         {
2389                             if (keyGetState(j))
2390                             {
2391                                 if (keypresscallback)
2392                                     keypresscallback(j, 0);
2393                             }
2394                             keySetState(j, 0);
2395                         }
2396                     }
2397                     break;
2398                 }
2399 
2400                 if (ev.key.type == SDL_KEYDOWN)
2401                 {
2402                     if (!keyGetState(code))
2403                     {
2404                         if (keypresscallback)
2405                             keypresscallback(code, 1);
2406                     }
2407                     keySetState(code, 1);
2408                 }
2409                 else
2410                 {
2411 # if 1
2412                     // The pause key generates a release event right after
2413                     // the pressing one. As a result, it gets unseen
2414                     // by the game most of the time.
2415                     if (code == 0x59)  // pause
2416                         break;
2417 # endif
2418                     keySetState(code, 0);
2419                     if (keypresscallback)
2420                         keypresscallback(code, 0);
2421                 }
2422                 break;
2423             }
2424 
2425             case SDL_MOUSEWHEEL:
2426                 // initprintf("wheel y %d\n",ev.wheel.y);
2427                 if (ev.wheel.y > 0)
2428                 {
2429                     g_mouseBits |= 16;
2430                     if (g_mouseCallback)
2431                         g_mouseCallback(5, 1);
2432                 }
2433                 if (ev.wheel.y < 0)
2434                 {
2435                     g_mouseBits |= 32;
2436                     if (g_mouseCallback)
2437                         g_mouseCallback(6, 1);
2438                 }
2439                 break;
2440 
2441             case SDL_WINDOWEVENT:
2442                 switch (ev.window.event)
2443                 {
2444                     case SDL_WINDOWEVENT_FOCUS_GAINED:
2445                     case SDL_WINDOWEVENT_FOCUS_LOST:
2446                         appactive = (ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED);
2447                         if (g_mouseGrabbed && g_mouseEnabled)
2448                             grabmouse_low(appactive);
2449 #ifdef _WIN32
2450                         windowsHandleFocusChange(appactive);
2451 #endif
2452                         break;
2453 
2454                     case SDL_WINDOWEVENT_MOVED:
2455                     {
2456                         windowx = ev.window.data1;
2457                         windowy = ev.window.data2;
2458 
2459                         r_displayindex = SDL_GetWindowDisplayIndex(sdl_window);
2460                         modeschecked = 0;
2461                         videoGetModes();
2462                         break;
2463                     }
2464                     case SDL_WINDOWEVENT_ENTER:
2465                     case SDL_WINDOWEVENT_LEAVE:
2466                         g_mouseInsideWindow = (ev.window.event == SDL_WINDOWEVENT_ENTER);
2467                         break;
2468                 }
2469 
2470                 break;
2471 
2472             default:
2473                 rv = handleevents_sdlcommon(&ev);
2474                 break;
2475         }
2476     }
2477 
2478     return rv;
2479 }
2480 #endif
2481 
handleevents(void)2482 int32_t handleevents(void)
2483 {
2484 #ifdef __ANDROID__
2485     if (mobile_halted) return 0;
2486 #endif
2487 
2488     int32_t rv;
2489 
2490     if (inputchecked && g_mouseEnabled)
2491     {
2492         if (g_mouseCallback)
2493         {
2494             if (g_mouseBits & 16)
2495                 g_mouseCallback(5, 0);
2496             if (g_mouseBits & 32)
2497                 g_mouseCallback(6, 0);
2498         }
2499 
2500         OSD_HandleWheel();
2501         g_mouseBits &= ~(16 | 32);
2502     }
2503 
2504     rv = handleevents_pollsdl();
2505 
2506     inputchecked = 0;
2507     timerUpdateClock();
2508 
2509     communityapiRunCallbacks();
2510 
2511 #ifndef _WIN32
2512     startwin_idle(NULL);
2513 #endif
2514 
2515     return rv;
2516 }
2517 
2518 #if SDL_MAJOR_VERSION < 2
2519 # include "sdlayer12.cpp"
2520 #endif
2521