diff --git a/examples/layer-shell.c b/examples/layer-shell.c index d2e07a908..b5542f7a3 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -23,7 +23,7 @@ struct wl_callback *frame_callback; static uint32_t output = 0; static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; static uint32_t anchor = 0; -static uint32_t width = 256, height = 256; +static int32_t width = 256, height = 256; static void draw(void); @@ -98,7 +98,7 @@ int main(int argc, char **argv) { char *namespace = "wlroots"; bool found; int c; - while ((c = getopt(argc, argv, "w:h:o:")) != -1) { + while ((c = getopt(argc, argv, "w:h:o:l:a:")) != -1) { switch (c) { case 'o': output = atoi(optarg); @@ -190,7 +190,10 @@ int main(int argc, char **argv) { struct zwlr_layer_surface_v1 *layer_surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell, wl_surface, wl_output, layer, namespace); + zwlr_layer_surface_v1_set_size(layer_surface, width, height); zwlr_layer_surface_v1_set_anchor(layer_surface, anchor); + zwlr_layer_surface_v1_add_listener(layer_surface, + &layer_surface_listener, layer_surface); // TODO: margin, interactivity, exclusive zone wl_surface_commit(wl_surface); wl_display_dispatch(display); diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index ad7654a11..92cc6d9d7 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -29,8 +29,6 @@ struct roots_desktop { struct wl_list views; // roots_view::link - struct wl_list layers[4]; // layer_surface::link - struct wl_list outputs; // roots_output::link struct timespec last_frame; diff --git a/include/rootston/layers.h b/include/rootston/layers.h index 269056ea6..f432d53d8 100644 --- a/include/rootston/layers.h +++ b/include/rootston/layers.h @@ -14,6 +14,9 @@ struct roots_layer_surface { struct wl_listener map; struct wl_listener unmap; struct wl_listener surface_commit; + + bool configured; + struct wlr_box geo; }; #endif diff --git a/include/rootston/output.h b/include/rootston/output.h index a852a2044..5545d76a2 100644 --- a/include/rootston/output.h +++ b/include/rootston/output.h @@ -14,6 +14,7 @@ struct roots_output { struct wl_list link; // roots_desktop:outputs struct roots_view *fullscreen_view; + struct wl_list layers[4]; // layer_surface::link struct timespec last_frame; struct wlr_output_damage *damage; @@ -35,5 +36,7 @@ void output_damage_from_view(struct roots_output *output, struct roots_view *view); void output_damage_whole_drag_icon(struct roots_output *output, struct roots_drag_icon *icon); +void output_damage_whole_surface(struct wlr_surface *surface, + double lx, double ly, float rotation, void *data); #endif diff --git a/include/wlr/types/wlr_layer_shell.h b/include/wlr/types/wlr_layer_shell.h index 0807fe8da..3002345dd 100644 --- a/include/wlr/types/wlr_layer_shell.h +++ b/include/wlr/types/wlr_layer_shell.h @@ -42,14 +42,12 @@ struct wlr_layer_client { }; struct wlr_layer_surface_state { - // Client uint32_t anchor; uint32_t exclusive_zone; struct { uint32_t top, right, bottom, left; } margin; bool keyboard_interactive; - // Server uint32_t width, height; }; @@ -61,6 +59,7 @@ struct wlr_layer_surface_configure { struct wlr_layer_surface { struct wlr_surface *surface; + struct wlr_output *output; struct wlr_layer_client *client; struct wl_resource *resource; struct wl_list link; // wlr_layer_client:surfaces diff --git a/protocol/wlr-layer-shell-unstable-v1.xml b/protocol/wlr-layer-shell-unstable-v1.xml index c4f2a7e36..d6de59abe 100644 --- a/protocol/wlr-layer-shell-unstable-v1.xml +++ b/protocol/wlr-layer-shell-unstable-v1.xml @@ -86,11 +86,27 @@ are designed to be rendered as a layer of a stacked desktop-like environment. - Layer surface state (anchor, exclusive zone, margin, interactivity) is - double-buffered, and will be applied at the time wl_surface.commit of the - corresponding wl_surface is called. + Layer surface state (size, anchor, exclusive zone, margin, interactivity) + is double-buffered, and will be applied at the time wl_surface.commit of + the corresponding wl_surface is called. + + + Sets the size of the surface in pixels. The compositor will display the + surface centered with respect to its anchors. + + If you pass -1 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are -1 by default. + + Size is double-buffered, see wl_surface.commit. + + + + + Requests that the compositor anchor the surface to the specified edges @@ -222,9 +238,9 @@ - - + + + diff --git a/render/egl.c b/render/egl.c index f29667749..d230f5892 100644 --- a/render/egl.c +++ b/render/egl.c @@ -426,7 +426,7 @@ int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, int **formats) { if (!egl->egl_exts.dmabuf_import || !egl->egl_exts.dmabuf_import_modifiers) { - wlr_log(L_ERROR, "dmabuf extension not present"); + wlr_log(L_DEBUG, "dmabuf extension not present"); return -1; } diff --git a/rootston/desktop.c b/rootston/desktop.c index f00fc63fc..015a82156 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -719,10 +719,6 @@ struct roots_desktop *desktop_create(struct roots_server *server, wl_signal_add(&desktop->layer_shell->events.new_surface, &desktop->layer_shell_surface); desktop->layer_shell_surface.notify = handle_layer_shell_surface; - for (size_t i = 0; - i < sizeof(desktop->layers) / sizeof(desktop->layers[0]); ++i) { - wl_list_init(&desktop->layers[i]); - } #ifdef WLR_HAS_XWAYLAND const char *cursor_theme = NULL; diff --git a/rootston/layer_shell.c b/rootston/layer_shell.c index 50bf64662..25e683fec 100644 --- a/rootston/layer_shell.c +++ b/rootston/layer_shell.c @@ -8,8 +8,112 @@ #include #include "rootston/desktop.h" #include "rootston/layers.h" +#include "rootston/output.h" #include "rootston/server.h" +static void apply_exclusive(struct wlr_box *output_area, + uint32_t anchor, uint32_t exclusive) { + struct { + uint32_t anchors; + int *value; + int multiplier; + } edges[] = { + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .value = &output_area->y, + .multiplier = 1, + }, + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .value = &output_area->height, + .multiplier = -1, + }, + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .value = &output_area->x, + .multiplier = 1, + }, + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .value = &output_area->width, + .multiplier = -1, + }, + }; + for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { + if ((anchor & edges[i].anchors)) { + edges[i].value += exclusive * edges[i].multiplier; + } + } +} + +static void arrange_layer(struct wlr_output *output, struct wl_list *list) { + struct wlr_box output_area = { .x = 0, .y = 0 }; + wlr_output_effective_resolution(output, + &output_area.width, &output_area.height); + struct roots_layer_surface *roots_surface; + wl_list_for_each(roots_surface, list, link) { + struct wlr_layer_surface *layer = roots_surface->layer_surface; + struct wlr_layer_surface_state *state = &layer->current; + struct wlr_box box = { .width = state->width, .height = state->height }; + // Horizontal axis + const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + if ((state->anchor & both_horiz) && box.width == -1) { + box.x = 0; + box.width = output_area.width; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x = output_area.x; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x = output_area.width - box.width; + } else { + box.x = (output_area.width / 2) - (box.width / 2); + } + // Vertical axis + const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + if ((state->anchor & both_vert) && box.height == -1) { + box.y = 0; + box.height = output_area.height; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y = output_area.y; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y = output_area.height - box.height; + } else { + box.y = (output_area.height / 2) - (box.height / 2); + } + wlr_log(L_DEBUG, "arranged layer at %dx%d@%d,%d", + box.width, box.height, box.x, box.y); + roots_surface->geo = box; + apply_exclusive(&output_area, state->anchor, state->exclusive_zone); + if (box.width != (int)state->width + || box.height != (int)state->height + || !roots_surface->configured) { + wlr_layer_surface_configure(layer, box.width, box.height); + roots_surface->configured = true; + } + } +} + +static void arrange_layers(struct wlr_output *_output) { + struct roots_output *output = _output->data; + size_t layers = sizeof(output->layers) / sizeof(output->layers[0]); + for (size_t i = 0; i < layers; ++i) { + arrange_layer(output->wlr_output, &output->layers[i]); + } +} + static void handle_destroy(struct wl_listener *listener, void *data) { // TODO } @@ -19,7 +123,13 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { } static void handle_map(struct wl_listener *listener, void *data) { - // TODO + struct wlr_layer_surface *layer_surface = data; + struct roots_layer_surface *layer = layer_surface->data; + struct wlr_output *wlr_output = layer_surface->output; + struct roots_output *output = wlr_output->data; + // TODO: This doesn't play right with output layouts and is also stupid + output_damage_whole_surface(layer_surface->surface, layer->geo.x, + layer->geo.y, 0, output); } static void handle_unmap(struct wl_listener *listener, void *data) { @@ -30,8 +140,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct wlr_layer_surface *layer_surface = data; struct roots_desktop *desktop = wl_container_of(listener, desktop, layer_shell_surface); - wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d", - layer_surface->namespace, layer_surface->layer); + wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d %dx%d %d,%d,%d,%d", + layer_surface->namespace, layer_surface->layer, layer_surface->layer, + layer_surface->client_pending.width, + layer_surface->client_pending.height, + layer_surface->client_pending.margin.top, + layer_surface->client_pending.margin.right, + layer_surface->client_pending.margin.bottom, + layer_surface->client_pending.margin.left); struct roots_layer_surface *roots_surface = calloc(1, sizeof(struct roots_layer_surface)); @@ -49,6 +165,17 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { wl_signal_add(&layer_surface->events.unmap, &roots_surface->unmap); roots_surface->layer_surface = layer_surface; + layer_surface->data = roots_surface; - wl_list_insert(&desktop->layers[layer_surface->layer], &roots_surface->link); + struct roots_output *output = layer_surface->output->data; + wl_list_insert(&output->layers[layer_surface->layer], &roots_surface->link); + + // Temporarily set the layer's current state to client_pending + // So that we can easily arrange it + struct wlr_layer_surface_state old_state = layer_surface->current; + layer_surface->current = layer_surface->client_pending; + + arrange_layers(output->wlr_output); + + layer_surface->current = old_state; } diff --git a/rootston/output.c b/rootston/output.c index 52ece54d5..714976089 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -13,6 +13,7 @@ #include #include #include "rootston/config.h" +#include "rootston/layers.h" #include "rootston/output.h" #include "rootston/server.h" @@ -417,6 +418,21 @@ static void surface_send_frame_done(struct wlr_surface *surface, double lx, wlr_surface_send_frame_done(surface, when); } +static void render_layer( + struct roots_output *output, + const struct wlr_box *output_layout_box, + struct render_data *data, + struct wl_list *layer) { + struct roots_layer_surface *roots_surface; + wl_list_for_each(roots_surface, layer, link) { + struct wlr_layer_surface *layer = roots_surface->layer_surface; + render_surface(layer->surface, + roots_surface->geo.x + output_layout_box->x, + roots_surface->geo.y + output_layout_box->y, + 0, data); + } +} + static void render_output(struct roots_output *output) { struct wlr_output *wlr_output = output->wlr_output; struct roots_desktop *desktop = output->desktop; @@ -433,14 +449,15 @@ static void render_output(struct roots_output *output) { float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; + const struct wlr_box *output_box = + wlr_output_layout_get_box(desktop->layout, wlr_output); + // Check if we can delegate the fullscreen surface to the output if (output->fullscreen_view != NULL && output->fullscreen_view->wlr_surface != NULL) { struct roots_view *view = output->fullscreen_view; // Make sure the view is centered on screen - const struct wlr_box *output_box = - wlr_output_layout_get_box(desktop->layout, wlr_output); struct wlr_box view_box; view_get_box(view, &view_box); double view_x = (double)(output_box->width - view_box.width) / 2 + @@ -498,6 +515,11 @@ static void render_output(struct roots_output *output) { wlr_renderer_clear(renderer, clear_color); } + render_layer(output, output_box, &data, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + render_layer(output, output_box, &data, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + // If a view is fullscreen on this output, render it if (output->fullscreen_view != NULL) { struct roots_view *view = output->fullscreen_view; @@ -520,20 +542,23 @@ static void render_output(struct roots_output *output) { render_surface, &data); } #endif - - goto renderer_end; - } - - // Render all views - struct roots_view *view; - wl_list_for_each_reverse(view, &desktop->views, link) { - render_view(view, &data); + } else { + // Render all views + struct roots_view *view; + wl_list_for_each_reverse(view, &desktop->views, link) { + render_view(view, &data); + } } // Render drag icons data.alpha = 1.0; drag_icons_for_each_surface(server->input, render_surface, &data); + render_layer(output, output_box, &data, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + render_layer(output, output_box, &data, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + renderer_end: wlr_renderer_scissor(renderer, NULL); wlr_renderer_end(renderer); @@ -603,7 +628,7 @@ static bool view_accept_damage(struct roots_output *output, return false; } -static void damage_whole_surface(struct wlr_surface *surface, +void output_damage_whole_surface(struct wlr_surface *surface, double lx, double ly, float rotation, void *data) { struct roots_output *output = data; @@ -647,13 +672,13 @@ void output_damage_whole_view(struct roots_output *output, } damage_whole_decoration(view, output); - view_for_each_surface(view, damage_whole_surface, output); + view_for_each_surface(view, output_damage_whole_surface, output); } void output_damage_whole_drag_icon(struct roots_output *output, struct roots_drag_icon *icon) { surface_for_each_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, 0, - damage_whole_surface, output); + output_damage_whole_surface, output); } static void damage_from_surface(struct wlr_surface *surface, @@ -781,6 +806,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { clock_gettime(CLOCK_MONOTONIC, &output->last_frame); output->desktop = desktop; output->wlr_output = wlr_output; + wlr_output->data = output; wl_list_insert(&desktop->outputs, &output->link); output->damage = wlr_output_damage_create(wlr_output); @@ -792,6 +818,11 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->damage_destroy.notify = output_damage_handle_destroy; wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); + size_t len = sizeof(output->layers) / sizeof(output->layers[0]); + for (size_t i = 0; i < len; ++i) { + wl_list_init(&output->layers[i]); + } + struct roots_output_config *output_config = roots_config_get_output(config, wlr_output); if (output_config) { diff --git a/types/wlr_layer_shell.c b/types/wlr_layer_shell.c index aaf9ee843..631b70090 100644 --- a/types/wlr_layer_shell.c +++ b/types/wlr_layer_shell.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "util/signal.h" @@ -69,12 +70,31 @@ static void layer_surface_handle_ack_configure(struct wl_client *client, surface->current.anchor = configure->state.anchor; surface->current.exclusive_zone = configure->state.exclusive_zone; surface->current.margin = configure->state.margin; + surface->current.width = configure->state.width; + surface->current.height = configure->state.height; layer_surface_configure_destroy(configure); } +static void layer_surface_handle_set_size(struct wl_client *client, + struct wl_resource *resource, int32_t width, int32_t height) { + struct wlr_layer_surface *surface = layer_surface_from_resource(resource); + surface->client_pending.width = width; + surface->client_pending.height = height; +} + static void layer_surface_handle_set_anchor(struct wl_client *client, struct wl_resource *resource, uint32_t anchor) { + const uint32_t max_anchor = + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + if (anchor > max_anchor) { + wl_resource_post_error(resource, + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR, + "invalid anchor %d", anchor); + } struct wlr_layer_surface *surface = layer_surface_from_resource(resource); surface->client_pending.anchor = anchor; } @@ -110,6 +130,7 @@ static void layer_surface_handle_get_popup(struct wl_client *client, static const struct zwlr_layer_surface_v1_interface layer_surface_implementation = { .destroy = resource_handle_destroy, .ack_configure = layer_surface_handle_ack_configure, + .set_size = layer_surface_handle_set_size, .set_anchor = layer_surface_handle_set_anchor, .set_exclusive_zone = layer_surface_handle_set_exclusive_zone, .set_margin = layer_surface_handle_set_margin, @@ -155,7 +176,7 @@ static void layer_surface_resource_destroy(struct wl_resource *resource) { static bool wlr_layer_surface_state_changed(struct wlr_layer_surface *surface) { if (!surface->configured) { - return false; + return true; } struct wlr_layer_surface_state *state; @@ -239,6 +260,8 @@ static void handle_wlr_surface_committed(struct wlr_surface *wlr_surface, surface->current.margin = surface->client_pending.margin; surface->current.keyboard_interactive = surface->client_pending.keyboard_interactive; + surface->current.width = surface->client_pending.width; + surface->current.height = surface->client_pending.height; if (!surface->added) { surface->added = true; @@ -280,6 +303,7 @@ static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, surface->client = client; surface->surface = wlr_surface_from_resource(surface_resource); + surface->output = wlr_output_from_resource(output_resource); surface->resource = wl_resource_create(wl_client, &zwlr_layer_surface_v1_interface, wl_resource_get_version(client_resource),