Send output enter/leave events correctly

Previously we used a reparent event to detect when a view changes
parent, then sent an output enter/leave to the surfaces if needed. This
worked for tiling views but not floating views, as floating views can
intersect another output without changing parent.

The solution implemented for floating views also applies cleanly to
tiling views, so the previous method has been completely replaced and
the reparent event has been removed.

This introduces a new function container_discover_outputs. This function
compares the container's `current` position to the outputs, sends enter
and leave events as needed, and keeps track of which outputs it's
intersecting in a new `container->outputs` list. If it has entered a new
output with a different scale then the title and marks textures will
also be recreated at the new scale.

The function is called when a transaction applies. This is convenient as
it means we don't have to call it from various places.

There is imperfect rendering when a floating view overlaps two outputs
with different scales. It renders correctly for the most recently
entered output, but there is only one title texture so it renders
incorrectly on the old output.

Fixes #2482
This commit is contained in:
Ryan Dwyer 2018-08-19 11:01:51 +10:00
parent 8db6c68db6
commit 0f6d212629
7 changed files with 93 additions and 101 deletions

View file

@ -138,6 +138,9 @@ struct sway_container {
struct sway_container *parent; struct sway_container *parent;
// Outputs currently being intersected
list_t *outputs; // struct sway_output
// Indicates that the container is a scratchpad container. // Indicates that the container is a scratchpad container.
// Both hidden and visible scratchpad containers have scratchpad=true. // Both hidden and visible scratchpad containers have scratchpad=true.
// Hidden scratchpad containers have a NULL parent. // Hidden scratchpad containers have a NULL parent.
@ -166,12 +169,7 @@ struct sway_container {
struct { struct {
struct wl_signal destroy; struct wl_signal destroy;
// Raised after the tree updates, but before arrange_windows
// Passed the previous parent
struct wl_signal reparent;
} events; } events;
struct wl_listener reparent;
}; };
struct sway_container *container_create(enum sway_container_type type); struct sway_container *container_create(enum sway_container_type type);
@ -353,4 +351,6 @@ bool container_is_floating_or_child(struct sway_container *container);
*/ */
bool container_is_fullscreen_or_child(struct sway_container *container); bool container_is_fullscreen_or_child(struct sway_container *container);
void container_discover_outputs(struct sway_container *con);
#endif #endif

View file

@ -120,7 +120,6 @@ struct sway_view {
} events; } events;
struct wl_listener surface_new_subsurface; struct wl_listener surface_new_subsurface;
struct wl_listener container_reparent;
}; };
struct sway_xdg_shell_v6_view { struct sway_xdg_shell_v6_view {

View file

@ -236,7 +236,6 @@ static void workspace_move_to_output(struct sway_container *workspace,
seat_get_focus_inactive(seat, output); seat_get_focus_inactive(seat, output);
container_add_child(output, workspace); container_add_child(output, workspace);
wl_signal_emit(&workspace->events.reparent, old_output);
// If moving the last workspace from the old output, create a new workspace // If moving the last workspace from the old output, create a new workspace
// on the old output // on the old output

View file

@ -193,6 +193,9 @@ static void transaction_apply(struct sway_transaction *transaction) {
} }
container->instruction = NULL; container->instruction = NULL;
if (container->type == C_CONTAINER || container->type == C_VIEW) {
container_discover_outputs(container);
}
} }
} }

View file

