wayland: apply CSD/SSD changes in the surface configure event

The configure event asks the client to change its decoration
    mode. The configured state should not be applied immediately.
    Clients must send an ack_configure in response to this event.
    See xdg_surface.configure and xdg_surface.ack_configure for
    details.

In particular, ”the configured state should *not* be applied
immediately”.

Instead, treat CSD/SSD changes like all other window dimension related
changes: store the to-be mode in win->configure, and apply it in the
surface configure event.

This fixes an issue where foot incorrectly resized the window when the
server switched between CSD/SSD at run-time.
This commit is contained in:
Daniel Eklöf 2021-06-22 18:58:38 +02:00
parent 7632e16e36
commit 2a83202fdd
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
5 changed files with 43 additions and 52 deletions

View file

@ -188,6 +188,7 @@
* Window title not being updated while window is hidden
(https://codeberg.org/dnkl/foot/issues/591).
* Crash on badly formatted URIs in e.g. OSC-8 URLs.
* Window being incorrectly resized on CSD/SSD run-time changes.
### Security

View file

@ -46,7 +46,7 @@ quirk_weston_subsurface_desync_off(struct wl_subsurface *sub)
void
quirk_weston_csd_on(struct terminal *term)
{
if (term->window->use_csd != CSD_YES)
if (term->window->csd_mode != CSD_YES)
return;
if (term->window->is_fullscreen)
return;
@ -58,7 +58,7 @@ quirk_weston_csd_on(struct terminal *term)
void
quirk_weston_csd_off(struct terminal *term)
{
if (term->window->use_csd != CSD_YES)
if (term->window->csd_mode != CSD_YES)
return;
if (term->window->is_fullscreen)
return;

View file

@ -1454,7 +1454,7 @@ struct csd_data {
static struct csd_data
get_csd_data(const struct terminal *term, enum csd_surface surf_idx)
{
xassert(term->window->use_csd == CSD_YES);
xassert(term->window->csd_mode == CSD_YES);
/* Only title bar is rendered in maximized mode */
const int border_width = !term->window->is_maximized
@ -1513,7 +1513,7 @@ render_csd_part(struct terminal *term,
struct wl_surface *surf, struct buffer *buf,
int width, int height, pixman_color_t *color)
{
xassert(term->window->use_csd == CSD_YES);
xassert(term->window->csd_mode == CSD_YES);
pixman_image_t *src = pixman_image_create_solid_fill(color);
@ -1526,7 +1526,7 @@ render_csd_part(struct terminal *term,
static void
render_csd_title(struct terminal *term)
{
xassert(term->window->use_csd == CSD_YES);
xassert(term->window->csd_mode == CSD_YES);
struct csd_data info = get_csd_data(term, CSD_SURF_TITLE);
struct wl_surface *surf = term->window->csd.surface[CSD_SURF_TITLE].surf;
@ -1559,7 +1559,7 @@ render_csd_title(struct terminal *term)
static void
render_csd_border(struct terminal *term, enum csd_surface surf_idx)
{
xassert(term->window->use_csd == CSD_YES);
xassert(term->window->csd_mode == CSD_YES);
xassert(surf_idx >= CSD_SURF_LEFT && surf_idx <= CSD_SURF_BOTTOM);
struct csd_data info = get_csd_data(term, surf_idx);
@ -1745,7 +1745,7 @@ render_csd_button_close(struct terminal *term, struct buffer *buf)
static void
render_csd_button(struct terminal *term, enum csd_surface surf_idx)
{
xassert(term->window->use_csd == CSD_YES);
xassert(term->window->csd_mode == CSD_YES);
xassert(surf_idx >= CSD_SURF_MINIMIZE && surf_idx <= CSD_SURF_CLOSE);
struct csd_data info = get_csd_data(term, surf_idx);
@ -1827,7 +1827,7 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx)
static void
render_csd(struct terminal *term)
{
xassert(term->window->use_csd == CSD_YES);
xassert(term->window->csd_mode == CSD_YES);
if (term->window->is_fullscreen)
return;
@ -3019,7 +3019,7 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
term->grid = term->url_grid_snapshot;
}
if (csd && term->window->use_csd == CSD_YES) {
if (csd && term->window->csd_mode == CSD_YES) {
quirk_weston_csd_on(term);
render_csd(term);
quirk_weston_csd_off(term);
@ -3168,7 +3168,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
width = term->conf->size.width;
height = term->conf->size.height;
if (term->window->use_csd == CSD_YES) {
if (term->window->csd_mode == CSD_YES) {
/* Take CSD title bar into account */
xassert(!term->window->is_fullscreen);
height -= term->conf->csd.title_height;
@ -3338,7 +3338,7 @@ damage_view:
{
bool title_shown = !term->window->is_fullscreen &&
term->window->use_csd == CSD_YES;
term->window->csd_mode == CSD_YES;
int title_height = title_shown ? term->conf->csd.title_height : 0;
xdg_surface_set_window_geometry(
@ -3476,7 +3476,7 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
term->grid = term->url_grid_snapshot;
}
if (csd && term->window->use_csd == CSD_YES) {
if (csd && term->window->csd_mode == CSD_YES) {
quirk_weston_csd_on(term);
render_csd(term);
quirk_weston_csd_off(term);
@ -3549,7 +3549,7 @@ render_refresh(struct terminal *term)
void
render_refresh_csd(struct terminal *term)
{
if (term->window->use_csd == CSD_YES)
if (term->window->csd_mode == CSD_YES)
term->render.refresh.csd = true;
}

View file

@ -597,14 +597,6 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
*/
struct wl_window *win = data;
if (!is_fullscreen && win->use_csd == CSD_YES && width > 0 && height > 0) {
/*
* We include the CSD title bar in our window geometry. Thus,
* the height we call render_resize() with must be adjusted,
* since it expects the size to refer to the main grid only.
*/
height -= win->term->conf->csd.title_height;
}
win->configure.is_activated = is_activated;
win->configure.is_fullscreen = is_fullscreen;
win->configure.is_maximized = is_maximized;
@ -642,23 +634,32 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
bool wasnt_configured = !win->is_configured;
bool was_resizing = win->is_resizing;
bool csd_was_enabled = win->csd_mode == CSD_YES && !win->is_fullscreen;
int new_width = win->configure.width;
int new_height = win->configure.height;
win->is_configured = true;
win->is_maximized = win->configure.is_maximized;
win->is_fullscreen = win->configure.is_fullscreen;
win->is_resizing = win->configure.is_resizing;
win->is_tiled_top = win->configure.is_tiled_top;
win->is_tiled_bottom = win->configure.is_tiled_bottom;
win->is_tiled_left = win->configure.is_tiled_left;
win->is_tiled_right = win->configure.is_tiled_right;
win->is_tiled = win->is_tiled_top || win->is_tiled_bottom || win->is_tiled_left || win->is_tiled_right;
win->is_tiled = (win->is_tiled_top ||
win->is_tiled_bottom ||
win->is_tiled_left ||
win->is_tiled_right);
win->csd_mode = win->configure.csd_mode;
if (win->is_fullscreen != win->configure.is_fullscreen && win->use_csd == CSD_YES) {
if (win->configure.is_fullscreen)
csd_destroy(win);
else
csd_instantiate(win);
}
win->is_fullscreen = win->configure.is_fullscreen;
bool enable_csd = win->csd_mode == CSD_YES && !win->is_fullscreen;
if (!csd_was_enabled && enable_csd)
csd_instantiate(win);
else if (csd_was_enabled && !enable_csd)
csd_destroy(win);
if (enable_csd && new_width > 0 && new_height > 0)
new_height -= win->term->conf->csd.title_height;
xdg_surface_ack_configure(xdg_surface, serial);
@ -713,10 +714,10 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
* the last, forced, resize *is* necessary.
*/
bool resized = was_resizing && !win->is_resizing
? render_resize_force(term, win->configure.width, win->configure.height)
: render_resize(term, win->configure.width, win->configure.height);
? render_resize_force(term, new_width, new_height)
: render_resize(term, new_width, new_height);
#else
bool resized = render_resize(term, win->configure.width, win->configure.height);
bool resized = render_resize(term, new_width, new_height);
#endif
if (win->configure.is_activated)
@ -752,32 +753,18 @@ xdg_toplevel_decoration_configure(void *data,
switch (mode) {
case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE:
LOG_INFO("using CSD decorations");
win->use_csd = CSD_YES;
csd_instantiate(win);
win->configure.csd_mode = CSD_YES;
break;
case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE:
LOG_INFO("using SSD decorations");
win->use_csd = CSD_NO;
csd_destroy(win);
win->configure.csd_mode = CSD_NO;
break;
default:
LOG_ERR("unimplemented: unknown XDG toplevel decoration mode: %u", mode);
break;
}
if (win->is_configured && win->use_csd == CSD_YES) {
struct terminal *term = win->term;
int scale = term->scale;
int width = term->width / scale;
int height = term->height / scale;
/* Take CSD title bar into account */
height -= term->conf->csd.title_height;
render_resize_force(term, width, height);
}
}
static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = {
@ -1345,7 +1332,7 @@ wayl_win_init(struct terminal *term)
}
win->term = term;
win->use_csd = CSD_UNKNOWN;
win->csd_mode = CSD_UNKNOWN;
win->csd.move_timeout_fd = -1;
win->resize_timeout_fd = -1;
@ -1378,7 +1365,7 @@ wayl_win_init(struct terminal *term)
if (conf->csd.preferred == CONF_CSD_PREFER_NONE) {
/* User specifically do *not* want decorations */
win->use_csd = CSD_NO;
win->csd_mode = CSD_NO;
LOG_INFO("window decorations disabled by user");
} else if (wayl->xdg_decoration_manager != NULL) {
win->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
@ -1397,7 +1384,7 @@ wayl_win_init(struct terminal *term)
win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win);
} else {
/* No decoration manager - thus we *must* draw our own decorations */
win->use_csd = CSD_YES;
win->csd_mode = CSD_YES;
csd_instantiate(win);
LOG_WARN("no decoration manager available - using CSDs unconditionally");
}

View file

@ -384,6 +384,8 @@ struct wl_url {
struct wl_surf_subsurf surf;
};
enum csd_mode {CSD_UNKNOWN, CSD_NO, CSD_YES};
struct wayland;
struct wl_window {
struct terminal *term;
@ -396,7 +398,7 @@ struct wl_window {
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration;
enum {CSD_UNKNOWN, CSD_NO, CSD_YES } use_csd;
enum csd_mode csd_mode;
struct {
struct wl_surf_subsurf surface[CSD_SURF_COUNT];
@ -433,6 +435,7 @@ struct wl_window {
bool is_tiled_bottom:1;
bool is_tiled_left:1;
bool is_tiled_right:1;
enum csd_mode csd_mode;
} configure;
int resize_timeout_fd;