wio

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

layers.c (raw) (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 }