@ -76,31 +76,6 @@ void container_update_textures_recursive(struct sway_container *con) {
} }
} }
static void handle_reparent(struct wl_listener *listener,
void *data) {
struct sway_container *container =
wl_container_of(listener, container, reparent);
struct sway_container *old_parent = data;
struct sway_container *old_output = old_parent;
if (old_output != NULL && old_output->type != C_OUTPUT) {
old_output = container_parent(old_output, C_OUTPUT);
}
struct sway_container *new_output = container->parent;
if (new_output != NULL && new_output->type != C_OUTPUT) {
new_output = container_parent(new_output, C_OUTPUT);
}
if (old_output && new_output) {
float old_scale = old_output->sway_output->wlr_output->scale;
float new_scale = new_output->sway_output->wlr_output->scale;
if (old_scale != new_scale) {
container_update_textures_recursive(container);
}
}
}
struct sway_container *container_create(enum sway_container_type type) { struct sway_container *container_create(enum sway_container_type type) {
// next id starts at 1 because 0 is assigned to root_container in layout.c // next id starts at 1 because 0 is assigned to root_container in layout.c
static size_t next_id = 1; static size_t next_id = 1;
@ -117,12 +92,9 @@ struct sway_container *container_create(enum sway_container_type type) {
c->children = create_list(); c->children = create_list();
c->current.children = create_list(); c->current.children = create_list();
} }
c->outputs = create_list();
wl_signal_init(&c->events.destroy); wl_signal_init(&c->events.destroy);
wl_signal_init(&c->events.reparent);
wl_signal_add(&c->events.reparent, &c->reparent);
c->reparent.notify = handle_reparent;
c->has_gaps = false; c->has_gaps = false;
c->gaps_inner = 0; c->gaps_inner = 0;
@ -156,6 +128,7 @@ void container_free(struct sway_container *cont) {
wlr_texture_destroy(cont->title_urgent); wlr_texture_destroy(cont->title_urgent);
list_free(cont->children); list_free(cont->children);
list_free(cont->current.children); list_free(cont->current.children);
list_free(cont->outputs);
switch (cont->type) { switch (cont->type) {
case C_ROOT: case C_ROOT:
@ -777,13 +750,25 @@ void container_damage_whole(struct sway_container *container) {
} }
} }
/**
* Return the output which will be used for scale purposes.
* This is the most recently entered output.
*/
static struct sway_output *container_get_effective_output(
struct sway_container *con) {
if (con->outputs->length == 0) {
return NULL;
}
return con->outputs->items[con->outputs->length - 1];
}
static void update_title_texture(struct sway_container *con, static void update_title_texture(struct sway_container *con,
struct wlr_texture **texture, struct border_colors *class) { struct wlr_texture **texture, struct border_colors *class) {
if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW,
"Unexpected type %s", container_type_to_str(con->type))) { "Unexpected type %s", container_type_to_str(con->type))) {
return; return;
} }
struct sway_container *output = container_parent(con, C_OUTPUT); struct sway_output *output = container_get_effective_output(con);
if (!output) { if (!output) {
return; return;
} }
@ -795,7 +780,7 @@ static void update_title_texture(struct sway_container *con,
return; return;
} }
double scale = output->sway_output->wlr_output->scale; double scale = output->wlr_output->scale;
int width = 0; int width = 0;
int height = con->title_height * scale; int height = con->title_height * scale;
@ -823,7 +808,7 @@ static void update_title_texture(struct sway_container *con,
unsigned char *data = cairo_image_surface_get_data(surface); unsigned char *data = cairo_image_surface_get_data(surface);
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
struct wlr_renderer *renderer = wlr_backend_get_renderer( struct wlr_renderer *renderer = wlr_backend_get_renderer(
output->sway_output->wlr_output->backend); output->wlr_output->backend);
*texture = wlr_texture_from_pixels( *texture = wlr_texture_from_pixels(
renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
cairo_surface_destroy(surface); cairo_surface_destroy(surface);
@ -1272,3 +1257,67 @@ bool container_is_fullscreen_or_child(struct sway_container *container) {
return false; return false;
} }
static void surface_send_enter_iterator(struct wlr_surface *surface,
int x, int y, void *data) {
struct wlr_output *wlr_output = data;
wlr_surface_send_enter(surface, wlr_output);
}
static void surface_send_leave_iterator(struct wlr_surface *surface,
int x, int y, void *data) {
struct wlr_output *wlr_output = data;
wlr_surface_send_leave(surface, wlr_output);
}
void container_discover_outputs(struct sway_container *con) {
if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW,
"Expected a container or view")) {
return;
}
struct wlr_box con_box = {
.x = con->current.swayc_x,
.y = con->current.swayc_y,
.width = con->current.swayc_width,
.height = con->current.swayc_height,
};
struct sway_output *old_output = container_get_effective_output(con);
for (int i = 0; i < root_container.children->length; ++i) {
struct sway_container *output = root_container.children->items[i];
struct sway_output *sway_output = output->sway_output;
struct wlr_box output_box;
container_get_box(output, &output_box);
struct wlr_box intersection;
bool intersects =
wlr_box_intersection(&con_box, &output_box, &intersection);
int index = list_find(con->outputs, sway_output);
if (intersects && index == -1) {
// Send enter
wlr_log(WLR_DEBUG, "Con %p entered output %p", con, sway_output);
if (con->type == C_VIEW) {
view_for_each_surface(con->sway_view,
surface_send_enter_iterator, sway_output->wlr_output);
}
list_add(con->outputs, sway_output);
} else if (!intersects && index != -1) {
// Send leave
wlr_log(WLR_DEBUG, "Con %p left output %p", con, sway_output);
if (con->type == C_VIEW) {
view_for_each_surface(con->sway_view,
surface_send_leave_iterator, sway_output->wlr_output);
}
list_del(con->outputs, index);
}
}
struct sway_output *new_output = container_get_effective_output(con);
double old_scale = old_output ? old_output->wlr_output->scale : -1;
double new_scale = new_output ? new_output->wlr_output->scale : -1;
if (old_scale != new_scale) {
container_update_title_textures(con);
if (con->type == C_VIEW) {
view_update_marks_textures(con->sway_view);
}
}
}

