sdlayer.cpp 76.8 KB
Newer Older
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>
Richard Gobeille's avatar
Richard Gobeille committed
5

6
7
#include "a.h"
#include "build.h"
Richard Gobeille's avatar
Richard Gobeille committed
8
9
#include "cache1d.h"
#include "compat.h"
10
#include "build_cpuid.h"
11
#include "engine_priv.h"
Richard Gobeille's avatar
Richard Gobeille committed
12
#include "osd.h"
13
#include "palette.h"
Richard Gobeille's avatar
Richard Gobeille committed
14
15
#include "renderlayer.h"
#include "sdl_inc.h"
16
#include "softsurface.h"
Richard Gobeille's avatar
Richard Gobeille committed
17

18
#if SDL_MAJOR_VERSION >= 2
19
20
# include "imgui.h"
# include "imgui_impl_sdl.h"
21
22
23
24
#ifdef USE_OPENGL
# include "imgui_impl_opengl3.h"
#endif
#endif
25

26
#ifdef USE_OPENGL
27
# include "glad/glad.h"
28
# include "glbuild.h"
29
# include "glsurface.h"
30
31
#endif

Evan Ramos's avatar
Evan Ramos committed
32
#if defined HAVE_GTK2
33
# include "gtkbits.h"
Evan Ramos's avatar
Evan Ramos committed
34
#endif
Richard Gobeille's avatar
Richard Gobeille committed
35

36
#ifdef __ANDROID__
37
# include <android/log.h>
Richard Gobeille's avatar
Richard Gobeille committed
38
39
40
41
42
#elif defined __APPLE__
# include "osxbits.h"
# include <mach/mach.h>
# include <mach/mach_time.h>
#elif defined GEKKO
43
44
45
# include "wiibits.h"
# include <ogc/lwp.h>
# include <ogc/lwp_watchdog.h>
Richard Gobeille's avatar
Richard Gobeille committed
46
47
#elif defined _WIN32
# include "winbits.h"
48
#endif
Philipp Kutin's avatar
Philipp Kutin committed
49

50
#include "vfs.h"
51
#include "communityapi.h"
52

53
54
55
#define MICROPROFILE_IMPL
#include "microprofile.h"

56
#if SDL_MAJOR_VERSION >= 2
57
static SDL_version linked;
58
59
#else
#define SDL_JoystickNameForIndex(x) SDL_JoystickName(x)
60
#endif
61

62
#if !defined STARTUP_SETUP_WINDOW
Richard Gobeille's avatar
Richard Gobeille committed
63
64
int32_t startwin_open(void) { return 0; }
int32_t startwin_close(void) { return 0; }
65
66
67
int32_t startwin_puts(const char *s) { UNREFERENCED_PARAMETER(s); return 0; }
int32_t startwin_idle(void *s) { UNREFERENCED_PARAMETER(s); return 0; }
int32_t startwin_settitle(const char *s) { UNREFERENCED_PARAMETER(s); return 0; }
68
int32_t startwin_run(void) { return 0; }
69
bool startwin_isopen(void) { return false; }
70
71
#endif

Philipp Kutin's avatar
Philipp Kutin committed
72
73
74
75
/// These can be useful for debugging sometimes...
//#define SDL_WM_GrabInput(x) SDL_WM_GrabInput(SDL_GRAB_OFF)
//#define SDL_ShowCursor(x) SDL_ShowCursor(SDL_ENABLE)

76
77
78
// undefine to restrict windowed resolutions to conventional sizes
#define ANY_WINDOWED_SIZE

79
// fix for mousewheel
80
int32_t inputchecked = 0;
81

82
char quitevent=0, appactive=1, novideo=0;
83
84

// video
85
static SDL_Surface *sdl_surface/*=NULL*/;
Richard Gobeille's avatar
Richard Gobeille committed
86

87
88
89
static vec2_t sdl_resize;
static int sdl_minimized;

90
#if SDL_MAJOR_VERSION >= 2
91
92
static SDL_Window *sdl_window;
static SDL_GLContext sdl_context;
93
static int vsync_unsupported;
94
#endif
Richard Gobeille's avatar
Richard Gobeille committed
95

