mirror of
https://github.com/swaywm/sway.git
synced 2024-11-24 00:41:28 +00:00
tree/view: Send estimated configure before mapping
Sending 0,0 as configure dimensions indicate that the client is free to pick its own dimensions. When tiling, the client needs to strictly adhere to the tile dimensions. Sway's handling of this has been to send a the appropriate dimensions in a new configure when the surface is mapped, leading to the first buffer most likely being incorrectly sized. Introduce a view_premap which goes through part of the mapping dance, calculating what the container dimensions would be at this time and sending it as a configure. This is only an estimate and inherently racey if the user very quickly changes focused workspace between surface role commit and first attached buffer, but when it works the client has a chance of getting the first buffer right, when it doesn't it is no worse than it would have been otherwise. Fixes: https://github.com/swaywm/sway/issues/2176
This commit is contained in:
parent
7288f77bbe
commit
b23e670824
|
@ -7,6 +7,7 @@ struct sway_container;
|
||||||
struct sway_node;
|
struct sway_node;
|
||||||
|
|
||||||
void arrange_container(struct sway_container *container);
|
void arrange_container(struct sway_container *container);
|
||||||
|
void next_sibling_container_geometry(struct sway_container *child, struct sway_container *sibling, bool fullscreen);
|
||||||
|
|
||||||
void arrange_workspace(struct sway_workspace *workspace);
|
void arrange_workspace(struct sway_workspace *workspace);
|
||||||
|
|
||||||
|
|
|
@ -297,6 +297,21 @@ void view_begin_destroy(struct sway_view *view);
|
||||||
void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
|
void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
|
||||||
bool fullscreen, struct wlr_output *fullscreen_output, bool decoration);
|
bool fullscreen, struct wlr_output *fullscreen_output, bool decoration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the view for its upcoming mapping, sending the intended dimensions
|
||||||
|
* so that the first frame has a chance of being correct. If CSD preferences or
|
||||||
|
* floating tendency changes, this may turn out to be inaccurate but no worse
|
||||||
|
* than skipping the step.
|
||||||
|
*
|
||||||
|
* `fullscreen` should be set to true (and optionally `fullscreen_output`
|
||||||
|
* should be populated) if the view should be made fullscreen immediately.
|
||||||
|
*
|
||||||
|
* `decoration` should be set to true if the client prefers CSD. The client's
|
||||||
|
* preference may be ignored.
|
||||||
|
*/
|
||||||
|
void view_premap(struct sway_view *view, struct wlr_surface *wlr_surface,
|
||||||
|
bool fullscreen, struct wlr_output *fullscreen_output, bool decoration);
|
||||||
|
|
||||||
void view_unmap(struct sway_view *view);
|
void view_unmap(struct sway_view *view);
|
||||||
|
|
||||||
void view_update_size(struct sway_view *view);
|
void view_update_size(struct sway_view *view);
|
||||||
|
|
|
@ -287,10 +287,23 @@ static void handle_commit(struct wl_listener *listener, void *data) {
|
||||||
if (view->xdg_decoration != NULL) {
|
if (view->xdg_decoration != NULL) {
|
||||||
set_xdg_decoration_mode(view->xdg_decoration);
|
set_xdg_decoration_mode(view->xdg_decoration);
|
||||||
}
|
}
|
||||||
// XXX: https://github.com/swaywm/sway/issues/2176
|
|
||||||
|
bool csd = false;
|
||||||
|
if (view->xdg_decoration) {
|
||||||
|
enum wlr_xdg_toplevel_decoration_v1_mode mode =
|
||||||
|
view->xdg_decoration->wlr_xdg_decoration->requested_mode;
|
||||||
|
csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
|
||||||
|
} else {
|
||||||
|
struct sway_server_decoration *deco =
|
||||||
|
decoration_from_surface(xdg_surface->surface);
|
||||||
|
csd = !deco || deco->wlr_server_decoration->mode ==
|
||||||
|
WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
|
||||||
|
}
|
||||||
|
|
||||||
wlr_xdg_surface_schedule_configure(xdg_surface);
|
wlr_xdg_surface_schedule_configure(xdg_surface);
|
||||||
wlr_xdg_toplevel_set_wm_capabilities(view->wlr_xdg_toplevel,
|
wlr_xdg_toplevel_set_wm_capabilities(view->wlr_xdg_toplevel,
|
||||||
XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
|
XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
|
||||||
|
view_premap(&xdg_shell_view->view, xdg_surface->surface, false, NULL, csd);
|
||||||
// TODO: wlr_xdg_toplevel_set_bounds()
|
// TODO: wlr_xdg_toplevel_set_bounds()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -249,6 +250,87 @@ void arrange_container(struct sway_container *container) {
|
||||||
node_set_dirty(&container->node);
|
node_set_dirty(&container->node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void next_sibling_container_geometry(struct sway_container *child, struct sway_container *sibling, bool fullscreen) {
|
||||||
|
assert(child->view);
|
||||||
|
struct sway_workspace *workspace = child->pending.workspace;
|
||||||
|
if (!workspace->output || workspace->width == 0 || workspace->height == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (workspace->fullscreen && !fullscreen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sway_output *output = workspace->output;
|
||||||
|
if (fullscreen) {
|
||||||
|
child->pending.x = output->lx;
|
||||||
|
child->pending.y = output->ly;
|
||||||
|
child->pending.width = output->width;
|
||||||
|
child->pending.height = output->height;
|
||||||
|
view_autoconfigure(child->view);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_t *siblings;
|
||||||
|
struct wlr_box box;
|
||||||
|
enum sway_container_layout layout;
|
||||||
|
|
||||||
|
if (sibling && sibling->pending.parent) {
|
||||||
|
struct sway_container *parent = sibling->pending.parent;
|
||||||
|
siblings = parent->pending.children;
|
||||||
|
layout = parent->pending.layout;
|
||||||
|
workspace_get_box(workspace, &box);
|
||||||
|
} else {
|
||||||
|
siblings = workspace->tiling;
|
||||||
|
layout = workspace->layout;
|
||||||
|
workspace_get_box(workspace, &box);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only want to mutate the specified child, not its siblings. Make a
|
||||||
|
// shallow cloned list of siblings and containers so that their updated
|
||||||
|
// geometry can be thrown away.
|
||||||
|
list_t *children = create_list();
|
||||||
|
if (!children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct sway_container *cons = calloc(siblings->length-1, sizeof(*cons));
|
||||||
|
if (!cons) {
|
||||||
|
list_free(children);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int idx = 0, ydx = 0; idx < siblings->length; idx++, ydx++) {
|
||||||
|
if (siblings->items[idx] == child) {
|
||||||
|
list_add(children, child);
|
||||||
|
ydx--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cons[ydx] = *(struct sway_container *)siblings->items[idx];
|
||||||
|
list_add(children, &cons[ydx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the geometry
|
||||||
|
switch (layout) {
|
||||||
|
case L_HORIZ:
|
||||||
|
apply_horiz_layout(children, &box);
|
||||||
|
break;
|
||||||
|
case L_VERT:
|
||||||
|
apply_vert_layout(children, &box);
|
||||||
|
break;
|
||||||
|
case L_TABBED:
|
||||||
|
apply_tabbed_layout(children, &box);
|
||||||
|
break;
|
||||||
|
case L_STACKED:
|
||||||
|
apply_stacked_layout(children, &box);
|
||||||
|
break;
|
||||||
|
case L_NONE:
|
||||||
|
apply_horiz_layout(children, &box);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
view_autoconfigure(child->view);
|
||||||
|
list_free(children);
|
||||||
|
free(cons);
|
||||||
|
}
|
||||||
|
|
||||||
void arrange_workspace(struct sway_workspace *workspace) {
|
void arrange_workspace(struct sway_workspace *workspace) {
|
||||||
if (config->reloading) {
|
if (config->reloading) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -714,6 +714,100 @@ static void handle_foreign_destroy(
|
||||||
wl_list_remove(&view->foreign_destroy.link);
|
wl_list_remove(&view->foreign_destroy.link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void view_premap(struct sway_view *view, struct wlr_surface *wlr_surface,
|
||||||
|
bool fullscreen, struct wlr_output *fullscreen_output,
|
||||||
|
bool decoration) {
|
||||||
|
|
||||||
|
// If there is a request to be opened fullscreen on a specific output, try
|
||||||
|
// to honor that request. Otherwise, fallback to assigns, pid mappings,
|
||||||
|
// focused workspace, etc
|
||||||
|
struct sway_workspace *ws = NULL;
|
||||||
|
if (fullscreen_output && fullscreen_output->data) {
|
||||||
|
struct sway_output *output = fullscreen_output->data;
|
||||||
|
ws = output_get_active_workspace(output);
|
||||||
|
}
|
||||||
|
if (!ws) {
|
||||||
|
ws = select_workspace(view);
|
||||||
|
}
|
||||||
|
if (!ws || !ws->output) {
|
||||||
|
// Nothing for us to do if we don't have a workspace on an output
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the output is determined, we can notify the client early about
|
||||||
|
// scale to reduce startup jitter.
|
||||||
|
float scale = ws->output->wlr_output->scale;
|
||||||
|
wlr_fractional_scale_v1_notify_scale(wlr_surface, scale);
|
||||||
|
wlr_surface_set_preferred_buffer_scale(wlr_surface, ceil(scale));
|
||||||
|
|
||||||
|
if (view->impl->wants_floating && view->impl->wants_floating(view)) {
|
||||||
|
// Nothing more to do for floating, let it pick its own dimensions
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sway_seat *seat = input_manager_current_seat();
|
||||||
|
struct sway_node *node = seat_get_focus_inactive(seat, &ws->node);
|
||||||
|
struct sway_container *target_sibling = NULL;
|
||||||
|
if (node && node->type == N_CONTAINER) {
|
||||||
|
if (container_is_floating(node->sway_container)) {
|
||||||
|
// If we're about to launch the view into the floating container, then
|
||||||
|
// launch it as a tiled view instead.
|
||||||
|
target_sibling = seat_get_focus_inactive_tiling(seat, ws);
|
||||||
|
if (target_sibling) {
|
||||||
|
struct sway_container *con =
|
||||||
|
seat_get_focus_inactive_view(seat, &target_sibling->node);
|
||||||
|
if (con) {
|
||||||
|
target_sibling = con;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target_sibling = node->sway_container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill out enough of a dummy container to satisfy configuration
|
||||||
|
struct sway_container con = {
|
||||||
|
.view = view,
|
||||||
|
.pending = (struct sway_container_state) {
|
||||||
|
.workspace = ws,
|
||||||
|
.parent = target_sibling ? target_sibling->pending.parent : NULL,
|
||||||
|
.border = config->border,
|
||||||
|
.border_thickness = config->border_thickness,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
view->container = &con;
|
||||||
|
|
||||||
|
// Insert the container into the appropriate children list so that smart
|
||||||
|
// gaps will work correctly
|
||||||
|
list_t *siblings;
|
||||||
|
int sibling_index;
|
||||||
|
if (target_sibling && target_sibling->pending.parent) {
|
||||||
|
struct sway_container *parent = target_sibling->pending.parent;
|
||||||
|
siblings = parent->pending.children;
|
||||||
|
sibling_index = list_find(siblings, target_sibling) + 1;
|
||||||
|
} else {
|
||||||
|
siblings = ws->tiling;
|
||||||
|
sibling_index = ws->tiling->length;
|
||||||
|
}
|
||||||
|
list_insert(siblings, sibling_index, &con);
|
||||||
|
|
||||||
|
view_set_tiled(view, true);
|
||||||
|
view_update_csd_from_client(view, decoration);
|
||||||
|
next_sibling_container_geometry(&con, target_sibling, fullscreen);
|
||||||
|
|
||||||
|
// Send the configure event for the calculated dimensions
|
||||||
|
view_configure(view,
|
||||||
|
con.pending.content_x,
|
||||||
|
con.pending.content_y,
|
||||||
|
con.pending.content_width,
|
||||||
|
con.pending.content_height);
|
||||||
|
|
||||||
|
sway_assert(siblings->items[sibling_index] == &con,
|
||||||
|
"container siblings mutated unexpectedly");
|
||||||
|
list_del(siblings, sibling_index);
|
||||||
|
view->container = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
|
void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
|
||||||
bool fullscreen, struct wlr_output *fullscreen_output,
|
bool fullscreen, struct wlr_output *fullscreen_output,
|
||||||
bool decoration) {
|
bool decoration) {
|
||||||
|
|
Loading…
Reference in a new issue