View file

@ -75,7 +75,6 @@ void container_insert_child(struct sway_container *parent,
list_insert(parent->children, i, child); list_insert(parent->children, i, child);
child->parent = parent; child->parent = parent;
container_handle_fullscreen_reparent(child, old_parent); container_handle_fullscreen_reparent(child, old_parent);
wl_signal_emit(&child->events.reparent, old_parent);
} }
struct sway_container *container_add_sibling(struct sway_container *fixed, struct sway_container *container_add_sibling(struct sway_container *fixed,
@ -91,7 +90,6 @@ struct sway_container *container_add_sibling(struct sway_container *fixed,
list_insert(parent->children, i + 1, active); list_insert(parent->children, i + 1, active);
active->parent = parent; active->parent = parent;
container_handle_fullscreen_reparent(active, old_parent); container_handle_fullscreen_reparent(active, old_parent);
wl_signal_emit(&active->events.reparent, old_parent);
return active->parent; return active->parent;
} }
@ -181,8 +179,6 @@ void container_move_to(struct sway_container *container,
} }
} }
wl_signal_emit(&container->events.reparent, old_parent);
if (container->type == C_VIEW) { if (container->type == C_VIEW) {
ipc_event_window(container, "move"); ipc_event_window(container, "move");
} }
@ -307,7 +303,6 @@ static void workspace_rejigger(struct sway_container *ws,
container_flatten(ws); container_flatten(ws);
container_reap_empty_recursive(original_parent); container_reap_empty_recursive(original_parent);
wl_signal_emit(&child->events.reparent, original_parent);
container_create_notify(new_parent); container_create_notify(new_parent);
} }
@ -859,7 +854,6 @@ struct sway_container *container_split(struct sway_container *child,
struct sway_container *ws_child = workspace->children->items[0]; struct sway_container *ws_child = workspace->children->items[0];
container_remove_child(ws_child); container_remove_child(ws_child);
container_add_child(cont, ws_child); container_add_child(cont, ws_child);
wl_signal_emit(&ws_child->events.reparent, workspace);
} }
container_add_child(workspace, cont); container_add_child(workspace, cont);
@ -867,11 +861,9 @@ struct sway_container *container_split(struct sway_container *child,
workspace->layout = layout; workspace->layout = layout;
cont->layout = old_layout; cont->layout = old_layout;
} else { } else {
struct sway_container *old_parent = child->parent;
cont->layout = layout; cont->layout = layout;
container_replace_child(child, cont); container_replace_child(child, cont);
container_add_child(cont, child); container_add_child(cont, child);
wl_signal_emit(&child->events.reparent, old_parent);
} }
if (set_focus) { if (set_focus) {

View file

@ -364,48 +364,6 @@ static void view_handle_surface_new_subsurface(struct wl_listener *listener,
view_subsurface_create(view, subsurface); view_subsurface_create(view, subsurface);
} }
static void surface_send_enter_iterator(struct wlr_surface *surface,
int x, int y, void *data) {
struct wlr_output *wlr_output = data;
wlr_surface_send_enter(surface, wlr_output);
}
static void surface_send_leave_iterator(struct wlr_surface *surface,
int x, int y, void *data) {
struct wlr_output *wlr_output = data;
wlr_surface_send_leave(surface, wlr_output);
}
static void view_handle_container_reparent(struct wl_listener *listener,
void *data) {
struct sway_view *view =
wl_container_of(listener, view, container_reparent);
struct sway_container *old_parent = data;
struct sway_container *old_output = old_parent;
if (old_output != NULL && old_output->type != C_OUTPUT) {
old_output = container_parent(old_output, C_OUTPUT);
}
struct sway_container *new_output = view->swayc->parent;
if (new_output != NULL && new_output->type != C_OUTPUT) {
new_output = container_parent(new_output, C_OUTPUT);
}
if (old_output == new_output) {
return;
}
if (old_output != NULL) {
view_for_each_surface(view, surface_send_leave_iterator,
old_output->sway_output->wlr_output);
}
if (new_output != NULL) {
view_for_each_surface(view, surface_send_enter_iterator,
new_output->sway_output->wlr_output);
}
}
static bool view_has_executed_criteria(struct sway_view *view, static bool view_has_executed_criteria(struct sway_view *view,
struct criteria *criteria) { struct criteria *criteria) {
for (int i = 0; i < view->executed_criteria->length; ++i) { for (int i = 0; i < view->executed_criteria->length; ++i) {
@ -567,9 +525,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
&view->surface_new_subsurface); &view->surface_new_subsurface);
view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
wl_signal_add(&view->swayc->events.reparent, &view->container_reparent);
view->container_reparent.notify = view_handle_container_reparent;
if (view->impl->wants_floating && view->impl->wants_floating(view)) { if (view->impl->wants_floating && view->impl->wants_floating(view)) {
view->border = config->floating_border; view->border = config->floating_border;
view->border_thickness = config->floating_border_thickness; view->border_thickness = config->floating_border_thickness;
@ -587,15 +542,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
view_update_title(view, false); view_update_title(view, false);
container_notify_subtree_changed(view->swayc->parent); container_notify_subtree_changed(view->swayc->parent);
view_execute_criteria(view); view_execute_criteria(view);
view_handle_container_reparent(&view->container_reparent, NULL);
} }
void view_unmap(struct sway_view *view) { void view_unmap(struct sway_view *view) {
wl_signal_emit(&view->events.unmap, view); wl_signal_emit(&view->events.unmap, view);
wl_list_remove(&view->surface_new_subsurface.link); wl_list_remove(&view->surface_new_subsurface.link);
wl_list_remove(&view->container_reparent.link);
if (view->urgent_timer) { if (view->urgent_timer) {
wl_event_source_remove(view->urgent_timer); wl_event_source_remove(view->urgent_timer);
@ -937,10 +889,8 @@ void view_add_mark(struct sway_view *view, char *mark) {
static void update_marks_texture(struct sway_view *view, static void update_marks_texture(struct sway_view *view,
struct wlr_texture **texture, struct border_colors *class) { struct wlr_texture **texture, struct border_colors *class) {
struct sway_container *output = container_parent(view->swayc, C_OUTPUT); struct sway_output *output =
if (!output) { view->swayc->outputs->items[view->swayc->outputs->length - 1];
return;
}
if (*texture) { if (*texture) {
wlr_texture_destroy(*texture); wlr_texture_destroy(*texture);
*texture = NULL; *texture = NULL;
@ -973,7 +923,7 @@ static void update_marks_texture(struct sway_view *view,
} }
free(part); free(part);
double scale = output->sway_output->wlr_output->scale; double scale = output->wlr_output->scale;
int width = 0; int width = 0;
int height = view->swayc->title_height * scale; int height = view->swayc->title_height * scale;
@ -999,7 +949,7 @@ static void update_marks_texture(struct sway_view *view,
unsigned char *data = cairo_image_surface_get_data(surface); unsigned char *data = cairo_image_surface_get_data(surface);
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
struct wlr_renderer *renderer = wlr_backend_get_renderer( struct wlr_renderer *renderer = wlr_backend_get_renderer(
output->sway_output->wlr_output->backend); output->wlr_output->backend);
*texture = wlr_texture_from_pixels( *texture = wlr_texture_from_pixels(
renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
cairo_surface_destroy(surface); cairo_surface_destroy(surface);