96
97
static int32_t vsync_renderlayer;
int32_t maxrefreshfreq=0;
98
99
int32_t xres=-1, yres=-1, bpp=0, fullscreen=0, bytesperline;
double refreshfreq = 59.0;
100
intptr_t frameplace=0;
Richard Gobeille's avatar
Richard Gobeille committed
101
int32_t lockcount=0;
102
103
104
char modechange=1;
char offscreenrendering=0;
char videomodereset = 0;
105
int32_t nofog=0;
106
#ifndef EDUKE32_GLES
Richard Gobeille's avatar
Richard Gobeille committed
107
static uint16_t sysgamma[3][256];
108
#endif
109
110
#ifdef USE_OPENGL
// OpenGL stuff
111
char nogl=0;
112
#endif
113
114
// last gamma, contrast
static float lastvidgcb[2];
115

116
117
//#define KEY_PRINT_DEBUG

118
#include "sdlkeytrans.cpp"
119

120
static SDL_Surface *appicon = NULL;
121
#if !defined __APPLE__ && !defined EDUKE32_TOUCH_DEVICES
122
static SDL_Surface *loadappicon(void);
123
#endif
124

125
static mutex_t m_initprintf;
126
#if SDL_MAJOR_VERSION >= 2
127
128
static ImGuiIO *g_ImGui_IO;
bool g_ImGuiCaptureInput = true;
129
#endif
130
uint8_t g_ImGuiCapturedDevices;
131
#ifdef _WIN32
132
# if SDL_MAJOR_VERSION >= 2
133
//
134
135
136
137
138
139
140
// win_gethwnd() -- gets the window handle
//
HWND win_gethwnd(void)
{
    struct SDL_SysWMinfo wmInfo;
    SDL_VERSION(&wmInfo.version);

Richard Gobeille's avatar
Richard Gobeille committed
141
142
143
    if (SDL_GetWindowWMInfo(sdl_window, &wmInfo) != SDL_TRUE)
        return 0;

144
145
146
    if (wmInfo.subsystem == SDL_SYSWM_WINDOWS)
        return wmInfo.info.win.window;

147
    LOG_F(ERROR, "Unknown WM subsystem?!");
148
149
150

    return 0;
}
151
# endif
152
//
153
154
// win_gethinstance() -- gets the application instance
//
155
HINSTANCE win_gethinstance(void)
156
{
157
    return (HINSTANCE)GetModuleHandle(NULL);
158
159
160
}
#endif

161

162
int32_t wm_msgbox(const char *name, const char *fmt, ...)
163
{
164
    char *buf = (char *)Balloca(MSGBOX_PRINTF_MAX);
Richard Gobeille's avatar
Richard Gobeille committed
165
    va_list va;
166

Pierre-Loup Griffais's avatar
Pierre-Loup Griffais committed
167
168
    UNREFERENCED_PARAMETER(name);

Richard Gobeille's avatar
Richard Gobeille committed
169
    va_start(va,fmt);
170
    Bvsnprintf(buf,MSGBOX_PRINTF_MAX,fmt,va);
Richard Gobeille's avatar
Richard Gobeille committed
171
    va_end(va);
172
    buf[MSGBOX_PRINTF_MAX-1] = 0;
173
#if defined EDUKE32_OSX
174
175
    auto result = osx_msgbox(name, buf);
    return result;
176
177
178
#elif defined _WIN32
    MessageBox(win_gethwnd(),buf,name,MB_OK|MB_TASKMODAL);
    return 0;
179
#elif defined EDUKE32_TOUCH_DEVICES
180
    LOG_F(INFO, "wm_msgbox called. Message: %s: %s",name,buf);
181
    return 0;
182
183
184
#elif defined GEKKO
    puts(buf);
    return 0;
185
#else
186
187
188
189
# if defined HAVE_GTK2
    if (gtkbuild_msgbox(name, buf) >= 0)
        return 0;
# endif
190
# if SDL_MAJOR_VERSION >= 2
191
192
193
#  if !defined _WIN32
    // Replace all tab chars with spaces because the hand-rolled SDL message
    // box diplays the former as N/L instead of whitespace.
194
    for (size_t i=0; i<MSGBOX_PRINTF_MAX; i++)
195
196
197
        if (buf[i] == '\t')
            buf[i] = ' ';
#  endif
198
199
    auto result = SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, name, buf, NULL);
    return result;
