blob: 2334e0998884b0c0db46a94d197380c6607c11b2 [file] [log] [blame]
Emmanuel Gil Peyrot5d43af32016-01-11 19:04:38 +00001/*
2 * Copyright © 2015 Collabora Ltd.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include <config.h>
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <stdbool.h>
29#include <assert.h>
30#include <unistd.h>
31#include <sys/mman.h>
32#include <signal.h>
33#include <fcntl.h>
34
35#include <drm_fourcc.h>
36
37#include <stropts.h>
38#include <sys/mman.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41#include <errno.h>
42#include <linux/videodev2.h>
43#include <linux/input.h>
44
45#include <wayland-client.h>
46#include "xdg-shell-unstable-v5-client-protocol.h"
47#include "fullscreen-shell-unstable-v1-client-protocol.h"
48#include "linux-dmabuf-unstable-v1-client-protocol.h"
49
50#define CLEAR(x) memset(&(x), 0, sizeof(x))
51
52static int
53xioctl(int fh, int request, void *arg)
54{
55 int r;
56
57 do {
58 r = ioctl(fh, request, arg);
59 } while (r == -1 && errno == EINTR);
60
61 return r;
62}
63
64static uint32_t
65parse_format(const char fmt[4])
66{
67 return fourcc_code(fmt[0], fmt[1], fmt[2], fmt[3]);
68}
69
70static inline const char *
71dump_format(uint32_t format, char out[4])
72{
73#if BYTE_ORDER == BIG_ENDIAN
74 format = __builtin_bswap32(format);
75#endif
76 memcpy(out, &format, 4);
77 return out;
78}
79
80struct buffer_format {
81 int width;
82 int height;
83 enum v4l2_buf_type type;
84 uint32_t format;
85
86 unsigned num_planes;
87 unsigned strides[VIDEO_MAX_PLANES];
88};
89
90struct display {
91 struct wl_display *display;
92 struct wl_registry *registry;
93 struct wl_compositor *compositor;
94 struct wl_seat *seat;
95 struct wl_keyboard *keyboard;
96 struct xdg_shell *shell;
97 struct zwp_fullscreen_shell_v1 *fshell;
98 struct zwp_linux_dmabuf_v1 *dmabuf;
99 bool requested_format_found;
100
101 int v4l_fd;
102 struct buffer_format format;
103 uint32_t drm_format;
104};
105
106struct buffer {
107 struct wl_buffer *buffer;
108 struct display *display;
109 int busy;
110 int index;
111
112 int dmabuf_fds[VIDEO_MAX_PLANES];
113 int data_offsets[VIDEO_MAX_PLANES];
114};
115
116#define NUM_BUFFERS 3
117
118struct window {
119 struct display *display;
120 struct wl_surface *surface;
121 struct xdg_surface *xdg_surface;
122 struct buffer buffers[NUM_BUFFERS];
123 struct wl_callback *callback;
124};
125
126static bool running = true;
127
128static int
129queue(struct display *display, struct buffer *buffer)
130{
131 struct v4l2_buffer buf;
132 struct v4l2_plane planes[VIDEO_MAX_PLANES];
133 unsigned i;
134
135 CLEAR(buf);
136 buf.type = display->format.type;
137 buf.memory = V4L2_MEMORY_MMAP;
138 buf.index = buffer->index;
139
140 if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
141 CLEAR(planes);
142 buf.length = VIDEO_MAX_PLANES;
143 buf.m.planes = planes;
144 }
145
146 if (xioctl(display->v4l_fd, VIDIOC_QUERYBUF, &buf) == -1) {
147 perror("VIDIOC_QUERYBUF");
148 return 0;
149 }
150
151 if (xioctl(display->v4l_fd, VIDIOC_QBUF, &buf) == -1) {
152 perror("VIDIOC_QBUF");
153 return 0;
154 }
155
156 if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
157 if (display->format.num_planes != buf.length) {
158 fprintf(stderr, "Wrong number of planes returned by "
159 "QUERYBUF\n");
160 return 0;
161 }
162
163 for (i = 0; i < buf.length; ++i)
164 buffer->data_offsets[i] = buf.m.planes[i].data_offset;
165 }
166
167 return 1;
168}
169
170static void
171buffer_release(void *data, struct wl_buffer *buffer)
172{
173 struct buffer *mybuf = data;
174
175 mybuf->busy = 0;
176
177 if (!queue(mybuf->display, mybuf))
178 running = false;
179}
180
181static const struct wl_buffer_listener buffer_listener = {
182 buffer_release
183};
184
185static unsigned int
186set_format(struct display *display, uint32_t format)
187{
188 struct v4l2_format fmt;
189 char buf[4];
190
191 CLEAR(fmt);
192
193 fmt.type = display->format.type;
194
195 if (xioctl(display->v4l_fd, VIDIOC_G_FMT, &fmt) == -1) {
196 perror("VIDIOC_G_FMT");
197 return 0;
198 }
199
200 /* No need to set the format if it already is the one we want */
201 if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
202 fmt.fmt.pix.pixelformat == format)
203 return 1;
204 if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
205 fmt.fmt.pix_mp.pixelformat == format)
206 return fmt.fmt.pix_mp.num_planes;
207
208 if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
209 fmt.fmt.pix.pixelformat = format;
210 else
211 fmt.fmt.pix_mp.pixelformat = format;
212
213 if (xioctl(display->v4l_fd, VIDIOC_S_FMT, &fmt) == -1) {
214 perror("VIDIOC_S_FMT");
215 return 0;
216 }
217
218 if ((display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
219 fmt.fmt.pix.pixelformat != format) ||
220 (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
221 fmt.fmt.pix_mp.pixelformat != format)) {
222 fprintf(stderr, "Failed to set format to %.4s\n",
223 dump_format(format, buf));
224 return 0;
225 }
226
227 if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
228 return fmt.fmt.pix_mp.num_planes;
229
230 return 1;
231}
232
233static int
234v4l_connect(struct display *display, const char *dev_name)
235{
236 struct v4l2_capability cap;
237 struct v4l2_requestbuffers req;
238 unsigned int num_planes;
239
240 display->v4l_fd = open(dev_name, O_RDWR);
241 if (display->v4l_fd < 0) {
242 perror(dev_name);
243 return 0;
244 }
245
246 if (xioctl(display->v4l_fd, VIDIOC_QUERYCAP, &cap) == -1) {
247 if (errno == EINVAL) {
248 fprintf(stderr, "%s is no V4L2 device\n", dev_name);
249 } else {
250 perror("VIDIOC_QUERYCAP");
251 }
252 return 0;
253 }
254
255 if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
256 display->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
257 else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
258 display->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
259 else {
260 fprintf(stderr, "%s is no video capture device\n", dev_name);
261 return 0;
262 }
263
264 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
265 fprintf(stderr, "%s does not support dmabuf i/o\n", dev_name);
266 return 0;
267 }
268
269 /* Select video input, video standard and tune here */
270
271 num_planes = set_format(display, display->format.format);
272 if (num_planes < 1)
273 return 0;
274
275 CLEAR(req);
276
277 req.type = display->format.type;
278 req.memory = V4L2_MEMORY_MMAP;
279 req.count = NUM_BUFFERS * num_planes;
280
281 if (xioctl(display->v4l_fd, VIDIOC_REQBUFS, &req) == -1) {
282 if (errno == EINVAL) {
283 fprintf(stderr, "%s does not support dmabuf\n",
284 dev_name);
285 } else {
286 perror("VIDIOC_REQBUFS");
287 }
288 return 0;
289 }
290
291 if (req.count < NUM_BUFFERS * num_planes) {
292 fprintf(stderr, "Insufficient buffer memory on %s\n", dev_name);
293 return 0;
294 }
295
296 printf("Created %d buffers\n", req.count);
297
298 return 1;
299}
300
301static void
302v4l_shutdown(struct display *display)
303{
304 close(display->v4l_fd);
305}
306
307static void
308create_succeeded(void *data,
309 struct zwp_linux_buffer_params_v1 *params,
310 struct wl_buffer *new_buffer)
311{
312 struct buffer *buffer = data;
313 unsigned i;
314
315 buffer->buffer = new_buffer;
316 wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
317
318 zwp_linux_buffer_params_v1_destroy(params);
319
320 for (i = 0; i < buffer->display->format.num_planes; ++i)
321 close(buffer->dmabuf_fds[i]);
322}
323
324static void
325create_failed(void *data, struct zwp_linux_buffer_params_v1 *params)
326{
327 struct buffer *buffer = data;
328 unsigned i;
329
330 buffer->buffer = NULL;
331
332 zwp_linux_buffer_params_v1_destroy(params);
333
334 for (i = 0; i < buffer->display->format.num_planes; ++i)
335 close(buffer->dmabuf_fds[i]);
336
337 running = false;
338
339 fprintf(stderr, "Error: zwp_linux_buffer_params.create failed.\n");
340}
341
342static const struct zwp_linux_buffer_params_v1_listener params_listener = {
343 create_succeeded,
344 create_failed
345};
346
347static void
348create_dmabuf_buffer(struct display *display, struct buffer *buffer)
349{
350 struct zwp_linux_buffer_params_v1 *params;
351 uint64_t modifier;
352 uint32_t flags;
353 unsigned i;
354
355 modifier = 0;
356 flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
357
358 params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
359 for (i = 0; i < display->format.num_planes; ++i)
360 zwp_linux_buffer_params_v1_add(params,
361 buffer->dmabuf_fds[i],
362 i, /* plane_idx */
363 buffer->data_offsets[i], /* offset */
364 display->format.strides[i],
365 modifier >> 32,
366 modifier & 0xffffffff);
367 zwp_linux_buffer_params_v1_add_listener(params, &params_listener,
368 buffer);
369 zwp_linux_buffer_params_v1_create(params,
370 display->format.width,
371 display->format.height,
372 display->drm_format,
373 flags);
374}
375
376static int
377buffer_export(struct display *display, int index, int dmafd[])
378{
379 struct v4l2_exportbuffer expbuf;
380 unsigned i;
381
382 CLEAR(expbuf);
383
384 for (i = 0; i < display->format.num_planes; ++i) {
385 expbuf.type = display->format.type;
386 expbuf.index = index;
387 expbuf.plane = i;
388 if (xioctl(display->v4l_fd, VIDIOC_EXPBUF, &expbuf) == -1) {
389 perror("VIDIOC_EXPBUF");
390 while (i)
391 close(dmafd[--i]);
392 return 0;
393 }
394 dmafd[i] = expbuf.fd;
395 }
396
397 return 1;
398}
399
400static int
401queue_initial_buffers(struct display *display,
402 struct buffer buffers[NUM_BUFFERS])
403{
404 struct buffer *buffer;
405 int index;
406
407 for (index = 0; index < NUM_BUFFERS; ++index) {
408 buffer = &buffers[index];
409 buffer->display = display;
410 buffer->index = index;
411
412 if (!queue(display, buffer)) {
413 fprintf(stderr, "Failed to queue buffer\n");
414 return 0;
415 }
416
417 assert(!buffer->buffer);
418 if (!buffer_export(display, index, buffer->dmabuf_fds))
419 return 0;
420
421 create_dmabuf_buffer(display, buffer);
422 }
423
424 return 1;
425}
426
427static int
428dequeue(struct display *display)
429{
430 struct v4l2_buffer buf;
431 struct v4l2_plane planes[VIDEO_MAX_PLANES];
432
433 CLEAR(buf);
434 buf.type = display->format.type;
435 buf.memory = V4L2_MEMORY_MMAP;
436 buf.length = VIDEO_MAX_PLANES;
437 buf.m.planes = planes;
438
439 /* This ioctl is blocking until a buffer is ready to be displayed */
440 if (xioctl(display->v4l_fd, VIDIOC_DQBUF, &buf) == -1) {
441 perror("VIDIOC_DQBUF");
442 return -1;
443 }
444
445 assert(buf.flags & V4L2_BUF_FLAG_DONE);
446
447 return buf.index;
448}
449
450static int
451fill_buffer_format(struct display *display)
452{
453 struct v4l2_format fmt;
454 struct v4l2_pix_format *pix;
455 struct v4l2_pix_format_mplane *pix_mp;
456 int i;
457 char buf[4];
458
459 CLEAR(fmt);
460 fmt.type = display->format.type;
461
462 /* Preserve original settings as set by v4l2-ctl for example */
463 if (xioctl(display->v4l_fd, VIDIOC_G_FMT, &fmt) == -1) {
464 perror("VIDIOC_G_FMT");
465 return 0;
466 }
467
468 if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
469 pix = &fmt.fmt.pix;
470
471 printf("%d×%d, %.4s\n", pix->width, pix->height,
472 dump_format(pix->pixelformat, buf));
473
474 display->format.num_planes = 1;
475 display->format.width = pix->width;
476 display->format.height = pix->height;
477 display->format.strides[0] = pix->bytesperline;
478 } else {
479 pix_mp = &fmt.fmt.pix_mp;
480
481 display->format.num_planes = pix_mp->num_planes;
482 display->format.width = pix_mp->width;
483 display->format.height = pix_mp->height;
484
485 for (i = 0; i < pix_mp->num_planes; ++i)
486 display->format.strides[i] = pix_mp->plane_fmt[i].bytesperline;
487
488 printf("%d×%d, %.4s, %d planes\n",
489 pix_mp->width, pix_mp->height,
490 dump_format(pix_mp->pixelformat, buf),
491 pix_mp->num_planes);
492 }
493
494 return 1;
495}
496
497static int
498v4l_init(struct display *display, struct buffer buffers[NUM_BUFFERS]) {
499 if (!fill_buffer_format(display)) {
500 fprintf(stderr, "Failed to fill buffer format\n");
501 return 0;
502 }
503
504 if (!queue_initial_buffers(display, buffers)) {
505 fprintf(stderr, "Failed to queue initial buffers\n");
506 return 0;
507 }
508
509 return 1;
510}
511
512static int
513start_capture(struct display *display)
514{
515 int type = display->format.type;
516
517 if (xioctl(display->v4l_fd, VIDIOC_STREAMON, &type) == -1) {
518 perror("VIDIOC_STREAMON");
519 return 0;
520 }
521
522 return 1;
523}
524
525static void
526handle_configure(void *data, struct xdg_surface *surface,
527 int32_t width, int32_t height,
528 struct wl_array *states, uint32_t serial)
529{
530}
531
532static void
533handle_delete(void *data, struct xdg_surface *xdg_surface)
534{
535 running = false;
536}
537
538static const struct xdg_surface_listener xdg_surface_listener = {
539 handle_configure,
540 handle_delete,
541};
542
543static struct window *
544create_window(struct display *display)
545{
546 struct window *window;
547
548 window = calloc(1, sizeof *window);
549 if (!window)
550 return NULL;
551
552 window->callback = NULL;
553 window->display = display;
554 window->surface = wl_compositor_create_surface(display->compositor);
555
556 if (display->shell) {
557 window->xdg_surface =
558 xdg_shell_get_xdg_surface(display->shell,
559 window->surface);
560
561 assert(window->xdg_surface);
562
563 xdg_surface_add_listener(window->xdg_surface,
564 &xdg_surface_listener, window);
565
566 xdg_surface_set_title(window->xdg_surface, "simple-dmabuf-v4l");
567 } else if (display->fshell) {
568 zwp_fullscreen_shell_v1_present_surface(display->fshell,
569 window->surface,
570 ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT,
571 NULL);
572 } else {
573 assert(0);
574 }
575
576 return window;
577}
578
579static void
580destroy_window(struct window *window)
581{
582 int i;
583 unsigned j;
584
585 if (window->callback)
586 wl_callback_destroy(window->callback);
587
588 if (window->xdg_surface)
589 xdg_surface_destroy(window->xdg_surface);
590 wl_surface_destroy(window->surface);
591
592 for (i = 0; i < NUM_BUFFERS; i++) {
593 if (!window->buffers[i].buffer)
594 continue;
595
596 wl_buffer_destroy(window->buffers[i].buffer);
597 for (j = 0; j < window->display->format.num_planes; ++j)
598 close(window->buffers[i].dmabuf_fds[j]);
599 }
600
601 v4l_shutdown(window->display);
602
603 free(window);
604}
605
606static const struct wl_callback_listener frame_listener;
607
608static void
609redraw(void *data, struct wl_callback *callback, uint32_t time)
610{
611 struct window *window = data;
612 struct buffer *buffer;
613 int index, num_busy = 0;
614
615 /* Check for a deadlock situation where we would block forever trying
616 * to dequeue a buffer while all of them are locked by the compositor.
617 */
618 for (index = 0; index < NUM_BUFFERS; ++index)
619 if (window->buffers[index].busy)
620 ++num_busy;
621
622 /* A robust application would just postpone redraw until it has queued
623 * a buffer.
624 */
625 assert(num_busy < NUM_BUFFERS);
626
627 index = dequeue(window->display);
628 if (index < 0) {
629 /* We couldn’t get any buffer out of the camera, exiting. */
630 running = false;
631 return;
632 }
633
634 buffer = &window->buffers[index];
635 assert(!buffer->busy);
636
637 wl_surface_attach(window->surface, buffer->buffer, 0, 0);
638 wl_surface_damage(window->surface, 0, 0,
639 window->display->format.width,
640 window->display->format.height);
641
642 if (callback)
643 wl_callback_destroy(callback);
644
645 window->callback = wl_surface_frame(window->surface);
646 wl_callback_add_listener(window->callback, &frame_listener, window);
647 wl_surface_commit(window->surface);
648 buffer->busy = 1;
649}
650
651static const struct wl_callback_listener frame_listener = {
652 redraw
653};
654
655static void
656dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
657 uint32_t format)
658{
659 struct display *d = data;
660
661 if (format == d->drm_format)
662 d->requested_format_found = true;
663}
664
665static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
666 dmabuf_format
667};
668
669static void
670keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
671 uint32_t format, int fd, uint32_t size)
672{
673 /* Just so we don’t leak the keymap fd */
674 close(fd);
675}
676
677static void
678keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
679 uint32_t serial, struct wl_surface *surface,
680 struct wl_array *keys)
681{
682}
683
684static void
685keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
686 uint32_t serial, struct wl_surface *surface)
687{
688}
689
690static void
691keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
692 uint32_t serial, uint32_t time, uint32_t key,
693 uint32_t state)
694{
695 struct display *d = data;
696
697 if (!d->shell)
698 return;
699
700 if (key == KEY_ESC && state)
701 running = false;
702}
703
704static void
705keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
706 uint32_t serial, uint32_t mods_depressed,
707 uint32_t mods_latched, uint32_t mods_locked,
708 uint32_t group)
709{
710}
711
712static const struct wl_keyboard_listener keyboard_listener = {
713 keyboard_handle_keymap,
714 keyboard_handle_enter,
715 keyboard_handle_leave,
716 keyboard_handle_key,
717 keyboard_handle_modifiers,
718};
719
720static void
721seat_handle_capabilities(void *data, struct wl_seat *seat,
722 enum wl_seat_capability caps)
723{
724 struct display *d = data;
725
726 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !d->keyboard) {
727 d->keyboard = wl_seat_get_keyboard(seat);
728 wl_keyboard_add_listener(d->keyboard, &keyboard_listener, d);
729 } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && d->keyboard) {
730 wl_keyboard_destroy(d->keyboard);
731 d->keyboard = NULL;
732 }
733}
734
735static const struct wl_seat_listener seat_listener = {
736 seat_handle_capabilities,
737};
738
739static void
740xdg_shell_ping(void *data, struct xdg_shell *shell, uint32_t serial)
741{
742 xdg_shell_pong(shell, serial);
743}
744
745static const struct xdg_shell_listener xdg_shell_listener = {
746 xdg_shell_ping,
747};
748
749#define XDG_VERSION 5 /* The version of xdg-shell that we implement */
750#ifdef static_assert
751static_assert(XDG_VERSION == XDG_SHELL_VERSION_CURRENT,
752 "Interface version doesn't match implementation version");
753#endif
754
755static void
756registry_handle_global(void *data, struct wl_registry *registry,
757 uint32_t id, const char *interface, uint32_t version)
758{
759 struct display *d = data;
760
761 if (strcmp(interface, "wl_compositor") == 0) {
762 d->compositor =
763 wl_registry_bind(registry,
764 id, &wl_compositor_interface, 1);
765 } else if (strcmp(interface, "wl_seat") == 0) {
766 d->seat = wl_registry_bind(registry,
767 id, &wl_seat_interface, 1);
768 wl_seat_add_listener(d->seat, &seat_listener, d);
769 } else if (strcmp(interface, "xdg_shell") == 0) {
770 d->shell = wl_registry_bind(registry,
771 id, &xdg_shell_interface, 1);
772 xdg_shell_use_unstable_version(d->shell, XDG_VERSION);
773 xdg_shell_add_listener(d->shell, &xdg_shell_listener, d);
774 } else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
775 d->fshell = wl_registry_bind(registry,
776 id, &zwp_fullscreen_shell_v1_interface,
777 1);
778 } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
779 d->dmabuf = wl_registry_bind(registry,
780 id, &zwp_linux_dmabuf_v1_interface,
781 1);
782 zwp_linux_dmabuf_v1_add_listener(d->dmabuf, &dmabuf_listener,
783 d);
784 }
785}
786
787static void
788registry_handle_global_remove(void *data, struct wl_registry *registry,
789 uint32_t name)
790{
791}
792
793static const struct wl_registry_listener registry_listener = {
794 registry_handle_global,
795 registry_handle_global_remove
796};
797
798static struct display *
799create_display(uint32_t requested_format)
800{
801 struct display *display;
802
803 display = malloc(sizeof *display);
804 if (display == NULL) {
805 fprintf(stderr, "out of memory\n");
806 exit(1);
807 }
808 display->display = wl_display_connect(NULL);
809 assert(display->display);
810
811 display->drm_format = requested_format;
812
813 display->registry = wl_display_get_registry(display->display);
814 wl_registry_add_listener(display->registry,
815 &registry_listener, display);
816 wl_display_roundtrip(display->display);
817 if (display->dmabuf == NULL) {
818 fprintf(stderr, "No zwp_linux_dmabuf global\n");
819 exit(1);
820 }
821
822 wl_display_roundtrip(display->display);
823
824 /* XXX: fake, because the compositor does not yet advertise anything */
825 display->requested_format_found = true;
826
827 if (!display->requested_format_found) {
828 fprintf(stderr, "DRM_FORMAT_YUYV not available\n");
829 exit(1);
830 }
831
832 return display;
833}
834
835static void
836destroy_display(struct display *display)
837{
838 if (display->dmabuf)
839 zwp_linux_dmabuf_v1_destroy(display->dmabuf);
840
841 if (display->shell)
842 xdg_shell_destroy(display->shell);
843
844 if (display->fshell)
845 zwp_fullscreen_shell_v1_release(display->fshell);
846
847 if (display->compositor)
848 wl_compositor_destroy(display->compositor);
849
850 wl_registry_destroy(display->registry);
851 wl_display_flush(display->display);
852 wl_display_disconnect(display->display);
853 free(display);
854}
855
856static void
857usage(const char *argv0)
858{
859 printf("Usage: %s [V4L2 device] [V4L2 format] [DRM format]\n"
860 "\n"
861 "The default V4L2 device is /dev/video0\n"
862 "\n"
863 "Both formats are FOURCC values (see http://fourcc.org/)\n"
864 "V4L2 formats are defined in <linux/videodev2.h>\n"
865 "DRM formats are defined in <libdrm/drm_fourcc.h>\n"
866 "The default for both formats is YUYV.\n"
867 "If the V4L2 and DRM formats differ, the data is simply "
868 "reinterpreted rather than converted.\n", argv0);
869 exit(0);
870}
871
872static void
873signal_int(int signum)
874{
875 running = false;
876}
877
878int
879main(int argc, char **argv)
880{
881 struct sigaction sigint;
882 struct display *display;
883 struct window *window;
884 const char *v4l_device;
885 uint32_t v4l_format, drm_format;
886 int ret = 0;
887
888 if (argc < 2) {
889 v4l_device = "/dev/video0";
890 } else if (!strcmp(argv[1], "--help")) {
891 usage(argv[0]);
892 } else {
893 v4l_device = argv[1];
894 }
895
896 if (argc < 3)
897 v4l_format = parse_format("YUYV");
898 else
899 v4l_format = parse_format(argv[2]);
900
901 if (argc < 4)
902 drm_format = v4l_format;
903 else
904 drm_format = parse_format(argv[3]);
905
906 display = create_display(drm_format);
907 display->format.format = v4l_format;
908
909 window = create_window(display);
910 if (!window)
911 return 1;
912
913 if (!v4l_connect(display, v4l_device))
914 return 1;
915
916 if (!v4l_init(display, window->buffers))
917 return 1;
918
919 sigint.sa_handler = signal_int;
920 sigemptyset(&sigint.sa_mask);
921 sigint.sa_flags = SA_RESETHAND;
922 sigaction(SIGINT, &sigint, NULL);
923
924 /* Here we retrieve the linux-dmabuf objects, or error */
925 wl_display_roundtrip(display->display);
926
927 /* In case of error, running will be 0 */
928 if (!running)
929 return 1;
930
931 /* We got all of our buffers, we can start the capture! */
932 if (!start_capture(display))
933 return 1;
934
935 redraw(window, NULL, 0);
936
937 while (running && ret != -1)
938 ret = wl_display_dispatch(display->display);
939
940 fprintf(stderr, "simple-dmabuf-v4l exiting\n");
941 destroy_window(window);
942 destroy_display(display);
943
944 return 0;
945}