raze/polymer/eduke32/build/src/sdlayer.c
hendricks266 e00115c043 Introduce USE_GLEXT macro and use it to conditionally compile out all GL ARB function calls when compiling for GL ES platforms.
This replaces the previous hack where we kept the dynamic pointers null and simply never tried calling any of them by a conspiracy of default runtime settings and disabled menu options.

Code affected: VBO support for models, VPX GLSL decoding, texture compression (texture cache), and multitexturing (glow/detail maps).

This commit also replaces EDUKE32_GLES conditionals with USE_GLEXT where appropriate.

I didn't touch polymer.c because it depends too heavily on extensions for conditionally compiling them to make sense.

git-svn-id: https://svn.eduke32.com/eduke32@5526 1a8010ca-5511-0410-912e-c29ae57300e0
2016-01-08 01:33:20 +00:00

2190 lines
54 KiB
C

// SDL interface layer
// for the Build Engine
// by Jonathon Fowler (jf@jonof.id.au)
//
// Use SDL 1.2 or 2.0 from http://www.libsdl.org
#define __STDC_FORMAT_MACROS
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stdlib.h>
#include <math.h> // pow
#include <signal.h>
#include "sdl_inc.h"
#include "compat.h"
#include "renderlayer.h"
#include "cache1d.h"
//#include "pragmas.h"
#include "a.h"
#include "build.h"
#include "osd.h"
#include "engine_priv.h"
#ifdef USE_OPENGL
# include "glbuild.h"
#endif
#if defined _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include "winbits.h"
#endif
#if defined __APPLE__
# include "osxbits.h"
# include <mach/mach.h>
# include <mach/mach_time.h>
#endif
#if defined HAVE_GTK2
# include "gtkbits.h"
#endif
#ifdef __ANDROID__
# include <android/log.h>
#endif
#if defined GEKKO
# include "wiibits.h"
# include <ogc/lwp.h>
# include <ogc/lwp_watchdog.h>
# define SDL_DISABLE_8BIT_BUFFER
#endif
#if !defined STARTUP_SETUP_WINDOW
int32_t startwin_open(void) { return 0; }
int32_t startwin_close(void) { return 0; }
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; }
int32_t startwin_run(void) { return 0; }
#endif
/// 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)
#define SURFACE_FLAGS (SDL_SWSURFACE|SDL_HWPALETTE|SDL_HWACCEL)
// undefine to restrict windowed resolutions to conventional sizes
#define ANY_WINDOWED_SIZE
// fix for mousewheel
int32_t inputchecked = 0;
char quitevent=0, appactive=1, novideo=0;
// video
static SDL_Surface *sdl_surface/*=NULL*/;
#if !defined SDL_DISABLE_8BIT_BUFFER
static SDL_Surface *sdl_buffersurface=NULL;
#else
# define sdl_buffersurface sdl_surface
#endif
#if SDL_MAJOR_VERSION==2
static SDL_Palette *sdl_palptr=NULL;
static SDL_Window *sdl_window=NULL;
static SDL_GLContext sdl_context=NULL;
static SDL_Texture *sdl_texture=NULL;
static SDL_Renderer *sdl_renderer=NULL;
#endif
int32_t xres=-1, yres=-1, bpp=0, fullscreen=0, bytesperline;
intptr_t frameplace=0;
int32_t lockcount=0;
char modechange=1;
char offscreenrendering=0;
char videomodereset = 0;
int32_t nofog=0;
static uint16_t sysgamma[3][256];
extern int32_t curbrightness, gammabrightness;
#ifdef USE_OPENGL
// OpenGL stuff
char nogl=0;
static int32_t vsync_render=0;
#endif
// last gamma, contrast, brightness
static float lastvidgcb[3];
//#define KEY_PRINT_DEBUG
#include "sdlkeytrans.c"
static SDL_Surface *appicon = NULL;
#if !defined __APPLE__ && !defined EDUKE32_TOUCH_DEVICES
static SDL_Surface *loadappicon(void);
#endif
static mutex_t m_initprintf;
// Joystick dead and saturation zones
uint16_t *joydead, *joysatur;
#ifdef _WIN32
# if SDL_MAJOR_VERSION != 1
//
// win_gethwnd() -- gets the window handle
//
HWND win_gethwnd(void)
{
struct SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
if (SDL_GetWindowWMInfo(sdl_window, &wmInfo) != SDL_TRUE)
return 0;
if (wmInfo.subsystem == SDL_SYSWM_WINDOWS)
return wmInfo.info.win.window;
initprintf("win_gethwnd: Unknown WM subsystem?!\n");
return 0;
}
# endif
//
// win_gethinstance() -- gets the application instance
//
HINSTANCE win_gethinstance(void)
{
return (HINSTANCE)GetModuleHandle(NULL);
}
#endif
int32_t wm_msgbox(const char *name, const char *fmt, ...)
{
char buf[2048];
va_list va;
UNREFERENCED_PARAMETER(name);
va_start(va,fmt);
vsnprintf(buf,sizeof(buf),fmt,va);
va_end(va);
#if defined EDUKE32_OSX
return osx_msgbox(name, buf);
#elif defined HAVE_GTK2
if (gtkbuild_msgbox(name, buf) >= 0) return 1;
#elif defined _WIN32
MessageBox(win_gethwnd(),buf,name,MB_OK|MB_TASKMODAL);
return 0;
#elif defined EDUKE32_TOUCH_DEVICES
initprintf("wm_msgbox called. Message: %s: %s",name,buf);
return 0;
#elif SDL_MAJOR_VERSION==2
# 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.
{
size_t i;
for (i=0; i<sizeof(buf); i++)
if (buf[i] == '\t')
buf[i] = ' ';
}
# endif
return SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, name, buf, NULL);
#elif defined GEKKO
puts(buf);
return 0;
#endif
puts(buf);
puts(" (press Return or Enter to continue)");
getchar();
return 0;
}
#if SDL_MAJOR_VERSION != 1
int32_t wm_ynbox(const char *name, const char *fmt, ...)
{
char buf[2048];
va_list va;
UNREFERENCED_PARAMETER(name);
va_start(va,fmt);
vsprintf(buf,fmt,va);
va_end(va);
#if defined EDUKE32_OSX
return osx_ynbox(name, buf);
#elif defined HAVE_GTK2
{
int32_t r = gtkbuild_ynbox(name, buf);
return r >= 0 ? r : 0;
}
#elif defined _WIN32
return (MessageBox(win_gethwnd(),buf,name,MB_YESNO|MB_ICONQUESTION|MB_TASKMODAL) == IDYES);
#elif defined EDUKE32_TOUCH_DEVICES
initprintf("wm_ynbox called, this is bad! Message: %s: %s",name,buf);
initprintf("Returning false..");
return 0;
#else
{
int32_t 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;
}
#endif
}
#endif
void wm_setapptitle(const char *name)
{
#ifndef EDUKE32_TOUCH_DEVICES
if (name != apptitle)
Bstrncpyz(apptitle, name, sizeof(apptitle));
#if !defined(__APPLE__)
if (!appicon)
appicon = loadappicon();
#endif
#if SDL_MAJOR_VERSION == 1
SDL_WM_SetCaption(apptitle, NULL);
if (appicon && sdl_surface)
SDL_WM_SetIcon(appicon, 0);
#else
SDL_SetWindowTitle(sdl_window, apptitle);
if (appicon)
SDL_SetWindowIcon(sdl_window, appicon);
#endif
startwin_settitle(apptitle);
#else
UNREFERENCED_PARAMETER(name);
#endif
}
//
//
// ---------------------------------------
//
// System
//
// ---------------------------------------
//
//
static void attach_debugger_here(void) {}
/* XXX: libexecinfo could be used on systems without gnu libc. */
#if !defined _WIN32 && defined __GNUC__ && !defined __OpenBSD__ && !(defined __APPLE__ && defined __BIG_ENDIAN__) && !defined GEKKO && !defined EDUKE32_TOUCH_DEVICES && !defined __OPENDINGUX__
# define PRINTSTACKONSEGV 1
# include <execinfo.h>
#endif
static inline char grabmouse_low(char a);
static void sighandler(int signum)
{
UNREFERENCED_PARAMETER(signum);
// if (signum==SIGSEGV)
{
grabmouse_low(0);
#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);
#endif
attach_debugger_here();
app_crashhandler();
uninitsystem();
Bexit(8);
}
}
#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
if (mobile_halted == 1)
G_Shutdown();
mobile_halted = 1;
return 0;
case SDL_APP_LOWMEMORY:
gltexinvalidatetype(INVALIDATE_ALL);
return 0;
case SDL_APP_WILLENTERBACKGROUND:
mobile_halted = 1;
return 0;
case SDL_APP_DIDENTERBACKGROUND:
// 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;
}
}
#endif
#ifdef _WIN32
int32_t WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int32_t nCmdShow)
#else
int32_t main(int32_t argc, char *argv[])
#endif
{
int32_t r;
#ifdef USE_OPENGL
char *argp;
if ((argp = Bgetenv("BUILD_NOFOG")) != NULL)
nofog = Batol(argp);
#endif
buildkeytranslationtable();
#ifndef __ANDROID__
signal(SIGSEGV, sighandler);
signal(SIGILL, sighandler); /* clang -fcatch-undefined-behavior uses an ill. insn */
signal(SIGABRT, sighandler);
signal(SIGFPE, sighandler);
#else
SDL_SetEventFilter(sdlayer_mobilefilter, NULL);
#endif
#ifdef _WIN32
UNREFERENCED_PARAMETER(hInst);
UNREFERENCED_PARAMETER(hPrevInst);
UNREFERENCED_PARAMETER(lpCmdLine);
UNREFERENCED_PARAMETER(nCmdShow);
win_open();
if (!CheckWinVersion())
{
MessageBox(0, "This application requires Windows XP or better to run.", apptitle, MB_OK | MB_ICONSTOP);
return -1;
}
#elif defined(GEKKO)
wii_open();
#elif defined(HAVE_GTK2)
// Pre-initialize SDL video system in order to make sure XInitThreads() is called
// before GTK starts talking to X11.
SDL_Init(SDL_INIT_VIDEO);
gtkbuild_init(&argc, &argv);
#endif
startwin_open();
maybe_redirect_outputs();
baselayer_init();
#ifdef _WIN32
char *argvbuf;
int32_t buildargc = win_buildargs(&argvbuf);
const char **buildargv = (const char **) Bmalloc(sizeof(char *)*(buildargc+1));
char *wp = argvbuf;
for (int i=0; i<buildargc; i++, wp++)
{
buildargv[i] = wp;
while (*wp) wp++;
}
buildargv[buildargc] = NULL;
r = app_main(buildargc, (const char **)buildargv);
#else
r = app_main(argc, (const char **)argv);
#endif
startwin_close();
#ifdef _WIN32
win_close();
#elif defined(HAVE_GTK2)
gtkbuild_exit(r);
#endif
return r;
}
#ifdef USE_OPENGL
void setvsync(int32_t sync);
#if SDL_MAJOR_VERSION != 1
void setvsync(int32_t sync)
{
if (vsync_render == sync) return;
vsync_render = sync;
if (sdl_context)
SDL_GL_SetSwapInterval(vsync_render);
}
#endif
#endif
int32_t sdlayer_checkversion(void);
#if SDL_MAJOR_VERSION != 1
int32_t sdlayer_checkversion(void)
{
SDL_version compiled;
SDL_version linked;
SDL_GetVersion(&linked);
SDL_VERSION(&compiled);
initprintf("Initializing SDL system interface "
"(compiled against SDL version %d.%d.%d, found version %d.%d.%d)\n",
compiled.major, compiled.minor, compiled.patch, linked.major, linked.minor, linked.patch);
if (SDL_VERSIONNUM(linked.major, linked.minor, linked.patch) < SDL_REQUIREDVERSION)
{
/*reject running under SDL versions older than what is stated in sdl_inc.h */
initprintf("You need at least v%d.%d.%d of SDL to run this game\n",SDL_MIN_X,SDL_MIN_Y,SDL_MIN_Z);
return -1;
}
return 0;
}
//
// initsystem() -- init SDL systems
//
int32_t initsystem(void)
{
const int sdlinitflags = SDL_INIT_VIDEO;
mutex_init(&m_initprintf);
#ifdef _WIN32
win_init();
#endif
if (sdlayer_checkversion())
return -1;
if (SDL_Init(sdlinitflags))
{
initprintf("Initialization failed! (%s)\nNon-interactive mode enabled\n", SDL_GetError());
novideo = 1;
#ifdef USE_OPENGL
nogl = 1;
#endif
}
atexit(uninitsystem);
frameplace = 0;
lockcount = 0;
if (!novideo)
{
const char *drvname = SDL_GetVideoDriver(0);
#ifdef USE_OPENGL
if (loadgldriver(getenv("BUILD_GLDRV")))
{
initprintf("Failed loading OpenGL driver. GL modes will be unavailable.\n");
nogl = 1;
}
#endif
if (drvname)
initprintf("Using \"%s\" video driver\n", drvname);
wm_setapptitle(apptitle);
}
return 0;
}
#endif
//
// uninitsystem() -- uninit SDL systems
//
void uninitsystem(void)
{
uninitinput();
uninittimer();
if (appicon)
{
SDL_FreeSurface(appicon);
appicon = NULL;
}
SDL_Quit();
#ifdef _WIN32
win_uninit();
#endif
#ifdef USE_OPENGL
unloadgldriver();
#endif
}
//
// system_getcvars() -- propagate any cvars that are read post-initialization
//
void system_getcvars(void)
{
#ifdef USE_OPENGL
setvsync(vsync);
#endif
}
//
// initprintf() -- prints a formatted string to the intitialization window
//
void initprintf(const char *f, ...)
{
va_list va;
char buf[2048];
va_start(va, f);
Bvsnprintf(buf, sizeof(buf), f, va);
va_end(va);
initputs(buf);
}
//
// initputs() -- prints a string to the intitialization window
//
void initputs(const char *buf)
{
static char dabuf[2048];
#ifdef __ANDROID__
__android_log_print(ANDROID_LOG_INFO,"DUKE", "%s",buf);
#endif
OSD_Puts(buf);
// Bprintf("%s", buf);
mutex_lock(&m_initprintf);
if (Bstrlen(dabuf) + Bstrlen(buf) > 1022)
{
startwin_puts(dabuf);
Bmemset(dabuf, 0, sizeof(dabuf));
}
Bstrcat(dabuf,buf);
if (flushlogwindow || Bstrlen(dabuf) > 768)
{
startwin_puts(dabuf);
#ifndef _WIN32
startwin_idle(NULL);
#else
handleevents();
#endif
Bmemset(dabuf, 0, sizeof(dabuf));
}
mutex_unlock(&m_initprintf);
}
//
// debugprintf() -- prints a formatted debug string to stderr
//
void debugprintf(const char *f, ...)
{
#if defined DEBUGGINGAIDS && !(defined __APPLE__ && defined __BIG_ENDIAN__)
va_list va;
va_start(va,f);
Bvfprintf(stderr, f, va);
va_end(va);
#else
UNREFERENCED_PARAMETER(f);
#endif
}
//
//
// ---------------------------------------
//
// All things Input
//
// ---------------------------------------
//
//
#ifdef _WIN32
static void switchlayout(const char *layout)
{
char layoutname[KL_NAMELENGTH];
GetKeyboardLayoutName(layoutname);
if (!Bstrcmp(layoutname, layout))
return;
initprintf("Switching keyboard layout from %s to %s\n", layoutname, layout);
LoadKeyboardLayout(layout, KLF_ACTIVATE|KLF_SETFORPROCESS|KLF_SUBSTITUTE_OK);
}
static void W_SetKeyboardLayoutUS(int32_t resetp)
{
static char defaultlayoutname[KL_NAMELENGTH];
if (!resetp)
{
static int done = 0;
if (!done)
{
GetKeyboardLayoutName(defaultlayoutname);
// 00000409 is "American English"
switchlayout("00000409");
done = 1;
}
}
else if (defaultlayoutname[0])
{
switchlayout(defaultlayoutname);
}
}
#endif
// static int32_t joyblast=0;
static SDL_Joystick *joydev = NULL;
//
// initinput() -- init input system
//
int32_t initinput(void)
{
int32_t i, j;
#ifdef _WIN32
W_SetKeyboardLayoutUS(0);
#endif
#if defined EDUKE32_OSX
// force OS X to operate in >1 button mouse mode so that LMB isn't adulterated
if (!getenv("SDL_HAS3BUTTONMOUSE"))
putenv("SDL_HAS3BUTTONMOUSE=1");
#endif
if (!keyremapinit)
for (i = 0; i < 256; i++) keyremap[i] = i;
keyremapinit = 1;
inputdevices = 1 | 2; // keyboard (1) and mouse (2)
mousegrab = 0;
memset(key_names, 0, sizeof(key_names));
#if SDL_MAJOR_VERSION == 1
#define SDL_SCANCODE_TO_KEYCODE(x) (SDLKey)(x)
#define SDL_JoystickNameForIndex(x) SDL_JoystickName(x)
#define SDL_NUM_SCANCODES SDLK_LAST
if (SDL_EnableKeyRepeat(250, 30))
initprintf("Error enabling keyboard repeat.\n");
SDL_EnableUNICODE(1); // let's hope this doesn't hit us too hard
#endif
for (i = SDL_NUM_SCANCODES - 1; i >= 0; i--)
{
if (!keytranslation[i])
continue;
Bstrncpyz(key_names[keytranslation[i]], SDL_GetKeyName(SDL_SCANCODE_TO_KEYCODE(i)), sizeof(key_names[i]));
}
if (!SDL_InitSubSystem(SDL_INIT_JOYSTICK))
{
i = SDL_NumJoysticks();
initprintf("%d joystick(s) found\n", i);
for (j = 0; j < i; j++)
initprintf(" %d. %s\n", j + 1, SDL_JoystickNameForIndex(j));
joydev = SDL_JoystickOpen(0);
if (joydev)
{
SDL_JoystickEventState(SDL_ENABLE);
inputdevices |= 4;
// KEEPINSYNC source/gamedefs.h, source/jmact/_control.h
joynumaxes = min(9, SDL_JoystickNumAxes(joydev));
joynumbuttons = min(32, SDL_JoystickNumButtons(joydev));
joynumhats = min((36-joynumbuttons)/4,SDL_JoystickNumHats(joydev));
initprintf("Joystick 1 has %d axes, %d buttons, and %d hat(s).\n", joynumaxes, joynumbuttons, joynumhats);
joyaxis = (int32_t *)Bcalloc(joynumaxes, sizeof(int32_t));
if (joynumhats)
joyhat = (int32_t *)Bcalloc(joynumhats, sizeof(int32_t));
for (i = 0; i < joynumhats; i++) joyhat[i] = -1; // centre
joydead = (uint16_t *)Bcalloc(joynumaxes, sizeof(uint16_t));
joysatur = (uint16_t *)Bcalloc(joynumaxes, sizeof(uint16_t));
}
}
return 0;
}
//
// uninitinput() -- uninit input system
//
void uninitinput(void)
{
#ifdef _WIN32
W_SetKeyboardLayoutUS(1);
#endif
uninitmouse();
if (joydev)
{
SDL_JoystickClose(joydev);
joydev = NULL;
}
}
#ifndef GEKKO
const char *getjoyname(int32_t what, int32_t num)
{
static char tmp[64];
switch (what)
{
case 0: // axis
if ((unsigned)num > (unsigned)joynumaxes)
return NULL;
Bsprintf(tmp, "Axis %d", num);
return (char *)tmp;
case 1: // button
if ((unsigned)num > (unsigned)joynumbuttons)
return NULL;
Bsprintf(tmp, "Button %d", num);
return (char *)tmp;
case 2: // hat
if ((unsigned)num > (unsigned)joynumhats)
return NULL;
Bsprintf(tmp, "Hat %d", num);
return (char *)tmp;
default: return NULL;
}
}
#endif
//
// initmouse() -- init mouse input
//
int32_t initmouse(void)
{
moustat=AppMouseGrab;
grabmouse(AppMouseGrab); // FIXME - SA
return 0;
}
//
// uninitmouse() -- uninit mouse input
//
void uninitmouse(void)
{
grabmouse(0);
moustat = 0;
}
#if SDL_MAJOR_VERSION != 1
//
// grabmouse_low() -- show/hide mouse cursor, lower level (doesn't check state).
// furthermore return 0 if successful.
//
static inline char grabmouse_low(char a)
{
#if !defined EDUKE32_TOUCH_DEVICES && (!defined DEBUGGINGAIDS || defined _WIN32 || defined __APPLE__)
/* FIXME: Maybe it's better to make sure that grabmouse_low
is called only when a window is ready? */
if (sdl_window)
SDL_SetWindowGrab(sdl_window, a ? SDL_TRUE : SDL_FALSE);
return SDL_SetRelativeMouseMode(a ? SDL_TRUE : SDL_FALSE);
#else
UNREFERENCED_PARAMETER(a);
return 0;
#endif
}
#endif
//
// grabmouse() -- show/hide mouse cursor
//
void grabmouse(char a)
{
if (appactive && moustat)
{
#if !defined EDUKE32_TOUCH_DEVICES && (!defined DEBUGGINGAIDS || defined _WIN32 || defined __APPLE__)
if ((a != mousegrab) && !grabmouse_low(a))
#endif
mousegrab = a;
}
else
mousegrab = a;
mousex = mousey = 0;
}
void AppGrabMouse(char a)
{
if (!(a & 2))
{
grabmouse(a);
AppMouseGrab = mousegrab;
}
SDL_ShowCursor((osd && osd->flags & OSD_CAPTURE) ? SDL_ENABLE : SDL_DISABLE);
}
//
// setjoydeadzone() -- sets the dead and saturation zones for the joystick
//
void setjoydeadzone(int32_t axis, uint16_t dead, uint16_t satur)
{
joydead[axis] = dead;
joysatur[axis] = satur;
}
//
// getjoydeadzone() -- gets the dead and saturation zones for the joystick
//
void getjoydeadzone(int32_t axis, uint16_t *dead, uint16_t *satur)
{
*dead = joydead[axis];
*satur = joysatur[axis];
}
//
// releaseallbuttons()
//
void releaseallbuttons(void)
{}
//
//
// ---------------------------------------
//
// All things Timer
// Ken did this
//
// ---------------------------------------
//
//
static uint32_t timerfreq;
static uint32_t timerlastsample;
int32_t timerticspersec=0;
static double msperu64tick = 0;
static void(*usertimercallback)(void) = NULL;
//
// inittimer() -- initialize timer
//
int32_t inittimer(int32_t tickspersecond)
{
if (timerfreq) return 0; // already installed
// initprintf("Initializing timer\n");
#ifdef _WIN32
{
int32_t t = win_inittimer();
if (t < 0)
return t;
}
#endif
timerfreq = 1000;
timerticspersec = tickspersecond;
timerlastsample = SDL_GetTicks() * timerticspersec / timerfreq;
usertimercallback = NULL;
msperu64tick = 1000.0 / (double)getu64tickspersec();
return 0;
}
//
// uninittimer() -- shut down timer
//
void uninittimer(void)
{
if (!timerfreq) return;
timerfreq=0;
#ifdef _WIN32
win_timerfreq=0;
#endif
msperu64tick = 0;
}
//
// sampletimer() -- update totalclock
//
void sampletimer(void)
{
uint32_t i;
int32_t n;
if (!timerfreq) return;
i = SDL_GetTicks();
n = tabledivide32(i * timerticspersec, timerfreq) - timerlastsample;
if (n <= 0) return;
totalclock += n;
timerlastsample += n;
if (usertimercallback)
for (; n > 0; n--) usertimercallback();
}
#if defined LUNATIC
//
// getticks() -- returns the sdl ticks count
//
uint32_t getticks(void)
{
return (uint32_t)SDL_GetTicks();
}
#endif
// high-resolution timers for profiling
#if SDL_MAJOR_VERSION!=1
uint64_t getu64ticks(void)
{
return SDL_GetPerformanceCounter();
}
uint64_t getu64tickspersec(void)
{
return SDL_GetPerformanceFrequency();
}
#endif
// Returns the time since an unspecified starting time in milliseconds.
// (May be not monotonic for certain configurations.)
ATTRIBUTE((flatten))
double gethiticks(void)
{
return (double)getu64ticks() * msperu64tick;
}
//
// gettimerfreq() -- returns the number of ticks per second the timer is configured to generate
//
int32_t gettimerfreq(void)
{
return timerticspersec;
}
//
// installusertimercallback() -- set up a callback function to be called when the timer is fired
//
void(*installusertimercallback(void(*callback)(void)))(void)
{
void(*oldtimercallback)(void);
oldtimercallback = usertimercallback;
usertimercallback = callback;
return oldtimercallback;
}
//
//
// ---------------------------------------
//
// All things Video
//
// ---------------------------------------
//
//
//
// getvalidmodes() -- figure out what video modes are available
//
static int sortmodes(const void *a_, const void *b_)
{
int32_t x;
const struct validmode_t *a = (const struct validmode_t *)a_;
const struct validmode_t *b = (const struct validmode_t *)b_;
if ((x = a->fs - b->fs) != 0) return x;
if ((x = a->bpp - b->bpp) != 0) return x;
if ((x = a->xdim - b->xdim) != 0) return x;
if ((x = a->ydim - b->ydim) != 0) return x;
return 0;
}
static char modeschecked=0;
#if SDL_MAJOR_VERSION != 1
void getvalidmodes(void)
{
int32_t i, maxx = 0, maxy = 0;
SDL_DisplayMode dispmode;
if (modeschecked || novideo)
return;
validmodecnt = 0;
// initprintf("Detecting video modes:\n");
// do fullscreen modes first
for (i = 0; i < SDL_GetNumDisplayModes(0); i++)
{
SDL_GetDisplayMode(0, i, &dispmode);
if ((dispmode.w > MAXXDIM) || (dispmode.h > MAXYDIM))
continue;
// HACK: 8-bit == Software, 32-bit == OpenGL
SDL_ADDMODE(dispmode.w, dispmode.h, 8, 1);
#ifdef USE_OPENGL
if (!nogl)
SDL_ADDMODE(dispmode.w, dispmode.h, 32, 1);
#endif
if ((dispmode.w > maxx) || (dispmode.h > maxy))
{
maxx = dispmode.w;
maxy = dispmode.h;
}
}
SDL_CHECKFSMODES(maxx, maxy);
// add windowed modes next
for (i = 0; defaultres[i][0]; i++)
{
if (!SDL_CHECKMODE(defaultres[i][0], defaultres[i][1]))
continue;
// HACK: 8-bit == Software, 32-bit == OpenGL
SDL_ADDMODE(defaultres[i][0], defaultres[i][1], 8, 0);
#ifdef USE_OPENGL
if (nogl)
continue;
SDL_ADDMODE(defaultres[i][0], defaultres[i][1], 32, 0);
#endif
}
qsort((void *)validmode, validmodecnt, sizeof(struct validmode_t), &sortmodes);
modeschecked = 1;
}
#endif
//
// checkvideomode() -- makes sure the video mode passed is legal
//
int32_t checkvideomode(int32_t *x, int32_t *y, int32_t c, int32_t fs, int32_t forced)
{
int32_t i, nearest=-1, dx, dy, odx=9999, ody=9999;
getvalidmodes();
if (c>8
#ifdef USE_OPENGL
&& nogl
#endif
) return -1;
// fix up the passed resolution values to be multiples of 8
// and at least 320x200 or at most MAXXDIMxMAXYDIM
*x = clamp(*x, 320, MAXXDIM);
*y = clamp(*y, 200, MAXYDIM);
for (i = 0; i < validmodecnt; i++)
{
if (validmode[i].bpp != c || validmode[i].fs != fs)
continue;
dx = klabs(validmode[i].xdim - *x);
dy = klabs(validmode[i].ydim - *y);
if (!(dx | dy))
{
// perfect match
nearest = i;
break;
}
if ((dx <= odx) && (dy <= ody))
{
nearest = i;
odx = dx;
ody = dy;
}
}
#ifdef ANY_WINDOWED_SIZE
if (!forced && (fs&1) == 0 && (nearest < 0 || (validmode[nearest].xdim!=*x || validmode[nearest].ydim!=*y)))
return 0x7fffffffl;
#endif
if (nearest < 0)
return -1;
*x = validmode[nearest].xdim;
*y = validmode[nearest].ydim;
return nearest;
}
static int32_t needpalupdate;
static SDL_Color sdlayer_pal[256];
static void destroy_window_resources()
{
#if !defined SDL_DISABLE_8BIT_BUFFER
if (sdl_buffersurface)
SDL_FreeSurface(sdl_buffersurface);
sdl_buffersurface = NULL;
#endif
/* We should NOT destroy the window surface. This is done automatically
when SDL_DestroyWindow or SDL_SetVideoMode is called. */
#if SDL_MAJOR_VERSION == 2
if (sdl_renderer && sdl_texture && sdl_surface)
SDL_FreeSurface(sdl_surface);
sdl_surface = NULL;
if (sdl_context)
SDL_GL_DeleteContext(sdl_context);
sdl_context = NULL;
if (sdl_texture)
SDL_DestroyTexture(sdl_texture);
sdl_texture = NULL;
if (sdl_renderer)
SDL_DestroyRenderer(sdl_renderer);
sdl_renderer = NULL;
if (sdl_window)
SDL_DestroyWindow(sdl_window);
sdl_window = NULL;
#endif
}
#ifdef USE_OPENGL
void sdlayer_setvideomode_opengl(void)
{
polymost_glreset();
bglEnable(GL_TEXTURE_2D);
bglShadeModel(GL_SMOOTH); // GL_FLAT
bglClearColor(0, 0, 0, 0.5); // Black Background
bglHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Use FASTEST for ortho!
// bglHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
#ifndef EDUKE32_GLES
bglDisable(GL_DITHER);
#endif
glinfo.vendor = (const char *) bglGetString(GL_VENDOR);
glinfo.renderer = (const char *) bglGetString(GL_RENDERER);
glinfo.version = (const char *) bglGetString(GL_VERSION);
glinfo.extensions = (const char *) bglGetString(GL_EXTENSIONS);
#ifdef POLYMER
if (!Bstrcmp(glinfo.vendor, "ATI Technologies Inc."))
{
pr_ati_fboworkaround = 1;
initprintf("Enabling ATI FBO color attachment workaround.\n");
if (Bstrstr(glinfo.renderer, "Radeon X1"))
{
pr_ati_nodepthoffset = 1;
initprintf("Enabling ATI R520 polygon offset workaround.\n");
}
else
pr_ati_nodepthoffset = 0;
#ifdef __APPLE__
// See bug description at http://lists.apple.com/archives/mac-opengl/2005/Oct/msg00169.html
if (!Bstrncmp(glinfo.renderer, "ATI Radeon 9600", 15))
{
pr_ati_textureformat_one = 1;
initprintf("Enabling ATI Radeon 9600 texture format workaround.\n");
}
else
pr_ati_textureformat_one = 0;
#endif
}
else
pr_ati_fboworkaround = 0;
#endif // defined POLYMER
glinfo.maxanisotropy = 1.0;
glinfo.bgra = 0;
glinfo.texcompr = 0;
glinfo.clamptoedge = 1;
glinfo.multitex = 1;
// process the extensions string and flag stuff we recognize
glinfo.texcompr = !!Bstrstr(glinfo.extensions, "GL_ARB_texture_compression") && Bstrcmp(glinfo.vendor, "ATI Technologies Inc.");
glinfo.texnpot = !!Bstrstr(glinfo.extensions, "GL_ARB_texture_non_power_of_two") || !!Bstrstr(glinfo.extensions, "GL_OES_texture_npot");
glinfo.multisample = !!Bstrstr(glinfo.extensions, "GL_ARB_multisample");
glinfo.nvmultisamplehint = !!Bstrstr(glinfo.extensions, "GL_NV_multisample_filter_hint");
glinfo.arbfp = !!Bstrstr(glinfo.extensions, "GL_ARB_fragment_program");
glinfo.depthtex = !!Bstrstr(glinfo.extensions, "GL_ARB_depth_texture");
glinfo.shadow = !!Bstrstr(glinfo.extensions, "GL_ARB_shadow");
glinfo.fbos = !!Bstrstr(glinfo.extensions, "GL_EXT_framebuffer_object") || !!Bstrstr(glinfo.extensions, "GL_OES_framebuffer_object");
#ifndef __ANDROID__
glinfo.bgra = !!Bstrstr(glinfo.extensions, "GL_EXT_bgra");
glinfo.clamptoedge = !!Bstrstr(glinfo.extensions, "GL_EXT_texture_edge_clamp") ||
!!Bstrstr(glinfo.extensions, "GL_SGIS_texture_edge_clamp");
glinfo.rect =
!!Bstrstr(glinfo.extensions, "GL_NV_texture_rectangle") || !!Bstrstr(glinfo.extensions, "GL_EXT_texture_rectangle");
glinfo.multitex = !!Bstrstr(glinfo.extensions, "GL_ARB_multitexture");
glinfo.envcombine = !!Bstrstr(glinfo.extensions, "GL_ARB_texture_env_combine");
glinfo.vbos = !!Bstrstr(glinfo.extensions, "GL_ARB_vertex_buffer_object");
glinfo.sm4 = !!Bstrstr(glinfo.extensions, "GL_EXT_gpu_shader4");
glinfo.occlusionqueries = !!Bstrstr(glinfo.extensions, "GL_ARB_occlusion_query");
glinfo.glsl = !!Bstrstr(glinfo.extensions, "GL_ARB_shader_objects");
glinfo.debugoutput = !!Bstrstr(glinfo.extensions, "GL_ARB_debug_output");
glinfo.bufferstorage = !!Bstrstr(glinfo.extensions, "GL_ARB_buffer_storage");
if (Bstrstr(glinfo.extensions, "WGL_3DFX_gamma_control"))
{
static int32_t warnonce;
// 3dfx cards have issues with fog
nofog = 1;
if (!(warnonce & 1))
initprintf("3dfx card detected: OpenGL fog disabled\n");
warnonce |= 1;
}
#endif
// if (Bstrstr(glinfo.extensions, "GL_EXT_texture_filter_anisotropic"))
bglGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glinfo.maxanisotropy);
if (!glinfo.dumped)
{
int32_t oldbpp = bpp;
bpp = 32;
osdcmd_glinfo(NULL);
glinfo.dumped = 1;
bpp = oldbpp;
}
}
#endif // defined USE_OPENGL
//
// setvideomode() -- set SDL video mode
//
int32_t setvideomode_sdlcommon(int32_t *x, int32_t *y, int32_t c, int32_t fs, int32_t *regrab)
{
if ((fs == fullscreen) && (*x == xres) && (*y == yres) && (c == bpp) && !videomodereset)
{
OSD_ResizeDisplay(xres, yres);
return 0;
}
if (checkvideomode(x, y, c, fs, 0) < 0)
return -1;
#ifdef GEKKO
if (!sdl_surface) // only run this the first time we set a video mode
wii_initgamevideo();
#endif
startwin_close();
if (mousegrab)
{
*regrab = 1;
grabmouse(0);
}
while (lockcount) enddrawing();
#ifdef USE_OPENGL
if (bpp > 8 && sdl_surface)
polymost_glreset();
#endif
// clear last gamma/contrast/brightness so that it will be set anew
lastvidgcb[0] = lastvidgcb[1] = lastvidgcb[2] = 0.0f;
return 1;
}
void setvideomode_sdlcommonpost(int32_t x, int32_t y, int32_t c, int32_t fs, int32_t regrab)
{
wm_setapptitle(apptitle);
#ifdef USE_OPENGL
if (c > 8)
sdlayer_setvideomode_opengl();
#endif
xres = x;
yres = y;
bpp = c;
fullscreen = fs;
// bytesperline = sdl_surface->pitch;
numpages = c > 8 ? 2 : 1;
frameplace = 0;
lockcount = 0;
modechange = 1;
videomodereset = 0;
OSD_ResizeDisplay(xres, yres);
// save the current system gamma to determine if gamma is available
if (!gammabrightness)
{
// float f = 1.0 + ((float)curbrightness / 10.0);
#if SDL_MAJOR_VERSION != 1
if (SDL_GetWindowGammaRamp(sdl_window, sysgamma[0], sysgamma[1], sysgamma[2]) == 0)
#else
if (SDL_GetGammaRamp(sysgamma[0], sysgamma[1], sysgamma[2]) >= 0)
#endif
gammabrightness = 1;
// see if gamma really is working by trying to set the brightness
if (gammabrightness && setgamma() < 0)
gammabrightness = 0; // nope
}
setpalettefade(palfadergb.r, palfadergb.g, palfadergb.b, palfadedelta);
if (regrab)
grabmouse(AppMouseGrab);
}
#if SDL_MAJOR_VERSION!=1
int32_t setvideomode(int32_t x, int32_t y, int32_t c, int32_t fs)
{
int32_t regrab = 0, ret;
ret = setvideomode_sdlcommon(&x, &y, c, fs, &regrab);
if (ret != 1) return ret;
// deinit
destroy_window_resources();
initprintf("Setting video mode %dx%d (%d-bpp %s)\n", x, y, c, ((fs & 1) ? "fullscreen" : "windowed"));
#ifdef USE_OPENGL
if (c > 8)
{
int32_t i, j;
#ifdef USE_GLEXT
int32_t multisamplecheck = (glmultisample > 0);
#else
int32_t multisamplecheck = 0;
#endif
if (nogl)
return -1;
#ifdef _WIN32
win_setvideomode(c);
#endif
struct glattribs
{
SDL_GLattr attr;
int32_t value;
} sdlayer_gl_attributes[] =
{
#ifdef EDUKE32_TOUCH_DEVICES
{ SDL_GL_CONTEXT_MAJOR_VERSION, 1 },
{ SDL_GL_CONTEXT_MINOR_VERSION, 1 },
#endif
{ SDL_GL_DOUBLEBUFFER, 1 },
#ifdef USE_GLEXT
{ SDL_GL_MULTISAMPLEBUFFERS, glmultisample > 0 },
{ SDL_GL_MULTISAMPLESAMPLES, glmultisample },
#endif
{ SDL_GL_STENCIL_SIZE, 1 },
{ SDL_GL_ACCELERATED_VISUAL, 1 },
};
do
{
SDL_GL_ATTRIBUTES(i, sdlayer_gl_attributes);
/* HACK: changing SDL GL attribs only works before surface creation,
so we have to create a new surface in a different format first
to force the surface we WANT to be recreated instead of reused. */
sdl_window = SDL_CreateWindow("", windowpos ? windowx : SDL_WINDOWPOS_CENTERED,
windowpos ? windowy : SDL_WINDOWPOS_CENTERED, x, y,
((fs & 1) ? SDL_WINDOW_FULLSCREEN : 0) | SDL_WINDOW_OPENGL);
if (sdl_window)
sdl_context = SDL_GL_CreateContext(sdl_window);
if (!sdl_window || !sdl_context)
{
initprintf("Unable to set video mode: %s failed: %s\n", sdl_window ? "SDL_GL_CreateContext" : "SDL_GL_CreateWindow", SDL_GetError());
destroy_window_resources();
return -1;
}
SDL_GL_SetSwapInterval(vsync_render);
#ifdef _WIN32
loadglextensions();
#endif
} while (multisamplecheck--);
}
else
#endif // defined USE_OPENGL
{
// init
sdl_window = SDL_CreateWindow("", windowpos ? windowx : SDL_WINDOWPOS_CENTERED,
windowpos ? windowy : SDL_WINDOWPOS_CENTERED, x, y,
((fs & 1) ? SDL_WINDOW_FULLSCREEN : 0));
if (!sdl_window)
SDL2_VIDEO_ERR("SDL_CreateWindow");
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
if (!sdl_renderer)
SDL2_VIDEO_FALLBACK("SDL_CreateRenderer")
else
{
SDL_RendererInfo sdl_rendererinfo;
SDL_GetRendererInfo(sdl_renderer, &sdl_rendererinfo);
if (sdl_rendererinfo.flags & SDL_RENDERER_SOFTWARE) // this would be useless
SDL2_RENDERER_DESTROY(sdl_renderer);
else
{
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, x, y);
if (!sdl_texture)
{
SDL2_VIDEO_FALLBACK("SDL_CreateTexture");
SDL2_RENDERER_DESTROY(sdl_renderer);
}
}
if (sdl_texture)
{
sdl_surface = SDL_CreateRGBSurface(0, x, y, 32, 0, 0, 0, 0);
if (!sdl_surface)
{
SDL2_VIDEO_FALLBACK("SDL_CreateRGBSurface");
SDL_DestroyTexture(sdl_texture);
sdl_texture = NULL;
SDL2_RENDERER_DESTROY(sdl_renderer);
}
}
}
if (!sdl_surface)
{
sdl_surface = SDL_GetWindowSurface(sdl_window);
if (!sdl_surface)
SDL2_VIDEO_ERR("SDL_GetWindowSurface");
}
#if !defined SDL_DISABLE_8BIT_BUFFER
sdl_buffersurface = SDL_CreateRGBSurface(0, x, y, c, 0, 0, 0, 0);
if (!sdl_buffersurface)
SDL2_VIDEO_ERR("SDL_CreateRGBSurface");
#endif
if (!sdl_palptr)
sdl_palptr = SDL_AllocPalette(256);
if (SDL_SetSurfacePalette(sdl_buffersurface, sdl_palptr) < 0)
initprintf("SDL_SetSurfacePalette failed: %s\n", SDL_GetError());
}
setvideomode_sdlcommonpost(x, y, c, fs, regrab);
return 0;
}
#endif
//
// resetvideomode() -- resets the video system
//
void resetvideomode(void)
{
videomodereset = 1;
modeschecked = 0;
}
//
// begindrawing() -- locks the framebuffer for drawing
//
#ifdef DEBUG_FRAME_LOCKING
uint32_t begindrawing_line[BEGINDRAWING_SIZE];
const char *begindrawing_file[BEGINDRAWING_SIZE];
void begindrawing_real(void)
#else
void begindrawing(void)
#endif
{
if (bpp > 8)
{
if (offscreenrendering) return;
frameplace = 0;
bytesperline = 0;
modechange = 0;
return;
}
// lock the frame
if (lockcount++ > 0)
return;
if (offscreenrendering) return;
if (SDL_MUSTLOCK(sdl_buffersurface)) SDL_LockSurface(sdl_buffersurface);
frameplace = (intptr_t)sdl_buffersurface->pixels;
if (sdl_buffersurface->pitch != bytesperline || modechange)
{
bytesperline = sdl_buffersurface->pitch;
calc_ylookup(bytesperline, ydim);
modechange=0;
}
}
//
// enddrawing() -- unlocks the framebuffer
//
void enddrawing(void)
{
if (bpp > 8)
{
if (!offscreenrendering) frameplace = 0;
return;
}
if (!frameplace) return;
if (lockcount > 1) { lockcount--; return; }
if (!offscreenrendering) frameplace = 0;
if (lockcount == 0) return;
lockcount = 0;
if (offscreenrendering) return;
if (SDL_MUSTLOCK(sdl_buffersurface)) SDL_UnlockSurface(sdl_buffersurface);
}
//
// showframe() -- update the display
//
#if SDL_MAJOR_VERSION != 1
#ifdef __ANDROID__
extern "C" void frameControls();
#endif
void showframe(int32_t w)
{
UNREFERENCED_PARAMETER(w);
#ifdef __ANDROID__
if (mobile_halted) return;
#endif
#ifdef USE_OPENGL
if (bpp > 8)
{
if (palfadedelta)
fullscreen_tint_gl(palfadergb.r, palfadergb.g, palfadergb.b, palfadedelta);
#ifdef __ANDROID__
frameControls();
#endif
SDL_GL_SwapWindow(sdl_window);
return;
}
#endif
if (offscreenrendering) return;
if (lockcount)
{
printf("Frame still locked %d times when showframe() called.\n", lockcount);
while (lockcount) enddrawing();
}
// deferred palette updating
if (needpalupdate)
{
if (SDL_SetPaletteColors(sdl_palptr, sdlayer_pal, 0, 256) < 0)
initprintf("SDL_SetPaletteColors failed: %s\n", SDL_GetError());
needpalupdate = 0;
}
#if !defined SDL_DISABLE_8BIT_BUFFER
SDL_BlitSurface(sdl_buffersurface, NULL, sdl_surface, NULL);
#endif
if (sdl_renderer && sdl_texture)
{
SDL_UpdateTexture(sdl_texture, NULL, sdl_surface->pixels, sdl_surface->pitch);
SDL_RenderClear(sdl_renderer);
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
SDL_RenderPresent(sdl_renderer);
}
else if (SDL_UpdateWindowSurface(sdl_window))
{
// If a fullscreen X11 window is minimized then this may be required.
// FIXME: What to do if this fails...
sdl_surface = SDL_GetWindowSurface(sdl_window);
SDL_UpdateWindowSurface(sdl_window);
}
}
#endif
//
// setpalette() -- set palette values
//
int32_t setpalette(int32_t start, int32_t num)
{
int32_t i, n;
if (bpp > 8)
return 0; // no palette in opengl
Bmemcpy(sdlayer_pal, curpalettefaded, 256 * 4);
for (i = start, n = num; n > 0; i++, n--)
curpalettefaded[i].f =
#if SDL_MAJOR_VERSION == 1
sdlayer_pal[i].unused
#else
sdlayer_pal[i].a
#endif
= 0;
needpalupdate = 1;
return 0;
}
//
// setgamma
//
int32_t setgamma(void)
{
// return 0;
int32_t i;
uint16_t gammaTable[768];
float gamma = max(0.1f, min(4.f, vid_gamma));
float contrast = max(0.1f, min(3.f, vid_contrast));
float bright = max(-0.8f, min(0.8f, vid_brightness));
float invgamma = 1.f / gamma;
float norm = powf(255.f, invgamma - 1.f);
if (novideo)
return 0;
if (lastvidgcb[0] == gamma && lastvidgcb[1] == contrast && lastvidgcb[2] == bright)
return 0;
// This formula is taken from Doomsday
for (i = 0; i < 256; i++)
{
float val = i * contrast - (contrast - 1.f) * 127.f;
if (gamma != 1.f)
val = powf(val, invgamma) / norm;
val += bright * 128.f;
gammaTable[i] = gammaTable[i + 256] = gammaTable[i + 512] = (uint16_t)max(0.f, min(65535.f, val * 256.f));
}
#if SDL_MAJOR_VERSION == 1
i = SDL_SetGammaRamp(&gammaTable[0], &gammaTable[256], &gammaTable[512]);
if (i != -1)
#else
i = INT32_MIN;
if (sdl_window)
i = SDL_SetWindowGammaRamp(sdl_window, &gammaTable[0], &gammaTable[256], &gammaTable[512]);
if (i < 0)
{
#ifndef __ANDROID__ // Don't do this check, it is really supported, TODO
if (i != INT32_MIN)
initprintf("Unable to set gamma: SDL_SetWindowGammaRamp failed: %s\n", SDL_GetError());
#endif
}
else
#endif
{
lastvidgcb[0] = gamma;
lastvidgcb[1] = contrast;
lastvidgcb[2] = bright;
}
return i;
}
#if !defined __APPLE__ && !defined EDUKE32_TOUCH_DEVICES
extern struct sdlappicon sdlappicon;
static inline SDL_Surface *loadappicon(void)
{
SDL_Surface *surf = SDL_CreateRGBSurfaceFrom((void *)sdlappicon.pixels, sdlappicon.width, sdlappicon.height, 32,
sdlappicon.width * 4, 0xffl, 0xff00l, 0xff0000l, 0xff000000l);
return surf;
}
#endif
//
//
// ---------------------------------------
//
// Miscellany
//
// ---------------------------------------
//
//
int32_t handleevents_peekkeys(void)
{
SDL_PumpEvents();
#if SDL_MAJOR_VERSION==1
return SDL_PeepEvents(NULL, 1, SDL_PEEKEVENT, SDL_EVENTMASK(SDL_KEYDOWN));
#else
return SDL_PeepEvents(NULL, 1, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_KEYDOWN);
#endif
}
void handleevents_updatemousestate(uint8_t state)
{
mousepressstate = state == SDL_RELEASED ? Mouse_Released : Mouse_Pressed;
}
//
// handleevents() -- process the SDL message queue
// returns !0 if there was an important event worth checking (like quitting)
//
int32_t handleevents_sdlcommon(SDL_Event *ev)
{
switch (ev->type)
{
#if !defined EDUKE32_IOS
case SDL_MOUSEMOTION:
#ifndef GEKKO
mouseabs.x = ev->motion.x;
mouseabs.y = ev->motion.y;
#endif
// SDL <VER> doesn't handle relative mouse movement correctly yet as the cursor still clips to the
// screen edges
// so, we call SDL_WarpMouse() to center the cursor and ignore the resulting motion event that occurs
// <VER> is 1.3 for PK, 1.2 for tueidj
if (appactive && mousegrab)
{
if (ev->motion.x != xdim >> 1 || ev->motion.y != ydim >> 1)
{
mousex += ev->motion.xrel;
mousey += ev->motion.yrel;
#if !defined DEBUGGINGAIDS || MY_DEVELOPER_ID == 805120924
# if SDL_MAJOR_VERSION==1
SDL_WarpMouse(xdim>>1, ydim>>1);
# else
SDL_WarpMouseInWindow(sdl_window, xdim>>1, ydim>>1);
# endif
#endif
}
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
{
int32_t j;
// some of these get reordered to match winlayer
switch (ev->button.button)
{
default: j = -1; break;
case SDL_BUTTON_LEFT: j = 0; handleevents_updatemousestate(ev->button.state); break;
case SDL_BUTTON_RIGHT: j = 1; break;
case SDL_BUTTON_MIDDLE: j = 2; break;
#if SDL_MAJOR_VERSION == 1
case SDL_BUTTON_WHEELUP: // 4
case SDL_BUTTON_WHEELDOWN: // 5
j = ev->button.button;
break;
#endif
/* Thumb buttons. */
#if SDL_MAJOR_VERSION==1 || !defined _WIN32
// NOTE: SDL1 does have SDL_BUTTON_X1, but that's not what is
// generated. Neither with SDL2 on Linux. (Other OSs: not tested.)
case 8: j = 3; break;
case 9: j = 6; break;
#else
// On SDL2/Windows, everything is as it should be.
case SDL_BUTTON_X1: j = 3; break;
case SDL_BUTTON_X2: j = 6; break;
#endif
}
if (j < 0)
break;
if (ev->button.state == SDL_PRESSED)
mouseb |= (1 << j);
else
#if SDL_MAJOR_VERSION==1
if (j != SDL_BUTTON_WHEELUP && j != SDL_BUTTON_WHEELDOWN)
#endif
mouseb &= ~(1 << j);
if (mousepresscallback)
mousepresscallback(j+1, ev->button.state == SDL_PRESSED);
break;
}
#else
# if SDL_MAJOR_VERSION != 1
case SDL_FINGERUP:
mousepressstate = Mouse_Released;
break;
case SDL_FINGERDOWN:
mousepressstate = Mouse_Pressed;
case SDL_FINGERMOTION:
mouseabs.x = Blrintf(ev->tfinger.x * xdim);
mouseabs.y = Blrintf(ev->tfinger.y * ydim);
break;
# endif
#endif
case SDL_JOYAXISMOTION:
if (appactive && ev->jaxis.axis < joynumaxes)
{
joyaxis[ev->jaxis.axis] = ev->jaxis.value * 10000 / 32767;
if ((joyaxis[ev->jaxis.axis] < joydead[ev->jaxis.axis]) &&
(joyaxis[ev->jaxis.axis] > -joydead[ev->jaxis.axis]))
joyaxis[ev->jaxis.axis] = 0;
else if (joyaxis[ev->jaxis.axis] >= joysatur[ev->jaxis.axis])
joyaxis[ev->jaxis.axis] = 10000;
else if (joyaxis[ev->jaxis.axis] <= -joysatur[ev->jaxis.axis])
joyaxis[ev->jaxis.axis] = -10000;
else
joyaxis[ev->jaxis.axis] = joyaxis[ev->jaxis.axis] * 10000 / joysatur[ev->jaxis.axis];
}
break;
case SDL_JOYHATMOTION:
{
int32_t hatvals[16] = {
-1, // centre
0, // up 1
9000, // right 2
4500, // up+right 3
18000, // down 4
-1, // down+up!! 5
13500, // down+right 6
-1, // down+right+up!! 7
27000, // left 8
27500, // left+up 9
-1, // left+right!! 10
-1, // left+right+up!! 11
22500, // left+down 12
-1, // left+down+up!! 13
-1, // left+down+right!! 14
-1, // left+down+right+up!! 15
};
if (appactive && ev->jhat.hat < joynumhats)
joyhat[ev->jhat.hat] = hatvals[ev->jhat.value & 15];
break;
}
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
if (appactive && ev->jbutton.button < joynumbuttons)
{
if (ev->jbutton.state == SDL_PRESSED)
joyb |= 1 << ev->jbutton.button;
else
joyb &= ~(1 << ev->jbutton.button);
#ifdef GEKKO
if (ev->jbutton.button == 0) // WII_A
handleevents_updatemousestate(ev->jbutton.state);
#endif
}
break;
case SDL_QUIT:
quitevent = 1;
return -1;
}
return 0;
}
int32_t handleevents_pollsdl(void);
#if SDL_MAJOR_VERSION != 1
// SDL 2.0 specific event handling
int32_t handleevents_pollsdl(void)
{
int32_t code, rv=0, j;
SDL_Event ev;
while (SDL_PollEvent(&ev))
{
switch (ev.type)
{
case SDL_TEXTINPUT:
j = 0;
do
{
code = ev.text.text[j];
if (code != scantoasc[OSD_OSDKey()] && !keyascfifo_isfull())
{
if (OSD_HandleChar(code))
keyascfifo_insert(code);
}
} while (j < SDL_TEXTINPUTEVENT_TEXT_SIZE && ev.text.text[++j]);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
{
const SDL_Scancode sc = ev.key.keysym.scancode;
code = keytranslation[sc];
// XXX: see osd.c, OSD_HandleChar(), there are more...
if (ev.key.type == SDL_KEYDOWN && !keyascfifo_isfull() &&
(sc == SDL_SCANCODE_RETURN || sc == SDL_SCANCODE_KP_ENTER ||
sc == SDL_SCANCODE_ESCAPE ||
sc == SDL_SCANCODE_BACKSPACE ||
sc == SDL_SCANCODE_TAB ||
(ev.key.keysym.mod==KMOD_LCTRL &&
(sc >= SDL_SCANCODE_A && sc <= SDL_SCANCODE_Z))))
{
char keyvalue;
switch (sc)
{
case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: keyvalue = '\r'; break;
case SDL_SCANCODE_ESCAPE: keyvalue = 27; break;
case SDL_SCANCODE_BACKSPACE: keyvalue = '\b'; break;
case SDL_SCANCODE_TAB: keyvalue = '\t'; break;
default: keyvalue = sc - SDL_SCANCODE_A + 1; break; // Ctrl+A --> 1, etc.
}
if (OSD_HandleChar(keyvalue))
keyascfifo_insert(keyvalue);
}
// initprintf("SDL2: got key %d, %d, %u\n", ev.key.keysym.scancode, code, ev.key.type);
// hook in the osd
if ((j = OSD_HandleScanCode(code, (ev.key.type == SDL_KEYDOWN))) <= 0)
{
if (j == -1) // osdkey
for (j = 0; j < KEYSTATUSSIZ; ++j)
if (GetKey(j))
{
SetKey(j, 0);
if (keypresscallback)
keypresscallback(j, 0);
}
break;
}
if (ev.key.type == SDL_KEYDOWN)
{
if (!GetKey(code))
{
SetKey(code, 1);
if (keypresscallback)
keypresscallback(code, 1);
}
}
else
{
# if 1
// The pause key generates a release event right after
// the pressing one. As a result, it gets unseen
// by the game most of the time.
if (code == 0x59) // pause
break;
# endif
SetKey(code, 0);
if (keypresscallback)
keypresscallback(code, 0);
}
break;
}
case SDL_MOUSEWHEEL:
// initprintf("wheel y %d\n",ev.wheel.y);
if (ev.wheel.y > 0)
{
mouseb |= 16;
if (mousepresscallback)
mousepresscallback(5, 1);
}
if (ev.wheel.y < 0)
{
mouseb |= 32;
if (mousepresscallback)
mousepresscallback(6, 1);
}
break;
case SDL_WINDOWEVENT:
switch (ev.window.event)
{
case SDL_WINDOWEVENT_FOCUS_GAINED:
case SDL_WINDOWEVENT_FOCUS_LOST:
appactive = (ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED);
if (mousegrab && moustat)
grabmouse_low(appactive);
#ifdef _WIN32
if (backgroundidle)
SetPriorityClass(GetCurrentProcess(), appactive ? NORMAL_PRIORITY_CLASS : IDLE_PRIORITY_CLASS);
#endif
break;
case SDL_WINDOWEVENT_MOVED:
if (windowpos)
{
windowx = ev.window.data1;
windowy = ev.window.data2;
}
break;
case SDL_WINDOWEVENT_ENTER:
mouseinwindow = 1;
break;
case SDL_WINDOWEVENT_LEAVE:
mouseinwindow = 0;
break;
}
break;
default:
rv = handleevents_sdlcommon(&ev);
break;
}
}
return rv;
}
#endif
int32_t handleevents(void)
{
#ifdef __ANDROID__
extern int mobile_halted;
if (mobile_halted) return 0;
#endif
int32_t rv;
if (inputchecked && moustat)
{
if (mousepresscallback)
{
if (mouseb & 16)
mousepresscallback(5, 0);
if (mouseb & 32)
mousepresscallback(6, 0);
}
mouseb &= ~(16 | 32);
}
rv = handleevents_pollsdl();
inputchecked = 0;
sampletimer();
#ifndef _WIN32
startwin_idle(NULL);
#endif
return rv;
}
#if SDL_MAJOR_VERSION == 1
#include "sdlayer12.c"
#endif