200
# else
Richard Gobeille's avatar
Richard Gobeille committed
201
202
203
204
    puts(buf);
    puts("   (press Return or Enter to continue)");
    getchar();
    return 0;
205
# endif
206
#endif
207
208
}

209
int32_t wm_ynbox(const char *name, const char *fmt, ...)
210
{
211
    char *buf = (char *)Balloca(MSGBOX_PRINTF_MAX);
Richard Gobeille's avatar
Richard Gobeille committed
212
    va_list va;
Pierre-Loup Griffais's avatar
Pierre-Loup Griffais committed
213
214

    UNREFERENCED_PARAMETER(name);
215

Richard Gobeille's avatar
Richard Gobeille committed
216
    va_start(va,fmt);
217
    Bvsnprintf(buf,MSGBOX_PRINTF_MAX,fmt,va);
Richard Gobeille's avatar
Richard Gobeille committed
218
    va_end(va);
219
    buf[MSGBOX_PRINTF_MAX-1] = 0;
220
#if defined EDUKE32_OSX
221
222
    auto result = osx_ynbox(name, buf);
    return result;
223
#elif defined _WIN32
224
225
    auto result = MessageBox(win_gethwnd(),buf,name,MB_YESNO|MB_ICONQUESTION|MB_TASKMODAL);
    return result == IDYES;
226
#elif defined EDUKE32_TOUCH_DEVICES
227
228
    LOG_F(WARNING, "wm_ynbox called, this is bad! Message: %s: %s",name,buf);
    LOG_F(INFO, "Returning false..");
229
    return 0;
230
231
232
233
#elif defined GEKKO
    puts(buf);
    puts("Assuming yes...");
    return 1;
234
#else
235
236
237
238
239
# if defined HAVE_GTK2
    int ret = gtkbuild_ynbox(name, buf);
    if (ret >= 0)
        return ret;
# endif
240
# if SDL_MAJOR_VERSION >= 2
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
    int r = -1;

    const SDL_MessageBoxButtonData buttons[] = {
        {
            SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
            0,
            "No"
        },{
            SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
            1,
            "Yes"
        },
    };

    SDL_MessageBoxData data = {
        SDL_MESSAGEBOX_INFORMATION,
        NULL, /* no parent window */
        name,
        buf,
        2,
        buttons,
        NULL /* Default color scheme */
    };

    SDL_ShowMessageBox(&data, &r);
    return r;
# else
    char c;

    puts(buf);
    puts("   (type 'Y' or 'N', and press Return or Enter to continue)");
    do c = getchar(); while (c != 'Y' && c != 'y' && c != 'N' && c != 'n');
    return c == 'Y' || c == 'y';
# endif
275
276
277
#endif
}

278
void wm_setapptitle(const char *name)
279
{
280
#ifndef EDUKE32_TOUCH_DEVICES
281
    if (name != apptitle)
282
        Bstrncpyz(apptitle, name, sizeof(apptitle));
283

284
#if !defined(__APPLE__)
285
286
    if (!appicon)
        appicon = loadappicon();
287
288
#endif

289
#if SDL_MAJOR_VERSION >= 2
290
291
292
    if (sdl_window)
    {
        SDL_SetWindowTitle(sdl_window, apptitle);
293

294
295
296
297
298
299
300
301
        if (appicon)
        {
#if defined _WIN32
        if (!EDUKE32_SDL_LINKED_PREREQ(linked, 2, 0, 5))
#endif
            SDL_SetWindowIcon(sdl_window, appicon);
        }
    }
302
303
304
305
306
#else
    SDL_WM_SetCaption(apptitle, NULL);

    if (appicon && sdl_surface)
        SDL_WM_SetIcon(appicon, 0);
307
#endif
308

309
    startwin_settitle(apptitle);
310
311
#else
    UNREFERENCED_PARAMETER(name);
312
#endif
313
314
315
316
317
318
319
320
321
322
323
324
}

//
//
// ---------------------------------------
//
// System
//
// ---------------------------------------
//
//

325
/* XXX: libexecinfo could be used on systems without gnu libc. */
326
#if !defined _WIN32 && defined __GNUC__ && !defined __OpenBSD__ && !(defined __APPLE__ && defined __BIG_ENDIAN__) && !defined GEKKO && !defined EDUKE32_TOUCH_DEVICES && !defined __OPENDINGUX__
327
328
329
330
331
332
# define PRINTSTACKONSEGV 1
# include <execinfo.h>
#endif

