SDL2: Support highdpi (#14703)

and handle DPI changes at runtime
This commit is contained in:
grorp 2024-06-16 17:49:42 +02:00 committed by GitHub
parent 7a64527db5
commit a9cca5e76c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 84 additions and 47 deletions

View File

@ -2187,9 +2187,6 @@ curl_file_download_timeout (cURL file download timeout) int 300000 5000 21474836
[**Miscellaneous]
# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.
screen_dpi (DPI) int 72 1
# Adjust the detected display density, used for scaling UI elements.
display_density_factor (Display Density Scaling Factor) float 1 0.5 5.0

View File

@ -205,6 +205,9 @@ enum EAPPLICATION_EVENT_TYPE
//! The application received a memory warning.
EAET_MEMORY_WARNING,
//! The display density changed (only works on SDL).
EAET_DPI_CHANGED,
//! No real event, but to get number of event types.
EAET_COUNT
};

View File

@ -285,6 +285,11 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters &param) :
SDL_SetHint(SDL_HINT_TV_REMOTE_AS_JOYSTICK, "0");
#endif
#if SDL_VERSION_ATLEAST(2, 24, 0)
// highdpi support on Windows
SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "1");
#endif
// Minetest has its own code to synthesize mouse events from touch events,
// so we prevent SDL from doing it.
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
@ -475,6 +480,7 @@ bool CIrrDeviceSDL::createWindow()
bool CIrrDeviceSDL::createWindowWithContext()
{
u32 SDL_Flags = 0;
SDL_Flags |= SDL_WINDOW_ALLOW_HIGHDPI;
SDL_Flags |= getFullscreenFlag(CreationParams.Fullscreen);
if (Resizable)
@ -589,13 +595,16 @@ bool CIrrDeviceSDL::createWindowWithContext()
return false;
}
// Update Width and Height to match the actual window size.
// In fullscreen mode, the window size specified in SIrrlichtCreationParameters
// is ignored, so we cannot rely on it.
int w = 0, h = 0;
SDL_GetWindowSize(Window, &w, &h);
Width = w;
Height = h;
updateSizeAndScale();
if (ScaleX != 1.0f || ScaleY != 1.0f) {
// The given window size is in pixels, not in screen coordinates.
// We can only do the conversion now since we didn't know the scale before.
SDL_SetWindowSize(Window, CreationParams.WindowSize.Width / ScaleX,
CreationParams.WindowSize.Height / ScaleY);
// Re-center, otherwise large, non-maximized windows go offscreen.
SDL_SetWindowPosition(Window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
updateSizeAndScale();
}
return true;
#endif // !_IRR_EMSCRIPTEN_PLATFORM_
@ -659,10 +668,10 @@ bool CIrrDeviceSDL::run()
irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT;
irrevent.MouseInput.Event = irr::EMIE_MOUSE_MOVED;
MouseX = irrevent.MouseInput.X = SDL_event.motion.x;
MouseY = irrevent.MouseInput.Y = SDL_event.motion.y;
MouseXRel = SDL_event.motion.xrel;
MouseYRel = SDL_event.motion.yrel;
MouseX = irrevent.MouseInput.X = SDL_event.motion.x * ScaleX;
MouseY = irrevent.MouseInput.Y = SDL_event.motion.y * ScaleY;
MouseXRel = SDL_event.motion.xrel * ScaleX;
MouseYRel = SDL_event.motion.yrel * ScaleY;
irrevent.MouseInput.ButtonStates = MouseButtonStates;
irrevent.MouseInput.Shift = (keymod & KMOD_SHIFT) != 0;
irrevent.MouseInput.Control = (keymod & KMOD_CTRL) != 0;
@ -694,8 +703,8 @@ bool CIrrDeviceSDL::run()
SDL_Keymod keymod = SDL_GetModState();
irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT;
irrevent.MouseInput.X = SDL_event.button.x;
irrevent.MouseInput.Y = SDL_event.button.y;
irrevent.MouseInput.X = SDL_event.button.x * ScaleX;
irrevent.MouseInput.Y = SDL_event.button.y * ScaleY;
irrevent.MouseInput.Shift = (keymod & KMOD_SHIFT) != 0;
irrevent.MouseInput.Control = (keymod & KMOD_CTRL) != 0;
@ -827,14 +836,25 @@ bool CIrrDeviceSDL::run()
case SDL_WINDOWEVENT:
switch (SDL_event.window.event) {
case SDL_WINDOWEVENT_RESIZED:
if ((SDL_event.window.data1 != (int)Width) || (SDL_event.window.data2 != (int)Height)) {
Width = SDL_event.window.data1;
Height = SDL_event.window.data2;
case SDL_WINDOWEVENT_SIZE_CHANGED:
#if SDL_VERSION_ATLEAST(2, 0, 18)
case SDL_WINDOWEVENT_DISPLAY_CHANGED:
#endif
u32 old_w = Width, old_h = Height;
f32 old_scale_x = ScaleX, old_scale_y = ScaleY;
updateSizeAndScale();
if (old_w != Width || old_h != Height) {
if (VideoDriver)
VideoDriver->OnResize(core::dimension2d<u32>(Width, Height));
}
if (old_scale_x != ScaleX || old_scale_y != ScaleY) {
irrevent.EventType = EET_APPLICATION_EVENT;
irrevent.ApplicationEvent.EventType = EAET_DPI_CHANGED;
postEventFromUser(irrevent);
}
break;
}
break;
case SDL_USEREVENT:
irrevent.EventType = irr::EET_USER_EVENT;
@ -1030,25 +1050,26 @@ bool CIrrDeviceSDL::activateJoysticks(core::array<SJoystickInfo> &joystickInfo)
return false;
}
void CIrrDeviceSDL::updateSizeAndScale()
{
int window_w, window_h;
SDL_GetWindowSize(Window, &window_w, &window_h);
int drawable_w, drawable_h;
SDL_GL_GetDrawableSize(Window, &drawable_w, &drawable_h);
ScaleX = (float)drawable_w / (float)window_w;
ScaleY = (float)drawable_h / (float)window_h;
Width = drawable_w;
Height = drawable_h;
}
//! Get the display density in dots per inch.
float CIrrDeviceSDL::getDisplayDensity() const
{
if (!Window)
return 0.0f;
int window_w;
int window_h;
SDL_GetWindowSize(Window, &window_w, &window_h);
int drawable_w;
int drawable_h;
SDL_GL_GetDrawableSize(Window, &drawable_w, &drawable_h);
// assume 96 dpi
float dpi_w = (float)drawable_w / (float)window_w * 96.0f;
float dpi_h = (float)drawable_h / (float)window_h * 96.0f;
return std::max(dpi_w, dpi_h);
return std::max(ScaleX * 96.0f, ScaleY * 96.0f);
}
void CIrrDeviceSDL::SwapWindow()

View File

@ -158,7 +158,8 @@ public:
//! Sets the new position of the cursor.
void setPosition(s32 x, s32 y) override
{
SDL_WarpMouseInWindow(Device->Window, x, y);
SDL_WarpMouseInWindow(Device->Window,
x / Device->ScaleX, y / Device->ScaleY);
if (SDL_GetRelativeMouseMode()) {
// There won't be an event for this warp (details on libsdl-org/SDL/issues/6034)
@ -300,6 +301,8 @@ private:
u32 MouseButtonStates;
u32 Width, Height;
f32 ScaleX = 1.0f, ScaleY = 1.0f;
void updateSizeAndScale();
bool Resizable;

View File

@ -1,3 +1,5 @@
# When enabling SDL2 by default on macOS, don't forget to change
# "NSHighResolutionCapable" to true in "Info.plist".
if(NOT APPLE)
set(DEFAULT_SDL2 ON)
endif()

View File

@ -10,7 +10,9 @@
</trustInfo>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- https://learn.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-with-the-application-manifest -->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
<heapType xmlns="http://schemas.microsoft.com/SMI/2020/WindowsSettings">SegmentHeap</heapType>
</windowsSettings>

View File

@ -69,6 +69,7 @@ static void dump_start_data(const GameStartData &data)
ClientLauncher::~ClientLauncher()
{
delete input;
g_settings->deregisterChangedCallback("dpi_change_notifier", setting_changed_callback, this);
g_settings->deregisterChangedCallback("gui_scaling", setting_changed_callback, this);
delete g_fontengine;
@ -129,6 +130,7 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
guienv = m_rendering_engine->get_gui_env();
config_guienv();
g_settings->registerChangedCallback("dpi_change_notifier", setting_changed_callback, this);
g_settings->registerChangedCallback("gui_scaling", setting_changed_callback, this);
g_fontengine = new FontEngine(guienv);

View File

@ -59,7 +59,7 @@ FontEngine::FontEngine(gui::IGUIEnvironment* env) :
"mono_font_path", "mono_font_path_bold", "mono_font_path_italic",
"mono_font_path_bold_italic",
"fallback_font_path",
"screen_dpi", "gui_scaling",
"dpi_change_notifier", "gui_scaling",
};
for (auto name : settings)

View File

@ -60,6 +60,7 @@ Hud::Hud(Client *client, LocalPlayer *player,
this->inventory = inventory;
readScalingSetting();
g_settings->registerChangedCallback("dpi_change_notifier", setting_changed_callback, this);
g_settings->registerChangedCallback("hud_scaling", setting_changed_callback, this);
for (auto &hbar_color : hbar_colors)
@ -153,6 +154,7 @@ void Hud::readScalingSetting()
Hud::~Hud()
{
g_settings->deregisterChangedCallback("dpi_change_notifier", setting_changed_callback, this);
g_settings->deregisterChangedCallback("hud_scaling", setting_changed_callback, this);
if (m_selection_mesh)

View File

@ -113,6 +113,15 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
return true;
}
if (event.EventType == EET_APPLICATION_EVENT &&
event.ApplicationEvent.EventType == EAET_DPI_CHANGED) {
// This is a fake setting so that we can use (de)registerChangedCallback
// not only to listen for gui/hud_scaling changes, but also for DPI changes.
g_settings->setU16("dpi_change_notifier",
g_settings->getU16("dpi_change_notifier") + 1);
return true;
}
// This is separate from other keyboard handling so that it also works in menus.
if (event.EventType == EET_KEY_INPUT_EVENT) {
const KeyPress keyCode(event.KeyInput);

View File

@ -462,18 +462,14 @@ const VideoDriverInfo &RenderingEngine::getVideoDriverInfo(irr::video::E_DRIVER_
float RenderingEngine::getDisplayDensity()
{
float user_factor = g_settings->getFloat("display_density_factor", 0.5f, 5.0f);
#ifndef __ANDROID__
static float cached_display_density = [&] {
float dpi = get_raw_device()->getDisplayDensity();
// fall back to manually specified dpi
if (dpi == 0.0f)
dpi = g_settings->getFloat("screen_dpi");
return dpi / 96.0f;
}();
return std::max(cached_display_density * g_settings->getFloat("display_density_factor"), 0.5f);
float dpi = get_raw_device()->getDisplayDensity();
if (dpi == 0.0f)
dpi = 96.0f;
return std::max(dpi / 96.0f * user_factor, 0.5f);
#else // __ANDROID__
return porting::getDisplayDensity();
return porting::getDisplayDensity() * user_factor;
#endif // __ANDROID__
}

View File

@ -536,8 +536,8 @@ void set_default_settings()
settings->setDefault("server_description", "");
settings->setDefault("enable_console", "false");
settings->setDefault("screen_dpi", "72");
settings->setDefault("display_density_factor", "1");
settings->setDefault("dpi_change_notifier", "0");
// Altered settings for macOS
#if defined(__MACH__) && defined(__APPLE__)