RingRacers/src/sdl/i_video.cpp
bitten2up cfacbd91be Fix implicit casts of int expecting 4-byte width
This fixes the issue with certain compilers that have int set to
different sizes by either explicitly casting or setting templates
manually
2024-05-03 17:53:53 +00:00

1842 lines
47 KiB
C++

// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2024 by Kart Krew.
// Copyright (C) 2020 by Sonic Team Junior.
// Copyright (C) 2000 by DooM Legacy Team.
// Copyright (C) 1996 by id Software, Inc.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \brief SRB2 graphics stuff for SDL
#include <stdlib.h>
#include <errno.h>
#include <memory>
#include <signal.h>
#include <imgui.h>
#include "../rhi/rhi.hpp"
#include "../rhi/gl2/gl2_rhi.hpp"
#include "rhi_gl2_platform.hpp"
#ifdef _MSC_VER
#pragma warning(disable : 4214 4244)
#endif
#ifdef HAVE_SDL
#define _MATH_DEFINES_DEFINED
#include "SDL.h"
#ifdef _MSC_VER
#include <windows.h>
#pragma warning(default : 4214 4244)
#endif
#ifdef HAVE_TTF
#include "i_ttf.h"
#endif
#ifdef HAVE_IMAGE
#include "SDL_image.h"
#elif defined (__unix__) || (!defined(__APPLE__) && defined (UNIXCOMMON)) // Windows & Mac don't need this, as SDL will do it for us.
#define LOAD_XPM //I want XPM!
#include "IMG_xpm.c" //Alam: I don't want to add SDL_Image.dll/so
#define HAVE_IMAGE //I have SDL_Image, sortof
#endif
#ifdef HAVE_IMAGE
#include "SDL_icon.xpm"
#endif
#include "../doomdef.h"
#ifdef _WIN32
#include "SDL_syswm.h"
#endif
#include "../doomstat.h"
#include "../i_system.h"
#include "../v_video.h"
#include "../m_argv.h"
#include "../k_menu.h"
#include "../d_main.h"
#include "../s_sound.h"
#include "../i_sound.h" // midi pause/unpause
#include "../i_joy.h"
#include "../st_stuff.h"
#include "../hu_stuff.h"
#include "../g_game.h"
#include "../i_video.h"
#include "../console.h"
#include "../command.h"
#include "../r_main.h"
#include "../lua_hook.h"
#include "sdlmain.h"
#include "../i_system.h"
#ifdef HWRENDER
#include "../hardware/hw_main.h"
#include "../hardware/hw_drv.h"
// For dynamic referencing of HW rendering functions
#include "hwsym_sdl.h"
#include "ogl_sdl.h"
#endif
#ifdef HAVE_DISCORDRPC
#include "../discord.h"
#endif
// maximum number of windowed modes (see windowedModes[][])
#define MAXWINMODES (18)
using namespace srb2;
/** \brief
*/
static INT32 numVidModes = -1;
/** \brief
*/
static char vidModeName[33][32]; // allow 33 different modes
rendermode_t rendermode = render_soft;
rendermode_t chosenrendermode = render_none; // set by command line arguments
UINT8 graphics_started = 0; // Is used in console.c and screen.c
// To disable fullscreen at startup; is set in VID_PrepareModeList
boolean allow_fullscreen = false;
static SDL_bool disable_fullscreen = SDL_FALSE;
#define USE_FULLSCREEN (disable_fullscreen||!allow_fullscreen)?0:cv_fullscreen.value
static SDL_bool disable_mouse = SDL_FALSE;
#define USE_MOUSEINPUT (!disable_mouse && cv_usemouse.value && havefocus)
#define MOUSE_MENU false //(!disable_mouse && cv_usemouse.value && menuactive && !USE_FULLSCREEN)
#define MOUSEBUTTONS_MAX MOUSEBUTTONS
// first entry in the modelist which is not bigger than MAXVIDWIDTHxMAXVIDHEIGHT
static INT32 firstEntry = 0;
// Total mouse motion X/Y offsets
static INT32 mousemovex = 0, mousemovey = 0;
// SDL vars
static SDL_Surface *vidSurface = NULL;
static SDL_Surface *bufSurface = NULL;
static SDL_Surface *icoSurface = NULL;
static SDL_Color localPalette[256];
Uint16 realwidth = BASEVIDWIDTH;
Uint16 realheight = BASEVIDHEIGHT;
static SDL_bool mousegrabok = SDL_TRUE;
static SDL_bool wrapmouseok = SDL_FALSE;
static SDL_bool exposevideo = SDL_FALSE;
static SDL_bool borderlesswindow = SDL_FALSE;
// SDL2 vars
SDL_Window *window;
static SDL_bool havefocus = SDL_TRUE;
static const char *fallback_resolution_name = "Fallback";
static std::unique_ptr<rhi::Rhi> g_rhi;
static uint32_t g_rhi_generation = 0;
// windowed video modes from which to choose from.
static INT32 windowedModes[MAXWINMODES][2] =
{
{1920,1200}, // 1.60,6.00
{1920,1080}, // 1.66
{1680,1050}, // 1.60,5.25
{1600,1200}, // 1.33
{1600, 900}, // 1.66
{1366, 768}, // 1.66
{1440, 900}, // 1.60,4.50
{1280,1024}, // 1.33?
{1280, 960}, // 1.33,4.00
{1280, 800}, // 1.60,4.00
{1280, 720}, // 1.66
{1152, 864}, // 1.33,3.60
{1024, 768}, // 1.33,3.20
{ 800, 600}, // 1.33,2.50
{ 640, 480}, // 1.33,2.00
{ 640, 400}, // 1.60,2.00
{ 320, 240}, // 1.33,1.00
{ 320, 200}, // 1.60,1.00
};
static void Impl_VideoSetupBuffer(void);
static SDL_bool Impl_CreateWindow(SDL_bool fullscreen);
//static void Impl_SetWindowName(const char *title);
static void Impl_SetWindowIcon(void);
static void SDLSetMode(int width, int height, SDL_bool fullscreen, SDL_bool reposition)
{
static SDL_bool wasfullscreen = SDL_FALSE;
realwidth = vid.width;
realheight = vid.height;
if (window)
{
if (fullscreen)
{
wasfullscreen = SDL_TRUE;
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
}
else // windowed mode
{
if (wasfullscreen)
{
wasfullscreen = SDL_FALSE;
SDL_SetWindowFullscreen(window, 0);
}
// Reposition window only in windowed mode
SDL_SetWindowSize(window, width, height);
if (reposition)
{
SDL_SetWindowPosition(window,
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)),
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window))
);
}
}
}
else
{
Impl_CreateWindow(fullscreen);
wasfullscreen = fullscreen;
SDL_SetWindowSize(window, width, height);
if (fullscreen)
{
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
}
}
#ifdef HWRENDER
if (rendermode == render_opengl)
{
OglSdlSurface(vid.width, vid.height);
}
else
#endif
{
SDL_GL_SetSwapInterval(cv_vidwait.value ? 1 : 0);
}
SDL_GetWindowSize(window, &width, &height);
vid.realwidth = static_cast<uint32_t>(width);
vid.realheight = static_cast<uint32_t>(height);
if (graphics_started)
{
I_UpdateNoVsync();
}
}
static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code)
{
if (code >= SDL_SCANCODE_A && code <= SDL_SCANCODE_Z)
{
// get lowercase ASCII
return code - SDL_SCANCODE_A + 'a';
}
if (code >= SDL_SCANCODE_1 && code <= SDL_SCANCODE_9)
{
return code - SDL_SCANCODE_1 + '1';
}
else if (code == SDL_SCANCODE_0)
{
return '0';
}
if (code >= SDL_SCANCODE_F1 && code <= SDL_SCANCODE_F10)
{
return KEY_F1 + (code - SDL_SCANCODE_F1);
}
switch (code)
{
// F11 and F12 are separated from the rest of the function keys
case SDL_SCANCODE_F11: return KEY_F11;
case SDL_SCANCODE_F12: return KEY_F12;
case SDL_SCANCODE_KP_0: return KEY_KEYPAD0;
case SDL_SCANCODE_KP_1: return KEY_KEYPAD1;
case SDL_SCANCODE_KP_2: return KEY_KEYPAD2;
case SDL_SCANCODE_KP_3: return KEY_KEYPAD3;
case SDL_SCANCODE_KP_4: return KEY_KEYPAD4;
case SDL_SCANCODE_KP_5: return KEY_KEYPAD5;
case SDL_SCANCODE_KP_6: return KEY_KEYPAD6;
case SDL_SCANCODE_KP_7: return KEY_KEYPAD7;
case SDL_SCANCODE_KP_8: return KEY_KEYPAD8;
case SDL_SCANCODE_KP_9: return KEY_KEYPAD9;
case SDL_SCANCODE_RETURN: return KEY_ENTER;
case SDL_SCANCODE_ESCAPE: return KEY_ESCAPE;
case SDL_SCANCODE_BACKSPACE: return KEY_BACKSPACE;
case SDL_SCANCODE_TAB: return KEY_TAB;
case SDL_SCANCODE_SPACE: return KEY_SPACE;
case SDL_SCANCODE_MINUS: return KEY_MINUS;
case SDL_SCANCODE_EQUALS: return KEY_EQUALS;
case SDL_SCANCODE_LEFTBRACKET: return '[';
case SDL_SCANCODE_RIGHTBRACKET: return ']';
case SDL_SCANCODE_BACKSLASH: return '\\';
case SDL_SCANCODE_NONUSHASH: return '#';
case SDL_SCANCODE_SEMICOLON: return ';';
case SDL_SCANCODE_APOSTROPHE: return '\'';
case SDL_SCANCODE_GRAVE: return '`';
case SDL_SCANCODE_COMMA: return ',';
case SDL_SCANCODE_PERIOD: return '.';
case SDL_SCANCODE_SLASH: return '/';
case SDL_SCANCODE_CAPSLOCK: return KEY_CAPSLOCK;
case SDL_SCANCODE_PRINTSCREEN: return 0; // undefined?
case SDL_SCANCODE_SCROLLLOCK: return KEY_SCROLLLOCK;
case SDL_SCANCODE_PAUSE: return KEY_PAUSE;
case SDL_SCANCODE_INSERT: return KEY_INS;
case SDL_SCANCODE_HOME: return KEY_HOME;
case SDL_SCANCODE_PAGEUP: return KEY_PGUP;
case SDL_SCANCODE_DELETE: return KEY_DEL;
case SDL_SCANCODE_END: return KEY_END;
case SDL_SCANCODE_PAGEDOWN: return KEY_PGDN;
case SDL_SCANCODE_RIGHT: return KEY_RIGHTARROW;
case SDL_SCANCODE_LEFT: return KEY_LEFTARROW;
case SDL_SCANCODE_DOWN: return KEY_DOWNARROW;
case SDL_SCANCODE_UP: return KEY_UPARROW;
case SDL_SCANCODE_NUMLOCKCLEAR: return KEY_NUMLOCK;
case SDL_SCANCODE_KP_DIVIDE: return KEY_KPADSLASH;
case SDL_SCANCODE_KP_MULTIPLY: return '*'; // undefined?
case SDL_SCANCODE_KP_MINUS: return KEY_MINUSPAD;
case SDL_SCANCODE_KP_PLUS: return KEY_PLUSPAD;
case SDL_SCANCODE_KP_ENTER: return KEY_ENTER;
case SDL_SCANCODE_KP_PERIOD: return KEY_KPADDEL;
case SDL_SCANCODE_NONUSBACKSLASH: return '\\';
case SDL_SCANCODE_LSHIFT: return KEY_LSHIFT;
case SDL_SCANCODE_RSHIFT: return KEY_RSHIFT;
case SDL_SCANCODE_LCTRL: return KEY_LCTRL;
case SDL_SCANCODE_RCTRL: return KEY_RCTRL;
case SDL_SCANCODE_LALT: return KEY_LALT;
case SDL_SCANCODE_RALT: return KEY_RALT;
case SDL_SCANCODE_LGUI: return KEY_LEFTWIN;
case SDL_SCANCODE_RGUI: return KEY_RIGHTWIN;
default: break;
}
return 0;
}
static boolean IgnoreMouse(void)
{
extern consvar_t cv_alwaysgrabmouse;
if (cv_alwaysgrabmouse.value)
return false;
if (menuactive)
return false; // return !M_MouseNeeded();
if (paused || con_destlines || chat_on)
return true;
if (gamestate != GS_LEVEL && gamestate != GS_INTERMISSION &&
gamestate != GS_CONTINUING && gamestate != GS_CUTSCENE)
return true;
return false;
}
static void SDLdoGrabMouse(void)
{
SDL_ShowCursor(SDL_DISABLE);
SDL_SetWindowGrab(window, SDL_TRUE);
if (SDL_SetRelativeMouseMode(SDL_TRUE) == 0) // already warps mouse if successful
wrapmouseok = SDL_TRUE; // TODO: is wrapmouseok or HalfWarpMouse needed anymore?
}
static void SDLdoUngrabMouse(void)
{
SDL_ShowCursor(SDL_ENABLE);
SDL_SetWindowGrab(window, SDL_FALSE);
wrapmouseok = SDL_FALSE;
SDL_SetRelativeMouseMode(SDL_FALSE);
}
void SDLforceUngrabMouse(void)
{
if (SDL_WasInit(SDL_INIT_VIDEO)==SDL_INIT_VIDEO && window != NULL)
SDLdoUngrabMouse();
}
void I_UpdateMouseGrab(void)
{
if (SDL_WasInit(SDL_INIT_VIDEO) == SDL_INIT_VIDEO && window != NULL
&& SDL_GetMouseFocus() == window && SDL_GetKeyboardFocus() == window
&& USE_MOUSEINPUT && !IgnoreMouse())
SDLdoGrabMouse();
}
static void VID_Command_NumModes_f (void)
{
CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), VID_NumModes());
}
// SDL2 doesn't have SDL_GetVideoSurface or a lot of the SDL_Surface flags that SDL 1.2 had
static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
{
INT32 vfBPP;
if (!infoSurface)
return;
if (!SurfaceText)
SurfaceText = M_GetText("Unknown Surface");
vfBPP = infoSurface->format?infoSurface->format->BitsPerPixel:0;
CONS_Printf("\x82" "%s\n", SurfaceText);
CONS_Printf(M_GetText(" %ix%i at %i bit color\n"), infoSurface->w, infoSurface->h, vfBPP);
if (infoSurface->flags&SDL_PREALLOC)
CONS_Printf("%s", M_GetText(" Uses preallocated memory\n"));
else
CONS_Printf("%s", M_GetText(" Stored in system memory\n"));
if (infoSurface->flags&SDL_RLEACCEL)
CONS_Printf("%s", M_GetText(" Colorkey RLE acceleration blit\n"));
}
static void VID_Command_Info_f (void)
{
SurfaceInfo(bufSurface, M_GetText("Current Engine Mode"));
SurfaceInfo(vidSurface, M_GetText("Current Video Mode"));
}
static void VID_Command_ModeList_f(void)
{
// List windowed modes
INT32 i = 0;
CONS_Printf("NOTE: Under SDL2, all modes are supported on all platforms.\n");
CONS_Printf("Under opengl, fullscreen only supports native desktop resolution.\n");
CONS_Printf("Under software, the mode is stretched up to desktop resolution.\n");
for (i = 0; i < MAXWINMODES; i++)
{
CONS_Printf("%2d: %dx%d\n", i, windowedModes[i][0], windowedModes[i][1]);
}
}
static void VID_Command_Mode_f (void)
{
INT32 modenum;
if (COM_Argc()!= 2)
{
CONS_Printf(M_GetText("vid_mode <modenum> : set video mode, current video mode %i\n"), vid.modenum);
return;
}
modenum = atoi(COM_Argv(1));
if (modenum >= VID_NumModes())
CONS_Printf(M_GetText("Video mode not present\n"));
else
setmodeneeded = modenum+1; // request vid mode change
}
static inline void SDLJoyRemap(event_t *event)
{
(void)event;
}
static INT32 SDLJoyAxis(const Sint16 axis, UINT8 pid)
{
// -32768 to 32767
INT32 raxis = axis / 32;
if (Joystick[pid].bGamepadStyle)
{
// gamepad control type, on or off, live or die
if (raxis < -(JOYAXISRANGE/2))
raxis = -1;
else if (raxis > (JOYAXISRANGE/2))
raxis = 1;
else
raxis = 0;
}
else
{
raxis = (abs(JoyInfo[pid].scale) > 1) ? ((raxis / JoyInfo[pid].scale) * JoyInfo[pid].scale) : raxis;
#ifdef SDL_JDEADZONE
if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE)
raxis = 0;
#endif
}
return raxis;
}
static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
{
#define FOCUSUNION static_cast<unsigned int>(mousefocus | (kbfocus << 1))
static SDL_bool firsttimeonmouse = SDL_TRUE;
static SDL_bool mousefocus = SDL_TRUE;
static SDL_bool kbfocus = SDL_TRUE;
const unsigned int oldfocus = FOCUSUNION;
switch (evt.event)
{
case SDL_WINDOWEVENT_ENTER:
mousefocus = SDL_TRUE;
break;
case SDL_WINDOWEVENT_LEAVE:
mousefocus = SDL_FALSE;
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
kbfocus = SDL_TRUE;
mousefocus = SDL_TRUE;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
kbfocus = SDL_FALSE;
mousefocus = SDL_FALSE;
break;
case SDL_WINDOWEVENT_MAXIMIZED:
break;
case SDL_WINDOWEVENT_MOVED:
window_x = evt.data1;
window_y = evt.data2;
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
vid.realwidth = evt.data1;
vid.realheight = evt.data2;
break;
}
if (FOCUSUNION == oldfocus) // No state change
{
return;
}
if (mousefocus && kbfocus)
{
// Tell game we got focus back, resume music if necessary
window_notinfocus = false;
S_SetMusicVolume();
if (!firsttimeonmouse)
{
if (cv_usemouse.value) I_StartupMouse();
}
//else firsttimeonmouse = SDL_FALSE;
if (USE_MOUSEINPUT && !IgnoreMouse())
SDLdoGrabMouse();
}
else if (!mousefocus && !kbfocus)
{
// Tell game we lost focus, pause music
window_notinfocus = true;
if (!(cv_bgaudio.value & 1))
I_SetMusicVolume(0);
if (!(cv_bgaudio.value & 2))
S_StopSounds();
if (!disable_mouse)
{
SDLforceUngrabMouse();
}
G_ResetAllDeviceGameKeyDown();
G_ResetAllDeviceResponding();
if (MOUSE_MENU)
{
SDLdoUngrabMouse();
}
}
#undef FOCUSUNION
}
static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type)
{
event_t event;
event.device = 0;
if (type == SDL_KEYUP)
{
event.type = ev_keyup;
}
else if (type == SDL_KEYDOWN)
{
event.type = ev_keydown;
}
else
{
return;
}
event.data1 = Impl_SDL_Scancode_To_Keycode(evt.keysym.scancode);
event.data2 = evt.repeat;
if (event.data1) D_PostEvent(&event);
}
static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
{
static boolean firstmove = true;
if (USE_MOUSEINPUT)
{
if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window) || (IgnoreMouse() && !firstmove))
{
SDLdoUngrabMouse();
firstmove = false;
return;
}
// If using relative mouse mode, don't post an event_t just now,
// add on the offsets so we can make an overall event later.
if (SDL_GetRelativeMouseMode())
{
if (SDL_GetMouseFocus() == window && SDL_GetKeyboardFocus() == window)
{
mousemovex += evt.xrel;
mousemovey += -evt.yrel;
SDL_SetWindowGrab(window, SDL_TRUE);
}
firstmove = false;
return;
}
// If the event is from warping the pointer to middle
// of the screen then ignore it.
if ((evt.x == realwidth/2) && (evt.y == realheight/2))
{
firstmove = false;
return;
}
// Don't send an event_t if not in relative mouse mode anymore,
// just grab and set relative mode
// this fixes the stupid camera jerk on mouse entering bug
// -- Monster Iestyn
if (SDL_GetMouseFocus() == window && SDL_GetKeyboardFocus() == window)
{
SDLdoGrabMouse();
}
}
firstmove = false;
}
static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type)
{
event_t event;
SDL_memset(&event, 0, sizeof(event_t));
// Ignore the event if the mouse is not actually focused on the window.
// This can happen if you used the mouse to restore keyboard focus;
// this apparently makes a mouse button down event but not a mouse button up event,
// resulting in whatever key was pressed down getting "stuck" if we don't ignore it.
// -- Monster Iestyn (28/05/18)
if (SDL_GetMouseFocus() != window || IgnoreMouse())
return;
/// \todo inputEvent.button.which
if (USE_MOUSEINPUT)
{
event.device = 0;
if (type == SDL_MOUSEBUTTONUP)
{
event.type = ev_keyup;
}
else if (type == SDL_MOUSEBUTTONDOWN)
{
event.type = ev_keydown;
}
else return;
if (evt.button == SDL_BUTTON_MIDDLE)
event.data1 = KEY_MOUSE1+2;
else if (evt.button == SDL_BUTTON_RIGHT)
event.data1 = KEY_MOUSE1+1;
else if (evt.button == SDL_BUTTON_LEFT)
event.data1 = KEY_MOUSE1;
else if (evt.button == SDL_BUTTON_X1)
event.data1 = KEY_MOUSE1+3;
else if (evt.button == SDL_BUTTON_X2)
event.data1 = KEY_MOUSE1+4;
if (event.type == ev_keyup || event.type == ev_keydown)
{
D_PostEvent(&event);
}
}
}
static void Impl_HandleMouseWheelEvent(SDL_MouseWheelEvent evt)
{
event_t event;
SDL_memset(&event, 0, sizeof(event_t));
event.device = 0;
if (evt.y > 0)
{
event.data1 = KEY_MOUSEWHEELUP;
event.type = ev_keydown;
}
if (evt.y < 0)
{
event.data1 = KEY_MOUSEWHEELDOWN;
event.type = ev_keydown;
}
if (evt.y == 0)
{
event.data1 = 0;
event.type = ev_keyup;
}
if (event.type == ev_keyup || event.type == ev_keydown)
{
D_PostEvent(&event);
}
}
static void Impl_HandleControllerAxisEvent(SDL_ControllerAxisEvent evt)
{
event_t event;
INT32 value;
event.type = ev_gamepad_axis;
event.device = 1 + evt.which;
if (event.device == INT32_MAX)
{
return;
}
event.data1 = event.data2 = event.data3 = INT32_MAX;
//axis
if (evt.axis > 2 * JOYAXISSETS)
{
return;
}
//vaule[sic]
value = SDLJoyAxis(evt.value, evt.which);
if (evt.axis & 1)
{
event.data3 = value;
}
else
{
event.data2 = value;
}
event.data1 = evt.axis / 2;
D_PostEvent(&event);
}
static void Impl_HandleControllerButtonEvent(SDL_ControllerButtonEvent evt, Uint32 type)
{
event_t event;
event.device = 1 + evt.which;
if (event.device == INT32_MAX)
{
return;
}
event.data1 = KEY_JOY1;
event.data2 = 0;
if (type == SDL_CONTROLLERBUTTONUP)
{
event.type = ev_keyup;
}
else if (type == SDL_CONTROLLERBUTTONDOWN)
{
event.type = ev_keydown;
}
else
{
return;
}
if (evt.button < JOYBUTTONS)
{
event.data1 += evt.button;
}
else
{
return;
}
SDLJoyRemap(&event);
if (event.type != ev_console)
{
D_PostEvent(&event);
}
}
static void Impl_HandleControllerDeviceAddedEvent(SDL_ControllerDeviceEvent event)
{
// The game is always interested in controller events, even if they aren't internally assigned to a player.
// Thus, we *always* open SDL controllers as they become available, to begin receiving their events.
SDL_GameController* controller = SDL_GameControllerOpen(event.which);
if (controller == NULL)
{
return;
}
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
SDL_JoystickID joystick_instance_id = SDL_JoystickInstanceID(joystick);
event_t engine_event {};
engine_event.type = ev_gamepad_device_added;
engine_event.device = 1 + joystick_instance_id;
D_PostEvent(&engine_event);
}
static void Impl_HandleControllerDeviceRemovedEvent(SDL_ControllerDeviceEvent event)
{
// SDL only posts Device Removed events for controllers that have actually been opened.
// Thus, we don't need to filter out controllers that may not have opened successfully prior to this event.
event_t engine_event {};
engine_event.type = ev_gamepad_device_removed;
engine_event.device = 1 + event.which;
D_PostEvent(&engine_event);
}
static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode)
{
switch (keycode)
{
case SDLK_TAB: return ImGuiKey_Tab;
case SDLK_LEFT: return ImGuiKey_LeftArrow;
case SDLK_RIGHT: return ImGuiKey_RightArrow;
case SDLK_UP: return ImGuiKey_UpArrow;
case SDLK_DOWN: return ImGuiKey_DownArrow;
case SDLK_PAGEUP: return ImGuiKey_PageUp;
case SDLK_PAGEDOWN: return ImGuiKey_PageDown;
case SDLK_HOME: return ImGuiKey_Home;
case SDLK_END: return ImGuiKey_End;
case SDLK_INSERT: return ImGuiKey_Insert;
case SDLK_DELETE: return ImGuiKey_Delete;
case SDLK_BACKSPACE: return ImGuiKey_Backspace;
case SDLK_SPACE: return ImGuiKey_Space;
case SDLK_RETURN: return ImGuiKey_Enter;
case SDLK_ESCAPE: return ImGuiKey_Escape;
case SDLK_QUOTE: return ImGuiKey_Apostrophe;
case SDLK_COMMA: return ImGuiKey_Comma;
case SDLK_MINUS: return ImGuiKey_Minus;
case SDLK_PERIOD: return ImGuiKey_Period;
case SDLK_SLASH: return ImGuiKey_Slash;
case SDLK_SEMICOLON: return ImGuiKey_Semicolon;
case SDLK_EQUALS: return ImGuiKey_Equal;
case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
case SDLK_BACKSLASH: return ImGuiKey_Backslash;
case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent;
case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;
case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;
case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;
case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;
case SDLK_PAUSE: return ImGuiKey_Pause;
case SDLK_KP_0: return ImGuiKey_Keypad0;
case SDLK_KP_1: return ImGuiKey_Keypad1;
case SDLK_KP_2: return ImGuiKey_Keypad2;
case SDLK_KP_3: return ImGuiKey_Keypad3;
case SDLK_KP_4: return ImGuiKey_Keypad4;
case SDLK_KP_5: return ImGuiKey_Keypad5;
case SDLK_KP_6: return ImGuiKey_Keypad6;
case SDLK_KP_7: return ImGuiKey_Keypad7;
case SDLK_KP_8: return ImGuiKey_Keypad8;
case SDLK_KP_9: return ImGuiKey_Keypad9;
case SDLK_KP_PERIOD: return ImGuiKey_KeypadDecimal;
case SDLK_KP_DIVIDE: return ImGuiKey_KeypadDivide;
case SDLK_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
case SDLK_KP_MINUS: return ImGuiKey_KeypadSubtract;
case SDLK_KP_PLUS: return ImGuiKey_KeypadAdd;
case SDLK_KP_ENTER: return ImGuiKey_KeypadEnter;
case SDLK_KP_EQUALS: return ImGuiKey_KeypadEqual;
case SDLK_LCTRL: return ImGuiKey_LeftCtrl;
case SDLK_LSHIFT: return ImGuiKey_LeftShift;
case SDLK_LALT: return ImGuiKey_LeftAlt;
case SDLK_LGUI: return ImGuiKey_LeftSuper;
case SDLK_RCTRL: return ImGuiKey_RightCtrl;
case SDLK_RSHIFT: return ImGuiKey_RightShift;
case SDLK_RALT: return ImGuiKey_RightAlt;
case SDLK_RGUI: return ImGuiKey_RightSuper;
case SDLK_APPLICATION: return ImGuiKey_Menu;
case SDLK_0: return ImGuiKey_0;
case SDLK_1: return ImGuiKey_1;
case SDLK_2: return ImGuiKey_2;
case SDLK_3: return ImGuiKey_3;
case SDLK_4: return ImGuiKey_4;
case SDLK_5: return ImGuiKey_5;
case SDLK_6: return ImGuiKey_6;
case SDLK_7: return ImGuiKey_7;
case SDLK_8: return ImGuiKey_8;
case SDLK_9: return ImGuiKey_9;
case SDLK_a: return ImGuiKey_A;
case SDLK_b: return ImGuiKey_B;
case SDLK_c: return ImGuiKey_C;
case SDLK_d: return ImGuiKey_D;
case SDLK_e: return ImGuiKey_E;
case SDLK_f: return ImGuiKey_F;
case SDLK_g: return ImGuiKey_G;
case SDLK_h: return ImGuiKey_H;
case SDLK_i: return ImGuiKey_I;
case SDLK_j: return ImGuiKey_J;
case SDLK_k: return ImGuiKey_K;
case SDLK_l: return ImGuiKey_L;
case SDLK_m: return ImGuiKey_M;
case SDLK_n: return ImGuiKey_N;
case SDLK_o: return ImGuiKey_O;
case SDLK_p: return ImGuiKey_P;
case SDLK_q: return ImGuiKey_Q;
case SDLK_r: return ImGuiKey_R;
case SDLK_s: return ImGuiKey_S;
case SDLK_t: return ImGuiKey_T;
case SDLK_u: return ImGuiKey_U;
case SDLK_v: return ImGuiKey_V;
case SDLK_w: return ImGuiKey_W;
case SDLK_x: return ImGuiKey_X;
case SDLK_y: return ImGuiKey_Y;
case SDLK_z: return ImGuiKey_Z;
case SDLK_F1: return ImGuiKey_F1;
case SDLK_F2: return ImGuiKey_F2;
case SDLK_F3: return ImGuiKey_F3;
case SDLK_F4: return ImGuiKey_F4;
case SDLK_F5: return ImGuiKey_F5;
case SDLK_F6: return ImGuiKey_F6;
case SDLK_F7: return ImGuiKey_F7;
case SDLK_F8: return ImGuiKey_F8;
case SDLK_F9: return ImGuiKey_F9;
case SDLK_F10: return ImGuiKey_F10;
case SDLK_F11: return ImGuiKey_F11;
case SDLK_F12: return ImGuiKey_F12;
}
return ImGuiKey_None;
}
static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
{
ImGuiIO& io = ImGui::GetIO();
io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & KMOD_CTRL) != 0);
io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & KMOD_SHIFT) != 0);
io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & KMOD_ALT) != 0);
io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & KMOD_GUI) != 0);
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
{
ImGuiIO& io = ImGui::GetIO();
switch (event->type)
{
case SDL_MOUSEMOTION:
{
io.AddMousePosEvent((float)event->motion.x, (float)event->motion.y);
return true;
}
case SDL_MOUSEWHEEL:
{
float wheel_x = (event->wheel.x > 0) ? 1.0f : (event->wheel.x < 0) ? -1.0f : 0.0f;
float wheel_y = (event->wheel.y > 0) ? 1.0f : (event->wheel.y < 0) ? -1.0f : 0.0f;
io.AddMouseWheelEvent(wheel_x, wheel_y);
return true;
}
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
{
int mouse_button = -1;
if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }
if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }
if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }
if (mouse_button == -1)
break;
io.AddMouseButtonEvent(mouse_button, (event->type == SDL_MOUSEBUTTONDOWN));
// bd->MouseButtonsDown = (event->type == SDL_MOUSEBUTTONDOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));
return true;
}
case SDL_TEXTINPUT:
{
io.AddInputCharactersUTF8(event->text.text);
return true;
}
case SDL_KEYDOWN:
case SDL_KEYUP:
{
ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod);
ImGuiKey key = ImGui_ImplSDL2_KeycodeToImGuiKey(event->key.keysym.sym);
io.AddKeyEvent(key, (event->type == SDL_KEYDOWN));
io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
return true;
}
case SDL_WINDOWEVENT:
{
// - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right.
// - However we won't get a correct LEAVE event for a captured window.
// - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
// causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why
// we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.
Uint8 window_event = event->window.event;
if (window_event == SDL_WINDOWEVENT_ENTER)
(void)0;
// bd->PendingMouseLeaveFrame = 0;
if (window_event == SDL_WINDOWEVENT_LEAVE)
(void)0;
// bd->PendingMouseLeaveFrame = ImGui::GetFrameCount() + 1;
if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED)
io.AddFocusEvent(true);
else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST)
io.AddFocusEvent(false);
return true;
}
}
return false;
}
void I_GetEvent(void)
{
SDL_Event evt;
// We only want the first motion event,
// otherwise we'll end up catching the warp back to center.
//int mouseMotionOnce = 0;
if (!graphics_started)
{
return;
}
mousemovex = mousemovey = 0;
ImGuiIO& io = ImGui::GetIO();
while (SDL_PollEvent(&evt))
{
ImGui_ImplSDL2_ProcessEvent(&evt);
if (io.WantCaptureMouse || io.WantCaptureKeyboard)
{
continue;
}
switch (evt.type)
{
case SDL_WINDOWEVENT:
Impl_HandleWindowEvent(evt.window);
break;
case SDL_KEYUP:
case SDL_KEYDOWN:
Impl_HandleKeyboardEvent(evt.key, evt.type);
break;
case SDL_MOUSEMOTION:
//if (!mouseMotionOnce)
Impl_HandleMouseMotionEvent(evt.motion);
//mouseMotionOnce = 1;
break;
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
Impl_HandleMouseButtonEvent(evt.button, evt.type);
break;
case SDL_MOUSEWHEEL:
Impl_HandleMouseWheelEvent(evt.wheel);
break;
case SDL_CONTROLLERAXISMOTION:
Impl_HandleControllerAxisEvent(evt.caxis);
break;
case SDL_CONTROLLERBUTTONUP:
case SDL_CONTROLLERBUTTONDOWN:
Impl_HandleControllerButtonEvent(evt.cbutton, evt.type);
break;
case SDL_CONTROLLERDEVICEADDED:
Impl_HandleControllerDeviceAddedEvent(evt.cdevice);
break;
case SDL_CONTROLLERDEVICEREMOVED:
Impl_HandleControllerDeviceRemovedEvent(evt.cdevice);
break;
case SDL_QUIT:
LUA_HookBool(true, HOOK(GameQuit));
I_Quit();
break;
}
}
// Send all relative mouse movement as one single mouse event.
if (mousemovex || mousemovey)
{
event_t event;
int wwidth, wheight;
SDL_GetWindowSize(window, &wwidth, &wheight);
//SDL_memset(&event, 0, sizeof(event_t));
event.type = ev_mouse;
event.data1 = 0;
event.data2 = (INT32)lround(mousemovex * ((float)wwidth / (float)realwidth));
event.data3 = (INT32)lround(mousemovey * ((float)wheight / (float)realheight));
D_PostEvent(&event);
}
// In order to make wheels act like buttons, we have to set their state to Up.
// This is because wheel messages don't have an up/down state.
G_GetDeviceGameKeyDownArray(0)[KEY_MOUSEWHEELDOWN] = G_GetDeviceGameKeyDownArray(0)[KEY_MOUSEWHEELUP] = 0;
}
static void half_warp_mouse(uint16_t x, uint16_t y) {
if (wrapmouseok)
{
SDL_WarpMouseInWindow(window, (Uint16)(x/2),(Uint16)(y/2));
}
}
void I_StartupMouse(void)
{
static SDL_bool firsttimeonmouse = SDL_TRUE;
if (disable_mouse)
return;
if (!firsttimeonmouse)
{
half_warp_mouse(realwidth, realheight); // warp to center
}
else
firsttimeonmouse = SDL_FALSE;
if (cv_usemouse.value && !IgnoreMouse())
SDLdoGrabMouse();
else
SDLdoUngrabMouse();
}
//
// I_OsPolling
//
void I_OsPolling(void)
{
SDL_Keymod mod;
if (consolevent)
I_GetConsoleEvents();
if (SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == (SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER))
SDL_GameControllerUpdate();
I_GetEvent();
mod = SDL_GetModState();
/* Handle here so that our state is always synched with the system. */
shiftdown = ctrldown = altdown = 0;
capslock = false;
if (mod & KMOD_LSHIFT) shiftdown |= 1;
if (mod & KMOD_RSHIFT) shiftdown |= 2;
if (mod & KMOD_LCTRL) ctrldown |= 1;
if (mod & KMOD_RCTRL) ctrldown |= 2;
if (mod & KMOD_LALT) altdown |= 1;
if (mod & KMOD_RALT) altdown |= 2;
if (mod & KMOD_CAPS) capslock = true;
}
//
// I_UpdateNoBlit
//
void I_UpdateNoBlit(void)
{
if (rendermode == render_none)
return;
if (exposevideo)
{
#ifdef HWRENDER
if (rendermode == render_opengl)
{
OglSdlFinishUpdate(cv_vidwait.value);
}
#endif
}
exposevideo = SDL_FALSE;
}
//
// I_FinishUpdate
//
static SDL_Rect src_rect = { 0, 0, 0, 0 };
//
// I_UpdateNoVsync
//
void I_UpdateNoVsync(void)
{
INT32 real_vidwait = cv_vidwait.value;
cv_vidwait.value = 0;
I_FinishUpdate();
cv_vidwait.value = real_vidwait;
}
//
// I_ReadScreen
//
void I_ReadScreen(UINT8 *scr)
{
if (rendermode != render_soft)
I_Error ("I_ReadScreen: called while in non-software mode");
else
VID_BlitLinearScreen(screens[0], scr,
vid.width*vid.bpp, vid.height,
vid.rowbytes, vid.rowbytes);
}
//
// I_SetPalette
//
void I_SetPalette(RGBA_t *palette)
{
size_t i;
for (i=0; i<256; i++)
{
localPalette[i].r = palette[i].s.red;
localPalette[i].g = palette[i].s.green;
localPalette[i].b = palette[i].s.blue;
}
}
// return number of fullscreen + X11 modes
INT32 VID_NumModes(void)
{
if (USE_FULLSCREEN && numVidModes != -1)
return numVidModes - firstEntry;
else
return MAXWINMODES;
}
const char *VID_GetModeName(INT32 modeNum)
{
#if 0
if (USE_FULLSCREEN && numVidModes != -1) // fullscreen modes
{
modeNum += firstEntry;
if (modeNum >= numVidModes)
return NULL;
sprintf(&vidModeName[modeNum][0], "%dx%d",
modeList[modeNum]->w,
modeList[modeNum]->h);
}
else // windowed modes
{
#endif
if (modeNum == -1)
{
return fallback_resolution_name;
}
if (modeNum > MAXWINMODES)
return NULL;
sprintf(&vidModeName[modeNum][0], "%dx%d",
windowedModes[modeNum][0],
windowedModes[modeNum][1]);
//}
return &vidModeName[modeNum][0];
}
INT32 VID_GetModeForSize(INT32 w, INT32 h)
{
int i;
for (i = 0; i < MAXWINMODES; i++)
{
if (windowedModes[i][0] == w && windowedModes[i][1] == h)
{
return i;
}
}
return -1;
#if 0
INT32 matchMode = -1, i;
VID_PrepareModeList();
if (USE_FULLSCREEN && numVidModes != -1)
{
for (i=firstEntry; i<numVidModes; i++)
{
if (modeList[i]->w == w &&
modeList[i]->h == h)
{
matchMode = i;
break;
}
}
if (-1 == matchMode) // use smaller mode
{
w -= w%BASEVIDWIDTH;
h -= h%BASEVIDHEIGHT;
for (i=firstEntry; i<numVidModes; i++)
{
if (modeList[i]->w == w &&
modeList[i]->h == h)
{
matchMode = i;
break;
}
}
if (-1 == matchMode) // use smallest mode
matchMode = numVidModes-1;
}
matchMode -= firstEntry;
}
else
{
for (i=0; i<MAXWINMODES; i++)
{
if (windowedModes[i][0] == w &&
windowedModes[i][1] == h)
{
matchMode = i;
break;
}
}
if (-1 == matchMode) // use smaller mode
{
w -= w%BASEVIDWIDTH;
h -= h%BASEVIDHEIGHT;
for (i=0; i<MAXWINMODES; i++)
{
if (windowedModes[i][0] == w &&
windowedModes[i][1] == h)
{
matchMode = i;
break;
}
}
if (-1 == matchMode) // use smallest mode
matchMode = MAXWINMODES-1;
}
}
return matchMode;
#endif
}
void VID_PrepareModeList(void)
{
// Under SDL2, we just use the windowed modes list, and scale in windowed fullscreen.
allow_fullscreen = true;
#if 0
INT32 i;
firstEntry = 0;
#ifdef HWRENDER
if (rendermode == render_opengl)
modeList = SDL_ListModes(NULL, SDL_OPENGL|SDL_FULLSCREEN);
else
#endif
modeList = SDL_ListModes(NULL, surfaceFlagsF|SDL_HWSURFACE); //Alam: At least hardware surface
if (disable_fullscreen?0:cv_fullscreen.value) // only fullscreen needs preparation
{
if (-1 != numVidModes)
{
for (i=0; i<numVidModes; i++)
{
if (modeList[i]->w <= MAXVIDWIDTH &&
modeList[i]->h <= MAXVIDHEIGHT)
{
firstEntry = i;
break;
}
}
}
}
allow_fullscreen = true;
#endif
}
static void init_imgui()
{
if (ImGui::GetCurrentContext() != NULL)
{
return;
}
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.IniFilename = NULL;
io.BackendFlags = 0;
io.BackendRendererName = "SRB2 SDL 2 RHI";
io.Fonts->AddFontDefault();
{
unsigned char* pixels;
int width;
int height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
}
ImGui::StyleColorsDark();
}
static SDL_bool Impl_CreateContext(void)
{
#ifdef HWRENDER
if (rendermode == render_opengl)
{
if (!g_legacy_gl_context)
{
SDL_GL_ResetAttributes();
g_legacy_gl_context = SDL_GL_CreateContext(window);
}
if (g_legacy_gl_context == NULL)
{
SDL_DestroyWindow(window);
I_Error("Failed to create a Legacy GL context: %s\n", SDL_GetError());
}
init_imgui();
SDL_GL_MakeCurrent(window, g_legacy_gl_context);
return SDL_TRUE;
}
#endif
// RHI always uses OpenGL 2.0 (for now)
if (!sdlglcontext)
{
SDL_GL_ResetAttributes();
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
sdlglcontext = SDL_GL_CreateContext(window);
}
if (sdlglcontext == NULL)
{
SDL_DestroyWindow(window);
I_Error("Failed to create an RHI GL context: %s\n", SDL_GetError());
}
init_imgui();
SDL_GL_MakeCurrent(window, sdlglcontext);
if (!g_rhi)
{
std::unique_ptr<rhi::SdlGl2Platform> platform = std::make_unique<rhi::SdlGl2Platform>();
platform->window = window;
g_rhi = std::make_unique<rhi::Gl2Rhi>(std::move(platform), reinterpret_cast<rhi::GlLoadFunc>(SDL_GL_GetProcAddress));
g_rhi_generation += 1;
}
return SDL_TRUE;
}
void VID_CheckGLLoaded(rendermode_t oldrender)
{
(void)oldrender;
}
boolean VID_CheckRenderer(void)
{
boolean rendererchanged = false;
if (dedicated)
return false;
if (setrenderneeded)
{
rendermode = static_cast<rendermode_t>(setrenderneeded);
rendererchanged = true;
if (rendererchanged)
{
Impl_CreateContext();
#ifdef HWRENDER
if (rendermode == render_opengl)
{
VID_StartupOpenGL();
if (vid.glstate != VID_GL_LIBRARY_LOADED)
{
rendererchanged = false;
}
}
#endif
}
setrenderneeded = 0;
}
SDLSetMode(vid.width, vid.height, static_cast<SDL_bool>(USE_FULLSCREEN), (setmodeneeded ? SDL_TRUE : SDL_FALSE));
Impl_VideoSetupBuffer();
if (rendermode == render_soft)
{
SCR_SetDrawFuncs();
}
#ifdef HWRENDER
else if (rendermode == render_opengl && rendererchanged)
{
HWR_Switch();
V_SetPalette(0);
}
#endif
M_RefreshAdvancedVideoOptions();
return rendererchanged;
}
static UINT32 refresh_rate;
static UINT32 VID_GetRefreshRate(void)
{
int index = SDL_GetWindowDisplayIndex(window);
SDL_DisplayMode m;
if (SDL_WasInit(SDL_INIT_VIDEO) == 0)
{
// Video not init yet.
return 0;
}
if (SDL_GetCurrentDisplayMode(index, &m) != 0)
{
// Error has occurred.
return 0;
}
return m.refresh_rate;
}
INT32 VID_SetMode(INT32 modeNum)
{
SDLdoUngrabMouse();
vid.recalc = 1;
vid.bpp = 1;
if (modeNum < 0)
modeNum = 0;
if (modeNum >= MAXWINMODES)
modeNum = MAXWINMODES-1;
vid.width = windowedModes[modeNum][0];
vid.height = windowedModes[modeNum][1];
vid.realwidth = vid.width;
vid.realheight = vid.height;
vid.modenum = modeNum;
src_rect.w = vid.width;
src_rect.h = vid.height;
refresh_rate = VID_GetRefreshRate();
VID_CheckRenderer();
return SDL_TRUE;
}
static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
{
uint32_t flags = SDL_WINDOW_RESIZABLE;
if (rendermode == render_none) // dedicated
return SDL_TRUE; // Monster Iestyn -- not sure if it really matters what we return here tbh
if (window != NULL)
return SDL_FALSE;
if (fullscreen)
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
if (borderlesswindow)
flags |= SDL_WINDOW_BORDERLESS;
// RHI: always create window as OPENGL
flags |= SDL_WINDOW_OPENGL;
// Create a window
window = SDL_CreateWindow("Dr. Robotnik's Ring Racers " VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
realwidth, realheight, flags);
if (window == NULL)
{
CONS_Printf(M_GetText("Couldn't create window: %s\n"), SDL_GetError());
return SDL_FALSE;
}
Impl_SetWindowIcon();
return Impl_CreateContext();
}
static void Impl_SetWindowIcon(void)
{
if (window && icoSurface)
SDL_SetWindowIcon(window, icoSurface);
}
static void Impl_VideoSetupBuffer(void)
{
// Set up game's software render buffer
vid.rowbytes = vid.width * vid.bpp;
vid.direct = NULL;
if (vid.buffer)
free(vid.buffer);
vid.buffer = static_cast<uint8_t*>(calloc(vid.rowbytes*vid.height, NUMSCREENS));
if (!vid.buffer)
{
I_Error("%s", M_GetText("Not enough memory for video buffer\n"));
}
}
void I_StartupGraphics(void)
{
if (dedicated)
{
rendermode = render_none;
return;
}
if (graphics_started)
return;
COM_AddCommand ("vid_nummodes", VID_Command_NumModes_f);
COM_AddCommand ("vid_info", VID_Command_Info_f);
COM_AddCommand ("vid_modelist", VID_Command_ModeList_f);
COM_AddCommand ("vid_mode", VID_Command_Mode_f);
{
extern CVarList *cvlist_graphics_driver;
CV_RegisterList(cvlist_graphics_driver);
}
disable_mouse = static_cast<SDL_bool>(M_CheckParm("-nomouse"));
disable_fullscreen = M_CheckParm("-win") ? SDL_TRUE : SDL_FALSE;
keyboard_started = true;
#if !defined(HAVE_TTF)
// Previously audio was init here for questionable reasons?
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
{
CONS_Printf(M_GetText("Couldn't initialize SDL's Video System: %s\n"), SDL_GetError());
return;
}
#endif
// Renderer choices
// Takes priority over the config.
if (M_CheckParm("-renderer"))
{
INT32 i = 0;
CV_PossibleValue_t *renderer_list = cv_renderer_t;
const char *modeparm = M_GetNextParm();
while (renderer_list[i].strvalue)
{
if (!stricmp(modeparm, renderer_list[i].strvalue))
{
chosenrendermode = static_cast<rendermode_t>(renderer_list[i].value);
break;
}
i++;
}
}
// Choose Software renderer
else if (M_CheckParm("-software"))
chosenrendermode = render_soft;
#ifdef HWRENDER
// Choose OpenGL renderer
else if (M_CheckParm("-opengl"))
chosenrendermode = render_opengl;
// Don't startup OpenGL
if (M_CheckParm("-nogl"))
{
vid.glstate = VID_GL_LIBRARY_ERROR;
if (chosenrendermode == render_opengl)
chosenrendermode = render_none;
}
#endif
if (chosenrendermode != render_none)
rendermode = chosenrendermode;
borderlesswindow = M_CheckParm("-borderless") ? SDL_TRUE : SDL_FALSE;
VID_Command_ModeList_f();
#ifdef HWRENDER
if (rendermode == render_opengl)
VID_StartupOpenGL();
#endif
// Window icon
#ifdef HAVE_IMAGE
icoSurface = IMG_ReadXPMFromArray(SDL_icon_xpm);
#endif
VID_SetMode(VID_GetModeForSize(BASEVIDWIDTH, BASEVIDHEIGHT));
vid.width = BASEVIDWIDTH; // Default size for startup
vid.height = BASEVIDHEIGHT; // BitsPerPixel is the SDL interface's
vid.recalc = true; // Set up the console stufff
vid.direct = NULL; // Maybe direct access?
vid.bpp = 1; // This is the game engine's Bpp
vid.WndParent = NULL; //For the window?
VID_SetMode(VID_GetModeForSize(BASEVIDWIDTH, BASEVIDHEIGHT));
if (M_CheckParm("-nomousegrab"))
mousegrabok = SDL_FALSE;
realwidth = (Uint16)vid.width;
realheight = (Uint16)vid.height;
VID_Command_Info_f();
SDLdoUngrabMouse();
SDL_RaiseWindow(window);
if (mousegrabok && !disable_mouse)
SDLdoGrabMouse();
graphics_started = true;
}
void VID_StartupOpenGL(void)
{
#ifdef HWRENDER
static boolean glstartup = false;
if (!glstartup)
{
CONS_Printf("VID_StartupOpenGL()...\n");
*(void**)&HWD.pfnInit = hwSym("Init",NULL);
*(void**)&HWD.pfnFinishUpdate = NULL;
*(void**)&HWD.pfnDraw2DLine = hwSym("Draw2DLine",NULL);
*(void**)&HWD.pfnDrawPolygon = hwSym("DrawPolygon",NULL);
*(void**)&HWD.pfnDrawIndexedTriangles = hwSym("DrawIndexedTriangles",NULL);
*(void**)&HWD.pfnRenderSkyDome = hwSym("RenderSkyDome",NULL);
*(void**)&HWD.pfnSetBlend = hwSym("SetBlend",NULL);
*(void**)&HWD.pfnClearBuffer = hwSym("ClearBuffer",NULL);
*(void**)&HWD.pfnSetTexture = hwSym("SetTexture",NULL);
*(void**)&HWD.pfnUpdateTexture = hwSym("UpdateTexture",NULL);
*(void**)&HWD.pfnDeleteTexture = hwSym("DeleteTexture",NULL);
*(void**)&HWD.pfnReadRect = hwSym("ReadRect",NULL);
*(void**)&HWD.pfnGClipRect = hwSym("GClipRect",NULL);
*(void**)&HWD.pfnClearMipMapCache = hwSym("ClearMipMapCache",NULL);
*(void**)&HWD.pfnSetSpecialState = hwSym("SetSpecialState",NULL);
*(void**)&HWD.pfnSetPalette = hwSym("SetPalette",NULL);
*(void**)&HWD.pfnGetTextureUsed = hwSym("GetTextureUsed",NULL);
*(void**)&HWD.pfnDrawModel = hwSym("DrawModel",NULL);
*(void**)&HWD.pfnCreateModelVBOs = hwSym("CreateModelVBOs",NULL);
*(void**)&HWD.pfnSetTransform = hwSym("SetTransform",NULL);
*(void**)&HWD.pfnPostImgRedraw = hwSym("PostImgRedraw",NULL);
*(void**)&HWD.pfnFlushScreenTextures=hwSym("FlushScreenTextures",NULL);
*(void**)&HWD.pfnStartScreenWipe = hwSym("StartScreenWipe",NULL);
*(void**)&HWD.pfnEndScreenWipe = hwSym("EndScreenWipe",NULL);
*(void**)&HWD.pfnDoScreenWipe = hwSym("DoScreenWipe",NULL);
*(void**)&HWD.pfnDrawIntermissionBG=hwSym("DrawIntermissionBG",NULL);
*(void**)&HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL);
*(void**)&HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL);
*(void**)&HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL);
*(void**)&HWD.pfnCompileShaders = hwSym("CompileShaders",NULL);
*(void**)&HWD.pfnCleanShaders = hwSym("CleanShaders",NULL);
*(void**)&HWD.pfnSetShader = hwSym("SetShader",NULL);
*(void**)&HWD.pfnUnSetShader = hwSym("UnSetShader",NULL);
*(void**)&HWD.pfnSetShaderInfo = hwSym("SetShaderInfo",NULL);
*(void**)&HWD.pfnLoadCustomShader = hwSym("LoadCustomShader",NULL);
glstartup = true;
}
// For RHI-Legacy GL compatibility: Init always fetches GL functions, but only dlsym's libraries once.
vid.glstate = HWD.pfnInit() ? VID_GL_LIBRARY_LOADED : VID_GL_LIBRARY_ERROR; // let load the OpenGL library
if (vid.glstate == VID_GL_LIBRARY_ERROR)
{
rendermode = render_soft;
setrenderneeded = 0;
}
#endif
}
void I_ShutdownGraphics(void)
{
rendermode = render_none;
if (icoSurface) SDL_FreeSurface(icoSurface);
icoSurface = NULL;
I_OutputMsg("I_ShutdownGraphics(): ");
// was graphics initialized anyway?
if (!graphics_started)
{
return;
}
graphics_started = false;
#ifdef HWRENDER
if (GLUhandle)
hwClose(GLUhandle);
if (sdlglcontext)
{
SDL_GL_DeleteContext(sdlglcontext);
}
#endif
SDL_QuitSubSystem(SDL_INIT_VIDEO);
framebuffer = SDL_FALSE;
}
rhi::Rhi* srb2::sys::get_rhi(rhi::Handle<rhi::Rhi> handle)
{
// TODO actually use handle...
return g_rhi.get();
}
#endif
UINT32 I_GetRefreshRate(void)
{
// Moved to VID_GetRefreshRate.
// Precalculating it like that won't work as
// well for windowed mode since you can drag
// the window around, but very slow PCs might have
// trouble querying mode over and over again.
return refresh_rate;
}
namespace srb2::cvarhandler
{
void on_set_vid_wait();
}
void srb2::cvarhandler::on_set_vid_wait()
{
int interval = 0;
if (cv_vidwait.value > 0)
{
interval = 1;
}
switch (rendermode)
{
case render_soft:
if (sdlglcontext == nullptr || SDL_GL_GetCurrentContext() != sdlglcontext)
{
return;
}
SDL_GL_SetSwapInterval(interval);
break;
#ifdef HWRENDER
case render_opengl:
if (g_legacy_gl_context == nullptr || SDL_GL_GetCurrentContext() != g_legacy_gl_context)
{
return;
}
SDL_GL_SetSwapInterval(interval);
#endif
default:
break;
}
}