-rw-r--r-- src/output.c
1 #include <math.h> 2 #include <time.h> 3 #include <pixman.h> 4 #include <wlr/types/wlr_gamma_control_v1.h> 5 #include <wlr/types/wlr_layer_shell_v1.h> 6 #include <wlr/types/wlr_matrix.h> 7 #include <wlr/types/wlr_output_damage.h> 8 #include <wlr/types/wlr_output_layout.h> 9 #include <wlr/types/wlr_output_management_v1.h> 10 #include <wlr/types/wlr_screencopy_v1.h> 11 #include <wlr/types/wlr_xdg_output_v1.h> 12 13 #include "output.h" 14 #include "types.h" 15 16 17 struct render_data { 18 struct wlr_output *output; 19 struct wlr_renderer *renderer; 20 struct wlr_surface *bordered; 21 struct timespec *when; 22 double zoom; 23 bool is_focussed; 24 int x; 25 int y; 26 }; 27 28 29 static void render_borders( 30 struct render_data *rdata, int x, int y, int width, int height 31 ) { 32 double border_width = ceil(wimp.current_desk->border_width * rdata->zoom); 33 int corner = CORNER * rdata->zoom; 34 35 struct wlr_output *output = rdata->output; 36 float *colour = rdata->is_focussed ? 37 wimp.current_desk->corner_focus : wimp.current_desk->corner_normal; 38 39 // edges including corners 40 struct wlr_box border = { 41 .x = x - border_width, 42 .y = y - border_width, 43 .width = width + border_width * 2, 44 .height = border_width, 45 }; 46 wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // N 47 border.y = y + height; 48 wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // S 49 border.y = y - border_width; 50 border.width = border_width; 51 border.height = height + border_width * 2; 52 wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // W 53 border.x = x + width; 54 wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // E 55 56 // edges excluding corners 57 colour = rdata->is_focussed ? wimp.current_desk->border_focus : wimp.current_desk->border_normal; 58 border.y += corner; 59 border.height -= corner * 2; 60 if (border.height > 0) { 61 wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // E 62 border.x = x - border_width; 63 wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // W 64 } else { 65 border.x = x - border_width; 66 } 67 border.y = y - border_width; 68 border.x += corner; 69 border.height = border_width; 70 border.width = width + border_width * 2 - corner * 2; 71 if (border.width > 0) { 72 wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // S 73 border.y = y + height; 74 wlr_render_rect(rdata->renderer, &border, colour, output->transform_matrix); // N 75 } 76 } 77 78 79 static void render_surface( 80 struct wlr_surface *surface, int sx, int sy, void *data 81 ) { 82 struct render_data *rdata = data; 83 struct wlr_output *output = rdata->output; 84 85 struct wlr_texture *texture = wlr_surface_get_texture(surface); 86 if (texture == NULL) { 87 return; 88 } 89 90 int x = (rdata->x + sx) * output->scale * rdata->zoom; 91 int y = (rdata->y + sy) * output->scale * rdata->zoom; 92 int width = surface->current.width * output->scale * rdata->zoom; 93 int height = surface->current.height * output->scale * rdata->zoom; 94 95 if (rdata->bordered == surface && wimp.current_desk->border_width > 0) { 96 render_borders(rdata, x, y, width, height); 97 } 98 99 struct wlr_box box = { 100 .x = x, 101 .y = y, 102 .width = width, 103 .height = height, 104 }; 105 106 float matrix[9]; 107 enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform); 108 wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix); 109 wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1); 110 111 wlr_surface_send_frame_done(surface, rdata->when); 112 } 113 114 115 static void on_frame(struct wl_listener *listener, void *data) { 116 struct output *output = wl_container_of(listener, output, frame_listener); 117 118 bool needs_frame; 119 pixman_region32_t damage; 120 pixman_region32_init(&damage); 121 122 if (!wlr_output_damage_attach_render(output->wlr_output_damage, &needs_frame, &damage)) { 123 goto finish; 124 } 125 126 if (!needs_frame) { 127 wlr_output_rollback(output->wlr_output); 128 goto finish; 129 } 130 131 struct wlr_renderer *renderer = wimp.renderer; 132 struct desk *desk = wimp.current_desk; 133 struct timespec now; 134 clock_gettime(CLOCK_MONOTONIC, &now); 135 136 int width, height; 137 double zoom = desk->zoom; 138 wlr_output_effective_resolution(output->wlr_output, &width, &height); 139 wlr_renderer_begin(renderer, width, height); 140 wlr_renderer_clear(renderer, desk->background); 141 142 double ox, oy; 143 struct wlr_output_layout_output *ol; 144 wl_list_for_each(ol, &wimp.output_layout->outputs, link) { 145 if (ol->output == output->wlr_output) { 146 ox = (double)ol->x; 147 oy = (double)ol->y; 148 break; 149 } 150 } 151 152 // paint wallpaper 153 struct wallpaper *wallpaper = desk->wallpaper; 154 if (wallpaper != NULL) { 155 int wh = wallpaper->height; 156 int ww = wallpaper->width; 157 float mat[9]; 158 memcpy(mat, output->wlr_output->transform_matrix, 9 * sizeof(float)); 159 wlr_matrix_scale(mat, zoom, zoom); 160 for (int x = ((int)desk->panned_x % ww) - ww; x < width / zoom; x += ww) { 161 for (int y = ((int)desk->panned_y % wh) - wh; y < height / zoom; y += wh) { 162 wlr_render_texture(renderer, wallpaper->texture, mat, x, y, 1.0f); 163 } 164 } 165 } 166 167 struct render_data rdata = { 168 .output = output->wlr_output, 169 .renderer = renderer, 170 .bordered = NULL, 171 .when = &now, 172 .zoom = 1, 173 .is_focussed = false, 174 .x = 0, 175 .y = 0, 176 }; 177 178 // paint background and bottom layers 179 struct layer_view *lview; 180 wl_list_for_each(lview, &output->layer_views[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], link) { 181 wlr_surface_for_each_surface( 182 lview->surface->surface, render_surface, &rdata 183 ); 184 } 185 wl_list_for_each(lview, &output->layer_views[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], link) { 186 wlr_surface_for_each_surface( 187 lview->surface->surface, render_surface, &rdata 188 ); 189 } 190 191 rdata.zoom = zoom; 192 193 // paint clients 194 struct view *view; 195 struct wlr_surface *focussed = wimp.seat->keyboard_state.focused_surface; 196 int border_width = wimp.current_desk->border_width; 197 wl_list_for_each_reverse(view, &desk->views, link) { 198 rdata.x = view->x - ox / zoom; 199 rdata.y = view->y - oy / zoom; 200 if ( 201 (rdata.x + view->surface->geometry.width + border_width < 0) || 202 (rdata.y + view->surface->geometry.height + border_width < 0) || 203 (rdata.x - border_width > width / zoom) || (rdata.y - border_width > height / zoom) 204 ) { 205 continue; 206 } 207 rdata.is_focussed = (view->surface->surface == focussed); 208 rdata.bordered = view->surface->surface; 209 wlr_xdg_surface_for_each_surface(view->surface, render_surface, &rdata); 210 } 211 212 rdata.zoom = 1; 213 214 struct scratchpad *scratchpad; 215 wl_list_for_each(scratchpad, &wimp.scratchpads, link) { 216 if (scratchpad->is_mapped) { 217 view = scratchpad->view; 218 rdata.x = view->x; 219 rdata.y = view->y; 220 rdata.is_focussed = (view->surface->surface == focussed); 221 rdata.bordered = view->surface->surface; 222 wlr_xdg_surface_for_each_surface(view->surface, render_surface, &rdata); 223 } 224 } 225 226 rdata.bordered = NULL; 227 rdata.is_focussed = false; 228 229 // paint top and overlay layers 230 wl_list_for_each(lview, &output->layer_views[ZWLR_LAYER_SHELL_V1_LAYER_TOP], link) { 231 rdata.x = lview->geo.x; 232 rdata.y = lview->geo.y; 233 wlr_surface_for_each_surface( 234 lview->surface->surface, render_surface, &rdata 235 ); 236 } 237 wl_list_for_each(lview, &output->layer_views[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { 238 rdata.x = lview->geo.x; 239 rdata.y = lview->geo.y; 240 wlr_surface_for_each_surface( 241 lview->surface->surface, render_surface, &rdata 242 ); 243 } 244 245 // paint mark indicator 246 if (wimp.mark_waiting) { 247 struct wlr_box indicator = wimp.mark_indicator.box; 248 indicator.y = height - indicator.height; 249 wlr_render_rect( 250 renderer, &indicator, wimp.mark_indicator.colour, 251 output->wlr_output->transform_matrix 252 ); 253 } 254 255 // paint snap box 256 if (wimp.can_snap) { 257 wlr_render_rect( 258 renderer, &wimp.snap_geobox, wimp.snapbox_colour, 259 output->wlr_output->transform_matrix 260 ); 261 } 262 263 wlr_output_render_software_cursors(output->wlr_output, NULL); // no-op with HW cursors 264 wlr_renderer_end(renderer); 265 wlr_output_commit(output->wlr_output); 266 267 finish: 268 pixman_region32_fini(&damage); 269 } 270 271 272 void damage_box(struct wlr_box *geo, bool add_borders) { 273 struct output *output; 274 275 if (add_borders) { 276 struct wlr_box damaged; 277 int border_width = wimp.current_desk->border_width; 278 memcpy(&damaged, geo, sizeof(struct wlr_box)); 279 damaged.x -= border_width; 280 damaged.y -= border_width; 281 damaged.width += border_width * 2; 282 damaged.height += border_width * 2; 283 wl_list_for_each(output, &wimp.outputs, link) { 284 wlr_output_damage_add_box(output->wlr_output_damage, &damaged); 285 } 286 } else { 287 wl_list_for_each(output, &wimp.outputs, link) { 288 wlr_output_damage_add_box(output->wlr_output_damage, geo); 289 } 290 } 291 } 292 293 294 void damage_by_view(struct view *view, bool with_borders) { 295 double zoom = wimp.current_desk->zoom; 296 297 struct wlr_box geo = { 298 .x = view->x, 299 .y = view->y, 300 .width = view->width * zoom, 301 .height = view->height * zoom, 302 }; 303 304 damage_box(&geo, with_borders); 305 } 306 307 308 void damage_by_lview(struct layer_view *lview) { 309 damage_box(&lview->geo, false); 310 } 311 312 313 void damage_all_outputs() { 314 struct output *output; 315 wl_list_for_each(output, &wimp.outputs, link) { 316 wlr_output_damage_add_whole(output->wlr_output_damage); 317 } 318 } 319 320 321 void damage_all_views() { 322 struct view *view; 323 wl_list_for_each(view, &wimp.current_desk->views, link) { 324 damage_by_view(view, true); 325 } 326 } 327 328 329 void damage_mark_indicator() { 330 struct output *output; 331 struct wlr_box indicator = wimp.mark_indicator.box; 332 int width, height; 333 334 wl_list_for_each(output, &wimp.outputs, link) { 335 wlr_output_effective_resolution(output->wlr_output, &width, &height); 336 struct wlr_box geo = { 337 .x = 0, 338 .y = height - indicator.height, 339 .width = indicator.width, 340 .height = indicator.height, 341 }; 342 wlr_output_damage_add_box(output->wlr_output_damage, &geo); 343 } 344 } 345 346 347 static void on_destroy(struct wl_listener *listener, void *data) { 348 struct output *output = wl_container_of(listener, output, destroy_listener); 349 350 struct layer_view *lview, *tlview; 351 for (int i = 0; i < 4; i++ ) { 352 wl_list_for_each_safe(lview, tlview, &output->layer_views[i], link) { 353 wl_list_remove(&lview->link); 354 wl_list_remove(&lview->map_listener.link); 355 wl_list_remove(&lview->unmap_listener.link); 356 wl_list_remove(&lview->destroy_listener.link); 357 free(lview); 358 }; 359 } 360 361 wl_list_remove(&output->frame_listener.link); 362 wl_list_remove(&output->destroy_listener.link); 363 wl_list_remove(&output->link); 364 free(output); 365 } 366 367 368 static void on_new_output(struct wl_listener *listener, void *data) { 369 struct wlr_output *wlr_output = data; 370 371 if (!wl_list_empty(&wlr_output->modes)) { 372 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 373 wlr_output_set_mode(wlr_output, mode); 374 wlr_output_enable(wlr_output, true); 375 if (!wlr_output_commit(wlr_output)) { 376 return; 377 } 378 } 379 380 struct output *output = calloc(1, sizeof(struct output)); 381 output->wlr_output = wlr_output; 382 wlr_output->data = output; 383 output->wlr_output_damage = wlr_output_damage_create(wlr_output); 384 385 output->frame_listener.notify = on_frame; 386 output->destroy_listener.notify = on_destroy; 387 wl_signal_add(&output->wlr_output_damage->events.frame, &output->frame_listener); 388 wl_signal_add(&wlr_output->events.destroy, &output->destroy_listener); 389 390 wl_list_insert(&wimp.outputs, &output->link); 391 wlr_output_layout_add_auto(wimp.output_layout, wlr_output); 392 393 wl_list_init(&output->layer_views[0]); 394 wl_list_init(&output->layer_views[1]); 395 wl_list_init(&output->layer_views[2]); 396 wl_list_init(&output->layer_views[3]); 397 } 398 399 400 static void output_manager_reconfigure( 401 struct wlr_output_configuration_v1 *config, bool commit 402 ) { 403 struct wlr_output_configuration_head_v1 *config_head; 404 bool ok = 1; 405 406 wl_list_for_each(config_head, &config->heads, link) { 407 struct wlr_output *wlr_output = config_head->state.output; 408 409 wlr_output_enable(wlr_output, config_head->state.enabled); 410 if (config_head->state.enabled) { 411 if (config_head->state.mode) { 412 wlr_output_set_mode(wlr_output, config_head->state.mode); 413 } else { 414 wlr_output_set_custom_mode( 415 wlr_output, 416 config_head->state.custom_mode.width, 417 config_head->state.custom_mode.height, 418 config_head->state.custom_mode.refresh 419 ); 420 } 421 422 wlr_output_layout_move( 423 wimp.output_layout, wlr_output, config_head->state.x, config_head->state.y 424 ); 425 wlr_output_set_transform(wlr_output, config_head->state.transform); 426 wlr_output_set_scale(wlr_output, config_head->state.scale); 427 } 428 429 ok = wlr_output_test(wlr_output); 430 if (!ok) { 431 break; 432 } 433 } 434 435 wl_list_for_each(config_head, &config->heads, link) { 436 if (ok && commit) { 437 wlr_output_commit(config_head->state.output); 438 } else { 439 wlr_output_rollback(config_head->state.output); 440 } 441 } 442 443 if (ok) { 444 wlr_output_configuration_v1_send_succeeded(config); 445 } else { 446 wlr_output_configuration_v1_send_failed(config); 447 } 448 wlr_output_configuration_v1_destroy(config); 449 } 450 451 452 static void on_output_manager_apply(struct wl_listener *listener, void *data) { 453 output_manager_reconfigure(data, true); 454 } 455 456 457 static void on_output_manager_test(struct wl_listener *listener, void *data) { 458 output_manager_reconfigure(data, false); 459 } 460 461 462 static void on_output_layout_change(struct wl_listener *listener, void *data) { 463 struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); 464 465 struct output *output; 466 wl_list_for_each(output, &wimp.outputs, link) { 467 struct wlr_output_configuration_head_v1 *config_head = 468 wlr_output_configuration_head_v1_create(config, output->wlr_output); 469 470 struct wlr_box *box = wlr_output_layout_get_box(wimp.output_layout, output->wlr_output); 471 config_head->state.x = box->x; 472 config_head->state.y = box->y; 473 config_head->state.enabled = output->wlr_output->enabled; 474 config_head->state.mode = output->wlr_output->current_mode; 475 } 476 477 wlr_output_manager_v1_set_configuration(wimp.output_manager, config); 478 } 479 480 481 void set_up_outputs() { 482 wimp.output_layout = wlr_output_layout_create(); 483 wimp.output_layout_change_listener.notify = on_output_layout_change; 484 wl_signal_add(&wimp.output_layout->events.change, &wimp.output_layout_change_listener); 485 486 wl_list_init(&wimp.outputs); 487 wimp.new_output_listener.notify = on_new_output; 488 wl_signal_add(&wimp.backend->events.new_output, &wimp.new_output_listener); 489 490 wlr_screencopy_manager_v1_create(wimp.display); 491 wlr_xdg_output_manager_v1_create(wimp.display, wimp.output_layout); 492 493 wimp.output_manager = wlr_output_manager_v1_create(wimp.display); 494 wimp.output_manager_apply_listener.notify = on_output_manager_apply; 495 wimp.output_manager_test_listener.notify = on_output_manager_test; 496 wl_signal_add(&wimp.output_manager->events.apply, &wimp.output_manager_apply_listener); 497 wl_signal_add(&wimp.output_manager->events.test, &wimp.output_manager_test_listener); 498 499 wlr_gamma_control_manager_v1_create(wimp.display); 500 } 501