Fix glitches resizing windows on Wayland, LIBDECOR & fractional scaling

When fractional scaling is used, window sizes from LIBDECOR are still
divided by the window-decorations internal buffer scale which doesn't
match the GHOST window (always 1 in the case of fractional scale).

Also resolve glitches caused by recalculating the window size when
no size change was intended. In this case reuse LIBDECOR's internal
size so there are never any changes (caused by rounding for e.g.).
This commit is contained in:
Campbell Barton
2023-10-18 14:37:11 +11:00
parent 3ad21b2169
commit bafb36f8bc
2 changed files with 58 additions and 15 deletions

View File

@@ -1141,25 +1141,57 @@ static void libdecor_frame_handle_configure(libdecor_frame *frame,
GWL_WindowFrame *frame_pending = &static_cast<GWL_Window *>(data)->frame_pending;
/* Set the size. */
int size_next[2];
int size_decor[2]{
libdecor_frame_get_content_width(frame),
libdecor_frame_get_content_height(frame),
};
int size_next[2] = {0, 0};
bool has_size = false;
{
GWL_Window *win = static_cast<GWL_Window *>(data);
const int scale = win->frame.buffer_scale;
if (!libdecor_configuration_get_content_size(
const int fractional_scale = win->frame.fractional_scale;
/* The size from LIBDECOR wont use the GHOST windows buffer size.
* so it's important to calculate the buffer size that would have been used
* if fractional scaling wasn't supported. */
const int scale = fractional_scale ? (fractional_scale / FRACTIONAL_DENOMINATOR) + 1 :
win->frame.buffer_scale;
const int scale_as_fractional = scale * FRACTIONAL_DENOMINATOR;
if (libdecor_configuration_get_content_size(
configuration, frame, &size_next[0], &size_next[1])) {
size_next[0] = win->frame.size[0] / scale;
size_next[1] = win->frame.size[1] / scale;
}
if (win->frame.fractional_scale) {
win->frame_pending.size[0] = gwl_window_fractional_to_viewport_round(win->frame,
size_next[0]);
win->frame_pending.size[1] = gwl_window_fractional_to_viewport_round(win->frame,
size_next[1]);
}
else {
frame_pending->size[0] = size_next[0] * scale;
frame_pending->size[1] = size_next[1] * scale;
}
if (win->frame.fractional_scale) {
win->frame_pending.size[0] = gwl_window_fractional_to_viewport_round(win->frame,
size_next[0]);
win->frame_pending.size[1] = gwl_window_fractional_to_viewport_round(win->frame,
size_next[1]);
/* Account for buffer rounding requirement, once fractional scaling is enabled
* the buffer scale will be 1, rounding is a requirement until then. */
gwl_round_int2_by(frame_pending->size, win->frame.buffer_scale);
has_size = true;
}
else {
frame_pending->size[0] = size_next[0] * scale;
frame_pending->size[1] = size_next[1] * scale;
/* The window decorations may be zero on startup. */
if (UNLIKELY(size_decor[0] == 0 || size_decor[1] == 0)) {
if (fractional_scale != 0 && (scale_as_fractional != fractional_scale)) {
/* Invert the fractional part: (1.25 -> 1.75), (2.75 -> 2.25), (1.5 -> 1.5).
* Needed to properly set the initial window size. */
const int scale_fractional_part_inv = scale_as_fractional -
(FRACTIONAL_DENOMINATOR -
(scale_as_fractional - fractional_scale));
size_decor[0] = ((win->frame.size[0] / scale_as_fractional) * scale_fractional_part_inv);
size_decor[1] = ((win->frame.size[1] / scale_as_fractional) * scale_fractional_part_inv);
}
else {
size_decor[0] = win->frame.size[0] / scale;
size_decor[1] = win->frame.size[1] / scale;
}
}
}
# ifdef USE_EVENT_BACKGROUND_THREAD
@@ -1168,8 +1200,7 @@ static void libdecor_frame_handle_configure(libdecor_frame *frame,
* (which uses a deferred update) and the window get noticeably out of sync.
* Rely on the new `frame_pending->size` to resize the window later. */
if (is_main_thread == false) {
size_next[0] = win->frame.size[0] / scale;
size_next[1] = win->frame.size[1] / scale;
has_size = false;
}
# endif
}
@@ -1202,6 +1233,10 @@ static void libdecor_frame_handle_configure(libdecor_frame *frame,
{
GWL_Window *win = static_cast<GWL_Window *>(data);
WGL_LibDecor_Window &decor = *win->libdecor;
if (has_size == false) {
size_next[0] = size_decor[0];
size_next[1] = size_decor[1];
}
libdecor_state *state = libdecor_state_new(UNPACK2(size_next));
libdecor_frame_commit(frame, state, configuration);
libdecor_state_free(state);

View File

@@ -21,6 +21,8 @@ WAYLAND_DYNLOAD_FN(libdecor_configuration_get_window_state)
WAYLAND_DYNLOAD_FN(libdecor_decorate)
WAYLAND_DYNLOAD_FN(libdecor_dispatch)
WAYLAND_DYNLOAD_FN(libdecor_frame_commit)
WAYLAND_DYNLOAD_FN(libdecor_frame_get_content_height)
WAYLAND_DYNLOAD_FN(libdecor_frame_get_content_width)
WAYLAND_DYNLOAD_FN(libdecor_frame_get_xdg_toplevel)
WAYLAND_DYNLOAD_FN(libdecor_frame_map)
WAYLAND_DYNLOAD_FN(libdecor_frame_set_app_id)
@@ -76,6 +78,8 @@ struct WaylandDynload_Libdecor {
void WL_DYN_FN(libdecor_frame_commit)(struct libdecor_frame *frame,
struct libdecor_state *state,
struct libdecor_configuration *configuration);
int WL_DYN_FN(libdecor_frame_get_content_width)(struct libdecor_frame *frame);
int WL_DYN_FN(libdecor_frame_get_content_height)(struct libdecor_frame *frame);
struct xdg_toplevel *WL_DYN_FN(libdecor_frame_get_xdg_toplevel)(struct libdecor_frame *frame);
void WL_DYN_FN(libdecor_frame_map)(struct libdecor_frame *frame);
void WL_DYN_FN(libdecor_frame_set_app_id)(struct libdecor_frame *frame, const char *app_id);
@@ -112,6 +116,10 @@ struct WaylandDynload_Libdecor {
# define libdecor_dispatch(...) (*wayland_dynload_libdecor.libdecor_dispatch)(__VA_ARGS__)
# define libdecor_frame_commit(...) \
(*wayland_dynload_libdecor.libdecor_frame_commit)(__VA_ARGS__)
# define libdecor_frame_get_content_height(...) \
(*wayland_dynload_libdecor.libdecor_frame_get_content_height)(__VA_ARGS__)
# define libdecor_frame_get_content_width(...) \
(*wayland_dynload_libdecor.libdecor_frame_get_content_width)(__VA_ARGS__)
# define libdecor_frame_get_xdg_toplevel(...) \
(*wayland_dynload_libdecor.libdecor_frame_get_xdg_toplevel)(__VA_ARGS__)
# define libdecor_frame_map(...) (*wayland_dynload_libdecor.libdecor_frame_map)(__VA_ARGS__)