wayland: Support new high-DPI system

- Adds support for scaled fullscreen modes
- General cleanup of scale factor setting and usage
This commit is contained in:
Frank Praznik 2023-01-25 21:49:18 -05:00 committed by Sam Lantinga
parent b462027c53
commit b2cfcbdb64
4 changed files with 87 additions and 91 deletions

View file

@ -194,7 +194,7 @@ static SDL_bool wayland_get_system_cursor(SDL_VideoData *vdata, Wayland_CursorDa
focusdata = focus->driverdata;
/* Cursors use integer scaling. */
*scale = SDL_ceilf(focusdata->scale_factor);
*scale = SDL_ceilf(focusdata->windowed_scale_factor);
size *= *scale;
for (i = 0; i < vdata->num_cursor_themes; i += 1) {
if (vdata->cursor_themes[i].size == size) {

View file

@ -419,6 +419,7 @@ static void AddEmulatedModes(SDL_VideoDisplay *dpy, SDL_bool rot_90)
for (i = 0; i < SDL_arraysize(mode_list); ++i) {
mode = *dpy->display_modes;
mode.display_scale = 1.0f;
if (rot_90) {
mode.w = mode_list[i].h;
@ -570,7 +571,7 @@ static void display_handle_done(void *data,
native_mode.w = driverdata->native_width;
native_mode.h = driverdata->native_height;
}
native_mode.display_scale = 1.0f; /* FIXME */
native_mode.display_scale = 1.0f;
native_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */
native_mode.driverdata = driverdata->output;
@ -579,7 +580,9 @@ static void display_handle_done(void *data,
desktop_mode.format = SDL_PIXELFORMAT_RGB888;
if (driverdata->has_logical_size) { /* If xdg-output is present, calculate the true scale of the desktop */
driverdata->scale_factor = (float)native_mode.w / (float)driverdata->width;
if (video->viewporter) {
driverdata->scale_factor = (float)native_mode.w / (float)driverdata->width;
}
} else { /* Scale the desktop coordinates, if xdg-output isn't present */
driverdata->width /= driverdata->scale_factor;
driverdata->height /= driverdata->scale_factor;
@ -593,21 +596,43 @@ static void display_handle_done(void *data,
desktop_mode.w = driverdata->height;
desktop_mode.h = driverdata->width;
}
desktop_mode.display_scale = driverdata->scale_factor;
desktop_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */
desktop_mode.driverdata = driverdata->output;
/*
* The native display mode is only exposed separately from the desktop size if the
* desktop is scaled and the wp_viewporter protocol is supported.
*/
if (driverdata->scale_factor > 1.0f && video->viewporter != NULL) {
if (driverdata->index > -1) {
SDL_AddDisplayMode(SDL_GetDisplay(driverdata->index), &native_mode);
if (driverdata->index > -1) {
dpy = SDL_GetDisplay(driverdata->index);
} else {
dpy = &driverdata->placeholder;
}
/* Set the desktop display mode. */
SDL_AddDisplayMode(dpy, &desktop_mode);
SDL_SetCurrentDisplayMode(dpy, &desktop_mode);
SDL_SetDesktopDisplayMode(dpy, &desktop_mode);
/* If the desktop is scaled... */
if (driverdata->scale_factor > 1.0f) {
/* ...expose the native resolution if viewports are available... */
if (video->viewporter != NULL) {
SDL_AddDisplayMode(dpy, &native_mode);
} else {
SDL_AddDisplayMode(&driverdata->placeholder, &native_mode);
/* ...if not, expose the integer scale desktop modes down to 1.0. */
int i;
for (i = (int)driverdata->scale_factor - 1; i > 0; --i) {
desktop_mode.display_scale = (float)i;
SDL_AddDisplayMode(dpy, &desktop_mode);
}
}
}
/* Add emulated modes if wp_viewporter is supported and mode emulation is enabled. */
if (video->viewporter && mode_emulation_enabled) {
const SDL_bool rot_90 = ((driverdata->transform & WL_OUTPUT_TRANSFORM_90) != 0) ||
(driverdata->width < driverdata->height);
AddEmulatedModes(dpy, rot_90);
}
/* Calculate the display DPI */
if (driverdata->transform & WL_OUTPUT_TRANSFORM_90) {
driverdata->hdpi = driverdata->physical_height ? (((float)driverdata->height) * 25.4f / driverdata->physical_height) : 0.0f;

View file

@ -57,7 +57,8 @@ SDL_FORCE_INLINE SDL_bool FloatEqual(float a, float b)
static void GetFullScreenDimensions(SDL_Window *window, int *width, int *height, int *drawable_width, int *drawable_height)
{
SDL_WindowData *wind = (SDL_WindowData *)window->driverdata;
SDL_WaylandOutputData *output = (SDL_WaylandOutputData *)SDL_GetDisplayForWindow(window)->driverdata;
SDL_VideoDisplay *disp = SDL_GetDisplayForWindow(window);
SDL_WaylandOutputData *output = (SDL_WaylandOutputData *)disp->driverdata;
int fs_width, fs_height;
int buf_width, buf_height;
@ -82,23 +83,18 @@ static void GetFullScreenDimensions(SDL_Window *window, int *width, int *height,
buf_height = fs_height;
}
} else {
/*
* If a mode was set, use it, otherwise use the native resolution
* for DPI aware apps and the desktop size for legacy apps.
*/
/* If a mode was set, use it, otherwise use the native resolution. */
if (window->fullscreen_mode.w != 0 && window->fullscreen_mode.h != 0) {
fs_width = window->fullscreen_mode.w;
fs_height = window->fullscreen_mode.h;
} else if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
fs_width = output->native_width;
fs_height = output->native_height;
buf_width = (int)SDL_lroundf((float)fs_width * window->fullscreen_mode.display_scale);
buf_height = (int)SDL_lroundf((float)fs_height * window->fullscreen_mode.display_scale);
} else {
fs_width = output_width;
fs_height = output_height;
fs_width = disp->display_modes[0].w;
fs_height = disp->display_modes[0].h;
buf_width = (int)SDL_lroundf((float)fs_width * disp->display_modes[0].display_scale);
buf_height = (int)SDL_lroundf((float)fs_width * disp->display_modes[0].display_scale);
}
buf_width = fs_width;
buf_height = fs_height;
}
if (width) {
@ -115,18 +111,19 @@ static void GetFullScreenDimensions(SDL_Window *window, int *width, int *height,
}
}
SDL_FORCE_INLINE SDL_bool SurfaceScaleIsFractional(SDL_Window *window)
{
SDL_WindowData *data = window->driverdata;
return !FloatEqual(SDL_roundf(data->scale_factor), data->scale_factor);
}
SDL_FORCE_INLINE SDL_bool FullscreenModeEmulation(SDL_Window *window)
{
return (window->flags & SDL_WINDOW_FULLSCREEN) &&
((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP);
}
SDL_bool SurfaceScaleIsFractional(SDL_Window *window)
{
SDL_WindowData *data = window->driverdata;
const float scale_value = !FullscreenModeEmulation(window) ? data->windowed_scale_factor : window->fullscreen_mode.display_scale;
return !FloatEqual(SDL_roundf(scale_value), scale_value);
}
static SDL_bool WindowNeedsViewport(SDL_Window *window)
{
SDL_WindowData *wind = window->driverdata;
@ -163,19 +160,12 @@ static void GetBufferSize(SDL_Window *window, int *width, int *height)
GetFullScreenDimensions(window, NULL, NULL, &buf_width, &buf_height);
} else if (WindowNeedsViewport(window)) {
/* Round fractional backbuffer sizes halfway away from zero. */
buf_width = (int)SDL_lroundf(window->w * data->scale_factor);
buf_height = (int)SDL_lroundf(window->h * data->scale_factor);
buf_width = (int)SDL_lroundf((float)window->w * data->windowed_scale_factor);
buf_height = (int)SDL_lroundf((float)window->h * data->windowed_scale_factor);
} else {
/*
* Integer scaled windowed or fullscreen with no viewport
*
* Round the scale factor up in the unlikely scenario of a compositor
* that supports fractional scaling, but not viewports.
*/
int scale_factor = (int)SDL_ceilf(data->scale_factor);
buf_width = window->w * scale_factor;
buf_height = window->h * scale_factor;
/* Integer scaled windowed or fullscreen with no viewport */
buf_width = window->w * (int)data->windowed_scale_factor;
buf_height = window->h * (int)data->windowed_scale_factor;
}
if (width) {
@ -233,7 +223,7 @@ static void ConfigureWindowGeometry(SDL_Window *window)
0, 0);
}
if (FullscreenModeEmulation(window) && WindowNeedsViewport(window)) {
if (FullscreenModeEmulation(window)) {
int fs_width, fs_height;
const int output_width = data->fs_output_width ? data->fs_output_width : output->width;
const int output_height = data->fs_output_height ? data->fs_output_height : output->height;
@ -243,11 +233,14 @@ static void ConfigureWindowGeometry(SDL_Window *window)
if (window_size_changed || drawable_size_changed) {
GetFullScreenDimensions(window, &fs_width, &fs_height, NULL, NULL);
/* Set the buffer scale to 1 since a viewport will be used. */
wl_surface_set_buffer_scale(data->surface, 1);
SetDrawSurfaceViewport(window, data->drawable_width, data->drawable_height,
output_width, output_height);
if (WindowNeedsViewport(window)) {
/* Set the buffer scale to 1 since a viewport will be used. */
wl_surface_set_buffer_scale(data->surface, 1);
SetDrawSurfaceViewport(window, data->drawable_width, data->drawable_height,
output_width, output_height);
} else {
wl_surface_set_buffer_scale(data->surface, (int32_t)window->fullscreen_mode.display_scale);
}
data->window_width = output_width;
data->window_height = output_height;
@ -263,9 +256,7 @@ static void ConfigureWindowGeometry(SDL_Window *window)
SetDrawSurfaceViewport(window, data->drawable_width, data->drawable_height, window->w, window->h);
} else {
UnsetDrawSurfaceViewport(window);
/* Round to the next integer in case of a fractional value. */
wl_surface_set_buffer_scale(data->surface, (int32_t)SDL_ceilf(data->scale_factor));
wl_surface_set_buffer_scale(data->surface, (int32_t)data->windowed_scale_factor);
}
/* Clamp the physical window size to the system minimum required size. */
@ -528,14 +519,14 @@ static const struct wl_callback_listener gles_swap_frame_listener = {
gles_swap_frame_done
};
static void Wayland_HandleResize(SDL_Window *window, int width, int height, float scale);
static void Wayland_HandleResize(SDL_Window *window, int width, int height);
static void handle_configure_xdg_shell_surface(void *data, struct xdg_surface *xdg, uint32_t serial)
{
SDL_WindowData *wind = (SDL_WindowData *)data;
SDL_Window *window = wind->sdlwindow;
Wayland_HandleResize(window, window->w, window->h, wind->scale_factor);
Wayland_HandleResize(window, window->w, window->h);
xdg_surface_ack_configure(xdg, serial);
wind->shell_surface.xdg.initial_configure_seen = SDL_TRUE;
@ -553,7 +544,6 @@ static void handle_configure_xdg_toplevel(void *data,
{
SDL_WindowData *wind = (SDL_WindowData *)data;
SDL_Window *window = wind->sdlwindow;
SDL_WaylandOutputData *driverdata;
enum xdg_toplevel_state *state;
SDL_bool fullscreen = SDL_FALSE;
@ -580,12 +570,10 @@ static void handle_configure_xdg_toplevel(void *data,
}
}
driverdata = (SDL_WaylandOutputData *)SDL_GetDisplayForWindow(window)->driverdata;
UpdateWindowFullscreen(window, fullscreen);
if (!fullscreen) {
if (width == 0 || height == 0) {
if ((floating && !wind->was_floating) || width == 0 || height == 0) {
/* This usually happens when we're being restored from a
* non-floating state, so use the cached floating size here.
*/
@ -658,13 +646,9 @@ static void handle_configure_xdg_toplevel(void *data,
window->h = height;
wind->needs_resize_event = SDL_TRUE;
}
/* This part is good though. */
if ((window->flags & SDL_WINDOW_ALLOW_HIGHDPI) && !FloatEqual(wind->scale_factor, driverdata->scale_factor)) {
wind->scale_factor = driverdata->scale_factor;
wind->needs_resize_event = SDL_TRUE;
}
}
wind->was_floating = floating;
}
static void handle_close_xdg_toplevel(void *data, struct xdg_toplevel *xdg_toplevel)
@ -813,12 +797,10 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
{
SDL_WindowData *wind = (SDL_WindowData *)user_data;
SDL_Window *window = wind->sdlwindow;
SDL_WaylandOutputData *driverdata;
struct libdecor_state *state;
enum libdecor_window_state window_state;
int width, height;
float scale_factor = wind->scale_factor;
SDL_bool focused = SDL_FALSE;
SDL_bool fullscreen = SDL_FALSE;
@ -838,8 +820,6 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
}
floating = !(fullscreen || maximized || tiled);
driverdata = (SDL_WaylandOutputData *)SDL_GetDisplayForWindow(window)->driverdata;
UpdateWindowFullscreen(window, fullscreen);
if (!fullscreen) {
@ -881,11 +861,6 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
if (FullscreenModeEmulation(window)) {
GetFullScreenDimensions(window, &width, &height, NULL, NULL);
}
/* This part is good though. */
if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
scale_factor = driverdata->scale_factor;
}
} else if (!(window->flags & SDL_WINDOW_RESIZABLE) || (floating && wind->floating_resize_pending)) {
width = window->windowed.w;
height = window->windowed.h;
@ -930,7 +905,7 @@ static void decoration_frame_configure(struct libdecor_frame *frame,
wind->was_floating = floating;
/* Do the resize on the SDL side (this will set window->w/h)... */
Wayland_HandleResize(window, width, height, scale_factor);
Wayland_HandleResize(window, width, height);
/* ... then commit the changes on the libdecor side. */
state = libdecor_state_new(wind->window_width, wind->window_height);
@ -997,7 +972,7 @@ static const struct qt_extended_surface_listener extended_surface_listener = {
static void update_scale_factor(SDL_WindowData *window)
{
float old_factor = window->scale_factor;
float old_factor = window->windowed_scale_factor;
float new_factor;
int i;
@ -1006,25 +981,21 @@ static void update_scale_factor(SDL_WindowData *window)
return;
}
if (FULLSCREEN_VISIBLE(window->sdlwindow)) {
/* For fullscreen, use the active display's scale factor */
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window->sdlwindow);
SDL_WaylandOutputData *driverdata = display->driverdata;
new_factor = driverdata->scale_factor;
} else if (window->num_outputs == 0) {
/* No monitor (somehow)? Just fall back. */
new_factor = old_factor;
} else {
if (window->num_outputs != 0) {
/* Check every display's factor, use the highest */
new_factor = 0.0f;
for (i = 0; i < window->num_outputs; i++) {
SDL_WaylandOutputData *driverdata = window->outputs[i];
new_factor = SDL_max(new_factor, driverdata->scale_factor);
}
} else {
/* No monitor (somehow)? Just fall back. */
new_factor = old_factor;
}
if (!FloatEqual(new_factor, old_factor)) {
Wayland_HandleResize(window->sdlwindow, window->sdlwindow->w, window->sdlwindow->h, new_factor);
window->windowed_scale_factor = new_factor;
Wayland_HandleResize(window->sdlwindow, window->sdlwindow->w, window->sdlwindow->h);
}
}
@ -1576,7 +1547,7 @@ void handle_preferred_scale_changed(void *data,
uint preferred_scale)
{
SDL_WindowData *window = data;
float old_factor = window->scale_factor;
float old_factor = window->windowed_scale_factor;
float new_factor = preferred_scale / 120.; /* 120 is a magic number defined in the spec as a common denominator*/
if (!(window->sdlwindow->flags & SDL_WINDOW_ALLOW_HIGHDPI)) {
@ -1585,7 +1556,8 @@ void handle_preferred_scale_changed(void *data,
}
if (!FloatEqual(new_factor, old_factor)) {
Wayland_HandleResize(window->sdlwindow, window->sdlwindow->w, window->sdlwindow->h, new_factor);
window->windowed_scale_factor = new_factor;
Wayland_HandleResize(window->sdlwindow, window->sdlwindow->w, window->sdlwindow->h);
}
}
@ -1925,13 +1897,13 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window)
data->waylandData = c;
data->sdlwindow = window;
data->scale_factor = 1.0f;
data->windowed_scale_factor = 1.0f;
if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
int i;
for (i = 0; i < SDL_GetVideoDevice()->num_displays; i++) {
float scale = ((SDL_WaylandOutputData *)SDL_GetVideoDevice()->displays[i].driverdata)->scale_factor;
data->scale_factor = SDL_max(data->scale_factor, scale);
data->windowed_scale_factor = SDL_max(data->windowed_scale_factor, scale);
}
}
@ -2033,7 +2005,7 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window)
return 0;
}
static void Wayland_HandleResize(SDL_Window *window, int width, int height, float scale)
static void Wayland_HandleResize(SDL_Window *window, int width, int height)
{
SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
const int old_w = window->w, old_h = window->h;
@ -2043,7 +2015,6 @@ static void Wayland_HandleResize(SDL_Window *window, int width, int height, floa
/* Update the window geometry. */
window->w = width;
window->h = height;
data->scale_factor = scale;
ConfigureWindowGeometry(window);
if (data->needs_resize_event ||

View file

@ -103,7 +103,7 @@ typedef struct
SDL_WaylandOutputData **outputs;
int num_outputs;
float scale_factor;
float windowed_scale_factor;
float pointer_scale_x;
float pointer_scale_y;
int drawable_width, drawable_height;