diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-03-28 19:27:52 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-28 19:27:52 -0400 |
commit | ca809d25199b229b3da7d69f427eb67539dc7bc0 (patch) | |
tree | b28c55c464feb85c61f314a26487404fd63f4fb3 | |
parent | 9070950eecded7bfa64e7bca3bb76b150ccc8b72 (diff) | |
parent | 8d6bce02afc656bf792815ed68121f4e614cd175 (diff) |
Merge pull request #1642 from swaywm/layer-shell
Implement layer shell (rendering)
-rw-r--r-- | include/sway/layers.h | 27 | ||||
-rw-r--r-- | include/sway/output.h | 10 | ||||
-rw-r--r-- | include/sway/server.h | 5 | ||||
-rw-r--r-- | protocols/meson.build | 55 | ||||
-rw-r--r-- | sway/commands/output.c | 2 | ||||
-rw-r--r-- | sway/config/output.c | 31 | ||||
-rw-r--r-- | sway/desktop/layer_shell.c | 320 | ||||
-rw-r--r-- | sway/desktop/output.c | 95 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 4 | ||||
-rw-r--r-- | sway/meson.build | 10 | ||||
-rw-r--r-- | sway/server.c | 6 | ||||
-rw-r--r-- | sway/tree/container.c | 4 | ||||
-rw-r--r-- | sway/tree/layout.c | 11 | ||||
-rw-r--r-- | swaybg/meson.build | 2 |
14 files changed, 525 insertions, 57 deletions
diff --git a/include/sway/layers.h b/include/sway/layers.h new file mode 100644 index 00000000..22054be1 --- /dev/null +++ b/include/sway/layers.h @@ -0,0 +1,27 @@ +#ifndef _SWAY_LAYERS_H +#define _SWAY_LAYERS_H +#include <stdbool.h> +#include <wlr/types/wlr_box.h> +#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_layer_shell.h> + +struct sway_layer_surface { + struct wlr_layer_surface *layer_surface; + struct wl_list link; + + struct wl_listener destroy; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener surface_commit; + struct wl_listener output_destroy; + struct wl_listener output_mode; + struct wl_listener output_transform; + + bool configured; + struct wlr_box geo; +}; + +struct sway_output; +void arrange_layers(struct sway_output *output); + +#endif diff --git a/include/sway/output.h b/include/sway/output.h index 95d64705..f899230f 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -1,7 +1,9 @@ #ifndef _SWAY_OUTPUT_H #define _SWAY_OUTPUT_H #include <time.h> +#include <unistd.h> #include <wayland-server.h> +#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_output.h> struct sway_server; @@ -13,8 +15,14 @@ struct sway_output { struct sway_server *server; struct timespec last_frame; + struct wl_list layers[4]; // sway_layer_surface::link + struct wlr_box usable_area; + struct wl_listener frame; - struct wl_listener output_destroy; + struct wl_listener destroy; + struct wl_listener mode; + + pid_t bg_pid; }; #endif diff --git a/include/sway/server.h b/include/sway/server.h index eb7fa2ff..25eb64fe 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -6,6 +6,7 @@ #include <wlr/backend/session.h> #include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_data_device.h> +#include <wlr/types/wlr_layer_shell.h> #include <wlr/types/wlr_xdg_shell_v6.h> #include <wlr/render/wlr_renderer.h> // TODO WLR: make Xwayland optional @@ -27,6 +28,9 @@ struct sway_server { struct wl_listener new_output; struct wl_listener output_frame; + struct wlr_layer_shell *layer_shell; + struct wl_listener layer_shell_surface; + struct wlr_xdg_shell_v6 *xdg_shell_v6; struct wl_listener xdg_shell_v6_surface; @@ -46,6 +50,7 @@ void server_run(struct sway_server *server); void handle_new_output(struct wl_listener *listener, void *data); +void handle_layer_shell_surface(struct wl_listener *listener, void *data); void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); void handle_xwayland_surface(struct wl_listener *listener, void *data); void handle_wl_shell_surface(struct wl_listener *listener, void *data); diff --git a/protocols/meson.build b/protocols/meson.build index 1fda600e..3f79e719 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -14,24 +14,57 @@ wayland_scanner_client = generator( arguments: ['client-header', '@INPUT@', '@OUTPUT@'], ) -protocols = [ +wayland_scanner_server = generator( + wayland_scanner, + output: '@BASENAME@-protocol.h', + arguments: ['server-header', '@INPUT@', '@OUTPUT@'], +) + +client_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], ['wlr-layer-shell-unstable-v1.xml'] ] -wl_protos_src = [] -wl_protos_headers = [] +server_protocols = [ + ['wlr-layer-shell-unstable-v1.xml'] +] + +client_protos_src = [] +client_protos_headers = [] + +server_protos_src = [] +server_protos_headers = [] -foreach p : protocols +foreach p : client_protocols xml = join_paths(p) - wl_protos_src += wayland_scanner_code.process(xml) - wl_protos_headers += wayland_scanner_client.process(xml) + client_protos_src += wayland_scanner_code.process(xml) + client_protos_headers += wayland_scanner_client.process(xml) endforeach -lib_wl_protos = static_library('wl_protos', wl_protos_src + wl_protos_headers, - dependencies: [wayland_client]) # for the include directory +foreach p : server_protocols + xml = join_paths(p) + server_protos_src += wayland_scanner_code.process(xml) + server_protos_headers += wayland_scanner_server.process(xml) +endforeach + +lib_client_protos = static_library( + 'client_protos', + client_protos_src + client_protos_headers, + dependencies: [wayland_client] +) # for the include directory + +client_protos = declare_dependency( + link_with: lib_client_protos, + sources: client_protos_headers, +) + +lib_server_protos = static_library( + 'server_protos', + server_protos_src + server_protos_headers, + dependencies: [wayland_client] +) # for the include directory -sway_protos = declare_dependency( - link_with: lib_wl_protos, - sources: wl_protos_headers, +server_protos = declare_dependency( + link_with: lib_server_protos, + sources: server_protos_headers, ) diff --git a/sway/commands/output.c b/sway/commands/output.c index e747eb4e..35bc8099 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -188,7 +188,7 @@ static struct cmd_results *cmd_output_background(struct output_config *output, } wordexp_t p; - char *src = join_args(argv + *i - 1, j); + char *src = join_args(argv + *i, j); if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { return cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s).", src); diff --git a/sway/config/output.c b/sway/config/output.c index 69e883f1..9e211861 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,7 +1,10 @@ #define _XOPEN_SOURCE 700 +#include <assert.h> #include <stdbool.h> #include <string.h> -#include <assert.h> +#include <signal.h> +#include <sys/wait.h> +#include <unistd.h> #include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output_layout.h> #include "sway/config.h" @@ -107,6 +110,16 @@ static void set_mode(struct wlr_output *output, int width, int height, } } +void terminate_swaybg(pid_t pid) { + int ret = kill(pid, SIGTERM); + if (ret != 0) { + wlr_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid); + } else { + int status; + waitpid(pid, &status, 0); + } +} + void apply_output_config(struct output_config *oc, swayc_t *output) { assert(output->type == C_OUTPUT); @@ -160,12 +173,12 @@ void apply_output_config(struct output_config *oc, swayc_t *output) { } if (oc && oc->background) { - // TODO swaybg - /*if (output->bg_pid != 0) { - terminate_swaybg(output->bg_pid); + if (output->sway_output->bg_pid != 0) { + terminate_swaybg(output->sway_output->bg_pid); } - wlr_log(L_DEBUG, "Setting background for output %d to %s", output_i, oc->background); + wlr_log(L_DEBUG, "Setting background for output %d to %s", + output_i, oc->background); size_t bufsize = 12; char output_id[bufsize]; @@ -173,17 +186,17 @@ void apply_output_config(struct output_config *oc, swayc_t *output) { output_id[bufsize-1] = 0; char *const cmd[] = { - "swaybg", + "./swaybg/swaybg", output_id, oc->background, oc->background_option, NULL, }; - output->bg_pid = fork(); - if (output->bg_pid == 0) { + output->sway_output->bg_pid = fork(); + if (output->sway_output->bg_pid == 0) { execvp(cmd[0], cmd); - }*/ + } } } diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c new file mode 100644 index 00000000..bd62f84a --- /dev/null +++ b/sway/desktop/layer_shell.c @@ -0,0 +1,320 @@ +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <wayland-server.h> +#include <wlr/types/wlr_box.h> +#include <wlr/types/wlr_layer_shell.h> +#include <wlr/types/wlr_output.h> +#include <wlr/util/log.h> +#include "sway/layers.h" +#include "sway/layout.h" +#include "sway/output.h" +#include "sway/server.h" + +static void apply_exclusive(struct wlr_box *usable_area, + uint32_t anchor, int32_t exclusive, + int32_t margin_top, int32_t margin_right, + int32_t margin_bottom, int32_t margin_left) { + if (exclusive <= 0) { + return; + } + struct { + uint32_t anchors; + int *positive_axis; + int *negative_axis; + int margin; + } edges[] = { + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .positive_axis = &usable_area->y, + .negative_axis = &usable_area->height, + .margin = margin_top, + }, + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->height, + .margin = margin_bottom, + }, + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = &usable_area->x, + .negative_axis = &usable_area->width, + .margin = margin_left, + }, + { + .anchors = + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->width, + .margin = margin_right, + }, + }; + for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { + if ((anchor & edges[i].anchors) == edges[i].anchors) { + if (edges[i].positive_axis) { + *edges[i].positive_axis += exclusive + edges[i].margin; + } + if (edges[i].negative_axis) { + *edges[i].negative_axis -= exclusive + edges[i].margin; + } + } + } +} + +static void arrange_layer(struct sway_output *output, struct wl_list *list, + struct wlr_box *usable_area, bool exclusive) { + struct sway_layer_surface *sway_layer; + struct wlr_box full_area = { 0 }; + wlr_output_effective_resolution(output->wlr_output, + &full_area.width, &full_area.height); + wl_list_for_each(sway_layer, list, link) { + struct wlr_layer_surface *layer = sway_layer->layer_surface; + struct wlr_layer_surface_state *state = &layer->current; + if (exclusive != (state->exclusive_zone > 0)) { + continue; + } + struct wlr_box bounds; + if (state->exclusive_zone == -1) { + bounds = full_area; + } else { + bounds = *usable_area; + } + struct wlr_box box = { + .width = state->desired_width, + .height = state->desired_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 == 0) { + box.x = bounds.x; + box.width = bounds.width; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x = bounds.x; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x = bounds.x + (bounds.width - box.width); + } else { + box.x = bounds.x + ((bounds.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 == 0) { + box.y = bounds.y; + box.height = bounds.height; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y = bounds.y; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y = bounds.y + (bounds.height - box.height); + } else { + box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); + } + // Margin + if ((state->anchor & both_horiz) == both_horiz) { + box.x += state->margin.left; + box.width -= state->margin.left + state->margin.right; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x += state->margin.left; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x -= state->margin.right; + } + if ((state->anchor & both_vert) == both_vert) { + box.y += state->margin.top; + box.height -= state->margin.top + state->margin.bottom; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y += state->margin.top; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y -= state->margin.bottom; + } + if (box.width < 0 || box.height < 0) { + // TODO: Bubble up a protocol error? + wlr_layer_surface_close(layer); + continue; + } + // Apply + sway_layer->geo = box; + apply_exclusive(usable_area, state->anchor, state->exclusive_zone, + state->margin.top, state->margin.right, + state->margin.bottom, state->margin.left); + wlr_layer_surface_configure(layer, box.width, box.height); + } +} + +void arrange_layers(struct sway_output *output) { + struct wlr_box usable_area = { 0 }; + wlr_output_effective_resolution(output->wlr_output, + &usable_area.width, &usable_area.height); + struct wlr_box usable_area_before = output->usable_area; + + // Arrange exclusive surfaces from top->bottom + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + &usable_area, true); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + &usable_area, true); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + &usable_area, true); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + &usable_area, true); + memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); + + if (memcmp(&usable_area_before, + &usable_area, sizeof(struct wlr_box)) != 0) { + wlr_log(L_DEBUG, "arrange"); + arrange_windows(output->swayc, -1, -1); + } + + // Arrange non-exlusive surfaces from top->bottom + usable_area.x = usable_area.y = 0; + wlr_output_effective_resolution(output->wlr_output, + &usable_area.width, &usable_area.height); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + &usable_area, false); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + &usable_area, false); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + &usable_area, false); + arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + &usable_area, false); +} + +static void handle_output_destroy(struct wl_listener *listener, void *data) { + struct sway_layer_surface *sway_layer = + wl_container_of(listener, sway_layer, output_destroy); + wl_list_remove(&sway_layer->output_destroy.link); + wl_list_remove(&sway_layer->output_mode.link); + wl_list_remove(&sway_layer->output_transform.link); + sway_layer->layer_surface->output = NULL; + wlr_layer_surface_close(sway_layer->layer_surface); +} + +static void handle_output_mode(struct wl_listener *listener, void *data) { + struct wlr_output *output = data; + arrange_layers((struct sway_output *)output->data); +} + +static void handle_output_transform(struct wl_listener *listener, void *data) { + struct wlr_output *output = data; + arrange_layers((struct sway_output *)output->data); +} + +static void handle_surface_commit(struct wl_listener *listener, void *data) { + struct sway_layer_surface *layer = + wl_container_of(listener, layer, surface_commit); + struct wlr_layer_surface *layer_surface = layer->layer_surface; + struct wlr_output *wlr_output = layer_surface->output; + if (wlr_output != NULL) { + struct sway_output *output = wlr_output->data; + struct wlr_box old_geo = layer->geo; + arrange_layers(output); + if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) { + // TODO DAMAGE apply whole surface from previous and new geos + } else { + // TODO DAMAGE from surface damage + } + } +} + +static void unmap(struct wlr_layer_surface *layer_surface) { + // TODO DAMAGE +} + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_layer_surface *sway_layer = wl_container_of( + listener, sway_layer, destroy); + if (sway_layer->layer_surface->mapped) { + unmap(sway_layer->layer_surface); + } + wl_list_remove(&sway_layer->link); + wl_list_remove(&sway_layer->destroy.link); + wl_list_remove(&sway_layer->map.link); + wl_list_remove(&sway_layer->unmap.link); + wl_list_remove(&sway_layer->surface_commit.link); + if (sway_layer->layer_surface->output != NULL) { + wl_list_remove(&sway_layer->output_destroy.link); + wl_list_remove(&sway_layer->output_mode.link); + wl_list_remove(&sway_layer->output_transform.link); + } + struct sway_output *output = sway_layer->layer_surface->output->data; + arrange_layers(output); + free(sway_layer); +} + +static void handle_map(struct wl_listener *listener, void *data) { + // TODO DAMAGE +} + +static void handle_unmap(struct wl_listener *listener, void *data) { + struct sway_layer_surface *sway_layer = wl_container_of( + listener, sway_layer, unmap); + unmap(sway_layer->layer_surface); +} + +void handle_layer_shell_surface(struct wl_listener *listener, void *data) { + struct wlr_layer_surface *layer_surface = data; + struct sway_server *server = + wl_container_of(listener, server, layer_shell_surface); + wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d " + "size %dx%d margin %d,%d,%d,%d", + layer_surface->namespace, layer_surface->layer, layer_surface->layer, + layer_surface->client_pending.desired_width, + layer_surface->client_pending.desired_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 sway_layer_surface *sway_layer = + calloc(1, sizeof(struct sway_layer_surface)); + if (!sway_layer) { + return; + } + + sway_layer->surface_commit.notify = handle_surface_commit; + wl_signal_add(&layer_surface->surface->events.commit, + &sway_layer->surface_commit); + + sway_layer->output_destroy.notify = handle_output_destroy; + wl_signal_add(&layer_surface->output->events.destroy, + &sway_layer->output_destroy); + + sway_layer->output_mode.notify = handle_output_mode; + wl_signal_add(&layer_surface->output->events.mode, + &sway_layer->output_mode); + + sway_layer->output_transform.notify = handle_output_transform; + wl_signal_add(&layer_surface->output->events.transform, + &sway_layer->output_transform); + + sway_layer->destroy.notify = handle_destroy; + wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); + sway_layer->map.notify = handle_map; + wl_signal_add(&layer_surface->events.map, &sway_layer->map); + sway_layer->unmap.notify = handle_unmap; + wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); + // TODO: Listen for subsurfaces + + sway_layer->layer_surface = layer_surface; + layer_surface->data = sway_layer; + + struct sway_output *output = layer_surface->output->data; + wl_list_insert(&output->layers[layer_surface->layer], &sway_layer->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); + layer_surface->current = old_state; +} diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 6c990c47..9e7fbcc6 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -4,14 +4,17 @@ #include <time.h> #include <wayland-server.h> #include <wlr/render/wlr_renderer.h> +#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_output.h> +#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_wl_shell.h> #include "log.h" #include "sway/container.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" +#include "sway/layers.h" #include "sway/layout.h" #include "sway/output.h" #include "sway/server.h" @@ -78,9 +81,7 @@ static void render_surface(struct wlr_surface *surface, rotate_child_position(&sx, &sy, sw, sh, width, height, rotation); render_surface(subsurface->surface, wlr_output, when, - lx + sx, - ly + sy, - rotation); + lx + sx, ly + sy, rotation); } } @@ -139,9 +140,15 @@ static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, } } +struct render_data { + struct sway_output *output; + struct timespec *now; +}; static void output_frame_view(swayc_t *view, void *data) { - struct sway_output *output = data; + struct render_data *rdata = data; + struct sway_output *output = rdata->output; + struct timespec *now = rdata->now; struct wlr_output *wlr_output = output->wlr_output; struct sway_view *sway_view = view->sway_view; struct wlr_surface *surface = sway_view->surface; @@ -154,29 +161,38 @@ static void output_frame_view(swayc_t *view, void *data) { case SWAY_XDG_SHELL_V6_VIEW: { int window_offset_x = view->sway_view->wlr_xdg_surface_v6->geometry.x; int window_offset_y = view->sway_view->wlr_xdg_surface_v6->geometry.y; - render_surface(surface, wlr_output, &output->last_frame, - view->x - window_offset_x, - view->y - window_offset_y, - 0); + render_surface(surface, wlr_output, now, + view->x - window_offset_x, view->y - window_offset_y, 0); render_xdg_v6_popups(sway_view->wlr_xdg_surface_v6, wlr_output, - &output->last_frame, - view->x - window_offset_x, view->y - window_offset_y, - 0); + now, view->x - window_offset_x, view->y - window_offset_y, 0); break; } case SWAY_WL_SHELL_VIEW: render_wl_shell_surface(sway_view->wlr_wl_shell_surface, wlr_output, - &output->last_frame, view->x, view->y, 0, false); + now, view->x, view->y, 0, false); break; case SWAY_XWAYLAND_VIEW: - render_surface(surface, wlr_output, &output->last_frame, view->x, - view->y, 0); + render_surface(surface, wlr_output, now, view->x, view->y, 0); break; default: break; } } +static void render_layer(struct sway_output *output, + const struct wlr_box *output_layout_box, + struct timespec *when, + struct wl_list *layer) { + struct sway_layer_surface *sway_layer; + wl_list_for_each(sway_layer, layer, link) { + struct wlr_layer_surface *layer = sway_layer->layer_surface; + render_surface(layer->surface, output->wlr_output, when, + sway_layer->geo.x + output_layout_box->x, + sway_layer->geo.y + output_layout_box->y, 0); + wlr_surface_send_frame_done(layer->surface, when); + } +} + static void output_frame_notify(struct wl_listener *listener, void *data) { struct sway_output *soutput = wl_container_of(listener, soutput, frame); struct wlr_output *wlr_output = data; @@ -189,13 +205,29 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { wlr_output_make_current(wlr_output, &buffer_age); wlr_renderer_begin(server->renderer, wlr_output->width, wlr_output->height); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + struct wlr_output_layout *layout = root_container.sway_root->output_layout; + const struct wlr_box *output_box = wlr_output_layout_get_box( + layout, wlr_output); + + render_layer(soutput, output_box, &now, + &soutput->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + render_layer(soutput, output_box, &now, + &soutput->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + struct sway_seat *seat = input_manager_current_seat(input_manager); swayc_t *focus = sway_seat_get_focus_inactive(seat, soutput->swayc); swayc_t *workspace = (focus->type == C_WORKSPACE ? focus : swayc_parent_by_type(focus, C_WORKSPACE)); - swayc_descendants_of_type(workspace, C_VIEW, output_frame_view, soutput); + struct render_data rdata = { + .output = soutput, + .now = &now, + }; + swayc_descendants_of_type(workspace, C_VIEW, output_frame_view, &rdata); // render unmanaged views on top struct sway_view *view; @@ -210,22 +242,31 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { } } - wlr_renderer_end(server->renderer); - wlr_output_swap_buffers(wlr_output, &soutput->last_frame, NULL); + // TODO: Consider revising this when fullscreen windows are supported + render_layer(soutput, output_box, &now, + &soutput->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + render_layer(soutput, output_box, &now, + &soutput->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + wlr_renderer_end(server->renderer); + wlr_output_swap_buffers(wlr_output, &now, NULL); soutput->last_frame = now; } static void handle_output_destroy(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, output_destroy); + struct sway_output *output = wl_container_of(listener, output, destroy); struct wlr_output *wlr_output = data; wlr_log(L_DEBUG, "Output %p %s removed", wlr_output, wlr_output->name); destroy_output(output->swayc); } +static void handle_output_mode(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, mode); + arrange_layers(output); + arrange_windows(output->swayc, -1, -1); +} + void handle_new_output(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; @@ -236,6 +277,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { return; } output->wlr_output = wlr_output; + wlr_output->data = output; output->server = server; if (!wl_list_empty(&wlr_output->modes)) { @@ -250,13 +292,20 @@ void handle_new_output(struct wl_listener *listener, void *data) { return; } + size_t len = sizeof(output->layers) / sizeof(output->layers[0]); + for (size_t i = 0; i < len; ++i) { + wl_list_init(&output->layers[i]); + } + sway_input_manager_configure_xcursor(input_manager); wl_signal_add(&wlr_output->events.frame, &output->frame); output->frame.notify = output_frame_notify; + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + output->destroy.notify = handle_output_destroy; + wl_signal_add(&wlr_output->events.mode, &output->mode); + output->mode.notify = handle_output_mode; - wl_signal_add(&wlr_output->events.destroy, &output->output_destroy); - output->output_destroy.notify = handle_output_destroy; - + arrange_layers(output); arrange_windows(&root_container, -1, -1); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 6b5e03f9..f9b5242b 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -84,7 +84,7 @@ static void set_activated(struct sway_view *view, bool activated) { wlr_xwayland_surface_activate(surface, activated); } -static void close(struct sway_view *view) { +static void close_view(struct sway_view *view) { if (!assert_xwayland(view)) { return; } @@ -203,7 +203,7 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { sway_view->iface.set_size = set_size; sway_view->iface.set_position = set_position; sway_view->iface.set_activated = set_activated; - sway_view->iface.close = close; + sway_view->iface.close = close_view; sway_view->wlr_xwayland_surface = xsurface; sway_view->sway_xwayland_surface = sway_surface; sway_view->surface = xsurface->surface; diff --git a/sway/meson.build b/sway/meson.build index 26e56ad2..8bddb11b 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -46,6 +46,7 @@ sway_sources = files( 'ipc-json.c', 'ipc-server.c', 'desktop/output.c', + 'desktop/layer_shell.c', 'desktop/wl_shell.c', 'desktop/xdg_shell_v6.c', 'desktop/xwayland.c', @@ -57,14 +58,15 @@ sway_sources = files( ) sway_deps = [ + jsonc, + libcap, + libinput, + math, pcre, pixman, + server_protos, wayland_server, - jsonc, wlroots, - libcap, - math, - libinput, xkbcommon, ] diff --git a/sway/server.c b/sway/server.c index ca08d7fb..92f72f13 100644 --- a/sway/server.c +++ b/sway/server.c @@ -7,6 +7,7 @@ #include <wlr/render/wlr_renderer.h> #include <wlr/render/gles2.h> #include <wlr/types/wlr_compositor.h> +#include <wlr/types/wlr_layer_shell.h> #include <wlr/types/wlr_wl_shell.h> // TODO WLR: make Xwayland optional #include <wlr/xwayland.h> @@ -51,6 +52,11 @@ bool server_init(struct sway_server *server) { server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); + server->layer_shell = wlr_layer_shell_create(server->wl_display); + wl_signal_add(&server->layer_shell->events.new_surface, + &server->layer_shell_surface); + server->layer_shell_surface.notify = handle_layer_shell_surface; + server->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display); wl_signal_add(&server->xdg_shell_v6->events.new_surface, &server->xdg_shell_v6_surface); diff --git a/sway/tree/container.c b/sway/tree/container.c index 705221d7..bbafe9ec 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -254,7 +254,9 @@ swayc_t *destroy_output(swayc_t *output) { } } - wl_list_remove(&output->sway_output->output_destroy.link); + wl_list_remove(&output->sway_output->frame.link); + wl_list_remove(&output->sway_output->destroy.link); + wl_list_remove(&output->sway_output->mode.link); wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); free_swayc(output); diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 3d04a1a7..de9e7b58 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -204,10 +204,13 @@ void arrange_windows(swayc_t *container, double width, double height) { case C_WORKSPACE: { swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); - container->width = output->width; - container->height = output->height; - container->x = x; - container->y = y; + struct wlr_box *area = &output->sway_output->usable_area; + wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", + area->width, area->height, area->x, area->y); + container->width = area->width; + container->height = area->height; + container->x = x = area->x; + container->y = y = area->y; wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", container->name, container->x, container->y); } diff --git a/swaybg/meson.build b/swaybg/meson.build index 5e10f3c7..8704de6d 100644 --- a/swaybg/meson.build +++ b/swaybg/meson.build @@ -4,12 +4,12 @@ executable( include_directories: [sway_inc], dependencies: [ cairo, + client_protos, gdk_pixbuf, jsonc, math, pango, pangocairo, - sway_protos, wayland_client, wlroots, ], |