Arrange & render layer surfaces

This commit is contained in:
Drew DeVault 2018-03-19 23:11:37 -04:00
parent fcf8c6c8a2
commit 4bf936360d
11 changed files with 235 additions and 35 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.
</description>
<request name="set_size">
<description summary="sets the size of the surface">
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.
</description>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</request>
<request name="set_anchor">
<description summary="configures the anchor point of the surface">
Requests that the compositor anchor the surface to the specified edges
@ -222,9 +238,9 @@
</event>
<enum name="error">
<entry name="invalid_anchor" value="0" summary="anchor bitfield is invalid"/>
<entry name="invalid_surface_state" value="1"
summary="the client provided an invalid surface state"/>
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
<entry name="invalid_size" value="1" summary="size is invalid"/>
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
</enum>
<enum name="anchor" bitfield="true">

View file

@ -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;
}

View file

@ -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;

View file

@ -8,8 +8,112 @@
#include <wlr/util/log.h>
#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;
}

View file

@ -13,6 +13,7 @@
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#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) {

View file

@ -4,6 +4,7 @@
#include <string.h>
#include <wayland-server.h>
#include <wlr/types/wlr_layer_shell.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/util/log.h>
#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),