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, ®rab);
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