static inline char grabmouse_low(char a);

333
#ifndef __ANDROID__
334
335
336
337
338
339
static void attach_debugger_here(void)
{
#ifdef DEBUGGINGAIDS
    debug_break();
#endif
}
340

341
342
343
344
345
346
static void sighandler(int signum)
{
    UNREFERENCED_PARAMETER(signum);
    //    if (signum==SIGSEGV)
    {
        grabmouse_low(0);
347

348
349
350
351
352
353
354
355
356
357
#if PRINTSTACKONSEGV
        {
            void *addr[32];
            int32_t errfd = fileno(stderr);
            int32_t n=backtrace(addr, ARRAY_SIZE(addr));
            backtrace_symbols_fd(addr, n, errfd);
        }
        // This is useful for attaching the debugger post-mortem. For those pesky
        // cases where the program runs through happily when inspected from the start.
        //        usleep(15000000);
358
#endif
359
360
        attach_debugger_here();
        app_crashhandler();
361
        Bexit(EXIT_FAILURE);
362
363
    }
}
364
#endif
365

366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
#ifdef __ANDROID__
int mobile_halted = 0;
#ifdef __cplusplus
extern "C"
{
#endif
void G_Shutdown(void);
#ifdef __cplusplus
}
#endif

int sdlayer_mobilefilter(void *userdata, SDL_Event *event)
{
    switch (event->type)
    {
        case SDL_APP_TERMINATING:
            // yes, this calls into the game, ugh
383
384
385
386
            if (mobile_halted == 1)
                G_Shutdown();

            mobile_halted = 1;
387
388
389
390
391
392
393
394
            return 0;
        case SDL_APP_LOWMEMORY:
            gltexinvalidatetype(INVALIDATE_ALL);
            return 0;
        case SDL_APP_WILLENTERBACKGROUND:
            mobile_halted = 1;
            return 0;
        case SDL_APP_DIDENTERBACKGROUND:
395
            gltexinvalidatetype(INVALIDATE_ALL);
396
397
398
399
400
401
402
403
404
405
406
            // tear down video?
            return 0;
        case SDL_APP_WILLENTERFOREGROUND:
            // restore video?
            return 0;
        case SDL_APP_DIDENTERFOREGROUND:
            mobile_halted = 0;
            return 0;
        default:
            return 1;//!halt;
    }
407
408

    UNREFERENCED_PARAMETER(userdata);
409
410
}

411
412
413
414
415
416
417
418
# include <setjmp.h>
static jmp_buf eduke32_exit_jmp_buf;
static int eduke32_return_value;

void eduke32_exit_return(int retval)
{
    eduke32_return_value = retval;
    longjmp(eduke32_exit_jmp_buf, 1);
419
    EDUKE32_UNREACHABLE_SECTION(return);
420
421
422
}
#endif

423
424
void sdlayer_sethints()
{
425
#if defined _WIN32 && !defined _MSC_VER
426
427
428
429
430
    // Thread naming interferes with debugging using MinGW-w64's GDB.
#if defined SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING
    SDL_SetHint(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, "1");
#endif
#if defined SDL_HINT_XINPUT_ENABLED
431
    if (Bgetenv("EDUKE32_NO_XINPUT"))
432
433
434
435
436
437
438
439
440
441
442
443
444
        SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "0");
#endif
#endif

#if defined SDL_HINT_NO_SIGNAL_HANDLERS
    SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
#endif
#if defined SDL_HINT_VIDEO_HIGHDPI_DISABLED
    SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "1");
#endif
#if defined SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
    SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "0");
#endif
445
446
447
#if defined SDL_HINT_AUDIO_RESAMPLING_MODE
    SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "3");
#endif
448
449
450
#if defined SDL_HINT_MOUSE_RELATIVE_SCALING
    SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_SCALING, "0");
#endif
451
452
}

