1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include "context.h"
7 #include <EGL/eglext.h>
8 #include "linux-dmabuf-unstable-v1.h"
9 
10 // --- dmabuf_frame event handlers ---
11 
dmabuf_frame_event_frame(void * data,struct zwlr_export_dmabuf_frame_v1 * frame,uint32_t width,uint32_t height,uint32_t x,uint32_t y,uint32_t buffer_flags,uint32_t frame_flags,uint32_t format,uint32_t mod_high,uint32_t mod_low,uint32_t num_objects)12 static void dmabuf_frame_event_frame(
13     void * data, struct zwlr_export_dmabuf_frame_v1 * frame,
14     uint32_t width, uint32_t height, uint32_t x, uint32_t y,
15     uint32_t buffer_flags, uint32_t frame_flags, uint32_t format,
16     uint32_t mod_high, uint32_t mod_low, uint32_t num_objects
17 ) {
18     ctx_t * ctx = (ctx_t *)data;
19     log_debug(ctx, "dmabuf_frame: received %dx%d frame with %d objects\n", width, height, num_objects);
20     if (ctx->mirror.state != STATE_WAIT_FRAME) {
21         log_error("dmabuf_frame: got frame while in state %d\n", ctx->mirror.state);
22         exit_fail(ctx);
23     } else if (num_objects > 4) {
24         log_error("dmabuf_frame: got frame with more than 4 objects\n");
25         exit_fail(ctx);
26     }
27 
28     uint32_t unhandled_buffer_flags = buffer_flags & ~(
29         ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT
30     );
31     if (unhandled_buffer_flags != 0) {
32         log_error("dmabuf_frame: frame uses unhandled buffer flags, buffer_flags = {");
33         if (buffer_flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) fprintf(stderr, "Y_INVERT, ");
34         if (buffer_flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED) fprintf(stderr, "INTERLACED, ");
35         if (buffer_flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST) fprintf(stderr, "BOTTOM_FIRST, ");
36         fprintf(stderr, "}\n");
37     }
38 
39     uint32_t unhandled_frame_flags = frame_flags & ~(
40         ZWLR_EXPORT_DMABUF_FRAME_V1_FLAGS_TRANSIENT
41     );
42     if (unhandled_frame_flags != 0) {
43         log_error("dmabuf_frame: frame uses unhandled frame flags, frame_flags = {");
44         if (frame_flags & ZWLR_EXPORT_DMABUF_FRAME_V1_FLAGS_TRANSIENT) fprintf(stderr, "TRANSIENT, ");
45         fprintf(stderr, "}\n");
46     }
47 
48     ctx->mirror.width = width;
49     ctx->mirror.height = height;
50     ctx->mirror.x = x;
51     ctx->mirror.y = y;
52     ctx->mirror.buffer_flags = buffer_flags;
53     ctx->mirror.frame_flags = frame_flags;
54     ctx->mirror.format = format;
55     ctx->mirror.modifier_high = mod_high;
56     ctx->mirror.modifier_low = mod_low;
57     ctx->mirror.num_objects = num_objects;
58 
59     ctx->mirror.state = STATE_WAIT_OBJECTS;
60     ctx->mirror.processed_objects = 0;
61 
62     (void)frame;
63 }
64 
dmabuf_frame_event_object(void * data,struct zwlr_export_dmabuf_frame_v1 * frame,uint32_t index,int32_t fd,uint32_t size,uint32_t offset,uint32_t stride,uint32_t plane_index)65 static void dmabuf_frame_event_object(
66     void * data, struct zwlr_export_dmabuf_frame_v1 * frame,
67     uint32_t index, int32_t fd, uint32_t size,
68     uint32_t offset, uint32_t stride, uint32_t plane_index
69 ) {
70     ctx_t * ctx = (ctx_t *)data;
71     log_debug(ctx, "dmabuf_frame: received %d byte object with plane_index %d\n", size, plane_index);
72     if (ctx->mirror.state != STATE_WAIT_OBJECTS) {
73         log_error("dmabuf_frame: got object while in state %d\n", ctx->mirror.state);
74         exit_fail(ctx);
75     } else if (index >= ctx->mirror.num_objects) {
76         log_error("dmabuf_frame: got object with out-of-bounds index %d\n", index);
77         exit_fail(ctx);
78     }
79 
80     ctx->mirror.objects[index].fd = fd;
81     ctx->mirror.objects[index].size = size;
82     ctx->mirror.objects[index].offset = offset;
83     ctx->mirror.objects[index].stride = stride;
84     ctx->mirror.objects[index].plane_index = plane_index;
85 
86     ctx->mirror.processed_objects++;
87     if (ctx->mirror.processed_objects == ctx->mirror.num_objects) {
88         ctx->mirror.state = STATE_WAIT_READY;
89     }
90 
91     (void)frame;
92 }
93 
dmabuf_frame_event_ready(void * data,struct zwlr_export_dmabuf_frame_v1 * frame,uint32_t sec_hi,uint32_t sec_lo,uint32_t nsec)94 static void dmabuf_frame_event_ready(
95     void * data, struct zwlr_export_dmabuf_frame_v1 * frame,
96     uint32_t sec_hi, uint32_t sec_lo, uint32_t nsec
97 ) {
98     ctx_t * ctx = (ctx_t *)data;
99     log_debug(ctx, "dmabuf_frame: frame is ready\n");
100     if (ctx->mirror.state != STATE_WAIT_READY) {
101         log_error("dmabuf_frame: got ready while in state %d\n", ctx->mirror.state);
102         exit_fail(ctx);
103     }
104 
105     if (ctx->mirror.frame_image != EGL_NO_IMAGE) {
106         log_debug(ctx, "dmabuf_frame: destroying old EGL image\n");
107         eglDestroyImage(ctx->egl.display, ctx->mirror.frame_image);
108     }
109 
110     log_debug(ctx, "dmabuf_frame: creating EGL image from dmabuf\n");
111     int i = 0;
112     EGLAttrib image_attribs[6 + 10 * 4 + 1];
113 
114     image_attribs[i++] = EGL_WIDTH;
115     image_attribs[i++] = ctx->mirror.width;
116     image_attribs[i++] = EGL_HEIGHT;
117     image_attribs[i++] = ctx->mirror.height;
118     image_attribs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
119     image_attribs[i++] = ctx->mirror.format;
120 
121     if (ctx->mirror.num_objects >= 1) {
122         image_attribs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT;
123         image_attribs[i++] = ctx->mirror.objects[0].fd;
124         image_attribs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
125         image_attribs[i++] = ctx->mirror.objects[0].offset;
126         image_attribs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
127         image_attribs[i++] = ctx->mirror.objects[0].stride;
128         image_attribs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
129         image_attribs[i++] = ctx->mirror.modifier_low;
130         image_attribs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
131         image_attribs[i++] = ctx->mirror.modifier_high;
132     }
133 
134     if (ctx->mirror.num_objects >= 2) {
135         image_attribs[i++] = EGL_DMA_BUF_PLANE1_FD_EXT;
136         image_attribs[i++] = ctx->mirror.objects[1].fd;
137         image_attribs[i++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
138         image_attribs[i++] = ctx->mirror.objects[1].offset;
139         image_attribs[i++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
140         image_attribs[i++] = ctx->mirror.objects[1].stride;
141         image_attribs[i++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
142         image_attribs[i++] = ctx->mirror.modifier_low;
143         image_attribs[i++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
144         image_attribs[i++] = ctx->mirror.modifier_high;
145     }
146 
147     if (ctx->mirror.num_objects >= 3) {
148         image_attribs[i++] = EGL_DMA_BUF_PLANE2_FD_EXT;
149         image_attribs[i++] = ctx->mirror.objects[2].fd;
150         image_attribs[i++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
151         image_attribs[i++] = ctx->mirror.objects[2].offset;
152         image_attribs[i++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
153         image_attribs[i++] = ctx->mirror.objects[2].stride;
154         image_attribs[i++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
155         image_attribs[i++] = ctx->mirror.modifier_low;
156         image_attribs[i++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
157         image_attribs[i++] = ctx->mirror.modifier_high;
158     }
159 
160     if (ctx->mirror.num_objects >= 4) {
161         image_attribs[i++] = EGL_DMA_BUF_PLANE3_FD_EXT;
162         image_attribs[i++] = ctx->mirror.objects[3].fd;
163         image_attribs[i++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT;
164         image_attribs[i++] = ctx->mirror.objects[3].offset;
165         image_attribs[i++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
166         image_attribs[i++] = ctx->mirror.objects[3].stride;
167         image_attribs[i++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT;
168         image_attribs[i++] = ctx->mirror.modifier_low;
169         image_attribs[i++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT;
170         image_attribs[i++] = ctx->mirror.modifier_high;
171     }
172 
173     image_attribs[i++] = EGL_NONE;
174 
175     ctx->mirror.frame_image = eglCreateImage(ctx->egl.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, image_attribs);
176     if (ctx->mirror.frame_image == EGL_NO_IMAGE) {
177         log_error("dmabuf_frame: failed to create EGL image from dmabuf\n");
178         exit_fail(ctx);
179     }
180 
181     log_debug(ctx, "dmabuf_frame: binding image to EGL texture\n");
182     glBindTexture(GL_TEXTURE_2D, ctx->egl.texture);
183     ctx->egl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, ctx->mirror.frame_image);
184     ctx->egl.texture_initialized = true;
185 
186     log_debug(ctx, "dmabuf_frame: setting buffer flags\n");
187     ctx->mirror.invert_y = ctx->mirror.buffer_flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
188 
189     log_debug(ctx, "dmabuf_frame: setting frame aspect ratio\n");
190     ctx->egl.width = ctx->mirror.width;
191     ctx->egl.height = ctx->mirror.height;
192     resize_viewport_egl(ctx);
193 
194     log_debug(ctx, "dmabuf_frame: closing dmabuf fds\n");
195     for (unsigned int i = 0; i < ctx->mirror.num_objects; i++) {
196         close(ctx->mirror.objects[i].fd);
197         ctx->mirror.objects[i].fd = -1;
198     }
199 
200     zwlr_export_dmabuf_frame_v1_destroy(ctx->mirror.frame);
201     ctx->mirror.frame = NULL;
202     ctx->mirror.state = STATE_READY;
203 
204     (void)frame;
205     (void)sec_hi;
206     (void)sec_lo;
207     (void)nsec;
208 }
209 
dmabuf_frame_event_cancel(void * data,struct zwlr_export_dmabuf_frame_v1 * frame,enum zwlr_export_dmabuf_frame_v1_cancel_reason reason)210 static void dmabuf_frame_event_cancel(
211     void * data, struct zwlr_export_dmabuf_frame_v1 * frame,
212     enum zwlr_export_dmabuf_frame_v1_cancel_reason reason
213 ) {
214     ctx_t * ctx = (ctx_t *)data;
215     log_debug(ctx, "dmabuf_frame: frame was canceled\n");
216 
217     zwlr_export_dmabuf_frame_v1_destroy(ctx->mirror.frame);
218     ctx->mirror.frame = NULL;
219     ctx->mirror.state = STATE_CANCELED;
220 
221     switch (reason) {
222         case ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT:
223             log_error("dmabuf_frame: permanent cancellation\n");
224             exit_fail(ctx);
225             break;
226 
227         case ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_TEMPORARY:
228             log_debug(ctx, "dmabuf_frame: temporary cancellation\n");
229             break;
230 
231         case ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_RESIZING:
232             log_debug(ctx, "dmabuf_frame: cancellation due to output resize\n");
233             break;
234 
235         default:
236             log_error("dmabuf_frame: unknown cancellation reason %d\n", reason);
237             exit_fail(ctx);
238             break;
239     }
240 
241     log_debug(ctx, "dmabuf_frame: closing files\n");
242     for (unsigned int i = 0; i < ctx->mirror.num_objects; i++) {
243         if (ctx->mirror.objects[i].fd != -1) close(ctx->mirror.objects[i].fd);
244         ctx->mirror.objects[i].fd = -1;
245     }
246 
247     (void)frame;
248 }
249 
250 static const struct zwlr_export_dmabuf_frame_v1_listener dmabuf_frame_listener = {
251     .frame = dmabuf_frame_event_frame,
252     .object = dmabuf_frame_event_object,
253     .ready = dmabuf_frame_event_ready,
254     .cancel = dmabuf_frame_event_cancel
255 };
256 
257 // --- frame_callback event handlers ---
258 
259 static const struct wl_callback_listener frame_callback_listener;
260 
frame_callback_event_done(void * data,struct wl_callback * frame_callback,uint32_t msec)261 static void frame_callback_event_done(
262     void * data, struct wl_callback * frame_callback, uint32_t msec
263 ) {
264     ctx_t * ctx = (ctx_t *)data;
265 
266     wl_callback_destroy(ctx->mirror.frame_callback);
267     ctx->mirror.frame_callback = NULL;
268 
269     log_debug(ctx, "frame_callback: requesting next callback\n");
270     ctx->mirror.frame_callback = wl_surface_frame(ctx->wl.surface);
271     wl_callback_add_listener(ctx->mirror.frame_callback, &frame_callback_listener, (void *)ctx);
272 
273     log_debug(ctx, "frame_callback: rendering frame\n");
274     draw_texture_egl(ctx);
275 
276     log_debug(ctx, "frame_callback: swapping buffers\n");
277     eglSwapInterval(ctx->egl.display, 0);
278     if (eglSwapBuffers(ctx->egl.display, ctx->egl.surface) != EGL_TRUE) {
279         log_error("frame_callback: failed to swap buffers\n");
280         exit_fail(ctx);
281     }
282 
283     if (ctx->mirror.state != STATE_WAIT_FRAME) {
284         log_debug(ctx, "frame_callback: clearing dmabuf_frame state\n");
285         ctx->mirror.width = 0;
286         ctx->mirror.height = 0;
287         ctx->mirror.x = 0;
288         ctx->mirror.y = 0;
289         ctx->mirror.buffer_flags = 0;
290         ctx->mirror.frame_flags = 0;
291         ctx->mirror.modifier_high = 0;
292         ctx->mirror.modifier_low = 0;
293         ctx->mirror.format = 0;
294         ctx->mirror.num_objects = 0;
295 
296         dmabuf_object_t empty_obj = {
297             .fd = -1,
298             .size = 0,
299             .offset = 0,
300             .stride = 0,
301             .plane_index = 0
302         };
303         ctx->mirror.objects[0] = empty_obj;
304         ctx->mirror.objects[1] = empty_obj;
305         ctx->mirror.objects[2] = empty_obj;
306         ctx->mirror.objects[3] = empty_obj;
307 
308         ctx->mirror.state = STATE_WAIT_FRAME;
309         ctx->mirror.processed_objects = 0;
310 
311         log_debug(ctx, "frame_callback: creating wlr_dmabuf_export_frame\n");
312         ctx->mirror.frame = zwlr_export_dmabuf_manager_v1_capture_output(
313             ctx->wl.dmabuf_manager, ctx->opt.show_cursor, ctx->mirror.current_target->output
314         );
315         if (ctx->mirror.frame == NULL) {
316             log_error("frame_callback: failed to create wlr_dmabuf_export_frame\n");
317             exit_fail(ctx);
318         }
319 
320         log_debug(ctx, "frame_callback: adding dmabuf_frame event listener\n");
321         zwlr_export_dmabuf_frame_v1_add_listener(ctx->mirror.frame, &dmabuf_frame_listener, (void *)ctx);
322     }
323 
324     (void)frame_callback;
325     (void)msec;
326 }
327 
328 static const struct wl_callback_listener frame_callback_listener = {
329     .done = frame_callback_event_done
330 };
331 
332 // --- init_mirror ---
333 
init_mirror(ctx_t * ctx)334 void init_mirror(ctx_t * ctx) {
335     log_debug(ctx, "init_mirror: initializing context structure\n");
336 
337     ctx->mirror.current_target = NULL;
338     ctx->mirror.frame_callback = NULL;
339     ctx->mirror.frame = NULL;
340     ctx->mirror.current_region = (region_t){ .x = 0, .y = 0, .width = 0, .height = 0 };
341     ctx->mirror.invert_y = false;
342 
343     ctx->mirror.width = 0;
344     ctx->mirror.height = 0;
345     ctx->mirror.x = 0;
346     ctx->mirror.y = 0;
347     ctx->mirror.buffer_flags = 0;
348     ctx->mirror.frame_flags = 0;
349     ctx->mirror.modifier_high = 0;
350     ctx->mirror.modifier_low = 0;
351     ctx->mirror.format = 0;
352     ctx->mirror.num_objects = 0;
353 
354     dmabuf_object_t empty_obj = {
355         .fd = -1,
356         .size = 0,
357         .offset = 0,
358         .stride = 0,
359         .plane_index = 0
360     };
361     ctx->mirror.objects[0] = empty_obj;
362     ctx->mirror.objects[1] = empty_obj;
363     ctx->mirror.objects[2] = empty_obj;
364     ctx->mirror.objects[3] = empty_obj;
365 
366     ctx->mirror.frame_image = EGL_NO_IMAGE;
367 
368     ctx->mirror.state = STATE_CANCELED;
369     ctx->mirror.processed_objects = 0;
370     ctx->mirror.initialized = true;
371 
372     if (!find_output_opt(ctx, &ctx->mirror.current_target, &ctx->mirror.current_region)) {
373         log_error("init_mirror: failed to find output\n");
374         exit_fail(ctx);
375     }
376 
377     update_options_mirror(ctx);
378 
379     log_debug(ctx, "init_mirror: requesting render callback\n");
380     ctx->mirror.frame_callback = wl_surface_frame(ctx->wl.surface);
381     wl_callback_add_listener(ctx->mirror.frame_callback, &frame_callback_listener, (void *)ctx);
382 }
383 
384 // --- output_removed_mirror ---
385 
output_removed_mirror(ctx_t * ctx,output_list_node_t * node)386 void output_removed_mirror(ctx_t * ctx, output_list_node_t * node) {
387     if (!ctx->mirror.initialized) return;
388     if (ctx->mirror.current_target == NULL) return;
389     if (ctx->mirror.current_target != node) return;
390 
391     log_error("output_removed_mirror: output disappeared, closing\n");
392     exit_fail(ctx);
393 }
394 
395 // --- update_options_mirror ---
396 
update_options_mirror(ctx_t * ctx)397 void update_options_mirror(ctx_t * ctx) {
398     log_debug(ctx, "init_mirror: formatting window title\n");
399     char * title = NULL;
400     int status = asprintf(&title, "Wayland Output Mirror for %s", ctx->mirror.current_target->name);
401     if (status == -1) {
402         log_error("init_mirror: failed to format window title\n");
403         exit_fail(ctx);
404     }
405 
406     log_debug(ctx, "init_mirror: setting window title\n");
407     xdg_toplevel_set_title(ctx->wl.xdg_toplevel, title);
408     free(title);
409 }
410 
411 // --- cleanup_mirror ---
412 
cleanup_mirror(ctx_t * ctx)413 void cleanup_mirror(ctx_t *ctx) {
414     if (!ctx->mirror.initialized) return;
415 
416     log_debug(ctx, "cleanup_mirror: destroying mirror objects\n");
417     if (ctx->mirror.frame_callback != NULL) wl_callback_destroy(ctx->mirror.frame_callback);
418     if (ctx->mirror.frame != NULL) zwlr_export_dmabuf_frame_v1_destroy(ctx->mirror.frame);
419     if (ctx->mirror.frame_image != EGL_NO_IMAGE) eglDestroyImage(ctx->egl.display, ctx->mirror.frame_image);
420 
421     for (int i = 0; i < 4; i++) {
422         if (ctx->mirror.objects[i].fd != -1) close(ctx->mirror.objects[i].fd);
423         ctx->mirror.objects[i].fd = -1;
424     }
425 
426     ctx->mirror.initialized = false;
427 }
428