diff --git a/include/layers.h b/include/layers.h index 6ed53234..d01a4102 100644 --- a/include/layers.h +++ b/include/layers.h @@ -1,13 +1,18 @@ #ifndef __LABWC_LAYERS_H #define __LABWC_LAYERS_H #include +#include #include struct server; +enum layer_parent { + LAYER_PARENT_LAYER, + LAYER_PARENT_POPUP, +}; + struct lab_layer_surface { struct wlr_layer_surface_v1 *layer_surface; - struct server *server; struct wl_list link; struct wl_listener destroy; @@ -15,9 +20,37 @@ struct lab_layer_surface { struct wl_listener unmap; struct wl_listener surface_commit; struct wl_listener output_destroy; + struct wl_listener new_popup; + struct wl_listener new_subsurface; struct wlr_box geo; bool mapped; + /* TODO: add extent and layer */ + struct server *server; +}; + +struct lab_layer_popup { + struct wlr_xdg_popup *wlr_popup; + enum layer_parent parent_type; + union { + struct lab_layer_surface *parent_layer; + struct lab_layer_popup *parent_popup; + }; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener commit; + struct wl_listener new_popup; +}; + +struct lab_layer_subsurface { + struct wlr_subsurface *wlr_subsurface; + struct lab_layer_surface *layer_surface; + + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener commit; }; void layers_init(struct server *server); diff --git a/src/layers.c b/src/layers.c index b8a07279..9d3168b6 100644 --- a/src/layers.c +++ b/src/layers.c @@ -312,6 +312,236 @@ map_notify(struct wl_listener *listener, void *data) wlr_surface_send_enter(l->surface, l->output); } +static void +subsurface_damage(struct lab_layer_subsurface *subsurface, bool whole) +{ + struct lab_layer_surface *layer = subsurface->layer_surface; + struct wlr_output *wlr_output = layer->layer_surface->output; + if (!wlr_output) { + return; + } +// struct output *output = wlr_output->data; +// int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; +// int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; + damage_all_outputs(layer->server); +} + +static void +subsurface_handle_unmap(struct wl_listener *listener, void *data) +{ + struct lab_layer_subsurface *subsurface = + wl_container_of(listener, subsurface, unmap); + subsurface_damage(subsurface, true); +} + +static void +subsurface_handle_map(struct wl_listener *listener, void *data) +{ + struct lab_layer_subsurface *subsurface = + wl_container_of(listener, subsurface, map); + subsurface_damage(subsurface, true); +} + +static void +subsurface_handle_commit(struct wl_listener *listener, void *data) +{ + struct lab_layer_subsurface *subsurface = + wl_container_of(listener, subsurface, commit); + subsurface_damage(subsurface, false); +} + +static void +subsurface_handle_destroy(struct wl_listener *listener, void *data) +{ + struct lab_layer_subsurface *subsurface = + wl_container_of(listener, subsurface, destroy); + + wl_list_remove(&subsurface->map.link); + wl_list_remove(&subsurface->unmap.link); + wl_list_remove(&subsurface->destroy.link); + wl_list_remove(&subsurface->commit.link); + free(subsurface); +} + +static struct +lab_layer_subsurface *create_subsurface(struct wlr_subsurface *wlr_subsurface, + struct lab_layer_surface *layer_surface) +{ + struct lab_layer_subsurface *subsurface = + calloc(1, sizeof(struct lab_layer_subsurface)); + if (!subsurface) { + return NULL; + } + + subsurface->wlr_subsurface = wlr_subsurface; + subsurface->layer_surface = layer_surface; + + subsurface->map.notify = subsurface_handle_map; + wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); + subsurface->unmap.notify = subsurface_handle_unmap; + wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); + subsurface->destroy.notify = subsurface_handle_destroy; + wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); + subsurface->commit.notify = subsurface_handle_commit; + wl_signal_add(&wlr_subsurface->surface->events.commit, + &subsurface->commit); + + return subsurface; +} + +static void +new_subsurface_notify(struct wl_listener *listener, void *data) +{ + struct lab_layer_surface *lab_layer_surface = + wl_container_of(listener, lab_layer_surface, new_subsurface); + struct wlr_subsurface *wlr_subsurface = data; + create_subsurface(wlr_subsurface, lab_layer_surface); +} + + +static struct +lab_layer_surface *popup_get_layer(struct lab_layer_popup *popup) +{ + while (popup->parent_type == LAYER_PARENT_POPUP) { + popup = popup->parent_popup; + } + return popup->parent_layer; +} + +static void +popup_damage(struct lab_layer_popup *layer_popup, bool whole) +{ + struct wlr_xdg_popup *popup = layer_popup->wlr_popup; +// struct wlr_surface *surface = popup->base->surface; + int popup_sx = popup->geometry.x - popup->base->current.geometry.x; + int popup_sy = popup->geometry.y - popup->base->current.geometry.y; + int ox = popup_sx, oy = popup_sy; + struct lab_layer_surface *layer; + while (true) { + if (layer_popup->parent_type == LAYER_PARENT_POPUP) { + layer_popup = layer_popup->parent_popup; + ox += layer_popup->wlr_popup->geometry.x; + oy += layer_popup->wlr_popup->geometry.y; + } else { + layer = layer_popup->parent_layer; + ox += layer->geo.x; + oy += layer->geo.y; + break; + } + } +// struct wlr_output *wlr_output = layer->layer_surface->output; +// struct output *output = wlr_output->data; + damage_all_outputs(layer->server); +} + +static void +popup_handle_map(struct wl_listener *listener, void *data) +{ + struct lab_layer_popup *popup = wl_container_of(listener, popup, map); + struct lab_layer_surface *layer = popup_get_layer(popup); + struct wlr_output *wlr_output = layer->layer_surface->output; + wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); + popup_damage(popup, true); +} + +static void +popup_handle_unmap(struct wl_listener *listener, void *data) +{ + struct lab_layer_popup *popup = wl_container_of(listener, popup, unmap); + popup_damage(popup, true); +} + +static void +popup_handle_commit(struct wl_listener *listener, void *data) +{ + struct lab_layer_popup *popup = wl_container_of(listener, popup, commit); + popup_damage(popup, false); +} + +static void +popup_handle_destroy(struct wl_listener *listener, void *data) +{ + struct lab_layer_popup *popup = + wl_container_of(listener, popup, destroy); + + wl_list_remove(&popup->map.link); + wl_list_remove(&popup->unmap.link); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->commit.link); + free(popup); +} + +static void +popup_unconstrain(struct lab_layer_popup *popup) +{ + struct lab_layer_surface *layer = popup_get_layer(popup); + struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; + struct output *output = layer->layer_surface->output->data; + + struct wlr_box output_box = { 0 }; + wlr_output_effective_resolution(output->wlr_output, &output_box.width, + &output_box.height); + + struct wlr_box output_toplevel_sx_box = { + .x = -layer->geo.x, + .y = -layer->geo.y, + .width = output_box.width, + .height = output_box.height, + }; + + wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); +} + +static void popup_handle_new_popup(struct wl_listener *listener, void *data); + +static struct lab_layer_popup * +create_popup(struct wlr_xdg_popup *wlr_popup, + enum layer_parent parent_type, void *parent) +{ + struct lab_layer_popup *popup = + calloc(1, sizeof(struct lab_layer_popup)); + if (!popup) { + return NULL; + } + + popup->wlr_popup = wlr_popup; + popup->parent_type = parent_type; + popup->parent_layer = parent; + + popup->map.notify = popup_handle_map; + wl_signal_add(&wlr_popup->base->events.map, &popup->map); + popup->unmap.notify = popup_handle_unmap; + wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); + popup->destroy.notify = popup_handle_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + popup->commit.notify = popup_handle_commit; + wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); + popup->new_popup.notify = popup_handle_new_popup; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + + popup_unconstrain(popup); + + return popup; +} + +static void +popup_handle_new_popup(struct wl_listener *listener, void *data) +{ + struct lab_layer_popup *lab_layer_popup = + wl_container_of(listener, lab_layer_popup, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + create_popup(wlr_popup, LAYER_PARENT_POPUP, lab_layer_popup); +} + +static void +new_popup_notify(struct wl_listener *listener, void *data) +{ + struct lab_layer_surface *lab_layer_surface = + wl_container_of(listener, lab_layer_surface, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + create_popup(wlr_popup, LAYER_PARENT_LAYER, lab_layer_surface); +} + static void new_layer_surface_notify(struct wl_listener *listener, void *data) { @@ -326,26 +556,16 @@ new_layer_surface_notify(struct wl_listener *listener, void *data) layer_surface->output = output; } - struct output *output = - output_from_wlr_output(server, layer_surface->output); - struct lab_layer_surface *surface = calloc(1, sizeof(struct lab_layer_surface)); if (!surface) { return; } - surface->layer_surface = layer_surface; - layer_surface->data = surface; - surface->server = server; surface->surface_commit.notify = surface_commit_notify; wl_signal_add(&layer_surface->surface->events.commit, &surface->surface_commit); - surface->output_destroy.notify = output_destroy_notify; - wl_signal_add(&layer_surface->output->events.destroy, - &surface->output_destroy); - surface->destroy.notify = destroy_notify; wl_signal_add(&layer_surface->events.destroy, &surface->destroy); @@ -355,6 +575,27 @@ new_layer_surface_notify(struct wl_listener *listener, void *data) surface->unmap.notify = unmap_notify; wl_signal_add(&layer_surface->events.unmap, &surface->unmap); + surface->new_popup.notify = new_popup_notify; + wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); + + surface->new_subsurface.notify = new_subsurface_notify; + wl_signal_add(&layer_surface->surface->events.new_subsurface, + &surface->new_subsurface); + + surface->layer_surface = layer_surface; + layer_surface->data = surface; + surface->server = server; + + struct output *output = layer_surface->output->data; + surface->output_destroy.notify = output_destroy_notify; + wl_signal_add(&layer_surface->output->events.destroy, + &surface->output_destroy); + + if (!output) { + wlr_log(WLR_ERROR, "no output for layer"); + return; + } + wl_list_insert(&output->layers[layer_surface->pending.layer], &surface->link); /* diff --git a/src/output.c b/src/output.c index e94db241..0e80fa67 100644 --- a/src/output.c +++ b/src/output.c @@ -29,7 +29,9 @@ struct surface_iterator_data { surface_iterator_func_t user_iterator; void *user_data; struct output *output; + struct view *view; double ox, oy; + int width, height; }; static bool @@ -290,10 +292,19 @@ output_layer_for_each_surface(struct output *output, wl_list_for_each(layer_surface, layer_surfaces, link) { struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = layer_surface->layer_surface; - output_surface_for_each_surface(output, - wlr_layer_surface_v1->surface, layer_surface->geo.x, - layer_surface->geo.y, iterator, user_data); - /* TODO: handle popups */ + struct wlr_surface *surface = wlr_layer_surface_v1->surface; + struct surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .output = output, + .view = NULL, + .ox = layer_surface->geo.x, + .oy = layer_surface->geo.y, + .width = surface->current.width, + .height = surface->current.height, + }; + wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1, + output_for_each_surface_iterator, &data); } } @@ -636,6 +647,43 @@ render_rootmenu(struct output *output, pixman_region32_t *output_damage) } } +static void +output_layer_for_each_popup_surface(struct output *output, + struct wl_list *layer_surfaces, + surface_iterator_func_t iterator, + void *user_data) +{ + struct lab_layer_surface *layer_surface; + wl_list_for_each(layer_surface, layer_surfaces, link) { + struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = + layer_surface->layer_surface; + struct wlr_surface *surface = wlr_layer_surface_v1->surface; + struct surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .output = output, + .view = NULL, + .ox = layer_surface->geo.x, + .oy = layer_surface->geo.y, + .width = surface->current.width, + .height = surface->current.height, + }; + wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1, + output_for_each_surface_iterator, &data); + } +} + +static void +render_layer_popups(struct output *output, pixman_region32_t *damage, + struct wl_list *layer_surfaces) +{ + struct render_data data = { + .damage = damage, + }; + output_layer_for_each_popup_surface(output, layer_surfaces, + render_surface_iterator, &data); +} + void output_layer_for_each_surface_toplevel(struct output *output, struct wl_list *layer_surfaces, surface_iterator_func_t iterator, @@ -749,6 +797,13 @@ output_render(struct output *output, pixman_region32_t *damage) render_layer_toplevel(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + render_layer_popups(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + render_layer_popups(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + render_layer_popups(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + render_layer_toplevel(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); @@ -915,6 +970,7 @@ new_output_notify(struct wl_listener *listener, void *data) struct output *output = calloc(1, sizeof(struct output)); output->wlr_output = wlr_output; + wlr_output->data = output; output->server = server; output->damage = wlr_output_damage_create(wlr_output); wlr_output_effective_resolution(wlr_output,