453
#ifdef _WIN32
454
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
455
456
457
458
459
#elif defined __ANDROID__
# ifdef __cplusplus
extern "C" int eduke32_android_main(int argc, char const *argv[]);
# endif
int eduke32_android_main(int argc, char const *argv[])
460
461
#elif defined GEKKO
int SDL_main(int argc, char *argv[])
462
#else
463
int main(int argc, char *argv[])
464
#endif
465
{
466
467
468
469
470
471
472
473
474
475
476
477
478
479
#ifdef _WIN32
    char * argvbuf;
    int    argc = windowsGetCommandLine(&argvbuf);
    char * wp   = argvbuf;
    char **argv = (char **)Bmalloc(sizeof(char *)*(argc+1));

    for (int i = 0; i < argc; i++, wp++)
    {
        argv[i] = wp;
        while (*wp) wp++;
    }
    argv[argc] = NULL;
#endif

480
    engineSetupAllocator();
481
482
483
484
485
486
487
488

#ifndef __ANDROID__
    signal(SIGSEGV, sighandler);
    signal(SIGILL, sighandler);  /* clang -fcatch-undefined-behavior uses an ill. insn */
    signal(SIGABRT, sighandler);
    signal(SIGFPE, sighandler);
#endif

489
    engineSetupLogging(argc, argv);
490
491
    
#if SDL_VERSION_ATLEAST(2, 0, 8)
Richard Gobeille's avatar
Richard Gobeille committed
492
493
494
495
    if (EDUKE32_SDL_LINKED_PREREQ(linked, 2, 0, 8))
        SDL_SetMemoryFunctions(_xmalloc, _xcalloc, _xrealloc, _xfree);
#endif

496
497
498
499
500
    MicroProfileOnThreadCreate("Main");
    MicroProfileSetForceEnable(true);
    MicroProfileSetEnableAllGroups(true);
    MicroProfileSetForceMetaCounters(true);

501
502
503
504
505
506
507
#ifdef __ANDROID__
    if (setjmp(eduke32_exit_jmp_buf))
    {
        return eduke32_return_value;
    }
#endif

508
    sdlayer_sethints();
509

510
#ifdef USE_OPENGL
Richard Gobeille's avatar
   
Richard Gobeille committed
511
    char *argp;
Richard Gobeille's avatar
Richard Gobeille committed
512

513
    if ((argp = Bgetenv("EDUKE32_NO_OPENGL_FOG")) != NULL)
514
        nofog = Batol(argp);
515

516
517
518
519
520
521
522
523
524
#if defined __linux__ || defined EDUKE32_BSD
    if (!Bgetenv("EDUKE32_NO_GL_THREADED_OPTIMIZATIONS"))
    {
        if (!Bgetenv("EDUKE32_NO_NVIDIA_THREADED_OPTIMIZATIONS"))
            setenv("__GL_THREADED_OPTIMIZATIONS", "1", 0);

        if (!Bgetenv("EDUKE32_NO_MESA_THREADED_OPTIMIZATIONS"))
            setenv("mesa_glthread", "true", 0);
    }
Richard Gobeille's avatar
Richard Gobeille committed
525
#endif
526
527
#endif

Richard Gobeille's avatar
Richard Gobeille committed
528
529
    buildkeytranslationtable();

530
#ifdef __ANDROID__
531
532
    SDL_SetEventFilter(sdlayer_mobilefilter, NULL);
#endif
533

534
#ifdef _WIN32
535
536
537
538
539
    UNREFERENCED_PARAMETER(hInst);
    UNREFERENCED_PARAMETER(hPrevInst);
    UNREFERENCED_PARAMETER(lpCmdLine);
    UNREFERENCED_PARAMETER(nCmdShow);

540
    if (windowsPreInit())
541
        return -1;
542

543
#elif defined(GEKKO)
544
    wii_open();
545
546
547
#elif defined(HAVE_GTK2)
    // Pre-initialize SDL video system in order to make sure XInitThreads() is called
    // before GTK starts talking to X11.
548
    if (SDL_WasInit(SDL_INIT_VIDEO) != SDL_INIT_VIDEO)
549
        SDL_InitSubSystem(SDL_INIT_VIDEO);
550

Richard Gobeille's avatar
Richard Gobeille committed
551
    gtkbuild_init(&argc, &argv);
552
553
#endif

554
555
556
#ifdef EDUKE32_OSX
    osx_preopen();
#endif
557
    startwin_open();
558
559
560
#ifdef EDUKE32_OSX
    osx_postopen();
#endif
561
    maybe_redirect_outputs();
562

563
564
#ifdef USE_PHYSFS
    int pfsi = PHYSFS_init(argv[0]);
565
    Bassert(pfsi != 0);
566
567
    PHYSFS_setWriteDir(PHYSFS_getUserDir());
#endif
568
    int const r = app_main(argc, argv);
569

570
    startwin_close();
571

572
#if defined(HAVE_GTK2)
573
    gtkbuild_exit(r);
574
575
#endif

Richard Gobeille's avatar
Richard Gobeille committed
576
    return r;
577
578
}

