#include <IOKit/hidsystem/IOHIDLib.h>
#include <IOKit/hidsystem/IOHIDParameter.h>
#include <IOKit/hidsystem/event_status_driver.h>
-static cvar_t apple_mouse_noaccel = {CVAR_CLIENT | CVAR_SAVE, "apple_mouse_noaccel", "1", "disables mouse acceleration while DarkPlaces is active"};
-static qboolean vid_usingnoaccel;
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 120000)
+ #define IOMainPort IOMasterPort
+#endif
+static cvar_t apple_mouse_noaccel = {CF_CLIENT | CF_ARCHIVE, "apple_mouse_noaccel", "1", "disables mouse acceleration while DarkPlaces is active"};
+static qbool vid_usingnoaccel;
static double originalMouseSpeed = -1.0;
-io_connect_t IN_GetIOHandle(void)
+static io_connect_t IN_GetIOHandle(void)
{
io_connect_t iohandle = MACH_PORT_NULL;
kern_return_t status;
io_service_t iohidsystem = MACH_PORT_NULL;
mach_port_t masterport;
- status = IOMasterPort(MACH_PORT_NULL, &masterport);
+ status = IOMainPort(MACH_PORT_NULL, &masterport);
if(status != KERN_SUCCESS)
return 0;
// Tell startup code that we have a client
int cl_available = true;
-qboolean vid_supportrefreshrate = false;
+qbool vid_supportrefreshrate = false;
-static qboolean vid_usingmouse = false;
-static qboolean vid_usingmouse_relativeworks = false; // SDL2 workaround for unimplemented RelativeMouse mode
-static qboolean vid_usinghidecursor = false;
-static qboolean vid_hasfocus = false;
-static qboolean vid_isfullscreen;
-static qboolean vid_usingvsync = false;
+static qbool vid_usingmouse = false;
+static qbool vid_usingmouse_relativeworks = false; // SDL2 workaround for unimplemented RelativeMouse mode
+static qbool vid_usinghidecursor = false;
+static qbool vid_hasfocus = false;
+static qbool vid_wmborder_waiting, vid_wmborderless;
static SDL_Joystick *vid_sdljoystick = NULL;
static SDL_GameController *vid_sdlgamecontroller = NULL;
-static cvar_t joy_sdl2_trigger_deadzone = {CVAR_SAVE | CVAR_CLIENT, "joy_sdl2_trigger_deadzone", "0.5", "deadzone for triggers to be registered as key presses"};
+static cvar_t joy_sdl2_trigger_deadzone = {CF_ARCHIVE | CF_CLIENT, "joy_sdl2_trigger_deadzone", "0.5", "deadzone for triggers to be registered as key presses"};
// GAME_STEELSTORM specific
static cvar_t *steelstorm_showing_map = NULL; // detect but do not create the cvar
static cvar_t *steelstorm_showing_mousecursor = NULL; // detect but do not create the cvar
static int win_half_width = 50;
static int win_half_height = 50;
-static int video_bpp;
static SDL_GLContext context;
static SDL_Window *window;
-static int window_flags;
-static vid_mode_t desktop_mode;
// Input handling
{
switch(sdlkey)
{
- default: return 0;
+ // sdlkey can be Unicode codepoint for non-ascii keys, which are valid
+ default: return sdlkey & SDLK_SCANCODE_MASK ? 0 : sdlkey;
// case SDLK_UNKNOWN: return K_UNKNOWN;
case SDLK_RETURN: return K_ENTER;
case SDLK_ESCAPE: return K_ESCAPE;
}
}
-qboolean VID_HasScreenKeyboardSupport(void)
+qbool VID_HasScreenKeyboardSupport(void)
{
return SDL_HasScreenKeyboardSupport() != SDL_FALSE;
}
-void VID_ShowKeyboard(qboolean show)
+void VID_ShowKeyboard(qbool show)
{
if (!SDL_HasScreenKeyboardSupport())
return;
}
}
-qboolean VID_ShowingKeyboard(void)
+qbool VID_ShowingKeyboard(void)
{
return SDL_IsTextInputActive() != 0;
}
-void VID_SetMouse(qboolean fullscreengrab, qboolean relative, qboolean hidecursor)
+static void VID_SetMouse(qbool relative, qbool hidecursor)
{
#ifndef DP_MOBILETOUCH
#ifdef MACOSX
if(relative)
if(vid_usingmouse && (vid_usingnoaccel != !!apple_mouse_noaccel.integer))
- VID_SetMouse(false, false, false); // ungrab first!
+ VID_SetMouse(false, false); // ungrab first!
#endif
if (vid_usingmouse != relative)
{
vid_usingmouse = relative;
cl_ignoremousemoves = 2;
vid_usingmouse_relativeworks = SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE) == 0;
-// Con_Printf("VID_SetMouse(%i, %i, %i) relativeworks = %i\n", (int)fullscreengrab, (int)relative, (int)hidecursor, (int)vid_usingmouse_relativeworks);
+// Con_Printf("VID_SetMouse(%i, %i) relativeworks = %i\n", (int)relative, (int)hidecursor, (int)vid_usingmouse_relativeworks);
#ifdef MACOSX
if(relative)
{
int multitouchs[MAXFINGERS];
// modified heavily by ELUAN
-static qboolean VID_TouchscreenArea(int corner, float px, float py, float pwidth, float pheight, const char *icon, float textheight, const char *text, float *resultmove, qboolean *resultbutton, keynum_t key, const char *typedtext, float deadzone, float oversizepixels_x, float oversizepixels_y, qboolean iamexclusive)
+static qbool VID_TouchscreenArea(int corner, float px, float py, float pwidth, float pheight, const char *icon, float textheight, const char *text, float *resultmove, qbool *resultbutton, keynum_t key, const char *typedtext, float deadzone, float oversizepixels_x, float oversizepixels_y, qbool iamexclusive)
{
int finger;
float fx, fy, fwidth, fheight;
float overfx, overfy, overfwidth, overfheight;
float rel[3];
float sqsum;
- qboolean button = false;
+ qbool button = false;
VectorClear(rel);
if (pwidth > 0 && pheight > 0)
{
// ELUAN:
// not reentrant, but we only need one mouse cursor anyway...
-static void VID_TouchscreenCursor(float px, float py, float pwidth, float pheight, qboolean *resultbutton, keynum_t key)
+static void VID_TouchscreenCursor(float px, float py, float pwidth, float pheight, qbool *resultbutton, keynum_t key)
{
int finger;
float fx, fy, fwidth, fheight;
- qboolean button = false;
+ qbool button = false;
static int cursorfinger = -1;
static int cursorfreemovement = false;
static int canclick = false;
int i, numfingers;
float xscale, yscale;
float move[3], aim[3];
- static qboolean oldbuttons[128];
- static qboolean buttons[128];
+ static qbool oldbuttons[128];
+ static qbool buttons[128];
keydest_t keydest = (key_consoleactive & KEY_CONSOLEACTIVE_USER) ? key_console : key_dest;
memcpy(oldbuttons, buttons, sizeof(oldbuttons));
memset(multitouchs, 0, sizeof(multitouchs));
{
int x, y;
float move[3], aim[3], click[3];
- static qboolean oldbuttons[128];
- static qboolean buttons[128];
+ static qbool oldbuttons[128];
+ static qbool buttons[128];
keydest_t keydest = (key_consoleactive & KEY_CONSOLEACTIVE_USER) ? key_console : key_dest;
memcpy(oldbuttons, buttons, sizeof(oldbuttons));
memset(multitouchs, 0, sizeof(multitouchs));
if (!VID_ShowingKeyboard())
{
// user entered a command, close the console now
- Con_ToggleConsole_f(&cmd_client);
+ Con_ToggleConsole_f(cmd_local);
}
VID_TouchscreenArea( 0, 0, 0, 0, 0, NULL , 0.0f, NULL, NULL, &buttons[15], (keynum_t)0, NULL, 0, 0, 0, true);
VID_TouchscreenArea( 0, 0, 0, 0, 0, NULL , 0.0f, NULL, move, &buttons[0], K_MOUSE4, NULL, 0, 0, 0, true);
static int old_x = 0, old_y = 0;
static int stuck = 0;
static keydest_t oldkeydest;
- static qboolean oldshowkeyboard;
+ static qbool oldshowkeyboard;
int x, y;
vid_joystate_t joystate;
keydest_t keydest = (key_consoleactive & KEY_CONSOLEACTIVE_USER) ? key_console : key_dest;
in_windowmouse_y = y;
}
+ //Con_Printf("Mouse position: in_mouse %f %f in_windowmouse %f %f\n", in_mouse_x, in_mouse_y, in_windowmouse_x, in_windowmouse_y);
+
VID_BuildJoyState(&joystate);
VID_ApplyJoyState(&joystate);
}
////
#ifdef SDL_R_RESTART
-static qboolean sdl_needs_restart;
+static qbool sdl_needs_restart;
static void sdl_start(void)
{
}
};
//#define DEBUGSDLEVENTS
-
-// SDL2
-void Sys_SendKeyEvents( void )
+static void VID_ChangeDisplay_c(cvar_t *var);
+void Sys_SDL_HandleEvents(void)
{
- static qboolean sound_active = true;
+ static qbool sound_active = true;
int keycode;
int i;
- qboolean isdown;
+ const char *chp;
+ qbool isdown;
Uchar unicode;
SDL_Event event;
#ifdef DEBUGSDLEVENTS
Con_DPrintf("SDL_Event: SDL_QUIT\n");
#endif
- Sys_Quit(0);
+ host.state = host_shutdown;
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
#endif
break;
case SDL_WINDOWEVENT_MOVED:
+ vid.xPos = event.window.data1;
+ vid.yPos = event.window.data2;
+ // Update vid.displayindex (current monitor) as it may have changed
+ // SDL_GetWindowDisplayIndex() doesn't work if the window manager moves the fullscreen window, but this works:
+ for (i = 0; i < vid_info_displaycount.integer; ++i)
+ {
+ SDL_Rect displaybounds;
+ if (SDL_GetDisplayBounds(i, &displaybounds) < 0)
+ {
+ Con_Printf(CON_ERROR "Error getting bounds of display %i: \"%s\"\n", i, SDL_GetError());
+ return;
+ }
+ if (vid.xPos >= displaybounds.x && vid.xPos < displaybounds.x + displaybounds.w)
+ if (vid.yPos >= displaybounds.y && vid.yPos < displaybounds.y + displaybounds.h)
+ {
+ vid.displayindex = i;
+ break;
+ }
+ }
+ // when the window manager adds/removes the border it's likely to move the SDL window
+ // we'll need to correct that to (re)align the xhair with the monitor
+ if (vid_wmborder_waiting)
+ {
+ SDL_GetWindowBordersSize(window, &i, NULL, NULL, NULL);
+ if (!i != vid_wmborderless) // border state changed
+ {
+ SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex), SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex));
+ SDL_GetWindowPosition(window, &vid.xPos, &vid.yPos);
+ vid_wmborder_waiting = false;
+ }
+ }
break;
- case SDL_WINDOWEVENT_RESIZED:
+ case SDL_WINDOWEVENT_RESIZED: // external events only
if(vid_resizable.integer < 2)
{
- vid.width = event.window.data1;
- vid.height = event.window.data2;
+ //vid.width = event.window.data1;
+ //vid.height = event.window.data2;
+ // get the real framebuffer size in case the platform's screen coordinates are DPI scaled
+ SDL_GL_GetDrawableSize(window, &vid.width, &vid.height);
#ifdef SDL_R_RESTART
// better not call R_Modules_Restart_f from here directly, as this may wreak havoc...
// so, let's better queue it for next frame
if(!sdl_needs_restart)
{
- Cbuf_AddText(&cmd_client, "\nr_restart\n");
+ Cbuf_AddText(cmd_local, "\nr_restart\n");
sdl_needs_restart = true;
}
#endif
}
break;
+ case SDL_WINDOWEVENT_SIZE_CHANGED: // internal and external events
+ break;
case SDL_WINDOWEVENT_MINIMIZED:
break;
case SDL_WINDOWEVENT_MAXIMIZED:
vid_hasfocus = false;
break;
case SDL_WINDOWEVENT_CLOSE:
- Sys_Quit(0);
+ host.state = host_shutdown;
+ break;
+ case SDL_WINDOWEVENT_TAKE_FOCUS:
+ break;
+ case SDL_WINDOWEVENT_HIT_TEST:
+ break;
+ case SDL_WINDOWEVENT_ICCPROF_CHANGED:
+ break;
+ case SDL_WINDOWEVENT_DISPLAY_CHANGED:
+ // this event can't be relied on in fullscreen, see SDL_WINDOWEVENT_MOVED above
+ vid.displayindex = event.window.data1;
break;
}
}
break;
+ case SDL_DISPLAYEVENT: // Display hotplugging
+ switch (event.display.event)
+ {
+ case SDL_DISPLAYEVENT_CONNECTED:
+ Con_Printf("Display %i connected: %s\nA vid_restart may be necessary!\n", event.display.display, SDL_GetDisplayName(event.display.display));
+ Cvar_SetValueQuick(&vid_info_displaycount, SDL_GetNumVideoDisplays());
+ // Ideally we'd call VID_ChangeDisplay_c() to try to switch to the preferred display here,
+ // but we may need a vid_restart first, see comments in VID_ChangeDisplay_c().
+ break;
+ case SDL_DISPLAYEVENT_DISCONNECTED:
+ Con_Printf("Display %i disconnected.\nA vid_restart may be necessary!\n", event.display.display);
+ Cvar_SetValueQuick(&vid_info_displaycount, SDL_GetNumVideoDisplays());
+ break;
+ case SDL_DISPLAYEVENT_ORIENTATION:
+ break;
+ }
+ break;
case SDL_TEXTEDITING:
#ifdef DEBUGSDLEVENTS
Con_DPrintf("SDL_Event: SDL_TEXTEDITING - composition = %s, cursor = %d, selection lenght = %d\n", event.edit.text, event.edit.start, event.edit.length);
#endif
// convert utf8 string to char
// NOTE: this code is supposed to run even if utf8enable is 0
- unicode = u8_getchar_utf8_enabled(event.text.text + (int)u8_bytelen(event.text.text, 0), NULL);
- Key_Event(K_TEXT, unicode, true);
- Key_Event(K_TEXT, unicode, false);
+ chp = event.text.text;
+ while (*chp != 0)
+ {
+ // input the chars one by one (there can be multiple chars when e.g. using an "input method")
+ unicode = u8_getchar_utf8_enabled(chp, &chp);
+ Key_Event(K_TEXT, unicode, true);
+ Key_Event(K_TEXT, unicode, false);
+ }
break;
case SDL_MOUSEMOTION:
break;
break;
}
+ vid_activewindow = !vid_hidden && vid_hasfocus;
+
// enable/disable sound on focus gain/loss
- if ((!vid_hidden && vid_activewindow) || !snd_mutewhenidle.integer)
+ if (vid_activewindow || !snd_mutewhenidle.integer)
{
if (!sound_active)
{
sound_active = false;
}
}
+
+ if (!vid_activewindow || key_consoleactive || scr_loading)
+ VID_SetMouse(false, false);
+ else if (key_dest == key_menu || key_dest == key_menu_grabbed)
+ VID_SetMouse(vid_mouse.integer && !in_client_mouse && !vid_touchscreen.integer, !vid_touchscreen.integer);
+ else
+ VID_SetMouse(vid_mouse.integer && !cl.csqc_wantsmousemove && cl_prydoncursor.integer <= 0 && (!cls.demoplayback || cl_demo_mousegrab.integer) && !vid_touchscreen.integer, !vid_touchscreen.integer);
}
/////////////////
return p;
}
-qboolean GL_ExtensionSupported(const char *name)
+qbool GL_ExtensionSupported(const char *name)
{
return SDL_GL_ExtensionSupported(name);
}
-static qboolean vid_sdl_initjoysticksystem = false;
+static void VID_ChangeDisplay_c(cvar_t *var)
+{
+ unsigned int fullscreenwanted, fullscreencurrent;
+ unsigned int displaywanted = bound(0, vid_display.integer, vid_info_displaycount.integer - 1);
+
+ if (!window)
+ return;
+
+ fullscreencurrent = SDL_GetWindowFlags(window) & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
+ if (vid_fullscreen.integer)
+ fullscreenwanted = vid_desktopfullscreen.integer ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN;
+ else
+ fullscreenwanted = 0;
+
+ // moving to another display, changing the fullscreen mode or switching to windowed
+ if (vid.displayindex != displaywanted // SDL seems unable to move any fullscreen window to another display
+ || fullscreencurrent != fullscreenwanted) // even for desktop <-> exclusive: switching to windowed first feels safer
+ {
+ if (SDL_SetWindowFullscreen(window, 0) < 0)
+ {
+ Con_Printf(CON_ERROR "ERROR: can't deactivate fullscreen on display %i because %s\n", vid.displayindex, SDL_GetError());
+ return;
+ }
+ vid.fullscreen = false;
+ Con_DPrintf("Fullscreen deactivated on display %i\n", vid.displayindex);
+ }
+
+ // switching to windowed
+ if (!fullscreenwanted)
+ {
+ int toppx;
+ SDL_SetWindowSize(window, vid.width = vid_width.integer, vid.height = vid_height.integer);
+ SDL_SetWindowResizable(window, vid_resizable.integer ? SDL_TRUE : SDL_FALSE);
+ SDL_SetWindowBordered(window, (SDL_bool)!vid_borderless.integer);
+ SDL_GetWindowBordersSize(window, &toppx, NULL, NULL, NULL);
+ vid_wmborderless = !toppx;
+ if (vid_borderless.integer != vid_wmborderless) // this is not the state we're looking for
+ vid_wmborder_waiting = true;
+ }
+
+ // moving to another display or switching to windowed
+ if (vid.displayindex != displaywanted || !fullscreenwanted)
+ {
+// SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(displaywanted), SDL_WINDOWPOS_CENTERED_DISPLAY(displaywanted));
+// SDL_GetWindowPosition(window, &vid.xPos, &vid.yPos);
+
+ /* bones_was_here BUG: after SDL_DISPLAYEVENT hotplug events,
+ * SDL_WINDOWPOS_CENTERED_DISPLAY(displaywanted) may place the window somewhere completely invisible.
+ * WORKAROUND: manual positioning seems safer: although SDL_GetDisplayBounds() may return outdated values,
+ * SDL_SetWindowPosition() always placed the window somewhere fully visible, even if it wasn't correct,
+ * when tested with SDL 2.26.5.
+ */
+ SDL_Rect displaybounds;
+ if (SDL_GetDisplayBounds(displaywanted, &displaybounds) < 0)
+ {
+ Con_Printf(CON_ERROR "Error getting bounds of display %i: \"%s\"\n", displaywanted, SDL_GetError());
+ return;
+ }
+ vid.xPos = displaybounds.x + 0.5 * (displaybounds.w - vid.width);
+ vid.yPos = displaybounds.y + 0.5 * (displaybounds.h - vid.height);
+ SDL_SetWindowPosition(window, vid.xPos, vid.yPos);
+
+ vid.displayindex = displaywanted;
+ }
+
+ // switching to a fullscreen mode
+ if (fullscreenwanted)
+ {
+ if (SDL_SetWindowFullscreen(window, fullscreenwanted) < 0)
+ {
+ Con_Printf(CON_ERROR "ERROR: can't activate fullscreen on display %i because %s\n", vid.displayindex, SDL_GetError());
+ return;
+ }
+ // get the real framebuffer size in case the platform's screen coordinates are DPI scaled
+ SDL_GL_GetDrawableSize(window, &vid.width, &vid.height);
+ vid.fullscreen = true;
+ Con_DPrintf("Fullscreen activated on display %i\n", vid.displayindex);
+ }
+}
+
+static void VID_SetVsync_c(cvar_t *var)
+{
+ signed char vsyncwanted = cls.timedemo ? 0 : bound(-1, vid_vsync.integer, 1);
+
+ if (!context)
+ return;
+ if (SDL_GL_GetSwapInterval() == vsyncwanted)
+ return;
+
+ if (SDL_GL_SetSwapInterval(vsyncwanted) >= 0)
+ Con_DPrintf("Vsync %s\n", vsyncwanted ? "activated" : "deactivated");
+ else
+ Con_Printf(CON_ERROR "ERROR: can't %s vsync because %s\n", vsyncwanted ? "activate" : "deactivate", SDL_GetError());
+}
+
+static void VID_SetHints_c(cvar_t *var)
+{
+ SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, vid_mouse_clickthrough.integer ? "1" : "0");
+ SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, vid_minimize_on_focus_loss.integer ? "1" : "0");
+}
void VID_Init (void)
{
+ SDL_version version;
+
#ifndef __IPHONEOS__
#ifdef MACOSX
Cvar_RegisterVariable(&apple_mouse_noaccel);
R_RegisterModule("SDL", sdl_start, sdl_shutdown, sdl_newmap, NULL, NULL);
#endif
+#if defined(__linux__)
+ // exclusive fullscreen is no longer functional (and when it worked was obnoxious and not faster)
+ Cvar_SetValueQuick(&vid_desktopfullscreen, 1);
+ vid_desktopfullscreen.flags |= CF_READONLY;
+#endif
+
+ Cvar_RegisterCallback(&vid_fullscreen, VID_ChangeDisplay_c);
+ Cvar_RegisterCallback(&vid_desktopfullscreen, VID_ChangeDisplay_c);
+ Cvar_RegisterCallback(&vid_display, VID_ChangeDisplay_c);
+ Cvar_RegisterCallback(&vid_resizable, VID_ChangeDisplay_c);
+ Cvar_RegisterCallback(&vid_borderless, VID_ChangeDisplay_c);
+ Cvar_RegisterCallback(&vid_vsync, VID_SetVsync_c);
+ Cvar_RegisterCallback(&vid_mouse_clickthrough, VID_SetHints_c);
+ Cvar_RegisterCallback(&vid_minimize_on_focus_loss, VID_SetHints_c);
+
if (SDL_Init(SDL_INIT_VIDEO) < 0)
Sys_Error ("Failed to init SDL video subsystem: %s", SDL_GetError());
- vid_sdl_initjoysticksystem = SDL_InitSubSystem(SDL_INIT_JOYSTICK) >= 0;
- if (!vid_sdl_initjoysticksystem)
- Con_Errorf("Failed to init SDL joystick subsystem: %s\n", SDL_GetError());
- vid_isfullscreen = false;
+ if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
+ Con_Printf(CON_ERROR "Failed to init SDL joystick subsystem: %s\n", SDL_GetError());
+
+ SDL_GetVersion(&version);
+ Con_Printf("Linked against SDL version %d.%d.%d\n"
+ "Using SDL library version %d.%d.%d\n",
+ SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
+ version.major, version.minor, version.patch);
}
static int vid_sdljoystickindex = -1;
-void VID_EnableJoystick(qboolean enable)
+void VID_EnableJoystick(qbool enable)
{
int index = joy_enable.integer > 0 ? joy_index.integer : -1;
int numsdljoysticks;
- qboolean success = false;
+ qbool success = false;
int sharedcount = 0;
int sdlindex = -1;
sharedcount = VID_Shared_SetJoystick(index);
}
else
{
- Con_Errorf("Joystick %i failed (SDL_JoystickOpen(%i) returned: %s)\n", index, sdlindex, SDL_GetError());
+ Con_Printf(CON_ERROR "Joystick %i failed (SDL_JoystickOpen(%i) returned: %s)\n", index, sdlindex, SDL_GetError());
sdlindex = -1;
}
}
Cvar_SetValueQuick(&joy_active, success ? 1 : 0);
}
-static void VID_OutputVersion(void)
-{
- SDL_version version;
- SDL_GetVersion(&version);
- Con_Printf( "Linked against SDL version %d.%d.%d\n"
- "Using SDL library version %d.%d.%d\n",
- SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
- version.major, version.minor, version.patch );
-}
-
#ifdef WIN32
static void AdjustWindowBounds(viddef_mode_t *mode, RECT *rect)
{
}
#endif
-extern cvar_t gl_info_vendor;
-extern cvar_t gl_info_renderer;
-extern cvar_t gl_info_version;
-extern cvar_t gl_info_platform;
-extern cvar_t gl_info_driver;
-
-static qboolean VID_InitModeGL(viddef_mode_t *mode)
+static qbool VID_InitModeGL(viddef_mode_t *mode)
{
int windowflags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL;
- // currently SDL_WINDOWPOS_UNDEFINED behaves exactly like SDL_WINDOWPOS_CENTERED, this might change some day
- // https://trello.com/c/j56vUcwZ/81-centered-vs-undefined-window-position
- int xPos = SDL_WINDOWPOS_UNDEFINED;
- int yPos = SDL_WINDOWPOS_UNDEFINED;
-#ifndef USE_GLES2
int i;
- const char *drivername;
+#ifndef USE_GLES2
+ // SDL usually knows best
+ const char *drivername = NULL;
#endif
+ // video display selection (multi-monitor)
+ Cvar_SetValueQuick(&vid_info_displaycount, SDL_GetNumVideoDisplays());
+ vid.displayindex = bound(0, vid_display.integer, vid_info_displaycount.integer - 1);
+ vid.xPos = SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex);
+ vid.yPos = SDL_WINDOWPOS_CENTERED_DISPLAY(vid.displayindex);
+ vid_wmborder_waiting = vid_wmborderless = false;
+
win_half_width = mode->width>>1;
win_half_height = mode->height>>1;
if(vid_resizable.integer)
windowflags |= SDL_WINDOW_RESIZABLE;
- VID_OutputVersion();
-
#ifndef USE_GLES2
- // SDL usually knows best
- drivername = NULL;
-
// COMMANDLINEOPTION: SDL GL: -gl_driver <drivername> selects a GL driver library, default is whatever SDL recommends, useful only for 3dfxogl.dll/3dfxvgl.dll or fxmesa or similar, if you don't know what this is for, you don't need it
- i = COM_CheckParm("-gl_driver");
+ i = Sys_CheckParm("-gl_driver");
if (i && i < sys.argc - 1)
drivername = sys.argv[i + 1];
if (SDL_GL_LoadLibrary(drivername) < 0)
{
- Con_Errorf("Unable to load GL driver \"%s\": %s\n", drivername, SDL_GetError());
+ Con_Printf(CON_ERROR "Unable to load GL driver \"%s\": %s\n", drivername, SDL_GetError());
return false;
}
#endif
windowflags |= SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
#endif
- vid_isfullscreen = false;
+
+ if (mode->fullscreen)
{
- if (mode->fullscreen) {
- if (vid_desktopfullscreen.integer)
- {
- vid_mode_t *m = VID_GetDesktopMode();
- mode->width = m->width;
- mode->height = m->height;
- windowflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
- }
- else
- windowflags |= SDL_WINDOW_FULLSCREEN;
- vid_isfullscreen = true;
+ if (vid_desktopfullscreen.integer)
+ {
+ vid_mode_t m = VID_GetDesktopMode();
+ mode->width = m.width;
+ mode->height = m.height;
+ windowflags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
- else {
- if (vid_borderless.integer)
- windowflags |= SDL_WINDOW_BORDERLESS;
+ else
+ windowflags |= SDL_WINDOW_FULLSCREEN;
+ }
+ else
+ {
+ if (vid_borderless.integer)
+ windowflags |= SDL_WINDOW_BORDERLESS;
+ else
+ vid_wmborder_waiting = true; // waiting for border to be added
#ifdef WIN32
- if (vid_ignore_taskbar.integer) {
- xPos = SDL_WINDOWPOS_CENTERED;
- yPos = SDL_WINDOWPOS_CENTERED;
- }
- else {
- RECT rect;
- AdjustWindowBounds(mode, &rect);
- xPos = rect.left;
- yPos = rect.top;
- }
-#endif
+ if (!vid_ignore_taskbar.integer)
+ {
+ RECT rect;
+ AdjustWindowBounds(mode, &rect);
+ vid.xPos = rect.left;
+ vid.xPos = rect.top;
+ vid_wmborder_waiting = false;
}
+#endif
}
- //flags |= SDL_HWSURFACE;
- if (vid_mouse_clickthrough.integer && !vid_isfullscreen)
- SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
+ // DPI scaling prevents use of the native resolution, causing blurry rendering
+ // and/or mouse cursor problems, so we need to opt-out.
+#ifdef WIN32
+ SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "1");
+#endif
+
+ VID_SetHints_c(NULL);
SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute (SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+ /* Requesting a Core profile and 3.2 minimum is mandatory on macOS and older Mesa drivers.
+ * It works fine on other drivers too except NVIDIA, see HACK below.
+ */
#endif
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, (gl_debug.integer > 0 ? SDL_GL_CONTEXT_DEBUG_FLAG : 0));
- video_bpp = mode->bitsperpixel;
- window_flags = windowflags;
- window = SDL_CreateWindow(gamename, xPos, yPos, mode->width, mode->height, windowflags);
+ window = SDL_CreateWindow(gamename, vid.xPos, vid.yPos, mode->width, mode->height, windowflags);
if (window == NULL)
{
- Con_Errorf("Failed to set video mode to %ix%i: %s\n", mode->width, mode->height, SDL_GetError());
+ Con_Printf(CON_ERROR "Failed to set video mode to %ix%i: %s\n", mode->width, mode->height, SDL_GetError());
VID_Shutdown();
return false;
}
- SDL_GetWindowSize(window, &mode->width, &mode->height);
+ // get the real framebuffer size in case the platform's screen coordinates are DPI scaled
+ SDL_GL_GetDrawableSize(window, &mode->width, &mode->height);
+ // After using SDL_WINDOWPOS_CENTERED_DISPLAY we don't know the real position
+ SDL_GetWindowPosition(window, &vid.xPos, &vid.yPos);
+
context = SDL_GL_CreateContext(window);
if (context == NULL)
+ Sys_Error("Failed to initialize OpenGL context: %s\n", SDL_GetError());
+
+ GL_InitFunctions();
+
+#if !defined(USE_GLES2) && !defined(MACOSX)
+ // NVIDIA hates the Core profile and limits the version to the minimum we specified.
+ // HACK: to detect NVIDIA we first need a context, fortunately replacing it takes a few milliseconds
+ gl_vendor = (const char *)qglGetString(GL_VENDOR);
+ if (strncmp(gl_vendor, "NVIDIA", 6) == 0)
{
- Con_Errorf("Failed to initialize OpenGL context: %s\n", SDL_GetError());
- VID_Shutdown();
- return false;
+ Con_DPrint("The Way It's Meant To Be Played: replacing OpenGL Core profile with Compatibility profile...\n");
+ SDL_GL_DeleteContext(context);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
+ context = SDL_GL_CreateContext(window);
+ if (context == NULL)
+ Sys_Error("Failed to initialize OpenGL context: %s\n", SDL_GetError());
}
+#endif
- SDL_GL_SetSwapInterval(bound(-1, vid_vsync.integer, 1));
- vid_usingvsync = (vid_vsync.integer != 0);
+ // apply vid_vsync
+ Cvar_Callback(&vid_vsync);
- gl_platform = "SDL";
+ vid_hidden = false;
+ vid_activewindow = true;
+ vid_hasfocus = true;
+ vid_usingmouse = false;
+ vid_usinghidecursor = false;
+
+ // clear to black (loading plaque will be seen over this)
+ GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 0);
+ VID_Finish(); // checks vid_hidden
GL_Setup();
Cvar_SetQuick(&gl_info_vendor, gl_vendor);
Cvar_SetQuick(&gl_info_renderer, gl_renderer);
Cvar_SetQuick(&gl_info_version, gl_version);
- Cvar_SetQuick(&gl_info_platform, gl_platform ? gl_platform : "");
- Cvar_SetQuick(&gl_info_driver, gl_driver);
+ Cvar_SetQuick(&gl_info_driver, drivername ? drivername : "");
- // LadyHavoc: report supported extensions
-#ifdef CONFIG_MENU
- Con_DPrintf("\nQuakeC extensions for server and client: %s\nQuakeC extensions for menu: %s\n", vm_sv_extensions, vm_m_extensions);
-#else
- Con_DPrintf("\nQuakeC extensions for server and client: %s\n", vm_sv_extensions);
-#endif
+ for (i = 0; i < vid_info_displaycount.integer; ++i)
+ Con_Printf("Display %i: %s\n", i, SDL_GetDisplayName(i));
- // clear to black (loading plaque will be seen over this)
- GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 0);
-
- vid_hidden = false;
- vid_activewindow = false;
- vid_hasfocus = true;
- vid_usingmouse = false;
- vid_usinghidecursor = false;
-
return true;
}
-extern cvar_t gl_info_extensions;
-extern cvar_t gl_info_vendor;
-extern cvar_t gl_info_renderer;
-extern cvar_t gl_info_version;
-extern cvar_t gl_info_platform;
-extern cvar_t gl_info_driver;
-
-qboolean VID_InitMode(viddef_mode_t *mode)
+qbool VID_InitMode(viddef_mode_t *mode)
{
// GAME_STEELSTORM specific
steelstorm_showing_map = Cvar_FindVar(&cvars_all, "steelstorm_showing_map", ~0);
void VID_Shutdown (void)
{
VID_EnableJoystick(false);
- VID_SetMouse(false, false, false);
+ VID_SetMouse(false, false);
+ SDL_GL_DeleteContext(context);
+ context = NULL;
SDL_DestroyWindow(window);
window = NULL;
SDL_QuitSubSystem(SDL_INIT_VIDEO);
-
- gl_driver[0] = 0;
- gl_extensions = "";
- gl_platform = "";
}
void VID_Finish (void)
{
- qboolean vid_usevsync;
- vid_activewindow = !vid_hidden && vid_hasfocus;
-
VID_UpdateGamma();
if (!vid_hidden)
CHECKGLERROR
if (r_speeds.integer == 2 || gl_finish.integer)
GL_Finish();
-
- vid_usevsync = (vid_vsync.integer && !cls.timedemo);
- if (vid_usingvsync != vid_usevsync)
- {
- vid_usingvsync = vid_usevsync;
- if (SDL_GL_SetSwapInterval(vid_usevsync != 0) >= 0)
- Con_DPrintf("Vsync %s\n", vid_usevsync ? "activated" : "deactivated");
- else
- Con_DPrintf("ERROR: can't %s vsync\n", vid_usevsync ? "activate" : "deactivate");
- }
SDL_GL_SwapWindow(window);
break;
}
}
}
-vid_mode_t *VID_GetDesktopMode(void)
+vid_mode_t VID_GetDesktopMode(void)
{
SDL_DisplayMode mode;
int bpp;
Uint32 rmask, gmask, bmask, amask;
- SDL_GetDesktopDisplayMode(0, &mode);
+ vid_mode_t desktop_mode;
+
+ SDL_GetDesktopDisplayMode(vid.displayindex, &mode);
SDL_PixelFormatEnumToMasks(mode.format, &bpp, &rmask, &gmask, &bmask, &amask);
desktop_mode.width = mode.w;
desktop_mode.height = mode.h;
desktop_mode.refreshrate = mode.refresh_rate;
desktop_mode.pixelheight_num = 1;
desktop_mode.pixelheight_denom = 1; // SDL does not provide this
- // TODO check whether this actually works, or whether we do still need
- // a read-window-size-after-entering-desktop-fullscreen hack for
- // multiscreen setups.
- return &desktop_mode;
+ return desktop_mode;
}
size_t VID_ListModes(vid_mode_t *modes, size_t maxcount)
{
size_t k = 0;
int modenum;
- int nummodes = SDL_GetNumDisplayModes(0);
+ int nummodes = SDL_GetNumDisplayModes(vid.displayindex);
SDL_DisplayMode mode;
for (modenum = 0;modenum < nummodes;modenum++)
{
if (k >= maxcount)
break;
- if (SDL_GetDisplayMode(0, modenum, &mode))
+ if (SDL_GetDisplayMode(vid.displayindex, modenum, &mode))
continue;
modes[k].width = mode.w;
modes[k].height = mode.h;