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 }