579

580
#if SDL_MAJOR_VERSION >= 2
Evan Ramos's avatar
Evan Ramos committed
581
#ifdef USE_OPENGL
582
583
584
585
586
587
588
589
590
591
592
593
static int sdlayer_getswapinterval(int const syncMode)
{
    static int intervals[] = { -1, 0, 1, 0};
    Bassert((unsigned)(syncMode + 1) < ARRAY_SIZE(intervals));
    return intervals[syncMode + 1];
}

static int sdlayer_checkvsync(int checkSync)
{
    int const actualSync = SDL_GL_GetSwapInterval();
    if (actualSync != sdlayer_getswapinterval(checkSync))
    {
594
        LOG_F(WARNING, "Video driver enforcing SwapInterval %d, unable to configure VSync!", actualSync);
595
        checkSync = actualSync;
596
        vsync_unsupported = true;
597
598
599
    }
    return checkSync;
}
Evan Ramos's avatar
Evan Ramos committed
600
#endif
601

602
int32_t videoSetVsync(int32_t newSync)
603
{
604
    if (newSync != 0 && vsync_unsupported)
605
    {
606
        LOG_F(WARNING, "VSync configuration locked by video driver.");
607
608
609
        return vsync_renderlayer;
    }

610
    if (vsync_renderlayer == newSync)
611
        return newSync;
612

613
#ifdef USE_OPENGL
614
    if (sdl_context)
615
    {
616
        int result = SDL_GL_SetSwapInterval(sdlayer_getswapinterval(newSync));
617
618
619
620
621

        if (result == -1)
        {
            if (newSync == -1)
            {
622
                LOG_F(WARNING, "Video driver rejected SwapInterval %d, unable to configure adaptive VSync!", sdlayer_getswapinterval(newSync));
623
                newSync = 1;
624
                result  = SDL_GL_SetSwapInterval(sdlayer_getswapinterval(newSync));
625
626
627
628
            }

            if (result == -1)
            {
629
                LOG_F(WARNING, "Video driver rejected SwapInterval %d, unable to configure VSync!", sdlayer_getswapinterval(newSync));
630
631
632
633
                newSync = 0;
            }
        }

634
        vsync_renderlayer = sdlayer_checkvsync(newSync);
635
636
    }
    else
637
#endif
638
    {
639
        vsync_renderlayer = newSync;
640

641
        videoResetMode();
642

643
        if (videoSetGameMode(fullscreen, xres, yres, bpp, upscalefactor))
644
            LOG_F(ERROR, "Failed to set video mode!");
645
646
    }

647
    return newSync;
648
}
649
#endif
650

651
int32_t sdlayer_checkversion(void);
652
#if SDL_MAJOR_VERSION >= 2
653
654
655
int32_t sdlayer_checkversion(void)
{
    SDL_version compiled;
656
657
    auto str = (char*)Balloca(128);
    str[0] = 0;
658

659
    SDL_GetVersion(&linked);
660

661
662
    // odd-numbered SDL patch levels are dev snapshots, even-numbered are proper releases
    // string is in the format "https://github.com/libsdl-org/SDL.git@bfd2f8993f173535efe436f8e60827cc44351bea"
663
664
665
    char const *rev;

    if (linked.patch & 1 && (rev = SDL_GetRevision()))
666
    {
667
668
669
670
671
672
673
674
        char buf[10] = {};

        int len = Bstrlen(rev);

        if (len > 49 && rev[len-41] == '@')
            Bmemcpy(buf, &rev[len-40], 9);

        Bsnprintf(str, 128, "Initializing SDL %s (%d.%d.%d snapshot)", buf, linked.major, linked.minor, linked.patch);
675
    }
676
677
678
    else
    {
        SDL_VERSION(&compiled);
679

680
681
682
683
684
685
686
        if (!Bmemcmp(&compiled, &linked, sizeof(SDL_version)))
            Bsnprintf(str, 128, "Initializing SDL %d.%d.%d", compiled.major, compiled.minor, compiled.patch);
        else
            Bsnprintf(str, 128, "Initializing SDL %d.%d.%d (built against version %d.%d.%d)",
                linked.major, linked.minor, linked.patch, compiled.major, compiled.minor, compiled.patch);
    }

687
    LOG_F(INFO, "%s", str);
688

689
    if (SDL_VERSIONNUM(linked.major, linked.minor, linked.patch) < SDL2_REQUIREDVERSION)
690
    {
691
        /*reject running under SDL versions older than what is stated in sdl_inc.h */
692
        LOG_F(ERROR, "You need SDL %d.%d.%d or newer to run %s.",SDL2_MIN_X,SDL2_MIN_Y,SDL2_MIN_Z,apptitle);
693
        return -1;
694
    }
695
696

    return 0;
697
698
}

