aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/wlr/types/wlr_scene.h6
-rw-r--r--types/scene/wlr_scene.c111
2 files changed, 107 insertions, 10 deletions
diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h
index bca0b269..1238653d 100644
--- a/include/wlr/types/wlr_scene.h
+++ b/include/wlr/types/wlr_scene.h
@@ -76,6 +76,8 @@ struct wlr_scene_surface {
// private state
+ int prev_width, prev_height;
+
struct wl_listener surface_destroy;
struct wl_listener surface_commit;
};
@@ -201,6 +203,10 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_node *parent);
* Add a node displaying a single surface to the scene-graph.
*
* The child sub-surfaces are ignored.
+ *
+ * wlr_surface_send_enter()/wlr_surface_send_leave() will be called
+ * automatically based on the position of the surface and outputs in
+ * the scene.
*/
struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent,
struct wlr_surface *surface);
diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c
index b1db7b8f..d2f3f4a7 100644
--- a/types/scene/wlr_scene.c
+++ b/types/scene/wlr_scene.c
@@ -39,6 +39,13 @@ static struct wlr_scene_buffer *scene_buffer_from_node(
return (struct wlr_scene_buffer *)node;
}
+static struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) {
+ while (node->parent != NULL) {
+ node = node->parent;
+ }
+ return scene_root_from_node(node);
+}
+
static void scene_node_state_init(struct wlr_scene_node_state *state) {
wl_list_init(&state->children);
wl_list_init(&state->link);
@@ -85,11 +92,11 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
scene_node_damage_whole(node);
scene_node_finish(node);
+ struct wlr_scene *scene = scene_node_get_root(node);
+ struct wlr_scene_output *scene_output;
switch (node->type) {
case WLR_SCENE_NODE_ROOT:;
- struct wlr_scene *scene = scene_root_from_node(node);
-
- struct wlr_scene_output *scene_output, *scene_output_tmp;
+ struct wlr_scene_output *scene_output_tmp;
wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) {
wlr_scene_output_destroy(scene_output);
}
@@ -102,8 +109,16 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
break;
case WLR_SCENE_NODE_SURFACE:;
struct wlr_scene_surface *scene_surface = wlr_scene_surface_from_node(node);
+
+ wl_list_for_each(scene_output, &scene->outputs, link) {
+ // This is a noop if wlr_surface_send_enter() wasn't previously called for
+ // the given output.
+ wlr_surface_send_leave(scene_surface->surface, scene_output->output);
+ }
+
wl_list_remove(&scene_surface->surface_commit.link);
wl_list_remove(&scene_surface->surface_destroy.link);
+
free(scene_surface);
break;
case WLR_SCENE_NODE_RECT:;
@@ -146,11 +161,60 @@ static void scene_surface_handle_surface_destroy(struct wl_listener *listener,
wlr_scene_node_destroy(&scene_surface->node);
}
-static struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) {
- while (node->parent != NULL) {
- node = node->parent;
+// This function must be called whenever the coordinates/dimensions of a scene
+// surface or scene output change. It is not necessary to call when a scene
+// surface's node is enabled/disabled or obscured by other nodes. To quote the
+// protocol: "The surface might be hidden even if no leave event has been sent."
+static void scene_surface_update_outputs(
+ struct wlr_scene_surface *scene_surface,
+ int lx, int ly, struct wlr_scene *scene) {
+ struct wlr_box surface_box = {
+ .x = lx,
+ .y = ly,
+ .width = scene_surface->surface->current.width,
+ .height = scene_surface->surface->current.height,
+ };
+
+ struct wlr_scene_output *scene_output;
+ wl_list_for_each(scene_output, &scene->outputs, link) {
+ struct wlr_box output_box = {
+ .x = scene_output->x,
+ .y = scene_output->y,
+ };
+ wlr_output_effective_resolution(scene_output->output,
+ &output_box.width, &output_box.height);
+
+ // These enter/leave functions are a noop if the event has already been
+ // sent for the given output.
+ struct wlr_box intersection;
+ if (wlr_box_intersection(&intersection, &surface_box, &output_box)) {
+ wlr_surface_send_enter(scene_surface->surface, scene_output->output);
+ } else {
+ wlr_surface_send_leave(scene_surface->surface, scene_output->output);
+ }
+ }
+}
+
+static void scene_node_update_surface_outputs_iterator(
+ struct wlr_scene_node *node, int lx, int ly, struct wlr_scene *scene) {
+ if (node->type == WLR_SCENE_NODE_SURFACE) {
+ struct wlr_scene_surface *scene_surface =
+ wlr_scene_surface_from_node(node);
+ scene_surface_update_outputs(scene_surface, lx, ly, scene);
+ }
+
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &node->state.children, state.link) {
+ scene_node_update_surface_outputs_iterator(child, lx + child->state.x,
+ ly + child->state.y, scene);
}
- return scene_root_from_node(node);
+}
+
+static void scene_node_update_surface_outputs(struct wlr_scene_node *node) {
+ struct wlr_scene *scene = scene_node_get_root(node);
+ int lx, ly;
+ wlr_scene_node_coords(node, &lx, &ly);
+ scene_node_update_surface_outputs_iterator(node, lx, ly, scene);
}
static void scene_surface_handle_surface_commit(struct wl_listener *listener,
@@ -163,12 +227,21 @@ static void scene_surface_handle_surface_commit(struct wl_listener *listener,
return;
}
+ struct wlr_scene *scene = scene_node_get_root(&scene_surface->node);
+
int lx, ly;
- if (!wlr_scene_node_coords(&scene_surface->node, &lx, &ly)) {
- return;
+ bool enabled = wlr_scene_node_coords(&scene_surface->node, &lx, &ly);
+
+ if (surface->current.width != scene_surface->prev_width ||
+ surface->current.height != scene_surface->prev_height) {
+ scene_surface_update_outputs(scene_surface, lx, ly, scene);
+ scene_surface->prev_width = surface->current.width;
+ scene_surface->prev_height = surface->current.height;
}
- struct wlr_scene *scene = scene_node_get_root(&scene_surface->node);
+ if (!enabled) {
+ return;
+ }
struct wlr_scene_output *scene_output;
wl_list_for_each(scene_output, &scene->outputs, link) {
@@ -212,6 +285,8 @@ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent
scene_node_damage_whole(&scene_surface->node);
+ scene_node_update_surface_outputs(&scene_surface->node);
+
return scene_surface;
}
@@ -438,6 +513,8 @@ void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) {
node->state.x = x;
node->state.y = y;
scene_node_damage_whole(node);
+
+ scene_node_update_surface_outputs(node);
}
void wlr_scene_node_place_above(struct wlr_scene_node *node,
@@ -511,6 +588,8 @@ void wlr_scene_node_reparent(struct wlr_scene_node *node,
wl_list_insert(new_parent->state.children.prev, &node->state.link);
scene_node_damage_whole(node);
+
+ scene_node_update_surface_outputs(node);
}
bool wlr_scene_node_coords(struct wlr_scene_node *node,
@@ -822,9 +901,19 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
return scene_output;
}
+static void scene_output_send_leave_iterator(struct wlr_surface *surface,
+ int sx, int sy, void *data) {
+ struct wlr_output *output = data;
+ wlr_surface_send_leave(surface, output);
+}
+
void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
wlr_addon_finish(&scene_output->addon);
wl_list_remove(&scene_output->link);
+
+ wlr_scene_output_for_each_surface(scene_output,
+ scene_output_send_leave_iterator, scene_output->output);
+
free(scene_output);
}
@@ -849,6 +938,8 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output,
scene_output->x = lx;
scene_output->y = ly;
wlr_output_damage_add_whole(scene_output->damage);
+
+ scene_node_update_surface_outputs(&scene_output->scene->node);
}
struct check_scanout_data {