diff --git a/layer_shell_v1.c b/layer_shell_v1.c index c66ef05..9843df4 100644 --- a/layer_shell_v1.c +++ b/layer_shell_v1.c @@ -9,10 +9,120 @@ #include "layer_shell_v1.h" #include "server.h" +#include #include #include +#include #include +static void +handle_output_destroy(struct wl_listener *listener, void *data) +{ + struct cg_layer_surface *cg_layer = wl_container_of(listener, cg_layer, output_destroy); + + wl_list_remove(&cg_layer->output_destroy.link); + wl_list_remove(&cg_layer->link); + wl_list_init(&cg_layer->link); + + cg_layer->layer_surface->output = NULL; + wlr_layer_surface_v1_destroy(cg_layer->layer_surface); +} + +static void +unmap(struct cg_layer_surface *cg_layer) +{ + struct wlr_output *wlr_output = cg_layer->layer_surface->output; + if (!wlr_output) { + return; + } + + struct cg_output *output = wlr_output->data; + if (!output) { + return; + } + + wlr_scene_node_destroy(cg_layer->scene_node); +} + +static void +handle_destroy(struct wl_listener *listener, void *data) +{ + struct cg_layer_surface *cg_layer = wl_container_of(listener, cg_layer, destroy); + + wlr_log(WLR_DEBUG, "Layer surface destroyed (%s)", cg_layer->layer_surface->namespace); + + if (cg_layer->layer_surface->mapped) { + unmap(cg_layer); + } + + wl_list_remove(&cg_layer->link); + wl_list_remove(&cg_layer->map.link); + wl_list_remove(&cg_layer->unmap.link); + wl_list_remove(&cg_layer->surface_commit.link); + wl_list_remove(&cg_layer->destroy.link); + + if (cg_layer->layer_surface->output) { + wl_list_remove(&cg_layer->output_destroy.link); + cg_layer->layer_surface->output = NULL; + } + + free(cg_layer); +} + +static void +handle_surface_commit(struct wl_listener *listener, void *data) +{ + struct cg_layer_surface *layer = wl_container_of(listener, layer, surface_commit); + struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; + struct wlr_output *wlr_output = layer_surface->output; + + if (!wlr_output) { + return; + } + + struct cg_output *output = wlr_output->data; + struct wlr_box old_geometry = layer->geometry; + + bool geometry_changed = memcmp(&old_geometry, &layer->geometry, sizeof(struct wlr_box)) != 0; + + bool layer_changed = false; + if (layer_surface->current.committed != 0) { + layer_changed = layer->layer != layer_surface->current.layer; + if (layer_changed) { + wl_list_remove(&layer->link); + wl_list_insert(&output->layers[layer_surface->current.layer], &layer->link); + layer->layer = layer_surface->current.layer; + } + } + + if (geometry_changed || layer_changed) { + // wlr_scene_node_set_position(layer->scene_node, layer->geometry.x, layer->geometry.y); + } +} + +static void +handle_unmap(struct wl_listener *listener, void *data) +{ + struct cg_layer_surface *cg_layer = wl_container_of(listener, cg_layer, unmap); + unmap(cg_layer); +} + +static void +handle_map(struct wl_listener *listener, void *data) +{ + struct cg_layer_surface *cg_layer = wl_container_of(listener, cg_layer, map); + struct wlr_surface *wlr_surface = cg_layer->layer_surface->surface; + + cg_layer->scene_node = wlr_scene_subsurface_tree_create(&cg_layer->server->scene->node, wlr_surface); + if (!cg_layer->scene_node) { + wl_resource_post_no_memory(wlr_surface->resource); + return; + } + cg_layer->scene_node->data = cg_layer; + + wlr_surface_send_enter(cg_layer->layer_surface->surface, cg_layer->layer_surface->output); +} + void handle_layer_shell_v1_surface_new(struct wl_listener *listener, void *data) { @@ -24,4 +134,38 @@ handle_layer_shell_v1_surface_new(struct wl_listener *listener, void *data) layer_surface->pending.desired_width, layer_surface->pending.desired_height, layer_surface->pending.margin.top, layer_surface->pending.margin.right, layer_surface->pending.margin.bottom, layer_surface->pending.margin.left); + + /* If the layer surface doesn't specify an output, we assign the first output. */ + // TODO: make this the output the user last interacted with, or the one that + // currently has input focus. + if (!layer_surface->output) { + struct cg_output *output = wl_container_of(server->outputs.prev, output, link); + layer_surface->output = output->wlr_output; + } + + struct cg_layer_surface *cg_layer = calloc(1, sizeof(struct cg_layer_surface)); + if (!cg_layer) { + wlr_log(WLR_ERROR, "Failed to allocate layer shell"); + return; + } + + cg_layer->server = server; + cg_layer->layer_surface = layer_surface; + layer_surface->data = cg_layer; + + cg_layer->map.notify = handle_map; + wl_signal_add(&layer_surface->events.map, &cg_layer->map); + cg_layer->unmap.notify = handle_unmap; + wl_signal_add(&layer_surface->events.unmap, &cg_layer->unmap); + cg_layer->surface_commit.notify = handle_surface_commit; + wl_signal_add(&layer_surface->surface->events.commit, &cg_layer->surface_commit); + cg_layer->destroy.notify = handle_destroy; + wl_signal_add(&layer_surface->events.destroy, &cg_layer->destroy); + // TODO: new popup, new subsurface + + struct cg_output *output = layer_surface->output->data; + cg_layer->output_destroy.notify = handle_output_destroy; + wl_signal_add(&output->wlr_output->events.destroy, &cg_layer->output_destroy); + + wl_list_insert(&output->layers[layer_surface->pending.layer], &cg_layer->link); } diff --git a/layer_shell_v1.h b/layer_shell_v1.h index d7ade6e..f51beae 100644 --- a/layer_shell_v1.h +++ b/layer_shell_v1.h @@ -2,6 +2,26 @@ #define CG_LAYER_SHELL_V1_H #include +#include +#include +#include + +struct cg_layer_surface { + struct cg_server *server; + struct wlr_layer_surface_v1 *layer_surface; + struct wlr_scene_node *scene_node; + struct wl_list link; // cg_output::layers + + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener surface_commit; + struct wl_listener destroy; + + struct wl_listener output_destroy; + + struct wlr_box geometry; + enum zwlr_layer_shell_v1_layer layer; +}; void handle_layer_shell_v1_surface_new(struct wl_listener *listener, void *data);