699
700
701
//
// initsystem() -- init SDL systems
//
Richard Gobeille's avatar
Richard Gobeille committed
702
int32_t initsystem(void)
703
{
704
705
    mutex_init(&m_initprintf);

706
#ifdef _WIN32
707
    windowsPlatformInit();
708
#endif
709
710
    sysReadCPUID();

711
    if (sdlayer_checkversion())
712
        return -1;
713

714
    int32_t err = 0;
715
716
717

    if (SDL_WasInit(SDL_INIT_VIDEO) != SDL_INIT_VIDEO)
        err = SDL_InitSubSystem(SDL_INIT_VIDEO);
718
719

    if (err)
720
    {
721
722
723
724
        LOG_F(ERROR, "SDL initialization failed: %s.", SDL_GetError());
        LOG_F(WARNING, "Non-interactive mode enabled; this is probably not what you want.");
        VLOG_F(LOG_GFX, "Video output disabled.");

725
726
727
728
        novideo = 1;
#ifdef USE_OPENGL
        nogl = 1;
#endif
Richard Gobeille's avatar
Richard Gobeille committed
729
    }
730

731
#if SDL_MAJOR_VERSION >= 2
732
    SDL_StopTextInput();
Richard Gobeille's avatar
Richard Gobeille committed
733
    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
734
735
#endif

736
737
    timerInit(CLOCKTICKSPERSECOND);

Richard Gobeille's avatar
Richard Gobeille committed
738
739
    frameplace = 0;
    lockcount = 0;
740

741
742
    if (!novideo)
    {
743
#ifdef USE_OPENGL
744
        if (SDL_GL_LoadLibrary(0))
745
        {
746
            LOG_F(ERROR, "Failed loading OpenGL driver: %s; all OpenGL modes are unavailable.", SDL_GetError());
747
            nogl = 1;
748
        }
749
750
#endif

751
752
753
#ifndef _WIN32
        const char *drvname = SDL_GetVideoDriver(0);

754
        if (drvname)
755
            LOG_F(INFO, "Using '%s' video driver.", drvname);
756
#endif
757
        wm_setapptitle(apptitle);
758
        g_numdisplays = SDL_GetNumVideoDisplays();
759
    }
760

Richard Gobeille's avatar
Richard Gobeille committed
761
    return 0;
762
}
763
#endif
764
765
766
767
768
769
770


//
// uninitsystem() -- uninit SDL systems
//
void uninitsystem(void)
{
Richard Gobeille's avatar
Richard Gobeille committed
771
    uninitinput();
772
    timerUninit();
773

Richard Gobeille's avatar
Richard Gobeille committed
774
775
776
777
#ifdef _WIN32
    windowsPlatformCleanup();
#endif

778
    if (appicon)
779
    {
780
        SDL_FreeSurface(appicon);
781
782
        appicon = NULL;
    }
783

784
#ifdef USE_OPENGL
785
# if SDL_MAJOR_VERSION >= 2
786
    SDL_GL_UnloadLibrary();
787
# endif
788
#endif
Richard Gobeille's avatar
Richard Gobeille committed
789
    SDL_Quit();
790
791
792
}


