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

output.c (raw) (14289B)


   1 #define _POSIX_C_SOURCE 200112L
   2 #include <stdlib.h>
   3 #include <string.h>
   4 #include <math.h>
   5 #include <time.h>
   6 #include <wayland-server.h>
   7 #include <wlr/types/wlr_matrix.h>
   8 #include <wlr/types/wlr_output.h>
   9 #include <wlr/render/wlr_renderer.h>
  10 #include "colors.h"
  11 #include "layers.h"
  12 #include "server.h"
  13 #include "view.h"
  14 
  15 struct render_data {
  16 	struct wlr_output *output;
  17 	struct wlr_renderer *renderer;
  18 	struct wio_view *view;
  19 	struct timespec *when;
  20 };
  21 
  22 static void render_surface(struct wlr_surface *surface,
  23 		int sx, int sy, void *data) {
  24 	struct render_data *rdata = data;
  25 	struct wio_view *view = rdata->view;
  26 	struct wlr_output *output = rdata->output;
  27 	struct wlr_texture *texture = wlr_surface_get_texture(surface);
  28 	if (texture == NULL) {
  29 		return;
  30 	}
  31 	double ox = 0, oy = 0;
  32 	wlr_output_layout_output_coords(
  33 			view->server->output_layout, output, &ox, &oy);
  34 	ox += view->x + sx, oy += view->y + sy;
  35 	struct wlr_box box = {
  36 		.x = ox * output->scale,
  37 		.y = oy * output->scale,
  38 		.width = surface->current.width * output->scale,
  39 		.height = surface->current.height * output->scale,
  40 	};
  41 	float matrix[9];
  42 	enum wl_output_transform transform =
  43 		wlr_output_transform_invert(surface->current.transform);
  44 	wlr_matrix_project_box(matrix, &box, transform, 0,
  45 		output->transform_matrix);
  46 	wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1);
  47 	wlr_surface_send_frame_done(surface, rdata->when);
  48 }
  49 
  50 static int scale_length(int length, int offset, float scale) {
  51 	return round((offset + length) * scale) - round(offset * scale);
  52 }
  53 
  54 void scale_box(struct wlr_box *box, float scale) {
  55 	box->width = scale_length(box->width, box->x, scale);
  56 	box->height = scale_length(box->height, box->y, scale);
  57 	box->x = round(box->x * scale);
  58 	box->y = round(box->y * scale);
  59 }
  60 
  61 static void render_menu(struct wio_output *output) {
  62 	struct wio_server *server = output->server;
  63 	struct wlr_renderer *renderer = server->renderer;
  64 
  65 	size_t ntextures =
  66 		sizeof(server->menu.inactive_textures) /
  67 		sizeof(server->menu.inactive_textures[0]);
  68 	int scale = output->wlr_output->scale;
  69 	int border = 3 * scale, margin = 4 * scale;
  70 	int text_height = 0, text_width = 0;
  71 	for (size_t i = 0; i < ntextures; ++i) {
  72 		int width, height;
  73 		// Assumes inactive/active textures are the same size
  74 		// (they probably are)
  75 		width = server->menu.inactive_textures[i]->width;
  76 		height = server->menu.inactive_textures[i]->height;
  77 		width /= scale, height /= scale;
  78 		text_height += height + margin;
  79 		if (width >= text_width) {
  80 			text_width = width;
  81 		}
  82 	}
  83 	text_width += border * 2 + margin;
  84 	text_height += border * 2 - margin;
  85 
  86 	double ox = server->menu.x, oy = server->menu.y;
  87 	wlr_output_layout_output_coords(
  88 			server->output_layout, output->wlr_output, &ox, &oy);
  89 
  90 	struct wlr_box bg_box = { 0 };
  91 	// Background
  92 	bg_box.x = ox;
  93 	bg_box.y = oy;
  94 	bg_box.width = text_width;
  95 	bg_box.height = text_height;
  96 	scale_box(&bg_box, scale);
  97 	wlr_render_rect(renderer, &bg_box, menu_unselected,
  98 			output->wlr_output->transform_matrix);
  99 	// Top
 100 	bg_box.x = ox;
 101 	bg_box.y = oy;
 102 	bg_box.width = text_width;
 103 	bg_box.height = border;
 104 	scale_box(&bg_box, scale);
 105 	wlr_render_rect(renderer, &bg_box, menu_border,
 106 			output->wlr_output->transform_matrix);
 107 	// Bottom
 108 	bg_box.x = ox;
 109 	bg_box.y = oy + text_height;
 110 	bg_box.width = text_width + border;
 111 	bg_box.height = border;
 112 	scale_box(&bg_box, scale);
 113 	wlr_render_rect(renderer, &bg_box, menu_border,
 114 			output->wlr_output->transform_matrix);
 115 	// Left
 116 	bg_box.x = ox;
 117 	bg_box.y = oy;
 118 	bg_box.width = border;
 119 	bg_box.height = text_height;
 120 	scale_box(&bg_box, scale);
 121 	wlr_render_rect(renderer, &bg_box, menu_border,
 122 			output->wlr_output->transform_matrix);
 123 	// Right
 124 	bg_box.x = ox + text_width;
 125 	bg_box.y = oy;
 126 	bg_box.width = border;
 127 	bg_box.height = text_height;
 128 	scale_box(&bg_box, scale);
 129 	wlr_render_rect(renderer, &bg_box, menu_border,
 130 			output->wlr_output->transform_matrix);
 131 
 132 	double cur_x = server->cursor->x, cur_y = server->cursor->y;
 133 	wlr_output_layout_output_coords(server->output_layout,
 134 			output->wlr_output, &cur_x, &cur_y);
 135 	server->menu.selected = -1;
 136 	ox += margin;
 137 	oy += margin;
 138 	for (size_t i = 0; i < ntextures; ++i) {
 139 		int width, height;
 140 		struct wlr_texture *texture = server->menu.inactive_textures[i];
 141 		width = texture->width;
 142 		height = texture->height;
 143 		width /= scale, height /= scale;
 144 		struct wlr_box box = { 0 };
 145 		box.x = ox - scale /* fudge */;
 146 		box.y = oy - scale /* fudge */;
 147 		box.width = text_width - border;
 148 		box.height = height + margin;
 149 		if (wlr_box_contains_point(&box, cur_x, cur_y)) {
 150 			server->menu.selected = i;
 151 			texture = server->menu.active_textures[i];
 152 			scale_box(&box, scale);
 153 			wlr_render_rect(renderer, &box, menu_selected,
 154 					output->wlr_output->transform_matrix);
 155 		} else {
 156 			width = texture->width;
 157 			height = texture->height;
 158 			width /= scale, height /= scale;
 159 		}
 160 		box.x = (ox + (text_width / 2 - width / 2)) * scale;
 161 		box.y = oy * scale;
 162 		box.width = width * scale;
 163 		box.height = height * scale;
 164 		float matrix[9];
 165 		wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
 166 			output->wlr_output->transform_matrix);
 167 		wlr_render_texture_with_matrix(renderer, texture, matrix, 1);
 168 		oy += height + margin;
 169 	}
 170 
 171 	server->menu.width = text_width;
 172 	server->menu.height = text_height;
 173 }
 174 
 175 static void render_view_border(struct wlr_renderer *renderer,
 176 		struct wio_output *output, struct wio_view *view,
 177 		int x, int y, int width, int height,
 178 		int selection) {
 179 	float color[4];
 180 	if (selection) {
 181 		memcpy(color, selection_box, sizeof(color));
 182 	} else if (!view || view->xdg_surface->toplevel->current.activated) {
 183 		memcpy(color, active_border, sizeof(color));
 184 	} else {
 185 		memcpy(color, inactive_border, sizeof(color));
 186 	}
 187 	struct wlr_output *wlr_output = output->wlr_output;
 188 	int scale = wlr_output->scale;
 189 	double ox = 0, oy = 0;
 190 	wlr_output_layout_output_coords(
 191 			output->server->output_layout, wlr_output, &ox, &oy);
 192 	ox *= scale, oy *= scale;
 193 	struct wlr_box borders;
 194 	// Top
 195 	borders.x = (x - window_border) * scale;
 196 	borders.x += ox;
 197 	borders.y = (y - window_border) * scale;
 198 	borders.y += oy;
 199 	borders.width = (width + window_border * 2) * scale;
 200 	borders.height = window_border * scale;
 201 	wlr_render_rect(renderer, &borders, color, wlr_output->transform_matrix);
 202 	// Right
 203 	borders.x = (x + width) * scale;
 204 	borders.x += ox;
 205 	borders.y = (y - window_border) * scale;
 206 	borders.y += oy;
 207 	borders.width = window_border * scale;
 208 	borders.height = (height + window_border * 2) * scale;
 209 	wlr_render_rect(renderer, &borders, color, wlr_output->transform_matrix);
 210 	// Bottom
 211 	borders.x = (x - window_border) * scale;
 212 	borders.x += ox;
 213 	borders.y = (y + height) * scale;
 214 	borders.y += oy;
 215 	borders.width = (width + window_border * 2) * scale;
 216 	borders.height = window_border * scale;
 217 	wlr_render_rect(renderer, &borders, color, wlr_output->transform_matrix);
 218 	// Left
 219 	borders.x = (x - window_border) * scale;
 220 	borders.x += ox;
 221 	borders.y = (y - window_border) * scale;
 222 	borders.y += oy;
 223 	borders.width = window_border * scale;
 224 	borders.height = (height + window_border * 2) * scale;
 225 	wlr_render_rect(renderer, &borders, color, wlr_output->transform_matrix);
 226 }
 227 
 228 struct render_data_layer {
 229 	struct wlr_output *output;
 230 	struct wlr_renderer *renderer;
 231 	struct wio_view *view;
 232 	struct timespec *when;
 233 };
 234 
 235 static void render_layer_surface(struct wlr_surface *surface,
 236 		int sx, int sy, void *data) {
 237 	struct wio_layer_surface *layer_surface = data;
 238 	struct wlr_texture *texture = wlr_surface_get_texture(surface);
 239 	if (texture == NULL) {
 240 		return;
 241 	}
 242 	struct wlr_output *output = layer_surface->layer_surface->output;
 243 	double ox = 0, oy = 0;
 244 	wlr_output_layout_output_coords(
 245 			layer_surface->server->output_layout, output, &ox, &oy);
 246 	ox += layer_surface->geo.x + sx, oy += layer_surface->geo.y + sy;
 247 	float matrix[9];
 248 	enum wl_output_transform transform =
 249 		wlr_output_transform_invert(surface->current.transform);
 250 	struct wlr_box box;
 251 	memcpy(&box, &layer_surface->geo, sizeof(struct wlr_box));
 252 	wlr_matrix_project_box(matrix, &box, transform, 0,
 253 		output->transform_matrix);
 254 	wlr_render_texture_with_matrix(layer_surface->server->renderer,
 255 			texture, matrix, 1);
 256 	// Hack because I'm too lazy to fish through a new rdata struct
 257 	struct timespec now;
 258 	clock_gettime(CLOCK_MONOTONIC, &now);
 259 	wlr_surface_send_frame_done(surface, &now);
 260 }
 261 
 262 static void render_layer(
 263 		struct wio_output *output, struct wl_list *layer_surfaces) {
 264 	struct wio_layer_surface *layer_surface;
 265 	wl_list_for_each(layer_surface, layer_surfaces, link) {
 266 		struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
 267 			layer_surface->layer_surface;
 268 		wlr_surface_for_each_surface(wlr_layer_surface_v1->surface,
 269 			render_layer_surface, layer_surface);
 270 	}
 271 }
 272 
 273 static void output_frame(struct wl_listener *listener, void *data) {
 274 	double x1, x2, y1, y2;
 275 	struct wio_output *output = wl_container_of(listener, output, frame);
 276 	struct wio_server *server = output->server;
 277 	struct wlr_renderer *renderer = server->renderer;
 278 
 279 	struct timespec now;
 280 	clock_gettime(CLOCK_MONOTONIC, &now);
 281 
 282 	if (!wlr_output_attach_render(output->wlr_output, NULL)) {
 283 		return;
 284 	}
 285 
 286 	struct wlr_output *wlr_output = output->wlr_output;
 287 	wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
 288 	wlr_renderer_clear(renderer, background);
 289 
 290 	render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
 291 	render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
 292 
 293 	struct wio_view *view;
 294 	wl_list_for_each_reverse(view, &server->views, link) {
 295 		if (!view->xdg_surface->mapped) {
 296 			continue;
 297 		}
 298 		struct render_data rdata = {
 299 			.output = wlr_output,
 300 			.view = view,
 301 			.renderer = renderer,
 302 			.when = &now,
 303 		};
 304 
 305 		render_view_border(renderer, output, view, view->x, view->y,
 306 				view->xdg_surface->surface->current.width,
 307 				view->xdg_surface->surface->current.height,
 308 				0);
 309 		wlr_xdg_surface_for_each_surface(view->xdg_surface,
 310 				render_surface, &rdata);
 311 	}
 312 	view = server->interactive.view;
 313 	switch (server->input_state) {
 314 	case INPUT_STATE_BORDER_DRAG_TOP:
 315 		render_view_border(renderer, output, view,
 316 			view->x,
 317 			server->cursor->y,
 318 			view->xdg_surface->surface->current.width,
 319 			view->xdg_surface->surface->current.height - (server->cursor->y - server->interactive.sy),
 320 			1);
 321 		break;
 322 	case INPUT_STATE_BORDER_DRAG_LEFT:
 323 		render_view_border(renderer, output, view,
 324 			server->cursor->x,
 325 			view->y,
 326 			view->xdg_surface->surface->current.width - (server->cursor->x - server->interactive.sx),
 327 			view->xdg_surface->surface->current.height,
 328 			1);
 329 		break;
 330 	case INPUT_STATE_BORDER_DRAG_BOTTOM:
 331 		render_view_border(renderer, output, view,
 332 			view->x,
 333 			view->y,
 334 			view->xdg_surface->surface->current.width,
 335 			server->cursor->y - server->interactive.sy,
 336 			1);
 337 		break;
 338 	case INPUT_STATE_BORDER_DRAG_RIGHT:
 339 		render_view_border(renderer, output, view,
 340 			view->x,
 341 			view->y,
 342 			server->cursor->x - server->interactive.sx,
 343 			view->xdg_surface->surface->current.height,
 344 			1);
 345 		break;
 346 	case INPUT_STATE_MOVE:
 347 		render_view_border(renderer, output, view,
 348 			server->cursor->x - server->interactive.sx,
 349 			server->cursor->y - server->interactive.sy,
 350 			view->xdg_surface->surface->current.width,
 351 			view->xdg_surface->surface->current.height,
 352 			1);
 353 		break;
 354 	case INPUT_STATE_NEW_END:
 355 	case INPUT_STATE_RESIZE_END:
 356 		x1 = server->cursor->x < server->interactive.sx ?
 357 			server->cursor->x : server->interactive.sx;
 358 		y1 = server->cursor->y < server->interactive.sy ?
 359 			server->cursor->y : server->interactive.sy;
 360 		x2 = server->cursor->x > server->interactive.sx ?
 361 			server->cursor->x : server->interactive.sx;
 362 		y2 = server->cursor->y > server->interactive.sy ?
 363 			server->cursor->y : server->interactive.sy;
 364 
 365 		render_view_border(renderer, output, NULL, x1, y1, x2 - x1, y2 - y1, 1);
 366 		break;
 367 	default:
 368 		break;
 369 	}
 370 
 371 	render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
 372 
 373 	if (server->menu.x != -1 && server->menu.y != -1) {
 374 		render_menu(output);
 375 	}
 376 
 377 	render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
 378 
 379 	wlr_output_render_software_cursors(wlr_output, NULL);
 380 	wlr_renderer_end(renderer);
 381 	wlr_output_commit(wlr_output);
 382 }
 383 
 384 void server_new_output(struct wl_listener *listener, void *data) {
 385 	struct wio_server *server =
 386 		wl_container_of(listener, server, new_output);
 387 	struct wlr_output *wlr_output = data;
 388 
 389 	struct wio_output *output = calloc(1, sizeof(struct wio_output));
 390 	output->wlr_output = wlr_output;
 391 	output->server = server;
 392 	output->frame.notify = output_frame;
 393 	wl_signal_add(&wlr_output->events.frame, &output->frame);
 394 	wl_list_insert(&server->outputs, &output->link);
 395 	wlr_output->data = output;
 396 
 397 	wl_list_init(&output->layers[0]);
 398 	wl_list_init(&output->layers[1]);
 399 	wl_list_init(&output->layers[2]);
 400 	wl_list_init(&output->layers[3]);
 401 
 402 	struct wio_output_config *_config, *config = NULL;
 403 	wl_list_for_each(_config, &server->output_configs, link) {
 404 		if (strcmp(_config->name, wlr_output->name) == 0) {
 405 			config = _config;
 406 			break;
 407 		}
 408 	}
 409 
 410 	if (config) {
 411 		if (config->x == -1 && config->y == -1) {
 412 			wlr_output_layout_add_auto(server->output_layout, wlr_output);
 413 		} else {
 414 			wlr_output_layout_add(server->output_layout, wlr_output,
 415 					config->x, config->y);
 416 		}
 417 		bool modeset = false;
 418 		if (config->width && config->height
 419 				&& !wl_list_empty(&wlr_output->modes)) {
 420 			struct wlr_output_mode *mode;
 421 			wl_list_for_each(mode, &wlr_output->modes, link) {
 422 				if (mode->width == config->width
 423 						&& mode->height == config->height) {
 424 					wlr_output_set_mode(wlr_output, mode);
 425 					modeset = true;
 426 				}
 427 			}
 428 		}
 429 		if (!modeset) {
 430 			struct wlr_output_mode *mode =
 431 				wlr_output_preferred_mode(wlr_output);
 432 			if (mode) {
 433 				wlr_output_set_mode(wlr_output, mode);
 434 			}
 435 		}
 436 		if (config->scale) {
 437 			wlr_output_set_scale(wlr_output, config->scale);
 438 		}
 439 		if (config->transform) {
 440 			wlr_output_set_transform(wlr_output, config->transform);
 441 		}
 442 		wlr_output_enable(wlr_output, true);
 443 	} else {
 444 		struct wlr_output_mode *mode =
 445 			wlr_output_preferred_mode(wlr_output);
 446 		if (mode) {
 447 			wlr_output_set_mode(wlr_output, mode);
 448 		}
 449 		wlr_output_enable(wlr_output, true);
 450 		wlr_output_layout_add_auto(server->output_layout, wlr_output);
 451 	}
 452 
 453 	wlr_output_commit(wlr_output);
 454 	wlr_output_create_global(wlr_output);
 455 }