# include "utils/display.h"
# include <algorithm>
# include <cmath>
# include <cstdint>
# include <utility>
# ifdef __vita__
# include <psp2/power.h>
# endif
# ifdef __3DS__
# include "platform/ctr/display.hpp"
# endif
# ifdef NXDK
# include <hal/video.h>
# endif
# include "DiabloUI/diabloui.h"
# include "control.h"
# include "controls/controller.h"
# ifndef USE_SDL1
# include "controls/devices/game_controller.h"
# endif
# include "controls/devices/joystick.h"
# include "controls/devices/kbcontroller.h"
# include "controls/game_controls.h"
# include "controls/touch/gamepad.h"
# include "engine/backbuffer_state.hpp"
# include "engine/dx.h"
# include "headless_mode.hpp"
# include "options.h"
# include "utils/log.hpp"
# include "utils/sdl_geometry.h"
# include "utils/sdl_wrap.h"
# include "utils/str_cat.hpp"
# ifdef USE_SDL1
# ifndef SDL1_VIDEO_MODE_BPP
# define SDL1_VIDEO_MODE_BPP 0
# endif
# ifndef SDL1_VIDEO_MODE_FLAGS
# define SDL1_VIDEO_MODE_FLAGS SDL_SWSURFACE
# endif
# endif
namespace devilution {
extern SDLSurfaceUniquePtr RendererTextureSurface ; /** defined in dx.cpp */
SDL_Window * ghMainWnd ;
Size forceResolution ;
Uint16 gnScreenWidth ;
Uint16 gnScreenHeight ;
Uint16 gnViewportHeight ;
Uint16 GetScreenWidth ( )
{
return gnScreenWidth ;
}
Uint16 GetScreenHeight ( )
{
return gnScreenHeight ;
}
Uint16 GetViewportHeight ( )
{
return gnViewportHeight ;
}
Rectangle UIRectangle ;
const Rectangle & GetUIRectangle ( )
{
return UIRectangle ;
}
namespace {
# ifndef USE_SDL1
void CalculatePreferredWindowSize ( int & width , int & height )
{
SDL_DisplayMode mode ;
if ( SDL_GetDesktopDisplayMode ( 0 , & mode ) ! = 0 ) {
ErrSdl ( ) ;
}
if ( mode . w < mode . h ) {
std : : swap ( mode . w , mode . h ) ;
}
if ( * GetOptions ( ) . Graphics . integerScaling ) {
int factor = std : : min ( mode . w / width , mode . h / height ) ;
width = mode . w / factor ;
height = mode . h / factor ;
return ;
}
float wFactor = ( float ) mode . w / width ;
float hFactor = ( float ) mode . h / height ;
if ( wFactor > hFactor ) {
width = mode . w * height / mode . h ;
} else {
height = mode . h * width / mode . w ;
}
}
void FreeRenderer ( )
{
# if defined(_WIN32) && !defined(NXDK)
bool wasD3D9 = false ;
if ( renderer ! = nullptr ) {
SDL_RendererInfo previousRendererInfo ;
SDL_GetRendererInfo ( renderer , & previousRendererInfo ) ;
wasD3D9 = ( std : : string_view ( previousRendererInfo . name ) = = " direct3d " ) ;
}
# endif
if ( renderer ! = nullptr ) {
SDL_DestroyRenderer ( renderer ) ;
renderer = nullptr ;
}
# if defined(_WIN32) && !defined(NXDK) && !defined(USE_SDL1)
// On Windows 11 the directx9 VSYNC timer doesn't get recreated properly, see https://github.com/libsdl-org/SDL/issues/5099
if ( wasD3D9 & & * GetOptions ( ) . Graphics . upscale & & * GetOptions ( ) . Graphics . frameRateControl ! = FrameRateControl : : VerticalSync ) {
std : : string title = SDL_GetWindowTitle ( ghMainWnd ) ;
Uint32 flags = SDL_GetWindowFlags ( ghMainWnd ) ;
Rectangle dimensions ;
SDL_GetWindowPosition ( ghMainWnd , & dimensions . position . x , & dimensions . position . y ) ;
SDL_GetWindowSize ( ghMainWnd , & dimensions . size . width , & dimensions . size . height ) ;
SDL_DestroyWindow ( ghMainWnd ) ;
ghMainWnd = SDL_CreateWindow (
title . c_str ( ) ,
dimensions . position . x ,
dimensions . position . y ,
dimensions . size . width ,
dimensions . size . height ,
flags ) ;
}
# endif
}
SDL_DisplayMode GetNearestDisplayMode ( Size preferredSize )
{
SDL_DisplayMode nearestDisplayMode ;
if ( SDL_GetWindowDisplayMode ( ghMainWnd , & nearestDisplayMode ) ! = 0 )
ErrSdl ( ) ;
int displayIndex = SDL_GetWindowDisplayIndex ( ghMainWnd ) ;
int modeCount = SDL_GetNumDisplayModes ( displayIndex ) ;
for ( int modeIndex = 0 ; modeIndex < modeCount ; modeIndex + + ) {
SDL_DisplayMode displayMode ;
if ( SDL_GetDisplayMode ( displayIndex , modeIndex , & displayMode ) ! = 0 )
continue ;
int diffHeight = std : : abs ( nearestDisplayMode . h - preferredSize . height ) - std : : abs ( displayMode . h - preferredSize . height ) ;
int diffWidth = std : : abs ( nearestDisplayMode . w - preferredSize . width ) - std : : abs ( displayMode . w - preferredSize . width ) ;
if ( diffHeight < 0 )
continue ;
if ( diffHeight = = 0 & & diffWidth < 0 )
continue ;
nearestDisplayMode = displayMode ;
}
return nearestDisplayMode ;
}
# endif
void CalculateUIRectangle ( )
{
constexpr Size UISize { 640 , 480 } ;
UIRectangle = {
{ ( gnScreenWidth - UISize . width ) / 2 , ( gnScreenHeight - UISize . height ) / 2 } ,
UISize
} ;
}
Size GetPreferredWindowSize ( )
{
Size windowSize = forceResolution . width ! = 0 ? forceResolution : * GetOptions ( ) . Graphics . resolution ;
# ifndef USE_SDL1
if ( * GetOptions ( ) . Graphics . upscale & & * GetOptions ( ) . Graphics . fitToScreen ) {
CalculatePreferredWindowSize ( windowSize . width , windowSize . height ) ;
}
# endif
AdjustToScreenGeometry ( windowSize ) ;
return windowSize ;
}
const auto OptionChangeHandlerResolution = ( GetOptions ( ) . Graphics . resolution . SetValueChangedCallback ( ResizeWindow ) , true ) ;
const auto OptionChangeHandlerFullscreen = ( GetOptions ( ) . Graphics . fullscreen . SetValueChangedCallback ( SetFullscreenMode ) , true ) ;
void OptionGrabInputChanged ( )
{
# ifdef USE_SDL1
SDL_WM_GrabInput ( * GetOptions ( ) . Gameplay . grabInput ? SDL_GRAB_ON : SDL_GRAB_OFF ) ;
# else
if ( ghMainWnd ! = nullptr )
SDL_SetWindowGrab ( ghMainWnd , * GetOptions ( ) . Gameplay . grabInput ? SDL_TRUE : SDL_FALSE ) ;
# endif
}
const auto OptionChangeHandlerGrabInput = ( GetOptions ( ) . Gameplay . grabInput . SetValueChangedCallback ( OptionGrabInputChanged ) , true ) ;
void UpdateAvailableResolutions ( )
{
GraphicsOptions & graphicsOptions = GetOptions ( ) . Graphics ;
std : : vector < Size > sizes ;
float scaleFactor = GetDpiScalingFactor ( ) ;
// Add resolutions
bool supportsAnyResolution = false ;
# ifdef USE_SDL1
auto * modes = SDL_ListModes ( nullptr , SDL_FULLSCREEN | SDL_HWPALETTE ) ;
// SDL_ListModes returns -1 if any resolution is allowed (for example returned on 3DS)
if ( modes = = ( SDL_Rect * * ) - 1 ) {
supportsAnyResolution = true ;
} else if ( modes ! = nullptr ) {
for ( size_t i = 0 ; modes [ i ] ! = nullptr ; i + + ) {
if ( modes [ i ] - > w < modes [ i ] - > h ) {
std : : swap ( modes [ i ] - > w , modes [ i ] - > h ) ;
}
sizes . emplace_back ( Size {
static_cast < int > ( modes [ i ] - > w * scaleFactor ) ,
static_cast < int > ( modes [ i ] - > h * scaleFactor ) } ) ;
}
}
# else
int displayModeCount = SDL_GetNumDisplayModes ( 0 ) ;
for ( int i = 0 ; i < displayModeCount ; i + + ) {
SDL_DisplayMode mode ;
if ( SDL_GetDisplayMode ( 0 , i , & mode ) ! = 0 ) {
ErrSdl ( ) ;
}
if ( mode . w < mode . h ) {
std : : swap ( mode . w , mode . h ) ;
}
sizes . emplace_back ( Size {
static_cast < int > ( mode . w * scaleFactor ) ,
static_cast < int > ( mode . h * scaleFactor ) } ) ;
}
supportsAnyResolution = * GetOptions ( ) . Graphics . upscale ;
# endif
if ( supportsAnyResolution & & sizes . size ( ) = = 1 ) {
// Attempt to provide sensible options for 4:3 and the native aspect ratio
const int width = sizes [ 0 ] . width ;
const int height = sizes [ 0 ] . height ;
const int commonHeights [ ] = { 480 , 540 , 720 , 960 , 1080 , 1440 , 2160 } ;
for ( int commonHeight : commonHeights ) {
if ( commonHeight > height )
break ;
sizes . emplace_back ( Size { commonHeight * 4 / 3 , commonHeight } ) ;
if ( commonHeight * width % height = = 0 )
sizes . emplace_back ( Size { commonHeight * width / height , commonHeight } ) ;
}
}
const Size configuredSize = * graphicsOptions . resolution ;
// Ensures that the ini specified resolution is present in resolution list even if it doesn't match a monitor resolution (for example if played in window mode)
sizes . push_back ( configuredSize ) ;
// Ensures that the platform's preferred default resolution is always present
sizes . emplace_back ( Size { DEFAULT_WIDTH , DEFAULT_HEIGHT } ) ;
// Ensures that the vanilla Diablo resolution is present on systems that would support it
if ( supportsAnyResolution )
sizes . emplace_back ( Size { 640 , 480 } ) ;
# ifndef USE_SDL1
if ( * graphicsOptions . fitToScreen ) {
SDL_DisplayMode mode ;
if ( SDL_GetDesktopDisplayMode ( 0 , & mode ) ! = 0 ) {
ErrSdl ( ) ;
}
for ( auto & size : sizes ) {
// Ensure that the ini specified resolution remains present in the resolution list
if ( size . height = = configuredSize . height )
size . width = configuredSize . width ;
else
size . width = size . height * mode . w / mode . h ;
}
}
# endif
// Sort by width then by height
c_sort ( sizes , [ ] ( const Size & x , const Size & y ) - > bool {
if ( x . width = = y . width )
return x . height > y . height ;
return x . width > y . width ;
} ) ;
// Remove duplicate entries
sizes . erase ( std : : unique ( sizes . begin ( ) , sizes . end ( ) ) , sizes . end ( ) ) ;
std : : vector < std : : pair < Size , std : : string > > resolutions ;
for ( auto & size : sizes ) {
# ifndef USE_SDL1
if ( * graphicsOptions . fitToScreen ) {
resolutions . emplace_back ( size , StrCat ( size . height , " p " ) ) ;
continue ;
}
# endif
resolutions . emplace_back ( size , StrCat ( size . width , " x " , size . height ) ) ;
}
graphicsOptions . resolution . setAvailableResolutions ( std : : move ( resolutions ) ) ;
}
# if !defined(USE_SDL1) || defined(__3DS__)
void ResizeWindowAndUpdateResolutionOptions ( )
{
ResizeWindow ( ) ;
# ifndef __3DS__
UpdateAvailableResolutions ( ) ;
# endif
}
const auto OptionChangeHandlerFitToScreen = ( GetOptions ( ) . Graphics . fitToScreen . SetValueChangedCallback ( ResizeWindowAndUpdateResolutionOptions ) , true ) ;
# endif
# ifndef USE_SDL1
const auto OptionChangeHandlerScaleQuality = ( GetOptions ( ) . Graphics . scaleQuality . SetValueChangedCallback ( ReinitializeTexture ) , true ) ;
const auto OptionChangeHandlerIntegerScaling = ( GetOptions ( ) . Graphics . integerScaling . SetValueChangedCallback ( ReinitializeIntegerScale ) , true ) ;
const auto OptionChangeHandlerVSync = ( GetOptions ( ) . Graphics . frameRateControl . SetValueChangedCallback ( ReinitializeRenderer ) , true ) ;
# endif
} // namespace
void AdjustToScreenGeometry ( Size windowSize )
{
gnScreenWidth = windowSize . width ;
gnScreenHeight = windowSize . height ;
CalculateUIRectangle ( ) ;
CalculatePanelAreas ( ) ;
}
float GetDpiScalingFactor ( )
{
# ifdef USE_SDL1
return 1.0F ;
# else
if ( renderer = = nullptr )
return 1.0F ;
int renderWidth ;
int renderHeight ;
SDL_GetRendererOutputSize ( renderer , & renderWidth , & renderHeight ) ;
int windowWidth ;
int windowHeight ;
SDL_GetWindowSize ( ghMainWnd , & windowWidth , & windowHeight ) ;
float hfactor = static_cast < float > ( renderWidth ) / windowWidth ;
float vhfactor = static_cast < float > ( renderHeight ) / windowHeight ;
return std : : min ( hfactor , vhfactor ) ;
# endif
}
# ifdef USE_SDL1
void SetVideoMode ( int width , int height , int bpp , uint32_t flags )
{
Log ( " Setting video mode {}x{} bpp={} flags=0x{:08X} " , width , height , bpp , flags ) ;
ghMainWnd = SDL_SetVideoMode ( width , height , bpp , flags ) ;
if ( ghMainWnd = = nullptr ) {
ErrSdl ( ) ;
}
const SDL_Surface * surface = SDL_GetVideoSurface ( ) ;
if ( surface = = nullptr ) {
ErrSdl ( ) ;
}
Log ( " Video surface is now {}x{} bpp={} flags=0x{:08X} " ,
surface - > w , surface - > h , surface - > format - > BitsPerPixel , surface - > flags ) ;
}
void SetVideoModeToPrimary ( bool fullscreen , int width , int height )
{
int flags = SDL1_VIDEO_MODE_FLAGS | SDL_HWPALETTE ;
if ( fullscreen )
flags | = SDL_FULLSCREEN ;
# ifdef __3DS__
flags & = ~ SDL_FULLSCREEN ;
flags | = Get3DSScalingFlag ( * GetOptions ( ) . Graphics . fitToScreen , width , height ) ;
# endif
SetVideoMode ( width , height , SDL1_VIDEO_MODE_BPP , flags ) ;
if ( OutputRequiresScaling ( ) )
Log ( " Using software scaling " ) ;
}
# endif
bool IsFullScreen ( )
{
# ifdef USE_SDL1
return ( SDL_GetVideoSurface ( ) - > flags & SDL_FULLSCREEN ) ! = 0 ;
# else
return ( SDL_GetWindowFlags ( ghMainWnd ) & ( SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP ) ) ! = 0 ;
# endif
}
bool SpawnWindow ( const char * lpWindowName )
{
# ifdef __vita__
scePowerSetArmClockFrequency ( 444 ) ;
# endif
# ifdef NXDK
{
Size windowSize = forceResolution . width ! = 0 ? forceResolution : * GetOptions ( ) . Graphics . resolution ;
VIDEO_MODE xmode ;
void * p = nullptr ;
while ( XVideoListModes ( & xmode , 0 , 0 , & p ) ) {
if ( windowSize . width > = xmode . width & & windowSize . height = = xmode . height )
break ;
}
XVideoSetMode ( xmode . width , xmode . height , xmode . bpp , xmode . refresh ) ;
}
# endif
# if SDL_VERSION_ATLEAST(2, 0, 4)
SDL_SetHint ( SDL_HINT_IME_INTERNAL_EDITING , " 1 " ) ;
# endif
# if SDL_VERSION_ATLEAST(2, 0, 6) && defined(__vita__)
SDL_SetHint ( SDL_HINT_TOUCH_MOUSE_EVENTS , " 0 " ) ;
# endif
# if SDL_VERSION_ATLEAST(2, 0, 10)
SDL_SetHint ( SDL_HINT_MOUSE_TOUCH_EVENTS , " 0 " ) ;
# endif
# if SDL_VERSION_ATLEAST(2, 0, 2)
SDL_SetHint ( SDL_HINT_ACCELEROMETER_AS_JOYSTICK , " 0 " ) ;
# endif
# if SDL_VERSION_ATLEAST(2, 0, 12)
SDL_SetHint ( SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS , " 0 " ) ;
# endif
int initFlags = SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ;
# ifndef NOSOUND
initFlags | = SDL_INIT_AUDIO ;
# endif
# ifndef USE_SDL1
initFlags | = SDL_INIT_GAMECONTROLLER ;
SDL_SetHint ( SDL_HINT_ORIENTATIONS , " LandscapeLeft LandscapeRight " ) ;
# endif
if ( SDL_Init ( initFlags ) < = - 1 ) {
ErrSdl ( ) ;
}
RegisterCustomEvents ( ) ;
# ifndef USE_SDL1
if ( GetOptions ( ) . Controller . szMapping [ 0 ] ! = ' \0 ' ) {
SDL_GameControllerAddMapping ( GetOptions ( ) . Controller . szMapping ) ;
}
# endif
# ifdef USE_SDL1
// On SDL 1, there are no ADDED/REMOVED events.
// Always try to initialize the first joystick.
Joystick : : Add ( 0 ) ;
# ifdef __SWITCH__
// TODO: There is a bug in SDL2 on Switch where it does not report controllers on startup (Jan 1, 2020)
GameController : : Add ( 0 ) ;
# endif
# endif
Size windowSize = GetPreferredWindowSize ( ) ;
# ifdef USE_SDL1
SDL_WM_SetCaption ( lpWindowName , WINDOW_ICON_NAME ) ;
SetVideoModeToPrimary ( * GetOptions ( ) . Graphics . fullscreen , windowSize . width , windowSize . height ) ;
if ( * GetOptions ( ) . Gameplay . grabInput )
SDL_WM_GrabInput ( SDL_GRAB_ON ) ;
atexit ( SDL_VideoQuit ) ; // Without this video mode is not restored after fullscreen.
# else
int flags = SDL_WINDOW_ALLOW_HIGHDPI ;
if ( * GetOptions ( ) . Graphics . upscale ) {
if ( * GetOptions ( ) . Graphics . fullscreen ) {
flags | = SDL_WINDOW_FULLSCREEN_DESKTOP ;
}
flags | = SDL_WINDOW_RESIZABLE ;
} else if ( * GetOptions ( ) . Graphics . fullscreen ) {
flags | = SDL_WINDOW_FULLSCREEN ;
}
ghMainWnd = SDL_CreateWindow ( lpWindowName , SDL_WINDOWPOS_UNDEFINED , SDL_WINDOWPOS_UNDEFINED , windowSize . width , windowSize . height , flags ) ;
// Note: https://github.com/libsdl-org/SDL/issues/962
// This is a solution to a problem related to SDL mouse grab.
// See https://github.com/diasurgical/devilutionX/issues/4251
if ( ghMainWnd ! = nullptr )
SDL_SetWindowGrab ( ghMainWnd , * GetOptions ( ) . Gameplay . grabInput ? SDL_TRUE : SDL_FALSE ) ;
# endif
if ( ghMainWnd = = nullptr ) {
ErrSdl ( ) ;
}
int refreshRate = 60 ;
# ifndef USE_SDL1
SDL_DisplayMode mode ;
SDL_GetDisplayMode ( 0 , 0 , & mode ) ;
if ( mode . refresh_rate ! = 0 ) {
refreshRate = mode . refresh_rate ;
}
# endif
refreshDelay = 1000000 / refreshRate ;
ReinitializeRenderer ( ) ;
if ( ghMainWnd ! = nullptr ) {
UpdateAvailableResolutions ( ) ;
return true ;
}
return false ;
}
# ifndef USE_SDL1
void ReinitializeTexture ( )
{
if ( texture )
texture . reset ( ) ;
if ( renderer = = nullptr )
return ;
auto quality = StrCat ( static_cast < int > ( * GetOptions ( ) . Graphics . scaleQuality ) ) ;
SDL_SetHint ( SDL_HINT_RENDER_SCALE_QUALITY , quality . c_str ( ) ) ;
texture = SDLWrap : : CreateTexture ( renderer , DEVILUTIONX_DISPLAY_TEXTURE_FORMAT , SDL_TEXTUREACCESS_STREAMING , gnScreenWidth , gnScreenHeight ) ;
}
void ReinitializeIntegerScale ( )
{
if ( * GetOptions ( ) . Graphics . fitToScreen ) {
ResizeWindow ( ) ;
return ;
}
if ( renderer ! = nullptr & & SDL_RenderSetIntegerScale ( renderer , * GetOptions ( ) . Graphics . integerScaling ? SDL_TRUE : SDL_FALSE ) < 0 ) {
ErrSdl ( ) ;
}
}
# endif
void ReinitializeRenderer ( )
{
if ( ghMainWnd = = nullptr )
return ;
# ifdef USE_SDL1
const SDL_Surface * surface = SDL_GetVideoSurface ( ) ;
if ( surface = = nullptr ) {
ErrSdl ( ) ;
}
AdjustToScreenGeometry ( Size ( surface - > w , surface - > h ) ) ;
# else
if ( texture )
texture . reset ( ) ;
FreeRenderer ( ) ;
if ( * GetOptions ( ) . Graphics . upscale ) {
Uint32 rendererFlags = 0 ;
if ( * GetOptions ( ) . Graphics . frameRateControl = = FrameRateControl : : VerticalSync ) {
rendererFlags | = SDL_RENDERER_PRESENTVSYNC ;
}
renderer = SDL_CreateRenderer ( ghMainWnd , - 1 , rendererFlags ) ;
if ( renderer = = nullptr ) {
ErrSdl ( ) ;
}
ReinitializeTexture ( ) ;
if ( SDL_RenderSetIntegerScale ( renderer , * GetOptions ( ) . Graphics . integerScaling ? SDL_TRUE : SDL_FALSE ) < 0 ) {
ErrSdl ( ) ;
}
if ( SDL_RenderSetLogicalSize ( renderer , gnScreenWidth , gnScreenHeight ) < = - 1 ) {
ErrSdl ( ) ;
}
Uint32 format ;
if ( SDL_QueryTexture ( texture . get ( ) , & format , nullptr , nullptr , nullptr ) < 0 )
ErrSdl ( ) ;
RendererTextureSurface = SDLWrap : : CreateRGBSurfaceWithFormat ( 0 , gnScreenWidth , gnScreenHeight , SDL_BITSPERPIXEL ( format ) , format ) ;
} else {
Size windowSize = { } ;
SDL_GetWindowSize ( ghMainWnd , & windowSize . width , & windowSize . height ) ;
AdjustToScreenGeometry ( windowSize ) ;
}
# endif
}
void SetFullscreenMode ( )
{
# ifdef USE_SDL1
Uint32 flags = ghMainWnd - > flags ^ SDL_FULLSCREEN ;
if ( * GetOptions ( ) . Graphics . fullscreen ) {
flags | = SDL_FULLSCREEN ;
}
ghMainWnd = SDL_SetVideoMode ( 0 , 0 , 0 , flags ) ;
if ( ghMainWnd = = NULL ) {
ErrSdl ( ) ;
}
# else
// When switching from windowed to "true fullscreen",
// update the display mode of the window before changing the
// fullscreen mode so that the display mode only has to change once
if ( * GetOptions ( ) . Graphics . fullscreen & & ! * GetOptions ( ) . Graphics . upscale ) {
Size windowSize = GetPreferredWindowSize ( ) ;
SDL_DisplayMode displayMode = GetNearestDisplayMode ( windowSize ) ;
if ( SDL_SetWindowDisplayMode ( ghMainWnd , & displayMode ) ! = 0 ) {
ErrSdl ( ) ;
}
}
Uint32 flags = 0 ;
if ( * GetOptions ( ) . Graphics . fullscreen ) {
flags = * GetOptions ( ) . Graphics . upscale ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN ;
}
if ( SDL_SetWindowFullscreen ( ghMainWnd , flags ) ! = 0 ) {
ErrSdl ( ) ;
}
if ( ! * GetOptions ( ) . Graphics . fullscreen ) {
SDL_RestoreWindow ( ghMainWnd ) ; // Avoid window being maximized before resizing
Size windowSize = GetPreferredWindowSize ( ) ;
SDL_SetWindowSize ( ghMainWnd , windowSize . width , windowSize . height ) ;
}
if ( ! * GetOptions ( ) . Graphics . upscale ) {
// Because "true fullscreen" is locked into specific resolutions based on the modes
// supported by the display, the resolution may have changed when fullscreen was toggled
ReinitializeRenderer ( ) ;
CreateBackBuffer ( ) ;
}
InitializeVirtualGamepad ( ) ;
# endif
RedrawEverything ( ) ;
}
void ResizeWindow ( )
{
if ( ghMainWnd = = nullptr )
return ;
Size windowSize = GetPreferredWindowSize ( ) ;
# ifdef USE_SDL1
SetVideoModeToPrimary ( * GetOptions ( ) . Graphics . fullscreen , windowSize . width , windowSize . height ) ;
# else
// For "true fullscreen" windows, the window resizes automatically based on the display mode
bool trueFullscreen = * GetOptions ( ) . Graphics . fullscreen & & ! * GetOptions ( ) . Graphics . upscale ;
if ( trueFullscreen ) {
SDL_DisplayMode displayMode = GetNearestDisplayMode ( windowSize ) ;
if ( SDL_SetWindowDisplayMode ( ghMainWnd , & displayMode ) ! = 0 )
ErrSdl ( ) ;
}
// Handle switching between "fake fullscreen" and "true fullscreen" when upscale is toggled
bool upscaleChanged = * GetOptions ( ) . Graphics . upscale ! = ( renderer ! = nullptr ) ;
if ( upscaleChanged & & * GetOptions ( ) . Graphics . fullscreen ) {
Uint32 flags = * GetOptions ( ) . Graphics . upscale ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN ;
if ( SDL_SetWindowFullscreen ( ghMainWnd , flags ) ! = 0 )
ErrSdl ( ) ;
if ( ! * GetOptions ( ) . Graphics . fullscreen )
SDL_RestoreWindow ( ghMainWnd ) ; // Avoid window being maximized before resizing
}
if ( ! trueFullscreen )
SDL_SetWindowSize ( ghMainWnd , windowSize . width , windowSize . height ) ;
# endif
ReinitializeRenderer ( ) ;
# ifndef USE_SDL1
SDL_SetWindowResizable ( ghMainWnd , renderer ! = nullptr ? SDL_TRUE : SDL_FALSE ) ;
InitializeVirtualGamepad ( ) ;
# endif
CreateBackBuffer ( ) ;
RedrawEverything ( ) ;
}
SDL_Surface * GetOutputSurface ( )
{
# ifdef USE_SDL1
SDL_Surface * ret = SDL_GetVideoSurface ( ) ;
if ( ret = = nullptr )
ErrSdl ( ) ;
return ret ;
# else
if ( renderer ! = nullptr )
return RendererTextureSurface . get ( ) ;
SDL_Surface * ret = SDL_GetWindowSurface ( ghMainWnd ) ;
if ( ret = = nullptr )
ErrSdl ( ) ;
return ret ;
# endif
}
bool OutputRequiresScaling ( )
{
# ifdef USE_SDL1
if ( HeadlessMode )
return false ;
return gnScreenWidth ! = GetOutputSurface ( ) - > w | | gnScreenHeight ! = GetOutputSurface ( ) - > h ;
# else // SDL2, scaling handled by renderer.
return false ;
# endif
}
void ScaleOutputRect ( SDL_Rect * rect )
{
if ( ! OutputRequiresScaling ( ) )
return ;
const SDL_Surface * surface = GetOutputSurface ( ) ;
rect - > x = rect - > x * surface - > w / gnScreenWidth ;
rect - > y = rect - > y * surface - > h / gnScreenHeight ;
rect - > w = rect - > w * surface - > w / gnScreenWidth ;
rect - > h = rect - > h * surface - > h / gnScreenHeight ;
}
# ifdef USE_SDL1
namespace {
SDLSurfaceUniquePtr CreateScaledSurface ( SDL_Surface * src )
{
SDL_Rect stretched_rect = MakeSdlRect ( 0 , 0 , src - > w , src - > h ) ;
ScaleOutputRect ( & stretched_rect ) ;
SDLSurfaceUniquePtr stretched = SDLWrap : : CreateRGBSurface (
SDL_SWSURFACE , stretched_rect . w , stretched_rect . h , src - > format - > BitsPerPixel ,
src - > format - > Rmask , src - > format - > Gmask , src - > format - > Bmask , src - > format - > Amask ) ;
if ( SDL_HasColorKey ( src ) ) {
SDL_SetColorKey ( stretched . get ( ) , SDL_SRCCOLORKEY , src - > format - > colorkey ) ;
if ( src - > format - > palette ! = NULL )
SDL_SetPalette ( stretched . get ( ) , SDL_LOGPAL , src - > format - > palette - > colors , 0 , src - > format - > palette - > ncolors ) ;
}
if ( SDL_SoftStretch ( ( src ) , NULL , stretched . get ( ) , & stretched_rect ) < 0 )
ErrSdl ( ) ;
return stretched ;
}
} // namespace
# endif // USE_SDL1
SDLSurfaceUniquePtr ScaleSurfaceToOutput ( SDLSurfaceUniquePtr surface )
{
# ifdef USE_SDL1
if ( OutputRequiresScaling ( ) )
return CreateScaledSurface ( surface . get ( ) ) ;
# endif
return surface ;
}
} // namespace devilution