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

output.c (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 }