793
794
795
796
797
//
// system_getcvars() -- propagate any cvars that are read post-initialization
//
void system_getcvars(void)
{
798
799
800
801
# ifdef _WIN32
    windowsDwmSetupComposition(false);
# endif

802
    vsync = videoSetVsync(vsync);
803
804
}

805
//
Evan Ramos's avatar
Evan Ramos committed
806
// debugprintf() -- prints a formatted debug string to stderr
807
//
808
int debugprintf(const char *f, ...)
809
{
Philipp Kutin's avatar
Philipp Kutin committed
810
#if defined DEBUGGINGAIDS && !(defined __APPLE__ && defined __BIG_ENDIAN__)
Richard Gobeille's avatar
Richard Gobeille committed
811
    va_list va;
812

Richard Gobeille's avatar
Richard Gobeille committed
813
    va_start(va,f);
814
    int len = Bvfprintf(stderr, f, va);
Richard Gobeille's avatar
Richard Gobeille committed
815
    va_end(va);
816
    return len;
817
818
#else
    UNREFERENCED_PARAMETER(f);
819
    return 0;
820
821
822
823
824
825
826
827
828
829
830
831
832
833
#endif
}


//
//
// ---------------------------------------
//
// All things Input
//
// ---------------------------------------
//
//

834
835
// static int32_t joyblast=0;
static SDL_Joystick *joydev = NULL;
836
837
#if SDL_MAJOR_VERSION >= 2
static SDL_GameController *controller = NULL;
838
static bool gameControllerDBLoaded;
839
840
841

static void LoadSDLControllerDB()
{
842
843
844
    if (gameControllerDBLoaded)
        return;

845
846
847
848
    buildvfs_kfd fh = kopen4load("gamecontrollerdb.txt", 0);
    if (fh == buildvfs_kfd_invalid)
        return;

849
    int const flen = kfilelength(fh);
850
851
852
853
854
855
    if (flen <= 0)
    {
        kclose(fh);
        return;
    }

856
    char * dbuf = (char *)Xaligned_alloc(16, flen + 1);
857
858
859
860
861
862
863
864
    if (!dbuf)
    {
        kclose(fh);
        return;
    }

    if (kread_and_test(fh, dbuf, flen))
    {
865
        Xaligned_free(dbuf);
866
867
868
869
870
871
872
        kclose(fh);
        return;
    }

    dbuf[flen] = '\0';
    kclose(fh);

873
    auto rwops = SDL_RWFromConstMem(dbuf, flen);
874
875
    if (!rwops)
    {
876
877
error:
        LOG_F(ERROR, "Failed loading game controller database: %s.", SDL_GetError());
878
        Xaligned_free(dbuf);
879
880
881
        return;
    }

882
883
    int i = SDL_GameControllerAddMappingsFromRW(rwops, 1);

884
    if (i == -1)
885
        goto error;
886
    else
887
        VLOG_F(LOG_INPUT, "Loaded game controller database.");
888

889
    Xaligned_free(dbuf);
890
891

    gameControllerDBLoaded = true;
892
893
}
#endif
894

895
896
static int numjoysticks;

897
898
void joyScanDevices()
{
899
    inputdevices &= ~DEV_JOYSTICK;
900

901
#if SDL_MAJOR_VERSION >= 2
902
903
904
905
906
    if (controller)
    {
        SDL_GameControllerClose(controller);
        controller = nullptr;
    }
907
#endif
908
909
910
911
912
913
    if (joydev)
    {
        SDL_JoystickClose(joydev);
        joydev = nullptr;
    }

914
915
    numjoysticks = SDL_NumJoysticks();

916
    if (numjoysticks < 1)
917
        VLOG_F(LOG_INPUT, "No game controllers found.");
918
    else
919
    {
920
        VLOG_F(LOG_INPUT, "Game controllers:");
921
922
923

        char name[128];

924
925
        for (int i = 0; i < numjoysticks; i++)
        {
926
#if SDL_MAJOR_VERSION >= 2
927
            if (SDL_IsGameController(i))
928
                Bstrncpyz(name, SDL_GameControllerNameForIndex(i), sizeof(name));
929
            else
930
#endif
931
932
                Bstrncpyz(name, SDL_JoystickNameForIndex(i), sizeof(name));
                
933
            VLOG_F(LOG_INPUT, "  %d. %s", i + 1, name);
934
        }