Merge branch 'SDL3'

This commit is contained in:
Daniel Gibson 2024-10-29 03:26:57 +01:00
commit c142dac6f7
25 changed files with 1080 additions and 298 deletions

View file

@ -46,17 +46,19 @@ set(DHEWM3BINARY "dhewm3")
include(CheckCXXCompilerFlag)
include(GNUInstallDirs OPTIONAL RESULT_VARIABLE GNUINSTALLDIRS)
include(TestBigEndian)
option(CORE "Build the core" ON)
option(BASE "Build the base game code" ON)
option(D3XP "Build the d3xp game code" ON)
if(MSVC)
option(TOOLS "Build the tools game code (Visual Studio+SDL2 only)" OFF)
option(TOOLS "Build the tools game code (Visual Studio+SDL2/SDL3 only)" OFF)
endif()
option(DEDICATED "Build the dedicated server" OFF)
option(ONATIVE "Optimize for the host CPU" OFF)
option(SDL2 "Use SDL2 instead of SDL1.2" ON)
option(IMGUI "Build with Dear ImGui integration - requires SDL2 and C++11" ON)
option(SDL3 "Use SDL3 instead of SDL2 or SDL1.2" OFF)
option(IMGUI "Build with Dear ImGui integration - requires SDL2/SDL3 and C++11" ON)
option(REPRODUCIBLE_BUILD "Replace __DATE__ and __TIME__ by hardcoded values for reproducible builds" OFF)
option(HARDLINK_GAME "Compile gamecode into executable (no game DLLs)" OFF)
@ -193,7 +195,19 @@ endif()
find_package(OpenAL REQUIRED)
include_directories(${OPENAL_INCLUDE_DIR})
if (SDL2)
if (SDL3)
if(SDL2)
message(WARNING "You enabled both SDL2 and SDL3. Only one can be used at a time, disabling SDL2")
set(SDL2 OFF CACHE BOOL "Use SDL2 (make sure SDL3 is disabled!)" FORCE)
endif()
# 1. Look for a SDL3 package,
# 2. look for the SDL3-shared component, and
# 3. fail if the shared component cannot be found.
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3-shared)
# TODO: include dirs?
set(SDLx_LIBRARY SDL3::SDL3)
add_definitions(-DD3_SDL3=1)
elseif (SDL2)
# skip SDL2main
if(APPLE OR WIN32)
set(SDL2_BUILDING_LIBRARY TRUE)
@ -217,12 +231,12 @@ if(REPRODUCIBLE_BUILD)
endif()
if(IMGUI)
if(SDL2)
if(SDL2 OR SDL3)
# we need C++11 for ImGui
set (CMAKE_CXX_STANDARD 11)
message(STATUS "Dear ImGui integration enabled")
else()
message(WARNING "Disabling IMGUI because SDL1.2 is used - it needs SDL2!")
message(WARNING "Disabling IMGUI because SDL1.2 is used - it needs SDL2 or SDL3!")
set(IMGUI OFF)
add_definitions(-DIMGUI_DISABLE)
endif()
@ -273,7 +287,7 @@ if(NOT WIN32)
endif()
# check if our SDL2 supports X11 in SDL_syswm so we can use it for DPI scaling ImGui
if(SDL2)
if(SDL2) # TODO: SDL3? Or just kick this feature?
set(CMAKE_REQUIRED_LIBRARIES SDL2)
check_c_source_compiles( "#include <SDL_syswm.h>
int main() { SDL_SysWMinfo wmInfo = {}; wmInfo.info.x11.display = NULL; return 0; }" HAVE_SDL_X11)
@ -300,6 +314,15 @@ unset(compiler_id_lower)
endif() # not MSVC
TEST_BIG_ENDIAN(is_big_endian)
if(is_big_endian)
message(STATUS "Detected Big Endian architecture, setting -DD3_IS_BIG_ENDIAN=1")
add_definitions(-DD3_IS_BIG_ENDIAN=1)
else()
message(STATUS "Detected Little Endian architecture, setting -DD3_IS_BIG_ENDIAN=0")
add_definitions(-DD3_IS_BIG_ENDIAN=0)
endif()
# compiler specific flags
if(D3_COMPILER_IS_GCC_OR_CLANG)
add_compile_options(-pipe)
@ -799,8 +822,14 @@ set(src_idlib
add_globbed_headers(src_idlib "idlib")
if(IMGUI)
set(src_imgui
libs/imgui/backends/imgui_impl_sdl2.cpp
if(SDL3)
set(src_imgui libs/imgui/backends/imgui_impl_sdl3.cpp)
else()
set(src_imgui libs/imgui/backends/imgui_impl_sdl2.cpp)
endif()
set(src_imgui ${src_imgui}
libs/imgui/backends/imgui_impl_opengl2.cpp
libs/imgui/imgui.h
@ -1152,9 +1181,13 @@ elseif(WIN32)
sys/win32/win_net.cpp
sys/win32/win_shared.cpp
sys/win32/win_syscon.cpp
sys/win32/SDL_win32_main.c
)
if(NOT SDL2 AND NOT SDL3)
# only SDL1.2 still uses SDL_win32_main.c
set(src_sys_base ${src_sys_base} sys/win32/SDL_win32_main.c)
endif()
# adding the few relevant headers in sys/ manually..
set(src_sys_base ${src_sys_base}
sys/platform.h
@ -1195,6 +1228,9 @@ include_directories(${CMAKE_BINARY_DIR})
include_directories(${CMAKE_SOURCE_DIR})
add_library(idlib STATIC ${src_idlib})
if(SDL3)
target_link_libraries(idlib SDL3::Headers) # so it can use SDL_Endian.h
endif()
if (AROS)
add_library(dll STATIC ${src_arosdll})
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "i386")

View file

@ -26,8 +26,6 @@ If you have questions concerning this license or the applicable additional terms
===========================================================================
*/
#include <SDL_endian.h>
#include "sys/platform.h"
#include "idlib/LangDict.h"
#include "idlib/Timer.h"
@ -527,7 +525,12 @@ void idGameLocal::SaveGame( idFile *f ) {
savegame.WriteString( D3_ARCH ); // CPU architecture (e.g. "x86" or "x86_64") - from CMake
savegame.WriteString( ENGINE_VERSION );
savegame.WriteShort( (short)sizeof(void*) ); // tells us if it's from a 32bit (4) or 64bit system (8)
savegame.WriteShort( SDL_BYTEORDER ) ; // SDL_LIL_ENDIAN or SDL_BIG_ENDIAN
#if D3_IS_BIG_ENDIAN
const short byteOrder = 4321; // SDL_BIG_ENDIAN
#else
const short byteOrder = 1234; // SDL_LIL_ENDIAN
#endif
savegame.WriteShort( byteOrder ) ;
// DG end
// go through all entities and threads and add them to the object list

View file

@ -26,7 +26,12 @@ If you have questions concerning this license or the applicable additional terms
===========================================================================
*/
#include <SDL.h>
#include "sys/sys_sdl.h"
#if SDL_VERSION_ATLEAST(3, 0, 0)
// DG: compat with SDL2
#define SDL_setenv SDL_setenv_unsafe
#endif
#include "sys/platform.h"
#include "idlib/containers/HashTable.h"
@ -2799,7 +2804,12 @@ void idCommonLocal::SetMachineSpec( void ) {
}
}
static unsigned int AsyncTimer(unsigned int interval, void *) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
static Uint32 AsyncTimer(void * /*userdata*/, SDL_TimerID /* timerID */, Uint32 interval)
#else // SDL2 or SDL1.2
static unsigned int AsyncTimer(unsigned int interval, void *)
#endif
{
common->Async();
Sys_TriggerEvent(TRIGGER_EVENT_ONE);
@ -2939,7 +2949,9 @@ void idCommonLocal::Init( int argc, char **argv ) {
// we want to use the SDL event queue for dedicated servers. That
// requires video to be initialized, so we just use the dummy
// driver for headless boxen
#if SDL_VERSION_ATLEAST(2, 0, 0)
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "dummy");
#elif SDL_VERSION_ATLEAST(2, 0, 0)
SDL_setenv("SDL_VIDEODRIVER", "dummy", 1);
#else
char dummy[] = "SDL_VIDEODRIVER=dummy\0";
@ -2947,13 +2959,19 @@ void idCommonLocal::Init( int argc, char **argv ) {
#endif
#endif
#if SDL_VERSION_ATLEAST(2, 0, 0)
#if SDL_VERSION_ATLEAST(3, 0, 0)
if ( ! SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD) )
{
if ( SDL_Init(SDL_INIT_VIDEO) ) { // retry without joystick/gamepad if it failed
Sys_Printf( "WARNING: Couldn't get SDL gamepad support! Gamepads won't work!\n" );
} else
#elif SDL_VERSION_ATLEAST(2, 0, 0)
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
{
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) == 0) { // retry without joystick/gamecontroller if it failed
Sys_Printf( "WARNING: Couldn't get SDL gamecontroller support! Gamepads won't work!\n" );
} else
#else
#else // SDL1.2
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) != 0) // no gamecontroller support in SDL1
{
#endif
@ -3005,14 +3023,22 @@ void idCommonLocal::Init( int argc, char **argv ) {
idCVar::RegisterStaticVars();
// print engine version
#if SDL_VERSION_ATLEAST(2, 0, 0)
#if SDL_VERSION_ATLEAST(3, 0, 0)
int sdlv = SDL_GetVersion();
int sdlvmaj = SDL_VERSIONNUM_MAJOR(sdlv);
int sdlvmin = SDL_VERSIONNUM_MINOR(sdlv);
int sdlvmicro = SDL_VERSIONNUM_MICRO(sdlv);
Printf( "%s using SDL v%d.%d.%d\n", version.string, sdlvmaj, sdlvmin, sdlvmicro );
#else
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_version sdlv;
SDL_GetVersion(&sdlv);
#else
#else
SDL_version sdlv = *SDL_Linked_Version();
#endif
#endif
Printf( "%s using SDL v%u.%u.%u\n",
version.string, sdlv.major, sdlv.minor, sdlv.patch );
#endif
#if SDL_VERSION_ATLEAST(2, 0, 0)
Printf( "SDL video driver: %s\n", SDL_GetCurrentVideoDriver() );

View file

@ -2,7 +2,7 @@
#include <algorithm> // std::sort - TODO: replace with something custom..
#include <SDL.h> // to show display size
#include "sys/sys_sdl.h"
#define IMGUI_DEFINE_MATH_OPERATORS
@ -1903,9 +1903,15 @@ static void DrawVideoOptionsMenu()
}
// resolution info text
int sdlDisplayIdx = SDL_GetWindowDisplayIndex( SDL_GL_GetCurrentWindow() );
#if SDL_VERSION_ATLEAST(3, 0, 0)
// in SDL3 it's a Display ID, in SDL2 a Display Index, in both cases the value
// can be fed into SDL_GetDisplayBounds()
SDL_DisplayID sdlDisplayId_x = SDL_GetDisplayForWindow( SDL_GL_GetCurrentWindow() );
#else // SDL2
int sdlDisplayId_x = SDL_GetWindowDisplayIndex( SDL_GL_GetCurrentWindow() );
#endif
SDL_Rect displayRect = {};
SDL_GetDisplayBounds( sdlDisplayIdx, &displayRect );
SDL_GetDisplayBounds( sdlDisplayId_x, &displayRect );
if ( (int)glConfig.winWidth != glConfig.vidWidth ) {
ImGui::TextDisabled( "Current Resolution: %g x %g (Physical: %d x %d)",
glConfig.winWidth, glConfig.winHeight, glConfig.vidWidth, glConfig.vidHeight );

View file

@ -26,8 +26,6 @@ If you have questions concerning this license or the applicable additional terms
===========================================================================
*/
#include <SDL_endian.h>
#include "sys/platform.h"
#include "idlib/LangDict.h"
#include "idlib/Timer.h"
@ -469,7 +467,12 @@ void idGameLocal::SaveGame( idFile *f ) {
savegame.WriteString( D3_ARCH ); // CPU architecture (e.g. "x86" or "x86_64") - from CMake
savegame.WriteString( ENGINE_VERSION );
savegame.WriteShort( (short)sizeof(void*) ); // tells us if it's from a 32bit (4) or 64bit system (8)
savegame.WriteShort( SDL_BYTEORDER ) ; // SDL_LIL_ENDIAN or SDL_BIG_ENDIAN
#if D3_IS_BIG_ENDIAN
const short byteOrder = 4321; // SDL_BIG_ENDIAN
#else
const short byteOrder = 1234; // SDL_LIL_ENDIAN
#endif
savegame.WriteShort( byteOrder ) ;
// DG end
// go through all entities and threads and add them to the object list

View file

@ -35,7 +35,32 @@ If you have questions concerning this license or the applicable additional terms
#include <unistd.h>
#endif
#include <SDL_endian.h>
#ifdef D3_SDL3
#include <SDL3/SDL_endian.h>
// some defines for backwards-compat with SDL2
#define SDL_SwapBE16(X) SDL_Swap16BE(X)
#define SDL_SwapLE16(X) SDL_Swap16LE(X)
#define SDL_SwapBE32(X) SDL_Swap32BE(X)
#define SDL_SwapLE32(X) SDL_Swap32LE(X)
#else // SDL1.2 or SDL2
#include <SDL_endian.h>
#endif
#ifndef D3_IS_BIG_ENDIAN
#error "D3_IS_BIG_ENDIAN should be defined by the build system (CMake)!"
#endif
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#if D3_IS_BIG_ENDIAN != 1
#error "CMake (which sets D3_IS_BIG_ENDIAN) and SDL disagree about the endianess! CMake says little, SDL says big"
#endif
#elif SDL_BYTEORDER == SDL_LIL_ENDIAN
#if D3_IS_BIG_ENDIAN != 0
#error "CMake (which sets D3_IS_BIG_ENDIAN) and SDL disagree about the endianess! CMake says big, SDL says little"
#endif
#else
#error "According to SDL, endianess is neither Big nor Little - dhewm3 doesn't support other byteorders!"
#endif
#include "sys/platform.h"
#include "idlib/math/Vector.h"

View file

@ -42,7 +42,11 @@ If you have questions concerning this license or the applicable additional terms
#endif
#endif
#include <SDL_opengl.h>
#ifdef D3_SDL3
#include <SDL3/SDL_opengl.h>
#else // SDL1.2 or SDL2
#include <SDL_opengl.h>
#endif
#if defined( ID_DEDICATED ) && defined( _WIN32 )
// restore WINGDIAPI

View file

@ -30,7 +30,7 @@ If you have questions concerning this license or the applicable additional terms
#include "renderer/tr_local.h"
static idCVar r_fillWindowAlphaChan( "r_fillWindowAlphaChan", "-1", CVAR_SYSTEM | CVAR_NOCHEAT | CVAR_ARCHIVE, "Make sure alpha channel of windows default framebuffer is completely opaque at the end of each frame. Needed at least when using Wayland.\n 1: do this, 0: don't do it, -1: let dhewm3 decide (default)" );
static idCVar r_fillWindowAlphaChan( "r_fillWindowAlphaChan", "-1", CVAR_SYSTEM | CVAR_NOCHEAT | CVAR_ARCHIVE, "Make sure alpha channel of windows default framebuffer is completely opaque at the end of each frame. Needed at least when using Wayland with older drivers.\n 1: do this, 0: don't do it, -1: let dhewm3 decide (default)" );
frameData_t *frameData;
backEndState_t backEnd;

View file

@ -1073,7 +1073,7 @@ typedef struct {
bool fullScreen;
bool fullScreenDesktop;
bool stereo;
int displayHz;
int displayHz; // TODO: SDL3 uses float
int multiSamples;
} glimpParms_t;
@ -1119,6 +1119,7 @@ void GLimp_DeactivateContext( void );
const int GRAB_GRABMOUSE = (1 << 0);
const int GRAB_HIDECURSOR = (1 << 1);
const int GRAB_RELATIVEMOUSE = (1 << 2);
const int GRAB_ENABLETEXTINPUT = (1 << 3); // only used with SDL3, where textinput must be explicitly activated
void GLimp_GrabInput(int flags);

View file

@ -26,9 +26,7 @@ If you have questions concerning this license or the applicable additional terms
===========================================================================
*/
#include "SDL_endian.h"
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#if D3_IS_BIG_ENDIAN
#define STB_VORBIS_BIG_ENDIAN
#endif
#define STB_VORBIS_NO_STDIO

View file

@ -5,8 +5,7 @@
// (I'm doing this instead of renaming stb_vorbis.h to stb_vorbis.c so the configuration
// like STB_VORBIS_BIG_ENDIAN etc can be done here in code)
#include "SDL_endian.h"
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#if D3_IS_BIG_ENDIAN
#define STB_VORBIS_BIG_ENDIAN
#endif
#define STB_VORBIS_NO_STDIO

View file

@ -28,7 +28,11 @@ If you have questions concerning this license or the applicable additional terms
#include <float.h>
#include <SDL_cpuinfo.h>
#ifdef D3_SDL3
#include <SDL3/SDL_cpuinfo.h>
#else // SDL1.2 or SDL2
#include <SDL_cpuinfo.h>
#endif
// MSVC header intrin.h uses strcmp and errors out when not set
#define IDSTR_NO_REDIRECT
@ -201,8 +205,11 @@ int Sys_GetProcessorId( void ) {
if (SDL_HasMMX())
flags |= CPUID_MMX;
// SDL3 doesn't support detecting 3DNow, and current CPUs (even from AMD) don't support it either
#ifndef D3_SDL3
if (SDL_Has3DNow())
flags |= CPUID_3DNOW;
#endif
if (SDL_HasSSE())
flags |= CPUID_SSE;

View file

@ -26,7 +26,7 @@ If you have questions concerning this license or the applicable additional terms
===========================================================================
*/
#include <SDL.h>
#include "sys/sys_sdl.h"
#include "sys/platform.h"
#include "idlib/containers/List.h"
@ -61,6 +61,103 @@ If you have questions concerning this license or the applicable additional terms
#define SDLK_PRINTSCREEN SDLK_PRINT
#endif
// unholy hacks to make it work with SDL3 (and also SDL2 and partly SDL1.2)
#if SDL_VERSION_ATLEAST(3, 0, 0)
// in SDL3 SDL_GetKeyFromScancode() got additional arguments that I don't care about
#define SDL_GetKeyFromScancode(SC) SDL_GetKeyFromScancode(SC, 0, true)
#define SDLK_z SDLK_Z
#define SDL_TEXTINPUT SDL_EVENT_TEXT_INPUT
#define SDL_KEYDOWN SDL_EVENT_KEY_DOWN
#define SDL_KEYUP SDL_EVENT_KEY_UP
#define SDL_USEREVENT SDL_EVENT_USER
#define SDL_QUIT SDL_EVENT_QUIT
#define SDL_MOUSEBUTTONDOWN SDL_EVENT_MOUSE_BUTTON_DOWN
#define SDL_MOUSEBUTTONUP SDL_EVENT_MOUSE_BUTTON_UP
#define SDL_MOUSEMOTION SDL_EVENT_MOUSE_MOTION
#define SDL_MOUSEWHEEL SDL_EVENT_MOUSE_WHEEL
#define SDL_JoystickGUID SDL_GUID
#define SDL_JoystickGetGUID SDL_GetJoystickGUID
#define SDL_JoystickName SDL_GetJoystickName
#define SDL_JoystickGetGUIDString SDL_GUIDToString
#define SDL_JOYDEVICEADDED SDL_EVENT_JOYSTICK_ADDED
#define SDL_JOYDEVICEREMOVED SDL_EVENT_JOYSTICK_REMOVED
// game controllers have been renamed to gamepads and the
// corresponding SDL_Event members are now prefixed with g instead of c
#define cbutton gbutton
#define caxis gaxis
#define SDL_GameController SDL_Gamepad
#define SDL_GameControllerAxis SDL_GamepadAxis
#define SDL_GameControllerButton SDL_GamepadButton
#define SDL_GameControllerGetType SDL_GetGamepadType
#define SDL_GameControllerName SDL_GetGamepadName
#define SDL_GameControllerGetJoystick SDL_GetGamepadJoystick
#define SDL_GameControllerGetVendor SDL_GetGamepadVendor
#define SDL_GameControllerGetProduct SDL_GetGamepadProduct
#define SDL_GameControllerOpen SDL_OpenGamepad
#define SDL_CONTROLLERAXISMOTION SDL_EVENT_GAMEPAD_AXIS_MOTION
#define SDL_CONTROLLERBUTTONDOWN SDL_EVENT_GAMEPAD_BUTTON_DOWN
#define SDL_CONTROLLERBUTTONUP SDL_EVENT_GAMEPAD_BUTTON_UP
#define SDL_CONTROLLER_AXIS_LEFTX SDL_GAMEPAD_AXIS_LEFTX
#define SDL_CONTROLLER_AXIS_LEFTY SDL_GAMEPAD_AXIS_LEFTY
#define SDL_CONTROLLER_AXIS_MAX SDL_GAMEPAD_AXIS_MAX
#define SDL_CONTROLLER_AXIS_RIGHTX SDL_GAMEPAD_AXIS_RIGHTX
#define SDL_CONTROLLER_AXIS_RIGHTY SDL_GAMEPAD_AXIS_RIGHTY
#define SDL_CONTROLLER_AXIS_TRIGGERLEFT SDL_GAMEPAD_AXIS_LEFT_TRIGGER
#define SDL_CONTROLLER_AXIS_TRIGGERRIGHT SDL_GAMEPAD_AXIS_RIGHT_TRIGGER
#define SDL_CONTROLLER_BUTTON_A SDL_GAMEPAD_BUTTON_SOUTH
#define SDL_CONTROLLER_BUTTON_B SDL_GAMEPAD_BUTTON_EAST
#define SDL_CONTROLLER_BUTTON_X SDL_GAMEPAD_BUTTON_WEST
#define SDL_CONTROLLER_BUTTON_Y SDL_GAMEPAD_BUTTON_NORTH
#define SDL_CONTROLLER_BUTTON_BACK SDL_GAMEPAD_BUTTON_BACK
#define SDL_CONTROLLER_BUTTON_DPAD_DOWN SDL_GAMEPAD_BUTTON_DPAD_DOWN
#define SDL_CONTROLLER_BUTTON_DPAD_LEFT SDL_GAMEPAD_BUTTON_DPAD_LEFT
#define SDL_CONTROLLER_BUTTON_DPAD_RIGHT SDL_GAMEPAD_BUTTON_DPAD_RIGHT
#define SDL_CONTROLLER_BUTTON_DPAD_UP SDL_GAMEPAD_BUTTON_DPAD_UP
#define SDL_CONTROLLER_BUTTON_GUIDE SDL_GAMEPAD_BUTTON_GUIDE
#define SDL_CONTROLLER_BUTTON_INVALID SDL_GAMEPAD_BUTTON_INVALID
#define SDL_CONTROLLER_BUTTON_LEFTSHOULDER SDL_GAMEPAD_BUTTON_LEFT_SHOULDER
#define SDL_CONTROLLER_BUTTON_LEFTSTICK SDL_GAMEPAD_BUTTON_LEFT_STICK
#define SDL_CONTROLLER_BUTTON_MISC1 SDL_GAMEPAD_BUTTON_MISC1
#define SDL_CONTROLLER_BUTTON_PADDLE1 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1
#define SDL_CONTROLLER_BUTTON_PADDLE2 SDL_GAMEPAD_BUTTON_LEFT_PADDLE1
#define SDL_CONTROLLER_BUTTON_PADDLE3 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2
#define SDL_CONTROLLER_BUTTON_PADDLE4 SDL_GAMEPAD_BUTTON_LEFT_PADDLE2
#define SDL_CONTROLLER_BUTTON_RIGHTSHOULDER SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER
#define SDL_CONTROLLER_BUTTON_RIGHTSTICK SDL_GAMEPAD_BUTTON_RIGHT_STICK
#define SDL_CONTROLLER_BUTTON_START SDL_GAMEPAD_BUTTON_START
#define SDL_CONTROLLER_BUTTON_TOUCHPAD SDL_GAMEPAD_BUTTON_TOUCHPAD
#define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT
#define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR
#define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT
#define SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO
#define SDL_CONTROLLER_TYPE_PS3 SDL_GAMEPAD_TYPE_PS3
#define SDL_CONTROLLER_TYPE_PS4 SDL_GAMEPAD_TYPE_PS4
#define SDL_CONTROLLER_TYPE_PS5 SDL_GAMEPAD_TYPE_PS5
#define SDL_CONTROLLER_TYPE_UNKNOWN SDL_GAMEPAD_TYPE_STANDARD
#define SDL_CONTROLLER_TYPE_VIRTUAL SDL_GAMEPAD_TYPE_VIRTUAL
#define SDL_CONTROLLER_TYPE_XBOX360 SDL_GAMEPAD_TYPE_XBOX360
#define SDL_CONTROLLER_TYPE_XBOXONE SDL_GAMEPAD_TYPE_XBOXONE
#define IS_SDL_BTN_DOWN(EV) EV.down
#else // SDL2 and SDL1.2
#define IS_SDL_BTN_DOWN(EV) (EV.state == SDL_PRESSED)
#endif // SDL_VERSION_ATLEAST(3, 0, 0) - unholy hacks
extern idCVar in_useGamepad; // from UsercmdGen.cpp
extern idCVar joy_deadZone; // ditto
@ -78,7 +175,7 @@ static idCVar in_ignoreConsoleKey("in_ignoreConsoleKey", "0", CVAR_SYSTEM | CVAR
"Console only opens with Shift+Esc, not ` or ^ etc");
static idCVar in_nograb("in_nograb", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "prevents input grabbing");
static idCVar in_grabKeyboard("in_grabKeyboard", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_BOOL,
idCVar in_grabKeyboard("in_grabKeyboard", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_BOOL,
"if enabled, grabs all keyboard input if mouse is grabbed (so keyboard shortcuts from the OS like Alt-Tab or Windows Key won't work)");
idCVar joy_gamepadLayout("joy_gamepadLayout", "-1", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_INTEGER,
@ -100,7 +197,7 @@ struct kbd_poll_t {
int key;
bool state;
kbd_poll_t() {
kbd_poll_t() : key(0), state(false) {
}
kbd_poll_t(int k, bool s) {
@ -113,7 +210,7 @@ struct mouse_poll_t {
int action;
int value;
mouse_poll_t() {
mouse_poll_t() : action(0), value(0) {
}
mouse_poll_t(int a, int v) {
@ -269,10 +366,11 @@ const char* Sys_GetLocalizedJoyKeyName( int key ) {
if (key >= K_FIRST_JOY && key <= K_LAST_JOY) {
if (key <= K_JOY_BTN_BACK) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
#if 0 //SDL_VERSION_ATLEAST(3, 0, 0)
// TODO: or use the SDL2 code and just set joy_gamepadLayout automatically based on SDL_GetGamepadType() ?
SDL_GamepadButton gpbtn = SDL_GAMEPAD_BUTTON_SOUTH + (key - K_JOY_BTN_SOUTH);
SDL_GamepadButtonLabel label = SDL_GetGamepadButtonLabelForType(TODO, gpbtn);
SDL_GamepadButton gpbtn = (SDL_GamepadButton)(SDL_GAMEPAD_BUTTON_SOUTH + (key - K_JOY_BTN_SOUTH));
SDL_GamepadType sdlGamepadType = TODO;
SDL_GamepadButtonLabel label = SDL_GetGamepadButtonLabelForType(sdlGamepadType, gpbtn);
switch(label) {
case SDL_GAMEPAD_BUTTON_LABEL_A:
return "Pad A";
@ -421,13 +519,14 @@ static const char* getLocalizedScancodeName( int key, bool useUtf8 )
}
} else if ( k != SDLK_UNKNOWN ) {
const char *ret = SDL_GetKeyName( k );
// the keyname from SDL2 is in UTF-8, which Doom3 can't print,
// so only return the name if it's ASCII, otherwise fall back to "SC_bla"
if ( ret && *ret != '\0' ) {
if( useUtf8 ) {
return ret;
}
// the keyname from SDL2 is in UTF-8, which Doom3 can't print (except with ImGui),
// so only return the name directly if it's ASCII, otherwise try to translate it
// to ISO8859-1, and if that fails fall back to SC_*
if( isAscii( ret ) ) {
return ret;
}
@ -844,7 +943,8 @@ void Sys_InitInput() {
in_kbd.SetModified();
Sys_GetConsoleKey(false); // initialize consoleKeymappingIdx from in_kbd
#if SDL_VERSION_ATLEAST(2, 0, 0)
const char* grabKeyboardEnv = SDL_getenv(SDL_HINT_GRAB_KEYBOARD);
// NOTE: SDL3 doesn't support that hint/env var, but whatever, support the env var anyway
const char* grabKeyboardEnv = SDL_getenv("SDL_GRAB_KEYBOARD");
if ( grabKeyboardEnv ) {
common->Printf( "The SDL_GRAB_KEYBOARD environment variable is set, setting the in_grabKeyboard CVar to the same value (%s)\n", grabKeyboardEnv );
in_grabKeyboard.SetString( grabKeyboardEnv );
@ -861,7 +961,19 @@ void Sys_InitInput() {
memset( buttonStates, 0, sizeof( buttonStates ) );
memset( joyAxis, 0, sizeof( joyAxis ) );
#if SDL_VERSION_ATLEAST(2, 0, 0) // gamecontroller/gamepad not supported in SDL1
#if SDL_VERSION_ATLEAST(3, 0, 0)
int numJoysticks = 0;
SDL_JoystickID* joysticks = SDL_GetJoysticks(&numJoysticks);
for( int i = 0; i < numJoysticks; ++i )
{
SDL_GameController* gc = SDL_GameControllerOpen( joysticks[i] );
if ( gc != NULL ) {
setGamepadType( gc );
}
}
SDL_free(joysticks);
#elif SDL_VERSION_ATLEAST(2, 0, 0)
// use button positions instead of button labels,
// Sys_GetLocalizedJoyKeyName() will do the translation
// (I think this also was the default before 2.0.12?)
@ -875,7 +987,7 @@ void Sys_InitInput() {
setGamepadType( gc );
}
}
#endif
#endif // gamecontroller/gamepad not supported in SDL1
}
/*
@ -1082,6 +1194,7 @@ sysEvent_t Sys_GetEvent() {
SDL_Event ev;
sysEvent_t res = { };
int key;
bool isDown = false;
static const sysEvent_t res_none = { SE_NONE, 0, 0, 0, NULL };
@ -1094,7 +1207,11 @@ sysEvent_t Sys_GetEvent() {
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
static char s[SDL_TEXTINPUTEVENT_TEXT_SIZE] = {0};
// s holds the string from the last SDL_TEXTINPUT event, to generate SE_CHARS D3 events,
// one event per call to this function until all chars have been handled
// it used to have SDL_TEXTINPUTEVENT_TEXT_SIZE (32) bytes, but in SDL3 the string can have
// arbitrary size, however I assume that 128 should still be more than enough
static char s[128] = {0};
static size_t s_pos = 0;
if (s[0] != '\0') {
@ -1103,7 +1220,7 @@ sysEvent_t Sys_GetEvent() {
++s_pos;
if (!s[s_pos] || s_pos == SDL_TEXTINPUTEVENT_TEXT_SIZE) {
if (!s[s_pos] || s_pos == sizeof(s)) {
memset(s, 0, sizeof(s));
s_pos = 0;
}
@ -1112,6 +1229,9 @@ sysEvent_t Sys_GetEvent() {
}
#endif
// c holds a single char for a SE_CHAR event, probably coming from a SDL_KEYDOWN event
// (that was also returned as SE_KEY), or from a SDL_TEXTINPUT event that contained just one char.
// It's 0 when not currently holding a char to generate an event
static byte c = 0;
if (c) {
@ -1131,7 +1251,35 @@ sysEvent_t Sys_GetEvent() {
}
switch (ev.type) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
#if SDL_VERSION_ATLEAST(3, 0, 0)
case SDL_EVENT_WINDOW_FOCUS_GAINED: {
// unset modifier, in case alt-tab was used to leave window and ALT is still set
// as that can cause fullscreen-toggling when pressing enter...
SDL_Keymod currentmod = SDL_GetModState();
int newmod = SDL_KMOD_NONE;
if (currentmod & SDL_KMOD_CAPS) // preserve capslock
newmod |= SDL_KMOD_CAPS;
SDL_SetModState((SDL_Keymod)newmod);
} // new context because visual studio complains about newmod and currentmod not initialized because of the case SDL_WINDOWEVENT_FOCUS_LOST
in_hasFocus = true;
// start playing the game sound world again (when coming from editor)
session->SetPlayingSoundWorld();
continue; // handle next event
case SDL_EVENT_WINDOW_FOCUS_LOST:
in_hasFocus = false;
continue; // handle next event
case SDL_EVENT_WINDOW_RESIZED:
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
GLimp_UpdateWindowSize();
continue; // handle next event
#elif SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_WINDOWEVENT:
switch (ev.window.event) {
case SDL_WINDOWEVENT_FOCUS_GAINED: {
@ -1146,7 +1294,6 @@ sysEvent_t Sys_GetEvent() {
SDL_SetModState((SDL_Keymod)newmod);
} // new context because visual studio complains about newmod and currentmod not initialized because of the case SDL_WINDOWEVENT_FOCUS_LOST
in_hasFocus = true;
// start playing the game sound world again (when coming from editor)
@ -1188,7 +1335,11 @@ sysEvent_t Sys_GetEvent() {
#endif
case SDL_KEYDOWN:
#if SDL_VERSION_ATLEAST(3, 0, 0)
if (ev.key.key == SDLK_RETURN && (ev.key.mod & SDL_KMOD_ALT) > 0) {
#else
if (ev.key.keysym.sym == SDLK_RETURN && (ev.key.keysym.mod & KMOD_ALT) > 0) {
#endif
cvarSystem->SetCVarBool("r_fullscreen", !renderSystem->IsFullScreen());
PushConsoleEvent("vid_restart partial");
return res_none;
@ -1196,7 +1347,7 @@ sysEvent_t Sys_GetEvent() {
// fall through
case SDL_KEYUP:
#if !SDL_VERSION_ATLEAST(2, 0, 0)
#if !SDL_VERSION_ATLEAST(2, 0, 0) // SDL1.2
key = mapkey(ev.key.keysym.sym);
if (!key) {
if ( !in_ignoreConsoleKey.GetBool() ) {
@ -1213,12 +1364,18 @@ sysEvent_t Sys_GetEvent() {
continue; // handle next event
}
}
#else
#else // SDL2+
{
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_Scancode sc = ev.key.scancode;
SDL_Keycode keycode = ev.key.key;
#else // SDL2
SDL_Scancode sc = ev.key.keysym.scancode;
SDL_Keycode keycode = ev.key.keysym.sym;
#endif
// workaround for AZERTY-keyboards, which don't have 1, 2, ..., 9, 0 in first row:
// always map those physical keys (scancodes) to those keycodes anyway
// see also https://bugzilla.libsdl.org/show_bug.cgi?id=3188
SDL_Scancode sc = ev.key.keysym.scancode;
if(sc == SDL_SCANCODE_0)
{
key = '0';
@ -1232,10 +1389,10 @@ sysEvent_t Sys_GetEvent() {
}
else
{
key = mapkey(ev.key.keysym.sym);
key = mapkey(keycode);
}
if ( !in_ignoreConsoleKey.GetBool() && ev.key.keysym.scancode == SDL_SCANCODE_GRAVE ) {
if ( !in_ignoreConsoleKey.GetBool() && sc == SDL_SCANCODE_GRAVE ) {
// that key between Esc, Tab and 1 is the console key
key = K_CONSOLE;
}
@ -1245,27 +1402,29 @@ sysEvent_t Sys_GetEvent() {
key = getKeynumForSDLscancode(sc);
if(!key) {
if (ev.type == SDL_KEYDOWN) {
common->Warning("unmapped SDL key %d (scancode %d)", ev.key.keysym.sym, (int)sc);
common->Warning("unmapped SDL key %d (scancode %d)", keycode, (int)sc);
}
continue; // handle next event
}
}
}
#endif
isDown = IS_SDL_BTN_DOWN( ev.key );
res.evType = SE_KEY;
res.evValue = key;
res.evValue2 = ev.key.state == SDL_PRESSED ? 1 : 0;
res.evValue2 = isDown;
kbd_polls.Append(kbd_poll_t(key, ev.key.state == SDL_PRESSED));
kbd_polls.Append( kbd_poll_t( key, isDown ) );
#if SDL_VERSION_ATLEAST(2, 0, 0)
if (key == K_BACKSPACE && ev.key.state == SDL_PRESSED)
if (key == K_BACKSPACE && isDown)
c = key;
#else
if (ev.key.state == SDL_PRESSED && (ev.key.keysym.unicode & 0xff00) == 0)
c = ev.key.keysym.unicode & 0xff;
#endif
// Note: c will be sent as SE_CHAR next time this function is called
return res;
@ -1277,8 +1436,17 @@ sysEvent_t Sys_GetEvent() {
if ( isAscii(ev.text.text) ) {
res.evValue = ev.text.text[0];
if ( ev.text.text[1] != '\0' ) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
// yes, it's ok if this is not 0-terminated, the code generating
// events from s handles that
strncpy(s, ev.text.text, sizeof(s));
#else // SDL2
memcpy( s, ev.text.text, SDL_TEXTINPUTEVENT_TEXT_SIZE );
s_pos = 1; // pos 0 is returned
s[SDL_TEXTINPUTEVENT_TEXT_SIZE] = '\0';
#endif
// pos 0 is returned, the rest of s is returned as SE_CHAR events
// at the next times this function is called
s_pos = 1;
}
return res;
} else if( D3_UTF8toISO8859_1( ev.text.text, s, sizeof(s) ) && s[0] != '\0' ) {
@ -1287,17 +1455,15 @@ sysEvent_t Sys_GetEvent() {
s_pos = 0;
s[0] = '\0';
} else {
s_pos = 1; // pos 0 is returned
// pos 0 is returned, the rest of s is returned as SE_CHAR events
// at the next times this function is called
s_pos = 1;
}
return res;
}
}
continue; // handle next event
case SDL_TEXTEDITING:
// on windows we get this event whenever the window gains focus.. just ignore it.
continue;
#endif
case SDL_MOUSEMOTION:
@ -1335,20 +1501,21 @@ sysEvent_t Sys_GetEvent() {
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
isDown = IS_SDL_BTN_DOWN(ev.button);
res.evType = SE_KEY;
switch (ev.button.button) {
case SDL_BUTTON_LEFT:
res.evValue = K_MOUSE1;
mouse_polls.Append(mouse_poll_t(M_ACTION1, ev.button.state == SDL_PRESSED ? 1 : 0));
mouse_polls.Append(mouse_poll_t(M_ACTION1, isDown));
break;
case SDL_BUTTON_MIDDLE:
res.evValue = K_MOUSE3;
mouse_polls.Append(mouse_poll_t(M_ACTION3, ev.button.state == SDL_PRESSED ? 1 : 0));
mouse_polls.Append(mouse_poll_t(M_ACTION3, isDown));
break;
case SDL_BUTTON_RIGHT:
res.evValue = K_MOUSE2;
mouse_polls.Append(mouse_poll_t(M_ACTION2, ev.button.state == SDL_PRESSED ? 1 : 0));
mouse_polls.Append(mouse_poll_t(M_ACTION2, isDown));
break;
#if !SDL_VERSION_ATLEAST(2, 0, 0)
@ -1370,14 +1537,14 @@ sysEvent_t Sys_GetEvent() {
{
int buttonIndex = ev.button.button - SDL_BUTTON_LEFT;
res.evValue = K_MOUSE1 + buttonIndex;
mouse_polls.Append( mouse_poll_t( M_ACTION1 + buttonIndex, ev.button.state == SDL_PRESSED ? 1 : 0 ) );
mouse_polls.Append( mouse_poll_t( M_ACTION1 + buttonIndex, isDown ) );
}
else
#endif
continue; // handle next event
}
res.evValue2 = ev.button.state == SDL_PRESSED ? 1 : 0;
res.evValue2 = isDown;
return res;
@ -1390,8 +1557,10 @@ sysEvent_t Sys_GetEvent() {
continue;
}
isDown = IS_SDL_BTN_DOWN(ev.cbutton);
res.evType = SE_KEY;
res.evValue2 = ev.cbutton.state == SDL_PRESSED ? 1 : 0;
res.evValue2 = isDown;
// special case: always treat the start button as escape so it opens/closes the menu
// (also makes that button non-bindable)
@ -1410,7 +1579,7 @@ sysEvent_t Sys_GetEvent() {
}
sys_jEvents jEvent = mapjoybutton( (SDL_GameControllerButton)ev.cbutton.button );
joystick_polls.Append( joystick_poll_t(jEvent, ev.cbutton.state == SDL_PRESSED ? 1 : 0) );
joystick_polls.Append( joystick_poll_t(jEvent, isDown) );
if ( ( jEvent >= J_ACTION_FIRST ) && ( jEvent <= J_ACTION_MAX ) ) {
res.evValue = K_FIRST_JOY + ( jEvent - J_ACTION_FIRST );
@ -1571,9 +1740,12 @@ static void handleMouseGrab() {
bool showCursor = true;
bool grabMouse = false;
bool relativeMouse = false;
bool enableTextInput = false;
const bool imguiHasFocus = D3::ImGuiHooks::ShouldShowCursor();
// if com_editorActive, release everything, just like when we have no focus
if ( in_hasFocus && !com_editorActive && !D3::ImGuiHooks::ShouldShowCursor() ) {
if ( in_hasFocus && !com_editorActive && !imguiHasFocus ) {
// Note: this generally handles fullscreen menus, but not the PDA, because the PDA
// is an ugly hack in gamecode that doesn't go through sessLocal.guiActive.
// It goes through weapon input code or sth? That's also the reason only
@ -1585,9 +1757,11 @@ static void handleMouseGrab() {
showCursor = false;
relativeMouse = false;
grabMouse = false; // TODO: or still grab to window? (maybe only if in exclusive fullscreen mode?)
enableTextInput = true;
} else if ( console->Active() ) {
showCursor = true;
relativeMouse = grabMouse = false;
enableTextInput = true;
} else { // in game
showCursor = false;
grabMouse = relativeMouse = true;
@ -1603,6 +1777,10 @@ static void handleMouseGrab() {
}
} else {
in_relativeMouseMode = false;
// if an ImGui window has focus, enable text input so one can write in there
// TODO: add explicit GRAB_DISABLETEXTINPUT and don't set it at all here for ImGui?
// in theory, ImGui handles that itself, but currently GLimp_GrabInput() seems to override it
enableTextInput = imguiHasFocus;
}
int flags = 0;
@ -1612,6 +1790,8 @@ static void handleMouseGrab() {
flags |= GRAB_GRABMOUSE;
if ( relativeMouse )
flags |= GRAB_RELATIVEMOUSE;
if ( enableTextInput )
flags |= GRAB_ENABLETEXTINPUT;
GLimp_GrabInput( flags );
}
@ -1630,7 +1810,8 @@ void Sys_GenerateEvents() {
if (s)
PushConsoleEvent(s);
#ifndef ID_DEDICATED // doesn't make sense on dedicated server
// doesn't make sense on dedicated server and SDL3 handles this in GLimp_GrabInput()
#if !defined(ID_DEDICATED) && ! SDL_VERSION_ATLEAST(3, 0, 0)
if ( in_grabKeyboard.IsModified() ) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_SetHint( SDL_HINT_GRAB_KEYBOARD, in_grabKeyboard.GetString() );

View file

@ -26,7 +26,17 @@ If you have questions concerning this license or the applicable additional terms
===========================================================================
*/
#include <SDL.h>
#include "sys/sys_sdl.h"
#if SDL_VERSION_ATLEAST(3, 0, 0)
// backwards-compat with SDL2
#define SDL_WINDOW_ALLOW_HIGHDPI SDL_WINDOW_HIGH_PIXEL_DENSITY
#define SDL_GL_DeleteContext SDL_GL_DestroyContext
typedef SDL_WindowFlags My_SDL_WindowFlags;
#else // SDL1.2 or SDL2
// for compat with SDL3 - unfortunately SDL2 also has a SDL_WindowFlags type, but it's an enum
typedef Uint32 My_SDL_WindowFlags;
#endif
#include "sys/platform.h"
#include "framework/Licensee.h"
@ -37,7 +47,11 @@ If you have questions concerning this license or the applicable additional terms
#if defined(_WIN32) && defined(ID_ALLOW_TOOLS)
#include "sys/win32/win_local.h"
#include <SDL_syswm.h>
// SDL3 doesn't have SDL_syswm.h
#if ! SDL_VERSION_ATLEAST(3, 0, 0)
#include <SDL_syswm.h>
#endif
// from SDL_windowsopengl.h (internal SDL2 header)
#ifndef WGL_ARB_pixel_format
@ -99,7 +113,6 @@ If you have questions concerning this license or the applicable additional terms
#endif // _WIN32 and ID_ALLOW_TOOLS
#if SDL_VERSION_ATLEAST(2, 0, 0)
static SDL_Window *window = NULL;
static SDL_GLContext context = NULL;
@ -109,6 +122,24 @@ static SDL_Surface *window = NULL;
#define SDL_WINDOW_FULLSCREEN SDL_FULLSCREEN
#endif
#if SDL_VERSION_ATLEAST(3, 0, 0)
extern idCVar in_grabKeyboard;
static void SetSDLIcon()
{
#include "doom_icon.h" // contains the struct d3_icon
SDL_Surface* icon = SDL_CreateSurfaceFrom(d3_icon.width, d3_icon.height,
SDL_PIXELFORMAT_RGBA32, (void*)d3_icon.pixel_data,
d3_icon.bytes_per_pixel*d3_icon.width);
SDL_SetWindowIcon(window, icon);
SDL_DestroySurface(icon);
}
#else // SDL2 and SDL1.2
static void SetSDLIcon()
{
Uint32 rmask, gmask, bmask, amask;
@ -141,6 +172,7 @@ static void SetSDLIcon()
SDL_FreeSurface(icon);
}
#endif // SDL2 and SDL1.2
/*
===================
@ -152,16 +184,23 @@ bool GLimp_Init(glimpParms_t parms) {
assert(SDL_WasInit(SDL_INIT_VIDEO));
Uint32 flags = SDL_WINDOW_OPENGL;
My_SDL_WindowFlags flags = SDL_WINDOW_OPENGL;
if (parms.fullScreen == 1)
{
#if SDL_VERSION_ATLEAST(2, 0, 0)
if(parms.fullScreenDesktop)
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
else
#endif
#if SDL_VERSION_ATLEAST(3, 0, 0)
// in SDL3 windows with SDL_WINDOW_FULLSCREEN set are fullscreen-desktop by default
// and for exclusive fullscreen SDL_SetWindowFullscreenMode() must be called
// after creating the window, so only set the flag if we want fullscreen-desktop mode
if(parms.fullScreen && parms.fullScreenDesktop) {
flags |= SDL_WINDOW_FULLSCREEN;
}
#elif SDL_VERSION_ATLEAST(2, 0, 0)
flags |= parms.fullScreenDesktop ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN;
#else // SDL1.2
flags |= SDL_WINDOW_FULLSCREEN;
#endif
}
r_windowResizable.ClearModified();
@ -180,17 +219,23 @@ bool GLimp_Init(glimpParms_t parms) {
* rendering bugs (the window is partly transparent or very white in areas with low alpha).
* Mesa introduced an EGL extension that's supposed to fix that (EGL_EXT_present_opaque)
* and newer SDL2 versions use it by default (in the Wayland backend).
* Unfortunately, the implementation of that extension is (currently?) broken (at least
* in Mesa), seems like they just give you a visual without any alpha chan - which doesn't
* Unfortunately, the implementation of that extension was broken (at least in Mesa before 24.1),
* seems like they just give you a visual without any alpha chan - which doesn't
* work for Doom3, as it needs a functioning alpha chan for blending operations, see above.
* See also: https://gitlab.freedesktop.org/mesa/mesa/-/issues/5886
*
* So to make sure dhewm3 (finally) works as expected on Wayland, we tell SDL2 to
* allow transparency and then fill the alpha-chan ourselves in RB_SwapBuffers()
* (unless the user disables that with r_fillWindowAlphaChan 0) */
* (unless the user disables that with r_fillWindowAlphaChan 0)
*
* NOTE: This bug is fixed in Mesa 24.1 and newer, and doesn't seem to occur with recent
* NVIDIA drivers either, so for SDL3 (which should be mostly used with current drivers/mesa)
* I don't enable this hack by default. If r_fillWindowAlphaChan == 1, it's enabled
* when creating the window, though.
*/
#ifdef SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY
SDL_SetHint(SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY, "1");
#else // little hack so this works if the SDL2 version used for building is older than runtime version
#elif SDL_MAJOR_VERSION == 2 // little hack so this works if the SDL2 version used for building is older than runtime version
SDL_SetHint("SDL_VIDEO_EGL_ALLOW_TRANSPARENCY", "1");
#endif
#endif
@ -277,7 +322,7 @@ try_again:
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (multisamples > 1) ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisamples);
#if SDL_VERSION_ATLEAST(2, 0, 0)
#if SDL_VERSION_ATLEAST(2, 0, 0) // SDL2 and SDL3 window creation
if ( r_glDebugContext.GetBool() ) {
common->Printf( "Requesting an OpenGL Debug Context (r_glDebugContext is enabled)\n" );
@ -292,37 +337,150 @@ try_again:
windowMode, parms.width, parms.height, r_mode.GetInteger());
}
int displayIndex = 0;
#if SDL_VERSION_ATLEAST(2, 0, 4)
Uint32 selectedDisplay = 0;
#if SDL_VERSION_ATLEAST(2, 0, 4)
// try to put the window on the display the mousecursor currently is on
{
#if SDL_VERSION_ATLEAST(3, 0, 0)
float x, y;
int numDisplays = 0;
SDL_DisplayID* displayIDs = SDL_GetDisplays(&numDisplays);
#else // SDL2
int numDisplays = SDL_GetNumVideoDisplays();
int x, y;
#endif
SDL_GetGlobalMouseState(&x, &y);
int numDisplays = SDL_GetNumVideoDisplays();
common->Printf("SDL detected %d displays: \n", numDisplays);
bool found = false;
for ( int j=0; j<numDisplays; ++j ) {
SDL_Rect rect;
if (SDL_GetDisplayBounds(j, &rect) == 0) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_DisplayID displayId_x = displayIDs[j];
int numModes = 0;
SDL_DisplayMode** modes = SDL_GetFullscreenDisplayModes(displayId_x, &numModes);
common->Printf( " Display %d (ID %u) has the following modes:\n", j, displayId_x );
for ( int dmIdx=0; dmIdx < numModes; ++dmIdx ) {
SDL_DisplayMode* mode = modes[dmIdx];
common->Printf( " - %d x %d @ %g Hz, density %g \n", mode->w, mode->h, mode->refresh_rate, mode->pixel_density );
}
SDL_free( modes );
if ( SDL_GetDisplayBounds(displayId_x, &rect) ) {
common->Printf(" Currently: %dx%d at (%d, %d) to (%d, %d)\n", rect.w, rect.h,
rect.x, rect.y, rect.x+rect.w, rect.y+rect.h);
#else // SDL2
int displayId_x = j;
if (SDL_GetDisplayBounds(displayId_x, &rect) == 0) {
common->Printf(" %d: %dx%d at (%d, %d) to (%d, %d)\n", j, rect.w, rect.h,
rect.x, rect.y, rect.x+rect.w, rect.y+rect.h);
#endif
if ( !found && x >= rect.x && x < rect.x + rect.w
&& y >= rect.y && y < rect.y + rect.h )
{
displayIndex = j;
selectedDisplay = j;
found = true;
}
}
}
common->Printf("Will use display %d because mouse cursor is at (%d, %d).\n",
displayIndex, x, y);
#if SDL_VERSION_ATLEAST(3, 0, 0)
if(displayIDs != NULL) {
SDL_DisplayID displayID = displayIDs[selectedDisplay];
common->Printf("Will use display %u (%u) because mouse cursor is at (%g, %g).\n",
selectedDisplay, displayID, x, y);
selectedDisplay = displayID;
SDL_free(displayIDs);
}
#else // SDL2
common->Printf("Will use display %u because mouse cursor is at (%d, %d).\n",
selectedDisplay, x, y);
#endif
}
#endif
#endif // SDL_VERSION_ATLEAST(2, 0, 4)
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, ENGINE_VERSION);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay));
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay));
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, parms.width);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, parms.height);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags);
// See above for the big comment about Wayland and alpha channels.
// When using SDL3 I assume that people usually won't be affected by this bug,
// because it's fixed in recent Mesa versions (and also works with the NVIDIA driver).
// However, with `r_fillWindowAlphaChan 1` its usage can still be enforced
// on Unix-like platforms (I don't think there's a point in this on Windows or Mac)
#if defined(__unix__) && !defined(__APPLE__)
if ( cvarSystem->GetCVarInteger( "r_fillWindowAlphaChan" ) == 1 ) {
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, true);
}
#endif
window = SDL_CreateWindowWithProperties(props);
SDL_DestroyProperties(props);
if (window == NULL) {
common->Warning("Couldn't set GL mode %d/%d/%d with %dx MSAA: %s",
channelcolorbits, tdepthbits, tstencilbits, parms.multiSamples, SDL_GetError());
// before trying to reduce color channel size or whatever, first try reducing MSAA, if possible
if (multisamples > 1) {
multisamples = (multisamples <= 2) ? 0 : (multisamples/2);
// using goto because enhancing that logic which reduces attributes
// based on i (so it'd first try reducing MSAA) would be too painful
goto try_again;
}
continue;
} else {
// creating the window succeeded, so adjust r_multiSamples to the value that was actually used
parms.multiSamples = multisamples;
r_multiSamples.SetInteger(multisamples);
}
// handle exclusive fullscreen mode (windowed mode and fullscreen
// desktop were set when creating the window)
// TODO: just call GLimp_SetScreenParms() ?
if (parms.fullScreen && !parms.fullScreenDesktop) {
SDL_DisplayID displayID = SDL_GetDisplayForWindow( window );
SDL_DisplayMode mode = {};
if ( SDL_GetClosestFullscreenDisplayMode(displayID, parms.width, parms.height,
parms.displayHz, true, &mode) )
{
if ( ! SDL_SetWindowFullscreenMode(window, &mode) ) {
common->Warning("Can't set window fullscreen mode: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
window = NULL;
return false; // trying other color depth etc is unlikely to help with this issue
}
if ( ! SDL_SetWindowFullscreen(window, true) ) {
common->Warning("Can't switch window to fullscreen mode: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
window = NULL;
return false; // trying other color depth etc is unlikely to help with this issue
}
} else {
common->Warning("Can't get display mode: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
window = NULL;
return false; // trying other color depth etc is unlikely to help with this issue
}
}
if ( ! SDL_SyncWindow(window) ) {
common->Warning("SDL_SyncWindow() failed: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
window = NULL;
return false; // trying other color depth etc is unlikely to help with this issue
}
#else // SDL2
window = SDL_CreateWindow(ENGINE_VERSION,
SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),
SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),
SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay),
SDL_WINDOWPOS_UNDEFINED_DISPLAY(selectedDisplay),
parms.width, parms.height, flags);
if (!window) {
@ -364,8 +522,8 @@ try_again:
common->Warning("Current display mode isn't requested display mode\n");
common->Warning("Likely SDL bug #4700, trying to work around it..\n");
int dIdx = SDL_GetWindowDisplayIndex(window);
if(dIdx != displayIndex) {
common->Warning("Window's display index is %d, but we wanted %d!\n", dIdx, displayIndex);
if(dIdx != selectedDisplay) {
common->Warning("Window's display index is %d, but we wanted %d!\n", dIdx, selectedDisplay);
}
/* Mkay, try to hack around that. */
@ -413,6 +571,7 @@ try_again:
common->Warning("Now we have the requested resolution (%d x %d)\n", parms.width, parms.height);
}
}
#endif // SDL2
context = SDL_GL_CreateContext(window);
@ -424,7 +583,8 @@ try_again:
SetSDLIcon(); // for SDL2 this must be done after creating the window
glConfig.isFullscreen = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN;
// TODO: also check for fullscreen-desktop?
glConfig.isFullscreen = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0;
const char* fsStr = glConfig.isFullscreen ? "fullscreen " : "";
if ( (int)glConfig.winWidth != glConfig.vidWidth ) {
common->Printf( "Got a HighDPI %swindow with physical resolution %d x %d and virtual resolution %g x %g\n",
@ -432,7 +592,7 @@ try_again:
} else {
common->Printf( "Got a %swindow with resolution %g x %g\n", fsStr, glConfig.winWidth, glConfig.winHeight );
}
#else
#else // SDL1.2 window creation
SDL_WM_SetCaption(ENGINE_VERSION, ENGINE_VERSION);
SetSDLIcon(); // for SDL1.2 this must be done before creating the window
@ -483,6 +643,13 @@ try_again:
// then we know we are win32 and we have to include this
// config to get the editors to work.
#if SDL_VERSION_ATLEAST(3, 0, 0)
HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
HDC hdc = (HDC)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HDC_POINTER, NULL);
if ( hwnd && hdc ) {
win32.hWnd = hwnd;
win32.hDC = hdc;
#else // SDL2
// Get the HWND for later use.
SDL_SysWMinfo sdlinfo;
SDL_version sdlver;
@ -491,6 +658,7 @@ try_again:
if (SDL_GetWindowWMInfo(window, &sdlinfo) && sdlinfo.subsystem == SDL_SYSWM_WINDOWS) {
win32.hWnd = sdlinfo.info.win.window;
win32.hDC = sdlinfo.info.win.hdc;
#endif
// NOTE: hInstance is set in main()
win32.hGLRC = qwglGetCurrentContext();
@ -592,7 +760,9 @@ try_again:
#if SDL_VERSION_ATLEAST(2, 0, 0)
const char* videoDriver = SDL_GetCurrentVideoDriver();
if (idStr::Icmp(videoDriver, "wayland") == 0) {
#if SDL_MAJOR_VERSION == 2 // don't enable this hack by default with SDL3
glConfig.shouldFillWindowAlpha = true;
#endif
glConfig.isWayland = true;
}
#endif
@ -600,7 +770,11 @@ try_again:
glConfig.haveDebugContext = false;
#if SDL_VERSION_ATLEAST(2, 0, 0)
int cflags = 0;
#if SDL_VERSION_ATLEAST(3, 0, 0)
if ( SDL_GL_GetAttribute( SDL_GL_CONTEXT_FLAGS, &cflags ) ) {
#else // SDL2
if ( SDL_GL_GetAttribute( SDL_GL_CONTEXT_FLAGS, &cflags ) == 0 ) {
#endif
glConfig.haveDebugContext = (cflags & SDL_GL_CONTEXT_DEBUG_FLAG) != 0;
if ( glConfig.haveDebugContext )
common->Printf( "Got a debug context!\n" );
@ -648,12 +822,21 @@ bool GLimp_SetScreenParms(glimpParms_t parms) {
}
if ( !parms.fullScreen ) { // we want windowed mode
if ( curState.fullScreen && SDL_SetWindowFullscreen( window, 0 ) != 0 ) {
if ( curState.fullScreen &&
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_SetWindowFullscreen( window, 0 ) == false
#else
SDL_SetWindowFullscreen( window, 0 ) != 0
#endif
) {
common->Warning( "GLimp_SetScreenParms(): Couldn't switch to windowed mode, SDL error: %s\n", SDL_GetError() );
return false;
}
SDL_RestoreWindow( window ); // make sure we're not maximized, then setting the size wouldn't work
SDL_SetWindowSize( window, parms.width, parms.height );
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_SyncWindow( window );
#endif
} else { // we want some kind of fullscreen mode
// it's probably safest to first switch to windowed mode
@ -661,6 +844,60 @@ bool GLimp_SetScreenParms(glimpParms_t parms) {
SDL_SetWindowFullscreen( window, 0 );
}
#if SDL_VERSION_ATLEAST(3, 0, 0)
if ( wantFullscreenDesktop ) {
SDL_SetWindowFullscreenMode( window, NULL ); // setting it to NULL enables fullscreen desktop mode
SDL_SetWindowFullscreen( window, true );
// TODO: check return values
if ( ! SDL_SyncWindow( window ) ) {
common->Warning( "SDL_SyncWindow() failed: %s\n", SDL_GetError() );
//TODO: probably not SDL_DestroyWindow( window );
//window = NULL;
return false;
}
} else { // want real fullscreen
SDL_DisplayID displayID = SDL_GetDisplayForWindow( window );
SDL_DisplayMode mode = {};
if ( SDL_GetClosestFullscreenDisplayMode( displayID, parms.width, parms.height,
parms.displayHz, true, &mode ) )
{
if ( ! SDL_SetWindowFullscreenMode( window, &mode ) ) {
common->Warning( "Can't set window fullscreen mode: %s\n", SDL_GetError() );
//TODO: probably not SDL_DestroyWindow( window );
//window = NULL;
return false;
}
if ( ! SDL_SetWindowFullscreen( window, true ) ) {
common->Warning( "Can't switch window to fullscreen mode: %s\n", SDL_GetError() );
//TODO: probably not SDL_DestroyWindow( window );
//window = NULL;
return false;
}
if ( ! SDL_SyncWindow( window ) ) {
common->Warning( "SDL_SyncWindow() failed: %s\n", SDL_GetError() );
//TODO: probably not SDL_DestroyWindow( window );
//window = NULL;
return false;
}
} else {
if ( parms.displayHz != 0 ) {
common->Warning( "Can't get display mode for %d x %d @ %dHz: %s\n", parms.width,
parms.height, parms.displayHz, SDL_GetError() );
} else {
common->Warning( "Can't get display mode for %d x %d: %s\n", parms.width,
parms.height, SDL_GetError() );
}
//TODO: probably not SDL_DestroyWindow( window );
//window = NULL;
return false;
}
}
#else // SDL2
if ( wantFullscreenDesktop ) {
if ( SDL_SetWindowFullscreen( window, SDL_WINDOW_FULLSCREEN_DESKTOP ) != 0 ) {
common->Warning( "GLimp_SetScreenParms(): Couldn't switch to fullscreen desktop mode, SDL error: %s\n", SDL_GetError() );
@ -703,6 +940,7 @@ bool GLimp_SetScreenParms(glimpParms_t parms) {
return false;
}
}
#endif // SDL2
}
glConfig.isFullscreen = (SDL_GetWindowFlags( window ) & SDL_WINDOW_FULLSCREEN) != 0;
@ -722,7 +960,32 @@ glimpParms_t GLimp_GetCurState()
glimpParms_t ret = {};
#if SDL_VERSION_ATLEAST(2, 0, 0)
My_SDL_WindowFlags winFlags = SDL_GetWindowFlags( window );
ret.fullScreen = (winFlags & SDL_WINDOW_FULLSCREEN) != 0;
int curMultiSamples = 0;
#if SDL_VERSION_ATLEAST(3, 0, 0)
if ( SDL_GL_GetAttribute( SDL_GL_MULTISAMPLEBUFFERS, &curMultiSamples ) && curMultiSamples > 0 ) {
if ( ! SDL_GL_GetAttribute( SDL_GL_MULTISAMPLESAMPLES, &curMultiSamples ) ) {
curMultiSamples = 0; // SDL_GL_GetAttribute() call failed, assume no MSAA
}
} else {
curMultiSamples = 0; // SDL_GL_GetAttribute() call failed, assume no MSAA
}
if (ret.fullScreen) {
const SDL_DisplayMode* fullscreenMode = SDL_GetWindowFullscreenMode( window );
if (fullscreenMode != NULL) {
ret.width = fullscreenMode->w;
ret.height = fullscreenMode->h;
ret.displayHz = fullscreenMode->refresh_rate;
} else {
// SDL_WINDOW_FULLSCREEN is set, but SDL_GetWindowFullscreenMode() returns NULL
// => fullscreen desktop mode
ret.fullScreenDesktop = true;
}
}
#else // SDL2
if ( SDL_GL_GetAttribute( SDL_GL_MULTISAMPLEBUFFERS, &curMultiSamples ) == 0 && curMultiSamples > 0 ) {
if ( SDL_GL_GetAttribute( SDL_GL_MULTISAMPLESAMPLES, &curMultiSamples ) != 0 ) {
curMultiSamples = 0; // SDL_GL_GetAttribute() call failed, assume no MSAA
@ -730,12 +993,8 @@ glimpParms_t GLimp_GetCurState()
} else {
curMultiSamples = 0; // SDL_GL_GetAttribute() call failed, assume no MSAA
}
ret.multiSamples = curMultiSamples;
Uint32 winFlags = SDL_GetWindowFlags( window );
ret.fullScreen = (winFlags & SDL_WINDOW_FULLSCREEN) != 0;
ret.fullScreenDesktop = (winFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
if ( ret.fullScreen && !ret.fullScreenDesktop ) { // I think SDL_GetWindowDisplayMode() is only for "real" fullscreen?
SDL_DisplayMode real_mode = {};
if ( SDL_GetWindowDisplayMode( window, &real_mode ) == 0 ) {
@ -746,7 +1005,11 @@ glimpParms_t GLimp_GetCurState()
common->Warning( "GLimp_GetCurState(): Can't get display mode: %s\n", SDL_GetError() );
}
}
if ( ret.width == 0 && ret.height == 0 ) { // windowed mode or SDL_GetWindowDisplayMode() failed
#endif
ret.multiSamples = curMultiSamples;
if ( ret.width == 0 && ret.height == 0 ) { // windowed mode, fullscreen-desktop mode or SDL_GetWindowDisplayMode() failed
SDL_GetWindowSize( window, &ret.width, &ret.height );
}
@ -797,11 +1060,14 @@ void GLimp_SwapBuffers() {
#endif
}
// SDL3 doesn't support hardware gamma
#if ! SDL_VERSION_ATLEAST(3, 0, 0)
static bool gammaOrigError = false;
static bool gammaOrigSet = false;
static unsigned short gammaOrigRed[256];
static unsigned short gammaOrigGreen[256];
static unsigned short gammaOrigBlue[256];
#endif
/*
=================
@ -809,6 +1075,12 @@ GLimp_SetGamma
=================
*/
void GLimp_SetGamma(unsigned short red[256], unsigned short green[256], unsigned short blue[256]) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
if ( ! r_gammaInShader.GetBool() ) {
common->Warning( "This build of dhewm3 uses SDL3, which does not support hardware gamma." );
common->Warning( "If you want to adjust gamma or brightness, enable r_gammaInShader" );
}
#else // SDL2 and SDL1.2
if (!window) {
common->Warning("GLimp_SetGamma called without window");
return;
@ -833,6 +1105,7 @@ void GLimp_SetGamma(unsigned short red[256], unsigned short green[256], unsigned
if (SDL_SetGammaRamp(red, green, blue))
#endif
common->Warning("Couldn't set gamma ramp: %s", SDL_GetError());
#endif // SDL2 and SDL1.2
}
/*
@ -843,6 +1116,7 @@ Restore original system gamma setting
=================
*/
void GLimp_ResetGamma() {
#if ! SDL_VERSION_ATLEAST(3, 0, 0) // only for SDL2 and SDL1.2
if( gammaOrigError ) {
common->Warning( "Can't reset hardware gamma because getting the Gamma Ramp at startup failed!\n" );
common->Warning( "You might have to restart the game for gamma/brightness in shaders to work properly.\n" );
@ -850,12 +1124,13 @@ void GLimp_ResetGamma() {
}
if( gammaOrigSet ) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_SetWindowGammaRamp( window, gammaOrigRed, gammaOrigGreen, gammaOrigBlue );
#else
#else
SDL_SetGammaRamp( gammaOrigRed, gammaOrigGreen, gammaOrigBlue );
#endif
#endif
}
#endif // ! SDL_VERSION_ATLEAST(3, 0, 0)
}
@ -893,8 +1168,26 @@ void GLimp_GrabInput(int flags) {
common->Warning("GLimp_GrabInput called without window");
return;
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
#if SDL_VERSION_ATLEAST(3, 0, 0)
if (flags & GRAB_HIDECURSOR) {
SDL_HideCursor();
} else {
SDL_ShowCursor();
}
SDL_SetWindowRelativeMouseMode( window, (flags & GRAB_RELATIVEMOUSE) != 0 );
if (flags & GRAB_GRABMOUSE) {
SDL_SetWindowMouseGrab( window, true );
SDL_SetWindowKeyboardGrab( window, in_grabKeyboard.GetBool() );
} else {
SDL_SetWindowMouseGrab( window, false );
SDL_SetWindowKeyboardGrab( window, false );
}
if (flags & GRAB_ENABLETEXTINPUT) {
SDL_StartTextInput( window );
} else {
SDL_StopTextInput( window );
}
#elif SDL_VERSION_ATLEAST(2, 0, 0)
SDL_ShowCursor( (flags & GRAB_HIDECURSOR) ? SDL_DISABLE : SDL_ENABLE );
SDL_SetRelativeMouseMode( (flags & GRAB_RELATIVEMOUSE) ? SDL_TRUE : SDL_FALSE );
SDL_SetWindowGrab( window, (flags & GRAB_GRABMOUSE) ? SDL_TRUE : SDL_FALSE );
@ -909,7 +1202,11 @@ void GLimp_GrabInput(int flags) {
bool GLimp_SetSwapInterval( int swapInterval )
{
#if SDL_VERSION_ATLEAST(2, 0, 0)
#if SDL_VERSION_ATLEAST(3, 0, 0)
if ( ! SDL_GL_SetSwapInterval( swapInterval ) ) {
#elif SDL_VERSION_ATLEAST(2, 0, 0)
if ( SDL_GL_SetSwapInterval( swapInterval ) < 0 ) {
#endif
common->Warning( "SDL_GL_SetSwapInterval( %d ) not supported", swapInterval );
return false;
}
@ -922,7 +1219,10 @@ bool GLimp_SetSwapInterval( int swapInterval )
bool GLimp_SetWindowResizable( bool enableResizable )
{
#if SDL_VERSION_ATLEAST(2, 0, 5)
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_SetWindowResizable( window, enableResizable );
return true;
#elif SDL_VERSION_ATLEAST(2, 0, 5)
SDL_SetWindowResizable( window, (SDL_bool)enableResizable );
return true;
#else
@ -934,7 +1234,18 @@ bool GLimp_SetWindowResizable( bool enableResizable )
void GLimp_UpdateWindowSize()
{
#if SDL_VERSION_ATLEAST(2, 0, 0)
Uint32 winFlags = SDL_GetWindowFlags( window );
My_SDL_WindowFlags winFlags = SDL_GetWindowFlags( window );
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_GetWindowSizeInPixels( window, &glConfig.vidWidth, &glConfig.vidHeight );
const SDL_DisplayMode* fullscreenMode = SDL_GetWindowFullscreenMode( window );
if ( (winFlags & SDL_WINDOW_FULLSCREEN) != 0 && fullscreenMode != NULL) {
glConfig.winWidth = fullscreenMode->w;
glConfig.winHeight = fullscreenMode->h;
}
#else // SDL2
SDL_GL_GetDrawableSize( window, &glConfig.vidWidth, &glConfig.vidHeight );
if ( (winFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN ) {
// real fullscreen mode => must use SDL_GetWindowDisplayMode()
// TODO: well, theoretically SDL_GetWindowSize() should work for fullscreen mode as well,
@ -951,13 +1262,13 @@ void GLimp_UpdateWindowSize()
} else {
common->Warning( "GLimp_UpdateWindowSize(): SDL_GetWindowDisplayMode() failed: %s\n", SDL_GetError() );
}
} else {
}
#endif // SDL2
else {
int ww=0, wh=0;
SDL_GetWindowSize( window, &ww, &wh );
glConfig.winWidth = ww;
glConfig.winHeight = wh;
}
SDL_GL_GetDrawableSize( window, &glConfig.vidWidth, &glConfig.vidHeight );
#endif
}

View file

@ -32,7 +32,7 @@ If you have questions concerning this license or the applicable additional terms
#include <sys/types.h>
#include <fcntl.h>
#include <SDL_main.h>
//#include <SDL_main.h> - not needed, dhewm3 doesn't currently use SDL's SDL_main
#include "sys/platform.h"
#include "framework/Licensee.h"

View file

@ -32,9 +32,6 @@ If you have questions concerning this license or the applicable additional terms
#include <fenv.h>
#include <mach/thread_status.h>
#include <AppKit/AppKit.h>
#include <SDL_main.h>
#include "sys/platform.h"
#include <sys/types.h>
@ -45,6 +42,11 @@ If you have questions concerning this license or the applicable additional terms
#include "sys/posix/posix_public.h"
// usually the following would be from SDL_main.h,
// but this way we should be able to avoid that header
// for better compatibility between SDL1.2 to SDL3
extern "C" int SDL_main( int argc, char *argv[] );
static char base_path[MAXPATHLEN];
static char exe_path[MAXPATHLEN];
static char save_path[MAXPATHLEN];
@ -194,13 +196,15 @@ int SDL_main( int argc, char *argv[] ) {
Sys_Error("Could not access application resources");
// DG: set exe_path so Posix_InitSignalHandlers() can call Posix_GetExePath()
SDL_strlcpy(exe_path, [ [ [ NSBundle mainBundle ] bundlePath ] cStringUsingEncoding:NSUTF8StringEncoding ], sizeof(exe_path));
strncpy(exe_path, [ [ [ NSBundle mainBundle ] bundlePath ] cStringUsingEncoding:NSUTF8StringEncoding ], sizeof(exe_path)-1);
exe_path[sizeof(exe_path)-1] = '\0';
// same for save_path for Posix_GetSavePath()
D3_snprintfC99(save_path, sizeof(save_path), "%s/Library/Application Support/dhewm3", [NSHomeDirectory() cStringUsingEncoding:NSUTF8StringEncoding]);
// and preinitializing basepath is easy enough so do that as well
{
char* snap;
SDL_strlcpy(base_path, exe_path, sizeof(base_path));
strncpy(base_path, exe_path, sizeof(base_path)-1);
base_path[sizeof(base_path)-1] = '\0';
snap = strrchr(base_path, '/');
if (snap)
*snap = '\0';

View file

@ -10,7 +10,15 @@
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
#include "SDL.h"
#ifdef D3_SDL3
#include <SDL3/SDL.h>
// as SDL.h doesn't implicitly include SDL_main.h anymore,
// declare SDL_main() here. I think it's the only part of SDL_main.h we used,
// we implement it in DOOMController.mm an call it here in applicationDidFinishLaunching
extern "C" int SDL_main( int argc, char *argv[] );
#else // SDL2 and SDL1.2
#include "SDL.h"
#endif
#include "SDLMain.h"
#include <sys/param.h> /* for MAXPATHLEN */
#include <unistd.h>

View file

@ -49,7 +49,9 @@ If you have questions concerning this license or the applicable additional terms
#include "sys/posix/posix_public.h"
#include <SDL.h> // clipboard
// SDL.h for clipboard:
#include "sys/sys_sdl.h"
#define COMMAND_HISTORY 64

View file

@ -1,7 +1,19 @@
#define IMGUI_DEFINE_MATH_OPERATORS
#include <SDL.h>
#include "sys_sdl.h"
#if SDL_VERSION_ATLEAST(3, 0, 0)
// compat with SDL2
#define SDL_TEXTINPUT SDL_EVENT_TEXT_INPUT
#define SDL_CONTROLLERAXISMOTION SDL_EVENT_GAMEPAD_AXIS_MOTION
#define SDL_CONTROLLERBUTTONDOWN SDL_EVENT_GAMEPAD_BUTTON_DOWN
#define SDL_MOUSEBUTTONDOWN SDL_EVENT_MOUSE_BUTTON_DOWN
#define SDL_MOUSEMOTION SDL_EVENT_MOUSE_MOTION
#define SDL_MOUSEWHEEL SDL_EVENT_MOUSE_WHEEL
#define SDL_KEYDOWN SDL_EVENT_KEY_DOWN
#endif
#include "sys_imgui.h"
@ -13,7 +25,20 @@ typedef char* (*MY_XGETDEFAULTFUN)(Display*, const char*, const char*);
#endif
#include "../libs/imgui/backends/imgui_impl_opengl2.h"
#include "../libs/imgui/backends/imgui_impl_sdl2.h"
#if SDL_VERSION_ATLEAST(3, 0, 0)
#include "../libs/imgui/backends/imgui_impl_sdl3.h"
#define ImGui_ImplSDLx_InitForOpenGL ImGui_ImplSDL3_InitForOpenGL
#define ImGui_ImplSDLx_Shutdown ImGui_ImplSDL3_Shutdown
#define ImGui_ImplSDLx_NewFrame ImGui_ImplSDL3_NewFrame
#define ImGui_ImplSDLx_ProcessEvent ImGui_ImplSDL3_ProcessEvent
#else
#include "../libs/imgui/backends/imgui_impl_sdl2.h"
#define ImGui_ImplSDLx_InitForOpenGL ImGui_ImplSDL2_InitForOpenGL
#define ImGui_ImplSDLx_Shutdown ImGui_ImplSDL2_Shutdown
#define ImGui_ImplSDLx_NewFrame ImGui_ImplSDL2_NewFrame
#define ImGui_ImplSDLx_ProcessEvent ImGui_ImplSDL2_ProcessEvent
#endif
#include "framework/Common.h"
#include "framework/KeyInput.h"
@ -145,7 +170,7 @@ void ShowWarningOverlay( const char* text )
warningOverlayStartPos = ImGui::GetMousePos();
}
#if SDL_MAJOR_VERSION == 2 // not used with SDL3
static float GetDefaultDPI()
{
SDL_Window* win = sdlWindow;
@ -185,6 +210,7 @@ static float GetDefaultDPI()
}
return dpi;
}
#endif // SDL2-only
static float GetDefaultScale()
{
@ -192,8 +218,13 @@ static float GetDefaultScale()
// in HighDPI mode, the font sizes are already scaled (to window coordinates), apparently
return 1.0f;
}
#if SDL_VERSION_ATLEAST(3, 0, 0)
float ret = SDL_GetWindowDisplayScale( sdlWindow );
#else
// TODO: different reference DPI on mac? also, doesn't work that well on my laptop..
float ret = GetDefaultDPI() / 96.0f;
#endif
ret = round(ret*2.0)*0.5; // round to .0 or .5
return ret;
}
@ -250,7 +281,7 @@ bool Init(void* _sdlWindow, void* sdlGlContext)
imgui_scale.SetModified(); // so NewFrame() will load the scaled font
// Setup Platform/Renderer backends
if ( ! ImGui_ImplSDL2_InitForOpenGL( sdlWindow, sdlGlContext ) ) {
if ( ! ImGui_ImplSDLx_InitForOpenGL( sdlWindow, sdlGlContext ) ) {
ImGui::DestroyContext( imguiCtx );
imguiCtx = NULL;
common->Warning( "Failed to initialize ImGui SDL platform backend!\n" );
@ -258,7 +289,7 @@ bool Init(void* _sdlWindow, void* sdlGlContext)
}
if ( ! ImGui_ImplOpenGL2_Init() ) {
ImGui_ImplSDL2_Shutdown();
ImGui_ImplSDLx_Shutdown();
ImGui::DestroyContext( imguiCtx );
imguiCtx = NULL;
common->Warning( "Failed to initialize ImGui OpenGL renderer backend!\n" );
@ -302,7 +333,7 @@ void Shutdown()
// TODO: only if init was successful!
ImGui_ImplOpenGL2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui_ImplSDLx_Shutdown();
ImGui::DestroyContext( imguiCtx );
imgui_initialized = false;
}
@ -345,7 +376,7 @@ void NewFrame()
else
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
ImGui_ImplSDL2_NewFrame();
ImGui_ImplSDLx_NewFrame();
ImGui::NewFrame();
haveNewFrame = true;
@ -379,7 +410,7 @@ bool ProcessEvent(const void* sdlEvent)
// - 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.
bool imguiUsedEvent = ImGui_ImplSDL2_ProcessEvent( ev );
bool imguiUsedEvent = ImGui_ImplSDLx_ProcessEvent( ev );
if ( keybindModeEnabled ) {
// in keybind mode, all input events are passed to Doom3 so it can translate them
// to internal events and we can access and use them to create a new binding
@ -424,7 +455,11 @@ bool ProcessEvent(const void* sdlEvent)
return true;
case SDL_KEYDOWN:
//case SDL_KEYUP: NOTE: see above why key up events are passed to the engine
#if SDL_VERSION_ATLEAST(3, 0, 0)
if ( ev->key.key < SDLK_F1 || ev->key.key > SDLK_F12) {
#else
if ( ev->key.keysym.sym < SDLK_F1 || ev->key.keysym.sym > SDLK_F12) {
#endif
// F1 - F12 are passed to the engine so its shortcuts
// (like quickload or screenshot) still work
// Doom3's menu does the same

14
neo/sys/sys_sdl.h Normal file
View file

@ -0,0 +1,14 @@
// just a wrapper for #include <SDL.h> to support SDL1.2, SDL2 and SDL3
#ifndef NEO_SYS_SYS_SDL_H_
#define NEO_SYS_SYS_SDL_H_
#ifdef D3_SDL3
// HACK: I don't want SDL.h to drag in SDL_oldnames.h to avoid all the warnings about redefined definitions
#define SDL_oldnames_h_
#include <SDL3/SDL.h>
#else // SDL1.2 or SDL2
#include <SDL.h>
#endif
#endif /* NEO_SYS_SYS_SDL_H_ */

View file

@ -26,10 +26,14 @@ If you have questions concerning this license or the applicable additional terms
===========================================================================
*/
#include <SDL_version.h>
#include <SDL_mutex.h>
#include <SDL_thread.h>
#include <SDL_timer.h>
#include "sys/sys_sdl.h"
#if 0 // TODO: was there a reason not to include full SDL.h?
#include <SDL_version.h>
#include <SDL_mutex.h>
#include <SDL_thread.h>
#include <SDL_timer.h>
#endif
#include "sys/platform.h"
#include "framework/Common.h"
@ -40,6 +44,15 @@ If you have questions concerning this license or the applicable additional terms
// SDL1.2 doesn't have SDL_threadID but uses Uint32.
// this typedef helps using the same code for SDL1.2 and SDL2
typedef Uint32 SDL_threadID;
#elif SDL_MAJOR_VERSION >= 3
// backwards-compat with SDL2
#define SDL_mutex SDL_Mutex
#define SDL_cond SDL_Condition
#define SDL_threadID SDL_ThreadID
#define SDL_CreateCond SDL_CreateCondition
#define SDL_DestroyCond SDL_DestroyCondition
#define SDL_CondWait SDL_WaitCondition
#define SDL_CondSignal SDL_SignalCondition
#endif
#if __cplusplus >= 201103
@ -159,8 +172,12 @@ Sys_EnterCriticalSection
void Sys_EnterCriticalSection(int index) {
assert(index >= 0 && index < MAX_CRITICAL_SECTIONS);
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_LockMutex(mutex[index]); // in SDL3, this returns void and can't fail
#else // SDL2 and SDL1.2
if (SDL_LockMutex(mutex[index]) != 0)
common->Error("ERROR: SDL_LockMutex failed\n");
#endif
}
/*
@ -171,8 +188,12 @@ Sys_LeaveCriticalSection
void Sys_LeaveCriticalSection(int index) {
assert(index >= 0 && index < MAX_CRITICAL_SECTIONS);
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_UnlockMutex(mutex[index]); // in SDL3, this returns void and can't fail
#else // SDL2 and SDL1.2
if (SDL_UnlockMutex(mutex[index]) != 0)
common->Error("ERROR: SDL_UnlockMutex failed\n");
#endif
}
/*
@ -204,8 +225,12 @@ void Sys_WaitForEvent(int index) {
signaled[index] = false;
} else {
waiting[index] = true;
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_CondWait(cond[index], mutex[CRITICAL_SECTION_SYS]); // in SDL3, this returns void and can't fail
#else // SDL2 and SDL1.2
if (SDL_CondWait(cond[index], mutex[CRITICAL_SECTION_SYS]) != 0)
common->Error("ERROR: SDL_CondWait failed\n");
#endif
waiting[index] = false;
}
@ -223,8 +248,12 @@ void Sys_TriggerEvent(int index) {
Sys_EnterCriticalSection(CRITICAL_SECTION_SYS);
if (waiting[index]) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_CondSignal(cond[index]); // in SDL3, this returns void and can't fail
#else // SDL2 and SDL1.2
if (SDL_CondSignal(cond[index]) != 0)
common->Error("ERROR: SDL_CondSignal failed\n");
#endif
} else {
// emulate windows behaviour: if no thread is waiting, leave the signal on so next wait keeps going
signaled[index] = true;

View file

@ -160,154 +160,6 @@ static void cleanup(void)
SDL_Quit();
}
/* Remove the output files if there was no output written */
static void cleanup_output(void) {
FILE *file;
int empty;
/* Flush the output in case anything is queued */
fclose(stdout);
fclose(stderr);
/* Without redirection we're done */
if (!stdioRedirectEnabled) {
return;
}
/* See if the files have any output in them */
if ( stdoutPath[0] ) {
file = fopen(stdoutPath, TEXT("rb"));
if ( file ) {
empty = (fgetc(file) == EOF) ? 1 : 0;
fclose(file);
if ( empty ) {
remove(stdoutPath);
}
}
}
if ( stderrPath[0] ) {
file = fopen(stderrPath, TEXT("rb"));
if ( file ) {
empty = (fgetc(file) == EOF) ? 1 : 0;
fclose(file);
if ( empty ) {
remove(stderrPath);
}
}
}
}
extern int Win_GetHomeDir(char *dst, size_t size);
/* Redirect the output (stdout and stderr) to a file */
static void redirect_output(void)
{
#ifdef _WIN32_WCE
wchar_t path[MAX_PATH];
#error "adapt homedir code for wchar_t!"
#else
char path[MAX_PATH];
struct _stat st;
/* DG: use "My Documents/My Games/dhewm3" to write stdout.txt and stderr.txt
* instead of the binary, which might not be writable */
Win_GetHomeDir(path, sizeof(path));
if (_stat(path, &st) == -1) {
/* oops, "My Documents/My Games/dhewm3" doesn't exist - does My Games/ at least exist? */
char myGamesPath[MAX_PATH];
char* lastslash;
memcpy(myGamesPath, path, MAX_PATH);
lastslash = strrchr(myGamesPath, '/');
if (lastslash != NULL) {
*lastslash = '\0';
}
if (_stat(myGamesPath, &st) == -1) {
/* if My Documents/My Games/ doesn't exist, create it */
_mkdir(myGamesPath);
}
_mkdir(path); /* create My Documents/My Games/dhewm3/ */
}
#endif
FILE *newfp;
#if 0 /* DG: don't do this anymore. */
DWORD pathlen;
pathlen = GetModuleFileName(NULL, path, SDL_arraysize(path));
while ( pathlen > 0 && path[pathlen] != '\\' ) {
--pathlen;
}
path[pathlen] = '\0';
#endif
#ifdef _WIN32_WCE
wcsncpy( stdoutPath, path, SDL_arraysize(stdoutPath) );
wcsncat( stdoutPath, DIR_SEPERATOR STDOUT_FILE, SDL_arraysize(stdoutPath) );
#else
SDL_strlcpy( stdoutPath, path, SDL_arraysize(stdoutPath) );
SDL_strlcat( stdoutPath, DIR_SEPERATOR STDOUT_FILE, SDL_arraysize(stdoutPath) );
#endif
{ /* DG: rename old stdout log */
#ifdef _WIN32_WCE
wchar_t stdoutPathBK[MAX_PATH];
wcsncpy( stdoutPathBK, path, SDL_arraysize(stdoutPath) );
wcsncat( stdoutPathBK, DIR_SEPERATOR TEXT("dhewm3log-old.txt"), SDL_arraysize(stdoutPath) );
_wrename( stdoutPath, stdoutpathBK );
#else
char stdoutPathBK[MAX_PATH];
SDL_strlcpy( stdoutPathBK, path, SDL_arraysize(stdoutPath) );
SDL_strlcat( stdoutPathBK, DIR_SEPERATOR TEXT("dhewm3log-old.txt"), SDL_arraysize(stdoutPath) );
rename( stdoutPath, stdoutPathBK );
#endif
} /* DG end */
/* Redirect standard input and standard output */
newfp = freopen(stdoutPath, TEXT("w"), stdout);
#ifndef _WIN32_WCE
if ( newfp == NULL ) { /* This happens on NT */
#if !defined(stdout)
stdout = fopen(stdoutPath, TEXT("w"));
#else
newfp = fopen(stdoutPath, TEXT("w"));
if ( newfp ) {
*stdout = *newfp;
}
#endif
}
#endif /* _WIN32_WCE */
#ifdef _WIN32_WCE
wcsncpy( stderrPath, path, SDL_arraysize(stdoutPath) );
wcsncat( stderrPath, DIR_SEPERATOR STDOUT_FILE, SDL_arraysize(stdoutPath) );
#else
SDL_strlcpy( stderrPath, path, SDL_arraysize(stderrPath) );
SDL_strlcat( stderrPath, DIR_SEPERATOR STDERR_FILE, SDL_arraysize(stderrPath) );
#endif
newfp = freopen(stderrPath, TEXT("w"), stderr);
#ifndef _WIN32_WCE
if ( newfp == NULL ) { /* This happens on NT */
#if !defined(stderr)
stderr = fopen(stderrPath, TEXT("w"));
#else
newfp = fopen(stderrPath, TEXT("w"));
if ( newfp ) {
*stderr = *newfp;
}
#endif
}
#endif /* _WIN32_WCE */
setvbuf(stdout, NULL, _IOLBF, BUFSIZ); /* Line buffered */
setbuf(stderr, NULL); /* No buffering */
stdioRedirectEnabled = 1;
}
#if defined(_MSC_VER) && !defined(_WIN32_WCE)
/* The VC++ compiler needs main defined */
#define console_main main
@ -397,6 +249,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
FreeLibrary(handle);
}
#if 0 // DG: output redirection is now done in dhewm3's main() aka SDL_main()
/* Check for stdio redirect settings and do the redirection */
if ((env_str = SDL_getenv("SDL_STDIO_REDIRECT"))) {
if (SDL_atoi(env_str)) {
@ -408,6 +261,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
redirect_output();
}
#endif
#endif // 0
#ifdef _WIN32_WCE
nLen = wcslen(szCmdLine)+128+1;

View file

@ -53,7 +53,15 @@ If you have questions concerning this license or the applicable additional terms
#include "tools/edit_public.h"
#include <SDL_main.h>
#include "sys/sys_sdl.h"
#ifdef D3_SDL3
#define SDL_MAIN_HANDLED // dhewm3 implements WinMain() itself
#include <SDL3/SDL_main.h>
#else // SDL1.2 or SDL2
#include <SDL_main.h>
#endif
idCVar Win32Vars_t::win_outputDebugString( "win_outputDebugString", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
idCVar Win32Vars_t::win_outputEditString( "win_outputEditString", "1", CVAR_SYSTEM | CVAR_BOOL, "" );
@ -1001,14 +1009,154 @@ int Win_ChoosePixelFormat(HDC hdc)
}
#endif
// stdout/stderr redirection, originally from SDL_win32_main.c
/* The standard output files */
#define STDOUT_FILE TEXT("dhewm3log.txt") /* DG: renamed this */
#define STDERR_FILE TEXT("stderr.txt")
/* Set a variable to tell if the stdio redirect has been enabled. */
static int stdioRedirectEnabled = 0;
static char stdoutPath[MAX_PATH];
static char stderrPath[MAX_PATH];
#define DIR_SEPERATOR TEXT("/")
/* Remove the output files if there was no output written */
static void cleanup_output(void) {
FILE *file;
int empty;
/* Flush the output in case anything is queued */
fclose(stdout);
fclose(stderr);
/* Without redirection we're done */
if (!stdioRedirectEnabled) {
return;
}
/* See if the files have any output in them */
if ( stdoutPath[0] ) {
file = fopen(stdoutPath, TEXT("rb"));
if ( file ) {
empty = (fgetc(file) == EOF) ? 1 : 0;
fclose(file);
if ( empty ) {
remove(stdoutPath);
}
}
}
if ( stderrPath[0] ) {
file = fopen(stderrPath, TEXT("rb"));
if ( file ) {
empty = (fgetc(file) == EOF) ? 1 : 0;
fclose(file);
if ( empty ) {
remove(stderrPath);
}
}
}
}
/* Redirect the output (stdout and stderr) to a file */
static void redirect_output(void)
{
char path[MAX_PATH];
struct _stat st;
/* DG: use "My Documents/My Games/dhewm3" to write stdout.txt and stderr.txt
* instead of the binary, which might not be writable */
Win_GetHomeDir(path, sizeof(path));
if (_stat(path, &st) == -1) {
/* oops, "My Documents/My Games/dhewm3" doesn't exist - does My Games/ at least exist? */
char myGamesPath[MAX_PATH];
char* lastslash;
memcpy(myGamesPath, path, MAX_PATH);
lastslash = strrchr(myGamesPath, '/');
if (lastslash != NULL) {
*lastslash = '\0';
}
if (_stat(myGamesPath, &st) == -1) {
/* if My Documents/My Games/ doesn't exist, create it */
_mkdir(myGamesPath);
}
_mkdir(path); /* create My Documents/My Games/dhewm3/ */
}
FILE *newfp;
#if 0 /* DG: don't do this anymore. */
DWORD pathlen;
pathlen = GetModuleFileName(NULL, path, SDL_arraysize(path));
while ( pathlen > 0 && path[pathlen] != '\\' ) {
--pathlen;
}
path[pathlen] = '\0';
#endif
SDL_strlcpy( stdoutPath, path, SDL_arraysize(stdoutPath) );
SDL_strlcat( stdoutPath, DIR_SEPERATOR STDOUT_FILE, SDL_arraysize(stdoutPath) );
{ /* DG: rename old stdout log */
char stdoutPathBK[MAX_PATH];
SDL_strlcpy( stdoutPathBK, path, SDL_arraysize(stdoutPath) );
SDL_strlcat( stdoutPathBK, DIR_SEPERATOR TEXT("dhewm3log-old.txt"), SDL_arraysize(stdoutPath) );
rename( stdoutPath, stdoutPathBK );
} /* DG end */
/* Redirect standard input and standard output */
newfp = freopen(stdoutPath, TEXT("w"), stdout);
if ( newfp == NULL ) { /* This happens on NT */
#if !defined(stdout)
stdout = fopen(stdoutPath, TEXT("w"));
#else
newfp = fopen(stdoutPath, TEXT("w"));
if ( newfp ) {
*stdout = *newfp;
}
#endif
}
SDL_strlcpy( stderrPath, path, SDL_arraysize(stderrPath) );
SDL_strlcat( stderrPath, DIR_SEPERATOR STDERR_FILE, SDL_arraysize(stderrPath) );
newfp = freopen(stderrPath, TEXT("w"), stderr);
if ( newfp == NULL ) { /* This happens on NT */
#if !defined(stderr)
stderr = fopen(stderrPath, TEXT("w"));
#else
newfp = fopen(stderrPath, TEXT("w"));
if ( newfp ) {
*stderr = *newfp;
}
#endif
}
setvbuf(stdout, NULL, _IOLBF, BUFSIZ); /* Line buffered */
setbuf(stderr, NULL); /* No buffering */
stdioRedirectEnabled = 1;
}
// end of stdout/stderr redirection code from old SDL
/*
==================
WinMain
The pseudo-main function called from real main (either in SDL_win32_main.c or WinMain() below)
NOTE: Currently argv[] are ANSI strings, not UTF-8 strings as usual in SDL2 and SDL3!
==================
*/
int main(int argc, char *argv[]) {
// SDL_win32_main.c creates the dhewm3log.txt and redirects stdout into it
// so here we can log its (approx.) creation time before anything else is logged:
int SDL_main(int argc, char *argv[]) {
// as the very first thing, redirect stdout to dhewm3log.txt (and stderr to stderr.txt)
// so we can log
redirect_output();
atexit(cleanup_output);
// now that stdout is redirected to dhewm3log.txt,
// log its (approx.) creation time before anything else is logged:
{
time_t tt = time(NULL);
const struct tm* tms = localtime(&tt);
@ -1017,8 +1165,6 @@ int main(int argc, char *argv[]) {
printf("Opened this log at %s\n", timeStr);
}
const HCURSOR hcurSave = ::SetCursor( LoadCursor( 0, IDC_WAIT ) );
InitializeCriticalSection( &printfCritSect );
#ifdef ID_DEDICATED
@ -1065,8 +1211,6 @@ int main(int argc, char *argv[]) {
SetThreadAffinityMask( GetCurrentThread(), 1 );
#endif
// ::SetCursor( hcurSave ); // DG: I think SDL handles the cursor fine..
// Launch the script debugger
if ( strstr( GetCommandLine(), "+debugger" ) ) {
@ -1204,3 +1348,80 @@ void idSysLocal::StartProcess( const char *exePath, bool doexit ) {
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
}
}
// the actual WinMain(), based on SDL2_main and SDL3's SDL_main_impl.h + SDL_RunApp()
// but modified to pass ANSI strings to SDL_main() instead of UTF-8,
// because dhewm3 doesn't use Unicode internally (except for Dear ImGui,
// which doesn't use commandline arguments)
// for SDL1.2, SDL_win32_main.c is still used instead
#if SDL_VERSION_ATLEAST(2, 0, 0)
/* Pop up an out of memory message, returns to Windows */
static BOOL OutOfMemory(void)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Out of memory - aborting", NULL);
return -1;
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
(void)hInst;
(void)hPrev;
(void)szCmdLine;
(void)sw;
LPWSTR *argvw;
char **argv;
int i, argc, result;
argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
if (!argvw) {
return OutOfMemory();
}
/* Note that we need to be careful about how we allocate/free memory here.
* If the application calls SDL_SetMemoryFunctions(), we can't rely on
* SDL_free() to use the same allocator after SDL_main() returns.
*/
/* Parse it into argv and argc */
argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv));
if (!argv) {
return OutOfMemory();
}
for (i = 0; i < argc; ++i) {
// NOTE: SDL2+ uses CP_UTF8 instead of CP_ACP here (and in the other call below)
// but Doom3 needs ANSI strings on Windows (so paths work with the Windows ANSI APIs)
const int ansiSize = WideCharToMultiByte(CP_ACP, 0, argvw[i], -1, NULL, 0, NULL, NULL);
if (!ansiSize) { // uhoh?
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL);
return -1;
}
argv[i] = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ansiSize); // this size includes the null-terminator character.
if (!argv[i]) {
return OutOfMemory();
}
if (WideCharToMultiByte(CP_ACP, 0, argvw[i], -1, argv[i], ansiSize, NULL, NULL) == 0) { // failed? uhoh!
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL);
return -1;
}
}
argv[i] = NULL;
LocalFree(argvw);
SDL_SetMainReady();
// Run the application main() code
result = SDL_main(argc, argv);
// Free argv, to avoid memory leak
for (i = 0; i < argc; ++i) {
HeapFree(GetProcessHeap(), 0, argv[i]);
}
HeapFree(GetProcessHeap(), 0, argv);
return result;
}
#endif

View file

@ -41,6 +41,14 @@ If you have questions concerning this license or the applicable additional terms
const int MAX_MSGLEN = 8600;
#endif
#if SDL_VERSION_ATLEAST(3, 0, 0)
// compat with SDL2
#define SDL_CreateCond SDL_CreateCondition
#define SDL_DestroyCond SDL_DestroyCondition
#define SDL_CondWait SDL_WaitCondition
#define SDL_CondSignal SDL_SignalCondition
#endif
/*
================
rvDebuggerServer::rvDebuggerServer

View file

@ -28,13 +28,20 @@ If you have questions concerning this license or the applicable additional terms
#ifndef DEBUGGERSERVER_H_
#define DEBUGGERSERVER_H_
#include <SDL.h>
#include "sys/sys_sdl.h"
#include "sys/platform.h"
#include "idlib/Str.h"
#include "DebuggerMessages.h"
#include "DebuggerBreakpoint.h"
#include "framework/Game.h"
#if SDL_VERSION_ATLEAST(3, 0, 0)
// backwards-compat with SDL <= 2
#define SDL_mutex SDL_Mutex
#define SDL_cond SDL_Condition
#endif
class function_t;
typedef struct prstack_s prstack_t;