wio

a wayland wm stylised after plan9 rio - forked from git.sr.ht/~srcmpwn/wio
git clone git://src.gearsix.net/wio
Log | Files | Refs | Atom | Submodules | README | LICENSE

layers.c (9537B)


      1 #include <stdlib.h>
      2 #include <string.h>
      3 #include <wayland-server.h>
      4 #include <wlr/types/wlr_layer_shell_v1.h>
      5 #include "layers.h"
      6 #include "server.h"
      7 
      8 static void apply_exclusive(struct wlr_box *usable_area,
      9 		uint32_t anchor, int32_t exclusive,
     10 		int32_t margin_top, int32_t margin_right,
     11 		int32_t margin_bottom, int32_t margin_left) {
     12 	if (exclusive <= 0) {
     13 		return;
     14 	}
     15 	struct {
     16 		uint32_t anchors;
     17 		int *positive_axis;
     18 		int *negative_axis;
     19 		int margin;
     20 	} edges[] = {
     21 		{
     22 			.anchors =
     23 				ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
     24 				ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
     25 				ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
     26 			.positive_axis = &usable_area->y,
     27 			.negative_axis = &usable_area->height,
     28 			.margin = margin_top,
     29 		},
     30 		{
     31 			.anchors =
     32 				ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
     33 				ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
     34 				ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
     35 			.positive_axis = NULL,
     36 			.negative_axis = &usable_area->height,
     37 			.margin = margin_bottom,
     38 		},
     39 		{
     40 			.anchors =
     41 				ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
     42 				ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
     43 				ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
     44 			.positive_axis = &usable_area->x,
     45 			.negative_axis = &usable_area->width,
     46 			.margin = margin_left,
     47 		},
     48 		{
     49 			.anchors =
     50 				ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
     51 				ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
     52 				ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
     53 			.positive_axis = NULL,
     54 			.negative_axis = &usable_area->width,
     55 			.margin = margin_right,
     56 		},
     57 	};
     58 	for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
     59 		if ((anchor & edges[i].anchors) == edges[i].anchors) {
     60 			if (edges[i].positive_axis) {
     61 				*edges[i].positive_axis += exclusive + edges[i].margin;
     62 			}
     63 			if (edges[i].negative_axis) {
     64 				*edges[i].negative_axis -= exclusive + edges[i].margin;
     65 			}
     66 		}
     67 	}
     68 }
     69 
     70 static void arrange_layer(struct wlr_output *output,
     71 		struct wl_list *list /* struct *wio_layer_surface */,
     72 		struct wlr_box *usable_area, bool exclusive) {
     73 	struct wio_layer_surface *wio_surface;
     74 	struct wlr_box full_area = { 0 };
     75 	wlr_output_effective_resolution(output,
     76 			&full_area.width, &full_area.height);
     77 	wl_list_for_each_reverse(wio_surface, list, link) {
     78 		struct wlr_layer_surface_v1 *layer = wio_surface->layer_surface;
     79 		struct wlr_layer_surface_v1_state *state = &layer->current;
     80 		if (exclusive != (state->exclusive_zone > 0)) {
     81 			continue;
     82 		}
     83 		struct wlr_box bounds;
     84 		if (state->exclusive_zone == -1) {
     85 			bounds = full_area;
     86 		} else {
     87 			bounds = *usable_area;
     88 		}
     89 		struct wlr_box box = {
     90 			.width = state->desired_width,
     91 			.height = state->desired_height
     92 		};
     93 		// Horizontal axis
     94 		const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
     95 			| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
     96 		if ((state->anchor & both_horiz) && box.width == 0) {
     97 			box.x = bounds.x;
     98 			box.width = bounds.width;
     99 		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
    100 			box.x = bounds.x;
    101 		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
    102 			box.x = bounds.x + (bounds.width - box.width);
    103 		} else {
    104 			box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
    105 		}
    106 		// Vertical axis
    107 		const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
    108 			| ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
    109 		if ((state->anchor & both_vert) && box.height == 0) {
    110 			box.y = bounds.y;
    111 			box.height = bounds.height;
    112 		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
    113 			box.y = bounds.y;
    114 		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
    115 			box.y = bounds.y + (bounds.height - box.height);
    116 		} else {
    117 			box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
    118 		}
    119 		// Margin
    120 		if ((state->anchor & both_horiz) == both_horiz) {
    121 			box.x += state->margin.left;
    122 			box.width -= state->margin.left + state->margin.right;
    123 		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
    124 			box.x += state->margin.left;
    125 		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
    126 			box.x -= state->margin.right;
    127 		}
    128 		if ((state->anchor & both_vert) == both_vert) {
    129 			box.y += state->margin.top;
    130 			box.height -= state->margin.top + state->margin.bottom;
    131 		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
    132 			box.y += state->margin.top;
    133 		} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
    134 			box.y -= state->margin.bottom;
    135 		}
    136 		if (box.width < 0 || box.height < 0) {
    137 			// TODO: Bubble up a protocol error?
    138 			wlr_layer_surface_v1_destroy(layer);
    139 			continue;
    140 		}
    141 
    142 		// Apply
    143 		wio_surface->geo = box;
    144 		apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
    145 				state->margin.top, state->margin.right,
    146 				state->margin.bottom, state->margin.left);
    147 		wlr_layer_surface_v1_configure(layer, box.width, box.height);
    148 	}
    149 }
    150 
    151 void arrange_layers(struct wio_output *output) {
    152 	struct wlr_box usable_area = { 0 };
    153 	wlr_output_effective_resolution(output->wlr_output,
    154 			&usable_area.width, &usable_area.height);
    155 
    156 	// Arrange exclusive surfaces from top->bottom
    157 	arrange_layer(output->wlr_output,
    158 			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
    159 			&usable_area, true);
    160 	arrange_layer(output->wlr_output,
    161 			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
    162 			&usable_area, true);
    163 	arrange_layer(output->wlr_output,
    164 			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
    165 			&usable_area, true);
    166 	arrange_layer(output->wlr_output,
    167 			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
    168 			&usable_area, true);
    169 
    170 	// Arrange non-exlusive surfaces from top->bottom
    171 	arrange_layer(output->wlr_output,
    172 			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
    173 			&usable_area, false);
    174 	arrange_layer(output->wlr_output,
    175 			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
    176 			&usable_area, false);
    177 	arrange_layer(output->wlr_output,
    178 			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
    179 			&usable_area, false);
    180 	arrange_layer(output->wlr_output,
    181 			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
    182 			&usable_area, false);
    183 
    184 	// Find topmost keyboard interactive layer, if such a layer exists
    185 	uint32_t layers_above_shell[] = {
    186 		ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
    187 		ZWLR_LAYER_SHELL_V1_LAYER_TOP,
    188 	};
    189 	size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
    190 	struct wio_layer_surface *layer, *topmost = NULL;
    191 	for (size_t i = 0; i < nlayers; ++i) {
    192 		wl_list_for_each_reverse(layer,
    193 				&output->layers[layers_above_shell[i]], link) {
    194 			if (layer->layer_surface->current.keyboard_interactive) {
    195 				topmost = layer;
    196 				break;
    197 			}
    198 		}
    199 		if (topmost != NULL) {
    200 			break;
    201 		}
    202 	}
    203 
    204 	// TODO: Focus topmost layer
    205 }
    206 
    207 static void handle_output_destroy(struct wl_listener *listener, void *data) {
    208 	struct wio_layer_surface *layer =
    209 		wl_container_of(listener, layer, output_destroy);
    210 	layer->layer_surface->output = NULL;
    211 	wl_list_remove(&layer->output_destroy.link);
    212 	wlr_layer_surface_v1_destroy(layer->layer_surface);
    213 }
    214 
    215 static void handle_surface_commit(struct wl_listener *listener, void *data) {
    216 	struct wio_layer_surface *layer =
    217 		wl_container_of(listener, layer, surface_commit);
    218 	struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
    219 	struct wlr_output *wlr_output = layer_surface->output;
    220 	if (wlr_output != NULL) {
    221 		struct wio_output *output = wlr_output->data;
    222 		arrange_layers(output);
    223 	}
    224 }
    225 
    226 static void handle_destroy(struct wl_listener *listener, void *data) {
    227 	struct wio_layer_surface *layer = wl_container_of(
    228 			listener, layer, destroy);
    229 	wl_list_remove(&layer->link);
    230 	wl_list_remove(&layer->destroy.link);
    231 	wl_list_remove(&layer->map.link);
    232 	wl_list_remove(&layer->surface_commit.link);
    233 	if (layer->layer_surface->output) {
    234 		wl_list_remove(&layer->output_destroy.link);
    235 		arrange_layers((struct wio_output *)layer->layer_surface->output->data);
    236 	}
    237 	free(layer);
    238 }
    239 
    240 static void handle_map(struct wl_listener *listener, void *data) {
    241 	struct wlr_layer_surface_v1 *layer_surface = data;
    242 	wlr_surface_send_enter(layer_surface->surface, layer_surface->output);
    243 }
    244 
    245 void server_new_layer_surface(struct wl_listener *listener, void *data) {
    246 	struct wio_server *server = wl_container_of(
    247 			listener, server, new_layer_surface);
    248 	struct wlr_layer_surface_v1 *layer_surface = data;
    249 	if (!layer_surface->output) {
    250 		struct wlr_output *output = wlr_output_layout_output_at(
    251 				server->output_layout, server->cursor->x, server->cursor->y);
    252 		layer_surface->output = output;
    253 	}
    254 
    255 	struct wio_output *output = layer_surface->output->data;
    256 	struct wio_layer_surface *wio_surface =
    257 		calloc(1, sizeof(struct wio_layer_surface));
    258 	if (!wio_surface) {
    259 		return;
    260 	}
    261 	wio_surface->layer_surface = layer_surface;
    262 	layer_surface->data = wio_surface;
    263 	wio_surface->server = server;
    264 
    265 	wio_surface->surface_commit.notify = handle_surface_commit;
    266 	wl_signal_add(&layer_surface->surface->events.commit,
    267 		&wio_surface->surface_commit);
    268 	wio_surface->output_destroy.notify = handle_output_destroy;
    269 	wl_signal_add(&layer_surface->output->events.destroy,
    270 		&wio_surface->output_destroy);
    271 	wio_surface->destroy.notify = handle_destroy;
    272 	wl_signal_add(&layer_surface->events.destroy, &wio_surface->destroy);
    273 	wio_surface->map.notify = handle_map;
    274 	wl_signal_add(&layer_surface->events.map, &wio_surface->map);
    275 	// TODO: popups
    276 
    277 	// TODO: Listen for subsurfaces
    278 	wl_list_insert(&output->layers[layer_surface->pending.layer], &wio_surface->link);
    279 	// Temporarily set the layer's current state to pending
    280 	// So that we can easily arrange it
    281 	struct wlr_layer_surface_v1_state old_state = layer_surface->current;
    282 	layer_surface->current = layer_surface->pending;
    283 	arrange_layers(output);
    284 	layer_surface->current = old_state;
    285 }