├── LICENSE ├── README.md ├── main.c ├── meson.build └── protocol ├── meson.build └── wlr-export-dmabuf-unstable-v1.xml /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 202-2021 ProgAndy 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wdomirror 2 | 3 | > [!IMPORTANT] 4 | > This project has been archived in favor of the more reliable [wf-mirror](https://github.com/Ferdi265/wl-mirror) by Ferdi265 5 | 6 | wdomirror utilizes the wlroots dmabuf export protocol to create a mirror of an outout 7 | with as little overhead as possible. 8 | 9 | ## Building 10 | 11 | meson build 12 | ninja -C build 13 | 14 | ## Usage 15 | 16 | List the outputs and their IDs. 17 | 18 | ./wdomirror 19 | 20 | Create the mirror 21 | 22 | ./wdomirror $ID 23 | 24 | wdomirror does not preserve the aspect ration, so make sure to set the size of the mirror window correctly. 25 | For fullscreen, make sure that both source and target outputs have the same aspect ratio. 26 | 27 | 28 | ## License 29 | 30 | [MIT](LICENSE) 31 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* Copyright (c) 2020 ProgAndy */ 3 | 4 | #define _POSIX_C_SOURCE 200809L 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "wlr-export-dmabuf-unstable-v1-client-protocol.h" 15 | #include "linux-dmabuf-unstable-v1-client-protocol.h" 16 | #include "xdg-shell-client-protocol.h" 17 | #include "viewporter-client-protocol.h" 18 | 19 | struct wayland_output { 20 | struct wl_list link; 21 | uint32_t id; 22 | struct wl_output *output; 23 | char *make; 24 | char *model; 25 | int width; 26 | int height; 27 | int framerate; 28 | }; 29 | 30 | struct frame_object { 31 | uint32_t index; 32 | int32_t fd; 33 | uint32_t size; 34 | uint32_t offset; 35 | uint32_t stride; 36 | uint32_t plane_index; 37 | }; 38 | 39 | struct frame { 40 | struct zwlr_export_dmabuf_frame_v1 *frame; 41 | struct wl_buffer *buffer; 42 | uint32_t width; 43 | uint32_t height; 44 | uint32_t offset_x; 45 | uint32_t offset_y; 46 | uint32_t buffer_flags; 47 | uint32_t flags; 48 | uint32_t format; 49 | uint32_t mod_high; 50 | uint32_t mod_low; 51 | uint32_t num_objects; 52 | struct frame_object objects[]; 53 | }; 54 | 55 | struct window { 56 | struct wl_surface *surface; 57 | struct xdg_surface *xdg_surface; 58 | struct xdg_toplevel *xdg_toplevel; 59 | struct wp_viewport *viewport; 60 | bool init; 61 | int width, height; 62 | }; 63 | 64 | struct mirror_context { 65 | struct wl_display *display; 66 | struct wl_registry *registry; 67 | struct zwlr_export_dmabuf_manager_v1 *export_manager; 68 | struct wl_compositor *compositor; 69 | struct xdg_wm_base *xdg_wm_base; 70 | struct zwp_linux_dmabuf_v1 *dmabuf; 71 | struct wp_viewporter *viewporter; 72 | 73 | struct wl_list output_list; 74 | 75 | struct window *window; 76 | /* Target */ 77 | struct wl_output *source_output; 78 | int w; 79 | int h; 80 | bool with_cursor; 81 | 82 | /* Main frame callback */ 83 | struct zwlr_export_dmabuf_frame_v1 *frame_callback; 84 | 85 | /* If something happens during capture */ 86 | int err; 87 | bool quit; 88 | 89 | /* dmabuf frames */ 90 | struct frame *incomplete_frame; 91 | struct frame *next_frame; 92 | 93 | 94 | }; 95 | struct mirror_context *q_ctx = NULL; 96 | 97 | void window_destroy(struct window *window); 98 | int window_create(struct mirror_context *ctx); 99 | 100 | static void frame_free(struct frame *f) { 101 | 102 | if (f) { 103 | if (f->buffer) 104 | wl_buffer_destroy(f->buffer); 105 | for (uint32_t i = 0; i < f->num_objects; ++i) { 106 | close(f->objects[i].fd); 107 | } 108 | if (f->frame) 109 | zwlr_export_dmabuf_frame_v1_destroy(f->frame); 110 | free(f); 111 | } 112 | 113 | } 114 | 115 | static void output_handle_geometry(void *data, struct wl_output *wl_output, 116 | int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, 117 | int32_t subpixel, const char *make, const char *model, 118 | int32_t transform) { 119 | struct wayland_output *output = data; 120 | output->make = strdup(make); 121 | output->model = strdup(model); 122 | } 123 | 124 | static void output_handle_mode(void *data, struct wl_output *wl_output, 125 | uint32_t flags, int32_t width, int32_t height, int32_t refresh) { 126 | if (flags & WL_OUTPUT_MODE_CURRENT) { 127 | struct wayland_output *output = data; 128 | output->width = width; 129 | output->height = height; 130 | output->framerate = refresh; 131 | } 132 | } 133 | 134 | static void output_handle_done(void* data, struct wl_output *wl_output) { 135 | /* Nothing to do */ 136 | } 137 | 138 | static void output_handle_scale(void* data, struct wl_output *wl_output, 139 | int32_t factor) { 140 | /* Nothing to do */ 141 | } 142 | 143 | static const struct wl_output_listener output_listener = { 144 | .geometry = output_handle_geometry, 145 | .mode = output_handle_mode, 146 | .done = output_handle_done, 147 | .scale = output_handle_scale, 148 | }; 149 | 150 | static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) 151 | { 152 | xdg_wm_base_pong(xdg_wm_base, serial); 153 | } 154 | 155 | static const struct xdg_wm_base_listener xdg_wm_base_listener = { 156 | xdg_wm_base_ping, 157 | }; 158 | 159 | 160 | static void registry_handle_add(void *data, struct wl_registry *reg, 161 | uint32_t id, const char *interface, uint32_t ver) { 162 | struct mirror_context *ctx = data; 163 | 164 | if (!strcmp(interface, wl_output_interface.name)) { 165 | struct wayland_output *output = calloc(1,sizeof(*output)); 166 | 167 | output->id = id; 168 | output->output = wl_registry_bind(reg, id, &wl_output_interface, 1); 169 | 170 | wl_output_add_listener(output->output, &output_listener, output); 171 | wl_list_insert(&ctx->output_list, &output->link); 172 | } 173 | 174 | if (!strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { 175 | ctx->export_manager = wl_registry_bind(reg, id, 176 | &zwlr_export_dmabuf_manager_v1_interface, 1); 177 | } 178 | 179 | if (!strcmp(interface, wl_compositor_interface.name)) { 180 | ctx->compositor = wl_registry_bind(reg, id, &wl_compositor_interface, 1); 181 | } 182 | 183 | if (!strcmp(interface, wp_viewporter_interface.name)) { 184 | ctx->viewporter = wl_registry_bind(reg, id, &wp_viewporter_interface, 1); 185 | } 186 | 187 | if (!strcmp(interface, xdg_wm_base_interface.name)) { 188 | ctx->xdg_wm_base = wl_registry_bind(reg, id, &xdg_wm_base_interface, 1); 189 | xdg_wm_base_add_listener(ctx->xdg_wm_base, &xdg_wm_base_listener, ctx); 190 | } 191 | 192 | if (!strcmp(interface, zwp_linux_dmabuf_v1_interface.name)) { 193 | if (ver < 3) { 194 | return; 195 | } 196 | ctx->dmabuf = wl_registry_bind(reg, id, &zwp_linux_dmabuf_v1_interface, 3); 197 | /* assume we get a compatible format from dmabuf export */ 198 | } 199 | } 200 | 201 | static void remove_output(struct wayland_output *out) { 202 | wl_list_remove(&out->link); 203 | free(out->make); 204 | free(out->model); 205 | free(out); 206 | } 207 | 208 | static struct wayland_output *find_output(struct mirror_context *ctx, 209 | struct wl_output *out, uint32_t id) { 210 | struct wayland_output *output, *tmp; 211 | wl_list_for_each_safe(output, tmp, &ctx->output_list, link) { 212 | if ((output->output == out) || (output->id == id)) { 213 | return output; 214 | } 215 | } 216 | return NULL; 217 | } 218 | 219 | static void registry_handle_remove(void *data, struct wl_registry *reg, 220 | uint32_t id) { 221 | remove_output(find_output((struct mirror_context *)data, NULL, id)); 222 | } 223 | 224 | static const struct wl_registry_listener registry_listener = { 225 | .global = registry_handle_add, 226 | .global_remove = registry_handle_remove, 227 | }; 228 | 229 | static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, 230 | uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y, 231 | uint32_t buffer_flags, uint32_t flags, uint32_t format, 232 | uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) { 233 | struct mirror_context *ctx = data; 234 | int err = 0; 235 | 236 | /* Allocate DRM specific struct */ 237 | struct frame *f = calloc(1,sizeof(*f)+num_objects*sizeof(struct frame_object)); 238 | if (!f) { 239 | err = ENOMEM; 240 | goto fail; 241 | } 242 | // suppress y-invert flag. 243 | flags &= ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; 244 | f->frame = frame; 245 | f->width = width; 246 | f->height = height; 247 | f->offset_x = offset_x; 248 | f->offset_y = offset_y; 249 | f->buffer_flags = buffer_flags; 250 | f->flags = flags; 251 | f->format = format; 252 | f->mod_high = mod_high; 253 | f->mod_low = mod_low; 254 | f->num_objects = num_objects; 255 | 256 | f->width = width; 257 | f->height = height; 258 | 259 | ctx->incomplete_frame = f; 260 | 261 | return; 262 | 263 | fail: 264 | ctx->err = err; 265 | if (f) frame_free(f); 266 | } 267 | 268 | static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, 269 | uint32_t index, int32_t fd, uint32_t size, uint32_t offset, 270 | uint32_t stride, uint32_t plane_index) { 271 | struct mirror_context *ctx = data; 272 | struct frame *f = ctx->incomplete_frame; 273 | f->objects[index].index = index; 274 | f->objects[index].fd = fd; 275 | f->objects[index].size = size; 276 | f->objects[index].offset = offset; 277 | f->objects[index].stride = stride; 278 | f->objects[index].plane_index = plane_index; 279 | 280 | } 281 | 282 | static void 283 | buffer_release(void *data, struct wl_buffer *buffer) 284 | { 285 | struct frame *f = data; 286 | 287 | frame_free(f); 288 | } 289 | 290 | static const struct wl_buffer_listener buffer_listener = { 291 | buffer_release 292 | }; 293 | 294 | 295 | static void display_frame(struct mirror_context *ctx); 296 | static void request_frame(struct mirror_context *ctx); 297 | 298 | static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, 299 | uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { 300 | struct mirror_context *ctx = data; 301 | struct frame *f = ctx->incomplete_frame; 302 | ctx->incomplete_frame = NULL; 303 | struct zwp_linux_buffer_params_v1 *params; 304 | params = zwp_linux_dmabuf_v1_create_params(ctx->dmabuf); 305 | 306 | for (uint32_t i = 0; i < f->num_objects; ++i) { 307 | zwp_linux_buffer_params_v1_add(params, 308 | f->objects[i].fd, 309 | f->objects[i].plane_index, 310 | f->objects[i].offset, 311 | f->objects[i].stride, 312 | f->mod_high, 313 | f->mod_low); 314 | } 315 | 316 | 317 | // TODO: handle failed creation 318 | // TODO: handle rotation 319 | // TODO: necessary to handle Y_INVERT differences? 320 | f->buffer = 321 | zwp_linux_buffer_params_v1_create_immed(params, 322 | f->width, 323 | f->height, 324 | f->format, 325 | f->flags); 326 | zwp_linux_buffer_params_v1_destroy(params); 327 | wl_buffer_add_listener(f->buffer, &buffer_listener, f); 328 | 329 | if (ctx->next_frame) { 330 | frame_free(ctx->next_frame); 331 | } 332 | ctx->next_frame = f; 333 | 334 | 335 | //* Frames will not be requested in the render loop 336 | if (!ctx->quit && !ctx->err) { 337 | if (ctx->window && ctx->window->init) { 338 | display_frame(ctx); 339 | } 340 | request_frame(ctx); 341 | } 342 | return; 343 | } 344 | 345 | static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, 346 | uint32_t reason) { 347 | struct mirror_context *ctx = data; 348 | frame_free(ctx->incomplete_frame); 349 | if (reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT) { 350 | /* Permanent error. Exit! */ 351 | ctx->err = true; 352 | } else { 353 | request_frame(ctx); 354 | } 355 | } 356 | 357 | static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = { 358 | .frame = frame_start, 359 | .object = frame_object, 360 | .ready = frame_ready, 361 | .cancel = frame_cancel, 362 | }; 363 | 364 | static void request_frame(struct mirror_context *ctx) { 365 | ctx->frame_callback = zwlr_export_dmabuf_manager_v1_capture_output( 366 | ctx->export_manager, ctx->with_cursor, ctx->source_output); 367 | 368 | zwlr_export_dmabuf_frame_v1_add_listener(ctx->frame_callback, 369 | &frame_listener, ctx); 370 | } 371 | 372 | 373 | 374 | 375 | static void xdg_surface_handle_configure(void *data, 376 | struct xdg_surface *surface, uint32_t serial) { 377 | struct mirror_context *ctx = data; 378 | 379 | xdg_surface_ack_configure(surface, serial); 380 | /* mark window ready */ 381 | ctx->window->init = true; 382 | } 383 | 384 | static const struct xdg_surface_listener xdg_surface_listener = { 385 | xdg_surface_handle_configure, 386 | }; 387 | 388 | static void xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel, 389 | int32_t width, int32_t height, struct wl_array *states) { 390 | 391 | struct mirror_context *ctx = data; 392 | if (width > 0) { 393 | ctx->window->width = width; 394 | } 395 | if (height > 0) { 396 | ctx->window->height = height; 397 | } 398 | 399 | if (ctx->window->viewport && ctx->window->width > 0 && ctx->window->height > 0) { 400 | // TODO: aspect ratio? 401 | wp_viewport_set_destination(ctx->window->viewport, ctx->window->width, ctx->window->height); 402 | } 403 | } 404 | 405 | static void 406 | xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) 407 | { 408 | struct mirror_context *ctx = data; 409 | ctx->quit = true; 410 | } 411 | 412 | static const struct xdg_toplevel_listener xdg_toplevel_listener = { 413 | xdg_toplevel_handle_configure, 414 | xdg_toplevel_handle_close, 415 | }; 416 | 417 | 418 | static void on_quit_signal(int signo) { 419 | if (q_ctx) { 420 | q_ctx->quit = true; 421 | } 422 | } 423 | 424 | 425 | static int main_loop(struct mirror_context *ctx) { 426 | 427 | q_ctx = ctx; 428 | 429 | if (signal(SIGINT, on_quit_signal) == SIG_ERR) { 430 | /*av_log(ctx, AV_LOG_ERROR, "Unable to install signal handler!\n");*/ 431 | return EINVAL; 432 | } 433 | 434 | request_frame(ctx); 435 | 436 | while (wl_display_dispatch(ctx->display) != -1 && !ctx->err && !ctx->quit); 437 | 438 | return ctx->err; 439 | } 440 | 441 | static int init(struct mirror_context *ctx) { 442 | wl_list_init(&ctx->output_list); 443 | 444 | ctx->display = wl_display_connect(NULL); 445 | if (!ctx->display) { 446 | puts("Failed to connect to display"); 447 | return EINVAL; 448 | } 449 | 450 | 451 | ctx->registry = wl_display_get_registry(ctx->display); 452 | wl_registry_add_listener(ctx->registry, ®istry_listener, ctx); 453 | 454 | wl_display_roundtrip(ctx->display); 455 | wl_display_dispatch(ctx->display); 456 | assert(ctx->compositor); 457 | 458 | if (!ctx->export_manager) { 459 | printf("Compositor doesn't support %s!\n", 460 | zwlr_export_dmabuf_manager_v1_interface.name); 461 | return -1; 462 | } 463 | if (!ctx->dmabuf) { 464 | printf("Compositor doesn't support %s!\n", 465 | zwp_linux_dmabuf_v1_interface.name); 466 | return -1; 467 | } 468 | if (!ctx->xdg_wm_base) { 469 | printf("Compositor doesn't support %s!\n", 470 | xdg_wm_base_interface.name); 471 | return -1; 472 | } 473 | 474 | return 0; 475 | } 476 | 477 | 478 | void window_destroy(struct window *window) { 479 | if (window->xdg_toplevel) { 480 | xdg_toplevel_destroy(window->xdg_toplevel); 481 | } 482 | if (window->xdg_surface) { 483 | xdg_surface_destroy(window->xdg_surface); 484 | } 485 | if (window->surface) { 486 | wl_surface_destroy(window->surface); 487 | } 488 | free(window); 489 | } 490 | 491 | int window_create(struct mirror_context *ctx) { 492 | struct window *window; 493 | int err = 0; 494 | 495 | assert(!ctx->window); 496 | 497 | window = calloc(1,sizeof(*window)); 498 | if (!window) { 499 | return 1; 500 | } 501 | 502 | window->width = ctx->w; 503 | window->height = ctx->h; 504 | 505 | window->surface = wl_compositor_create_surface(ctx->compositor); 506 | 507 | window->xdg_surface = xdg_wm_base_get_xdg_surface(ctx->xdg_wm_base, window->surface); 508 | xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, ctx); 509 | 510 | window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); 511 | xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, ctx); 512 | 513 | if (ctx->viewporter) { 514 | window->viewport = wp_viewporter_get_viewport(ctx->viewporter, window->surface); 515 | } 516 | 517 | xdg_toplevel_set_title(window->xdg_toplevel, "wlr dmabuf output mirror"); 518 | 519 | wl_surface_commit(window->surface); 520 | 521 | ctx->window = window; 522 | 523 | return err; 524 | } 525 | 526 | 527 | static void display_frame(struct mirror_context *ctx) { 528 | 529 | struct frame *next = ctx->next_frame; 530 | ctx->next_frame = NULL; 531 | if (!next) return; 532 | 533 | 534 | wl_surface_attach(ctx->window->surface, next->buffer, 0, 0); 535 | wl_surface_damage(ctx->window->surface, 0, 0, ctx->window->width, ctx->window->height); 536 | 537 | wl_surface_commit(ctx->window->surface); 538 | } 539 | 540 | 541 | static void uninit(struct mirror_context *ctx); 542 | 543 | int main(int argc, char *argv[]) { 544 | int err; 545 | struct mirror_context ctx = { 0 }; 546 | struct wayland_output *o, *tmp_o; 547 | 548 | 549 | err = init(&ctx); 550 | if (err) { 551 | puts("Could not initialize wayland"); 552 | goto end; 553 | } 554 | 555 | if (argc != 2 || !strcmp(argv[1], "-h")) { 556 | printf("wdomirror SOURCE_ID\n" 557 | "Mirror wlroots output with dmabuf protocols.\n\n"); 558 | wl_list_for_each_reverse_safe(o, tmp_o, &ctx.output_list, link) { 559 | printf("Mirrorable output: %s Model: %s: ID: %i\n", 560 | o->make, o->model, o->id); 561 | } 562 | err = 1; 563 | goto end; 564 | } 565 | 566 | const int o_id = strtol(argv[1], NULL, 10); 567 | o = find_output(&ctx, NULL, o_id); 568 | if (!o) { 569 | printf("Unable to find output with ID %i!\n", o_id); 570 | err = 1; 571 | goto end; 572 | } 573 | printf("Mirroring output: %s Model: %s: ID: %i\n", 574 | o->make, o->model, o->id); 575 | 576 | ctx.source_output = o->output; 577 | ctx.w = o->width; 578 | ctx.h = o->height; 579 | ctx.with_cursor = true; 580 | 581 | 582 | err = window_create(&ctx); 583 | if (err) { 584 | goto end; 585 | } 586 | 587 | err = main_loop(&ctx); 588 | if (err) { 589 | goto end; 590 | } 591 | 592 | end: 593 | uninit(&ctx); 594 | return err; 595 | } 596 | 597 | static void uninit(struct mirror_context *ctx) { 598 | struct wayland_output *output, *tmp_o; 599 | wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) { 600 | remove_output(output); 601 | } 602 | if (ctx->window) 603 | window_destroy(ctx->window); 604 | 605 | if (ctx->export_manager) { 606 | zwlr_export_dmabuf_manager_v1_destroy(ctx->export_manager); 607 | } 608 | if (ctx->dmabuf) { 609 | zwp_linux_dmabuf_v1_destroy(ctx->dmabuf); 610 | } 611 | if (ctx->xdg_wm_base) { 612 | xdg_wm_base_destroy(ctx->xdg_wm_base); 613 | } 614 | if (ctx->compositor) { 615 | wl_compositor_destroy(ctx->compositor); 616 | } 617 | if (ctx->registry) { 618 | wl_registry_destroy(ctx->registry); 619 | } 620 | if (ctx->display) { 621 | wl_display_flush(ctx->display); 622 | wl_display_disconnect(ctx->display); 623 | } 624 | 625 | if (ctx->next_frame) { 626 | frame_free(ctx->next_frame); 627 | } 628 | 629 | } 630 | 631 | /* vim: set ts=2 sw=2 et: */ 632 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'wdomirror', 3 | 'c', 4 | version: '0.1.0', 5 | license: 'MIT', 6 | meson_version: '>=0.54.0', 7 | default_options: [ 8 | 'c_std=c11', 9 | 'warning_level=2', 10 | 'werror=true', 11 | ], 12 | ) 13 | 14 | cc = meson.get_compiler('c') 15 | 16 | add_project_arguments(cc.get_supported_arguments([ 17 | '-Wno-missing-braces', 18 | '-Wno-unused-parameter', 19 | ]), language: 'c') 20 | 21 | wayland_client = dependency('wayland-client') 22 | wayland_protos = dependency('wayland-protocols', version: '>=1.17') 23 | 24 | subdir('protocol') 25 | 26 | mirror_files = [ 'main.c' ] 27 | mirror_deps = [ 28 | wayland_client, 29 | wayland_protos, 30 | client_protos, 31 | ] 32 | 33 | 34 | proto_inc = include_directories('protocol') 35 | 36 | executable( 37 | meson.project_name(), 38 | mirror_files, 39 | dependencies: mirror_deps, 40 | include_directories: proto_inc, 41 | install: true 42 | ) 43 | -------------------------------------------------------------------------------- /protocol/meson.build: -------------------------------------------------------------------------------- 1 | wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') 2 | 3 | wayland_scanner = find_program('wayland-scanner') 4 | 5 | # should check wayland_scanner's version, but it is hard to get 6 | if wayland_client.version().version_compare('>=1.14.91') 7 | code_type = 'private-code' 8 | else 9 | code_type = 'code' 10 | endif 11 | 12 | wayland_scanner_code = generator( 13 | wayland_scanner, 14 | output: '@BASENAME@-protocol.c', 15 | arguments: [code_type, '@INPUT@', '@OUTPUT@'], 16 | ) 17 | 18 | wayland_scanner_client = generator( 19 | wayland_scanner, 20 | output: '@BASENAME@-client-protocol.h', 21 | arguments: ['client-header', '@INPUT@', '@OUTPUT@'], 22 | ) 23 | 24 | client_protocols = [ 25 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 26 | [wl_protocol_dir, 'stable/viewporter/viewporter.xml'], 27 | [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], 28 | 'wlr-export-dmabuf-unstable-v1.xml', 29 | ] 30 | 31 | 32 | client_protos_src = [] 33 | client_protos_headers = [] 34 | 35 | foreach p : client_protocols 36 | xml = join_paths(p) 37 | client_protos_src += wayland_scanner_code.process(xml) 38 | client_protos_headers += wayland_scanner_client.process(xml) 39 | endforeach 40 | 41 | lib_client_protos = static_library( 42 | 'client_protos', 43 | client_protos_src + client_protos_headers, 44 | dependencies: [wayland_client] 45 | ) # for the include directory 46 | 47 | client_protos = declare_dependency( 48 | link_with: lib_client_protos, 49 | sources: client_protos_headers, 50 | ) 51 | -------------------------------------------------------------------------------- /protocol/wlr-export-dmabuf-unstable-v1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright © 2018 Rostislav Pehlivanov 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a 7 | copy of this software and associated documentation files (the "Software"), 8 | to deal in the Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | and/or sell copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice (including the next 14 | paragraph) shall be included in all copies or substantial portions of the 15 | Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | 25 | 26 | 27 | An interface to capture surfaces in an efficient way by exporting DMA-BUFs. 28 | 29 | Warning! The protocol described in this file is experimental and 30 | backward incompatible changes may be made. Backward compatible changes 31 | may be added together with the corresponding interface version bump. 32 | Backward incompatible changes are done by bumping the version number in 33 | the protocol and interface names and resetting the interface version. 34 | Once the protocol is to be declared stable, the 'z' prefix and the 35 | version number in the protocol and interface names are removed and the 36 | interface version number is reset. 37 | 38 | 39 | 40 | 41 | This object is a manager with which to start capturing from sources. 42 | 43 | 44 | 45 | 46 | Capture the next frame of a an entire output. 47 | 48 | 49 | 51 | 52 | 53 | 54 | 55 | 56 | All objects created by the manager will still remain valid, until their 57 | appropriate destroy request has been called. 58 | 59 | 60 | 61 | 62 | 63 | 64 | This object represents a single DMA-BUF frame. 65 | 66 | If the capture is successful, the compositor will first send a "frame" 67 | event, followed by one or several "object". When the frame is available 68 | for readout, the "ready" event is sent. 69 | 70 | If the capture failed, the "cancel" event is sent. This can happen anytime 71 | before the "ready" event. 72 | 73 | Once either a "ready" or a "cancel" event is received, the client should 74 | destroy the frame. Once an "object" event is received, the client is 75 | responsible for closing the associated file descriptor. 76 | 77 | All frames are read-only and may not be written into or altered. 78 | 79 | 80 | 81 | 82 | Special flags that should be respected by the client. 83 | 84 | 86 | 87 | 88 | 89 | 90 | Main event supplying the client with information about the frame. If the 91 | capture didn't fail, this event is always emitted first before any other 92 | events. 93 | 94 | This event is followed by a number of "object" as specified by the 95 | "num_objects" argument. 96 | 97 | 99 | 101 | 103 | 105 | 108 | 110 | 112 | 114 | 116 | 118 | 119 | 120 | 121 | 122 | Event which serves to supply the client with the file descriptors 123 | containing the data for each object. 124 | 125 | After receiving this event, the client must always close the file 126 | descriptor as soon as they're done with it and even if the frame fails. 127 | 128 | 130 | 132 | 134 | 136 | 138 | 140 | 141 | 142 | 143 | 144 | This event is sent as soon as the frame is presented, indicating it is 145 | available for reading. This event includes the time at which 146 | presentation happened at. 147 | 148 | The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, 149 | each component being an unsigned 32-bit value. Whole seconds are in 150 | tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, 151 | and the additional fractional part in tv_nsec as nanoseconds. Hence, 152 | for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part 153 | may have an arbitrary offset at start. 154 | 155 | After receiving this event, the client should destroy this object. 156 | 157 | 159 | 161 | 163 | 164 | 165 | 166 | 167 | Indicates reason for cancelling the frame. 168 | 169 | 171 | 173 | 175 | 176 | 177 | 178 | 179 | If the capture failed or if the frame is no longer valid after the 180 | "frame" event has been emitted, this event will be used to inform the 181 | client to scrap the frame. 182 | 183 | If the failure is temporary, the client may capture again the same 184 | source. If the failure is permanent, any further attempts to capture the 185 | same source will fail again. 186 | 187 | After receiving this event, the client should destroy this object. 188 | 189 | 191 | 192 | 193 | 194 | 195 | Unreferences the frame. This request must be called as soon as its no 196 | longer used. 197 | 198 | It can be called at any time by the client. The client will still have 199 | to close any FDs it has been given. 200 | 201 | 202 | 203 | 204 | --------------------------------------------------------------------------------