FreeRDP
Loading...
Searching...
No Matches
uwac-display.c
1/*
2 * Copyright © 2014 David FORT <contact@hardening-consulting.com>
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#include "uwac-priv.h"
23#include "uwac-utils.h"
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <stdarg.h>
29#include <assert.h>
30#include <errno.h>
31#include <time.h>
32#include <unistd.h>
33#include <sys/epoll.h>
34
35#include "uwac-os.h"
36#include "wayland-cursor.h"
37
38#define TARGET_COMPOSITOR_INTERFACE 3U
39#define TARGET_SHM_INTERFACE 1U
40#define TARGET_SHELL_INTERFACE 1U
41#define TARGET_DDM_INTERFACE 1U
42#define TARGET_SEAT_INTERFACE 5U
43#define TARGET_XDG_VERSION 5U /* The version of xdg-shell that we implement */
44#define TARGET_LINUX_DMABUF_VERSION 1U
45
46#if !defined(NDEBUG)
47static const char* event_names[] = {
48 "new seat", "removed seat", "new output", "configure", "pointer enter",
49 "pointer leave", "pointer motion", "pointer buttons", "pointer axis", "keyboard enter",
50 "key", "touch frame begin", "touch up", "touch down", "touch motion",
51 "touch cancel", "touch frame end", "frame done", "close", nullptr
52};
53#endif
54
55static bool uwac_default_error_handler(UwacDisplay* display, UwacReturnCode code, const char* msg,
56 ...)
57{
58 va_list args = WINPR_C_ARRAY_INIT;
59 va_start(args, msg);
60 (void)vfprintf(stderr, "%s", args);
61 va_end(args);
62 return false;
63}
64
65UwacErrorHandler uwacErrorHandler = uwac_default_error_handler;
66
67bool UwacDisplayHasLinuxDmabuf(const UwacDisplay* display)
68{
69 return display && display->linux_dmabuf;
70}
71
72void UwacInstallErrorHandler(UwacErrorHandler handler)
73{
74 if (handler)
75 uwacErrorHandler = handler;
76 else
77 uwacErrorHandler = uwac_default_error_handler;
78}
79
80static void cb_shm_format(void* data, struct wl_shm* wl_shm, uint32_t format)
81{
82 UwacDisplay* d = data;
83
84 if (format == WL_SHM_FORMAT_RGB565)
85 d->has_rgb565 = true;
86
87 d->shm_formats_nb++;
88 d->shm_formats =
89 xrealloc((void*)d->shm_formats, sizeof(enum wl_shm_format) * d->shm_formats_nb);
90 d->shm_formats[d->shm_formats_nb - 1] = format;
91}
92
93static struct wl_shm_listener shm_listener = { cb_shm_format };
94
95static void cb_linux_dmabuf_format(WINPR_ATTR_UNUSED void* data,
96 WINPR_ATTR_UNUSED struct zwp_linux_dmabuf_v1* dmabuf,
97 WINPR_ATTR_UNUSED uint32_t format)
98{
99}
100
101static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_listener = {
102 cb_linux_dmabuf_format,
103};
104
105static void xdg_shell_ping(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial)
106{
107 xdg_wm_base_pong(xdg_wm_base, serial);
108}
109
110static const struct xdg_wm_base_listener xdg_wm_base_listener = {
111 xdg_shell_ping,
112};
113
114#ifdef BUILD_FULLSCREEN_SHELL
115static void fullscreen_capability(void* data,
116 struct zwp_fullscreen_shell_v1* zwp_fullscreen_shell_v1,
117 uint32_t capability)
118{
119}
120
121static const struct zwp_fullscreen_shell_v1_listener fullscreen_shell_listener = {
122 fullscreen_capability,
123};
124#endif
125
126static void display_destroy_seat(UwacDisplay* d, uint32_t name)
127{
128 UwacSeat* seat = nullptr;
129 UwacSeat* tmp = nullptr;
130 wl_list_for_each_safe(seat, tmp, &d->seats, link)
131 {
132 if (seat->seat_id == name)
133 {
134 UwacSeatDestroy(seat);
135 }
136 }
137}
138
139static void UwacSeatRegisterDDM(UwacSeat* seat)
140{
141 UwacDisplay* d = seat->display;
142 if (!d->data_device_manager)
143 return;
144
145 if (!seat->data_device)
146 seat->data_device =
147 wl_data_device_manager_get_data_device(d->data_device_manager, seat->seat);
148}
149
150static void UwacRegisterCursor(UwacSeat* seat)
151{
152 if (!seat || !seat->display || !seat->display->compositor)
153 return;
154
155 if (!seat->pointer_surface)
156 seat->pointer_surface = wl_compositor_create_surface(seat->display->compositor);
157}
158
159static void registry_handle_global(void* data, struct wl_registry* registry, uint32_t id,
160 const char* interface, uint32_t version)
161{
162 UwacDisplay* d = data;
163 UwacGlobal* global = nullptr;
164 global = xzalloc(sizeof *global);
165 global->name = id;
166 global->interface = xstrdup(interface);
167 global->version = version;
168 wl_list_insert(d->globals.prev, &global->link);
169
170 if (strcmp(interface, "wl_compositor") == 0)
171 {
172 d->compositor = wl_registry_bind(registry, id, &wl_compositor_interface,
173 min(TARGET_COMPOSITOR_INTERFACE, version));
174
175 /* seats announced before the compositor have no cursor surface yet */
176 UwacSeat* seat = nullptr;
177 UwacSeat* tmp = nullptr;
178 wl_list_for_each_safe(seat, tmp, &d->seats, link) UwacRegisterCursor(seat);
179 }
180 else if (strcmp(interface, "wl_shm") == 0)
181 {
182 d->shm =
183 wl_registry_bind(registry, id, &wl_shm_interface, min(TARGET_SHM_INTERFACE, version));
184 wl_shm_add_listener(d->shm, &shm_listener, d);
185 }
186 else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0)
187 {
188 d->linux_dmabuf = wl_registry_bind(registry, id, &zwp_linux_dmabuf_v1_interface,
189 min(TARGET_LINUX_DMABUF_VERSION, version));
190 zwp_linux_dmabuf_v1_add_listener(d->linux_dmabuf, &linux_dmabuf_listener, d);
191 }
192 else if (strcmp(interface, "wl_output") == 0)
193 {
194 UwacOutput* output = nullptr;
195 UwacOutputNewEvent* ev = nullptr;
196 output = UwacCreateOutput(d, id, version);
197
198 if (!output)
199 {
200 assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to create output\n"));
201 return;
202 }
203
204 ev = (UwacOutputNewEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_OUTPUT);
205
206 if (ev)
207 ev->output = output;
208 }
209 else if (strcmp(interface, "wl_seat") == 0)
210 {
211 UwacSeatNewEvent* ev = nullptr;
212 UwacSeat* seat = nullptr;
213 seat = UwacSeatNew(d, id, min(version, TARGET_SEAT_INTERFACE));
214
215 if (!seat)
216 {
217 assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to create new seat\n"));
218 return;
219 }
220
221 UwacSeatRegisterDDM(seat);
222 UwacSeatRegisterClipboard(seat);
223 UwacRegisterCursor(seat);
224 ev = (UwacSeatNewEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_SEAT);
225
226 if (!ev)
227 {
228 assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to create new seat event\n"));
229 return;
230 }
231
232 ev->seat = seat;
233 }
234 else if (strcmp(interface, "wl_data_device_manager") == 0)
235 {
236 UwacSeat* seat = nullptr;
237 UwacSeat* tmp = nullptr;
238
239 d->data_device_manager = wl_registry_bind(registry, id, &wl_data_device_manager_interface,
240 min(TARGET_DDM_INTERFACE, version));
241
242 wl_list_for_each_safe(seat, tmp, &d->seats, link)
243 {
244 UwacSeatRegisterDDM(seat);
245 UwacSeatRegisterClipboard(seat);
246 UwacRegisterCursor(seat);
247 }
248 }
249 else if (strcmp(interface, "wl_shell") == 0)
250 {
251 d->shell = wl_registry_bind(registry, id, &wl_shell_interface,
252 min(TARGET_SHELL_INTERFACE, version));
253 }
254 else if (strcmp(interface, "xdg_wm_base") == 0)
255 {
256 d->xdg_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1);
257 xdg_wm_base_add_listener(d->xdg_base, &xdg_wm_base_listener, d);
258 }
259 else if (strcmp(interface, "wp_viewporter") == 0)
260 {
261 d->viewporter = wl_registry_bind(registry, id, &wp_viewporter_interface, 1);
262 }
263 else if (strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0)
264 {
265 d->keyboard_inhibit_manager =
266 wl_registry_bind(registry, id, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
267 }
268 else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0)
269 {
270 d->deco_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1);
271 }
272 else if (strcmp(interface, "org_kde_kwin_server_decoration_manager") == 0)
273 {
274 d->kde_deco_manager =
275 wl_registry_bind(registry, id, &org_kde_kwin_server_decoration_manager_interface, 1);
276 }
277#if BUILD_IVI
278 else if (strcmp(interface, "ivi_application") == 0)
279 {
280 d->ivi_application = wl_registry_bind(registry, id, &ivi_application_interface, 1);
281 }
282#endif
283#if BUILD_FULLSCREEN_SHELL
284 else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0)
285 {
286 d->fullscreen_shell = wl_registry_bind(registry, id, &zwp_fullscreen_shell_v1_interface, 1);
287 zwp_fullscreen_shell_v1_add_listener(d->fullscreen_shell, &fullscreen_shell_listener, d);
288 }
289#endif
290#if 0
291 else if (strcmp(interface, "text_cursor_position") == 0)
292 {
293 d->text_cursor_position = wl_registry_bind(registry, id, &text_cursor_position_interface, 1);
294 }
295 else if (strcmp(interface, "workspace_manager") == 0)
296 {
297 //init_workspace_manager(d, id);
298 }
299 else if (strcmp(interface, "wl_subcompositor") == 0)
300 {
301 d->subcompositor = wl_registry_bind(registry, id, &wl_subcompositor_interface, 1);
302#endif
303}
304
305static void registry_handle_global_remove(void* data, struct wl_registry* registry, uint32_t name)
306{
307 UwacDisplay* d = data;
308 UwacGlobal* global = nullptr;
309 UwacGlobal* tmp = nullptr;
310 wl_list_for_each_safe(global, tmp, &d->globals, link)
311 {
312 if (global->name != name)
313 continue;
314
315#if 0
316
317 if (strcmp(global->interface, "wl_output") == 0)
318 display_destroy_output(d, name);
319
320#endif
321
322 if (strcmp(global->interface, "wl_seat") == 0)
323 {
324 UwacSeatRemovedEvent* ev = nullptr;
325 display_destroy_seat(d, name);
326 ev = (UwacSeatRemovedEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_REMOVED_SEAT);
327
328 if (ev)
329 ev->id = name;
330 }
331
332 wl_list_remove(&global->link);
333 free(global->interface);
334 free(global);
335 }
336}
337
338static void UwacDestroyGlobal(UwacGlobal* global)
339{
340 free(global->interface);
341 wl_list_remove(&global->link);
342 free(global);
343}
344
345static void* display_bind(UwacDisplay* display, uint32_t name, const struct wl_interface* interface,
346 uint32_t version)
347{
348 return wl_registry_bind(display->registry, name, interface, version);
349}
350
351static const struct wl_registry_listener registry_listener = { registry_handle_global,
352 registry_handle_global_remove };
353
354int UwacDisplayWatchFd(UwacDisplay* display, int fd, uint32_t events, UwacTask* task)
355{
356 struct epoll_event ep;
357 ep.events = events;
358 ep.data.ptr = task;
359 return epoll_ctl(display->epoll_fd, EPOLL_CTL_ADD, fd, &ep);
360}
361
362static void UwacDisplayUnwatchFd(UwacDisplay* display, int fd)
363{
364 epoll_ctl(display->epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
365}
366
367static void display_exit(UwacDisplay* display)
368{
369 display->running = false;
370}
371
372static void display_dispatch_events(UwacTask* task, uint32_t events)
373{
374 UwacDisplay* display = container_of(task, UwacDisplay, dispatch_fd_task);
375 struct epoll_event ep;
376 int ret = 0;
377 display->display_fd_events = events;
378
379 if ((events & EPOLLERR) || (events & EPOLLHUP))
380 {
381 display_exit(display);
382 return;
383 }
384
385 if (events & EPOLLIN)
386 {
387 ret = wl_display_dispatch(display->display);
388
389 if (ret == -1)
390 {
391 display_exit(display);
392 return;
393 }
394 }
395
396 if (events & EPOLLOUT)
397 {
398 ret = wl_display_flush(display->display);
399
400 if (ret == 0)
401 {
402 ep.events = EPOLLIN | EPOLLERR | EPOLLHUP;
403 ep.data.ptr = &display->dispatch_fd_task;
404 epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, display->display_fd, &ep);
405 }
406 else if (ret == -1 && errno != EAGAIN)
407 {
408 display_exit(display);
409 return;
410 }
411 }
412}
413
414UwacDisplay* UwacOpenDisplay(const char* name, UwacReturnCode* err)
415{
416 UwacDisplay* ret = nullptr;
417 ret = (UwacDisplay*)xzalloc(sizeof(*ret));
418
419 if (!ret)
420 {
421 *err = UWAC_ERROR_NOMEMORY;
422 return nullptr;
423 }
424
425 wl_list_init(&ret->globals);
426 wl_list_init(&ret->seats);
427 wl_list_init(&ret->outputs);
428 wl_list_init(&ret->windows);
429 ret->display = wl_display_connect(name);
430
431 if (ret->display == nullptr)
432 {
433 char buffer[256] = WINPR_C_ARRAY_INIT;
434 (void)fprintf(stderr, "failed to connect to Wayland display %s: %s\n", name,
435 uwac_strerror(errno, buffer, sizeof(buffer)));
436 *err = UWAC_ERROR_UNABLE_TO_CONNECT;
437 goto out_free;
438 }
439
440 ret->epoll_fd = uwac_os_epoll_create_cloexec();
441
442 if (ret->epoll_fd < 0)
443 {
444 *err = UWAC_NOT_ENOUGH_RESOURCES;
445 goto out_disconnect;
446 }
447
448 ret->display_fd = wl_display_get_fd(ret->display);
449 ret->registry = wl_display_get_registry(ret->display);
450
451 if (!ret->registry)
452 {
453 *err = UWAC_ERROR_NOMEMORY;
454 goto out_close_epoll;
455 }
456
457 wl_registry_add_listener(ret->registry, &registry_listener, ret);
458
459 if ((wl_display_roundtrip(ret->display) < 0) || (wl_display_roundtrip(ret->display) < 0))
460 {
461 uwacErrorHandler(ret, UWAC_ERROR_UNABLE_TO_CONNECT,
462 "Failed to process Wayland connection: %m\n");
463 *err = UWAC_ERROR_UNABLE_TO_CONNECT;
464 goto out_free_registry;
465 }
466
467 ret->dispatch_fd_task.run = display_dispatch_events;
468
469 if (UwacDisplayWatchFd(ret, ret->display_fd, EPOLLIN | EPOLLERR | EPOLLHUP,
470 &ret->dispatch_fd_task) < 0)
471 {
472 uwacErrorHandler(ret, UWAC_ERROR_INTERNAL, "unable to watch display fd: %m\n");
473 *err = UWAC_ERROR_INTERNAL;
474 goto out_free_registry;
475 }
476
477 ret->running = true;
478 ret->last_error = *err = UWAC_SUCCESS;
479 return ret;
480out_free_registry:
481 wl_registry_destroy(ret->registry);
482out_close_epoll:
483 close(ret->epoll_fd);
484out_disconnect:
485 wl_display_disconnect(ret->display);
486out_free:
487 free(ret);
488 return nullptr;
489}
490
491int UwacDisplayDispatch(UwacDisplay* display, int timeout)
492{
493 int ret = 0;
494 int count = 0;
495 UwacTask* task = nullptr;
496 struct epoll_event ep[16];
497 wl_display_dispatch_pending(display->display);
498
499 if (!display->running)
500 return 0;
501
502 ret = wl_display_flush(display->display);
503
504 if (ret < 0 && errno == EAGAIN)
505 {
506 ep[0].events = (EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP);
507 ep[0].data.ptr = &display->dispatch_fd_task;
508 epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, display->display_fd, &ep[0]);
509 }
510 else if (ret < 0)
511 {
512 return -1;
513 }
514
515 count = epoll_wait(display->epoll_fd, ep, ARRAY_LENGTH(ep), timeout);
516
517 for (int i = 0; i < count; i++)
518 {
519 task = ep[i].data.ptr;
520 task->run(task, ep[i].events);
521 }
522
523 return 1;
524}
525
526UwacReturnCode UwacDisplayGetLastError(const UwacDisplay* display)
527{
528 return display->last_error;
529}
530
531UwacReturnCode UwacCloseDisplay(UwacDisplay** pdisplay)
532{
533 UwacDisplay* display = nullptr;
534 UwacSeat* seat = nullptr;
535 UwacSeat* tmpSeat = nullptr;
536 UwacWindow* window = nullptr;
537 UwacWindow* tmpWindow = nullptr;
538 UwacOutput* output = nullptr;
539 UwacOutput* tmpOutput = nullptr;
540 UwacGlobal* global = nullptr;
541 UwacGlobal* tmpGlobal = nullptr;
542 assert(pdisplay);
543 display = *pdisplay;
544
545 if (!display)
546 return UWAC_ERROR_INVALID_DISPLAY;
547
548 /* destroy windows */
549 wl_list_for_each_safe(window, tmpWindow, &display->windows, link)
550 {
551 UwacDestroyWindow(&window);
552 }
553 /* destroy seats */
554 wl_list_for_each_safe(seat, tmpSeat, &display->seats, link)
555 {
556 UwacSeatDestroy(seat);
557 }
558 /* destroy output */
559 wl_list_for_each_safe(output, tmpOutput, &display->outputs, link)
560 {
561 UwacDestroyOutput(output);
562 }
563 /* destroy globals */
564 wl_list_for_each_safe(global, tmpGlobal, &display->globals, link)
565 {
566 UwacDestroyGlobal(global);
567 }
568
569 if (display->compositor)
570 wl_compositor_destroy(display->compositor);
571
572 if (display->keyboard_inhibit_manager)
573 zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(display->keyboard_inhibit_manager);
574
575 if (display->deco_manager)
576 zxdg_decoration_manager_v1_destroy(display->deco_manager);
577
578 if (display->kde_deco_manager)
579 org_kde_kwin_server_decoration_manager_destroy(display->kde_deco_manager);
580
581#ifdef BUILD_FULLSCREEN_SHELL
582
583 if (display->fullscreen_shell)
584 zwp_fullscreen_shell_v1_destroy(display->fullscreen_shell);
585
586#endif
587#ifdef BUILD_IVI
588
589 if (display->ivi_application)
590 ivi_application_destroy(display->ivi_application);
591
592#endif
593
594 if (display->xdg_toplevel)
595 xdg_toplevel_destroy(display->xdg_toplevel);
596
597 if (display->xdg_base)
598 xdg_wm_base_destroy(display->xdg_base);
599
600 if (display->shell)
601 wl_shell_destroy(display->shell);
602
603 if (display->shm)
604 wl_shm_destroy(display->shm);
605
606 if (display->linux_dmabuf)
607 zwp_linux_dmabuf_v1_destroy(display->linux_dmabuf);
608
609 if (display->viewporter)
610 wp_viewporter_destroy(display->viewporter);
611
612 if (display->subcompositor)
613 wl_subcompositor_destroy(display->subcompositor);
614
615 if (display->data_device_manager)
616 wl_data_device_manager_destroy(display->data_device_manager);
617
618 free(display->shm_formats);
619 wl_registry_destroy(display->registry);
620 close(display->epoll_fd);
621 wl_display_disconnect(display->display);
622
623 /* cleanup the event queue */
624 while (display->push_queue)
625 {
626 UwacEventListItem* item = display->push_queue;
627 display->push_queue = item->tail;
628 free(item);
629 }
630
631 free(display);
632 *pdisplay = nullptr;
633 return UWAC_SUCCESS;
634}
635
636int UwacDisplayGetFd(UwacDisplay* display)
637{
638 return display->epoll_fd;
639}
640
641static const char* errorStrings[] = {
642 "success",
643 "out of memory error",
644 "unable to connect to wayland display",
645 "invalid UWAC display",
646 "not enough resources",
647 "timed out",
648 "not found",
649 "closed connection",
650
651 "internal error",
652};
653
654const char* UwacErrorString(UwacReturnCode error)
655{
656 if (error < UWAC_SUCCESS || error >= UWAC_ERROR_LAST)
657 return "invalid error code";
658
659 return errorStrings[error];
660}
661
662UwacReturnCode UwacDisplayQueryInterfaceVersion(const UwacDisplay* display, const char* name,
663 uint32_t* version)
664{
665 const UwacGlobal* global = nullptr;
666 const UwacGlobal* tmp = nullptr;
667
668 if (!display)
669 return UWAC_ERROR_INVALID_DISPLAY;
670
671 wl_list_for_each_safe(global, tmp, &display->globals, link)
672 {
673 if (strcmp(global->interface, name) == 0)
674 {
675 if (version)
676 *version = global->version;
677
678 return UWAC_SUCCESS;
679 }
680 }
681 return UWAC_NOT_FOUND;
682}
683
684uint32_t UwacDisplayQueryGetNbShmFormats(UwacDisplay* display)
685{
686 if (!display)
687 {
688 return 0;
689 }
690
691 if (!display->shm)
692 {
693 display->last_error = UWAC_NOT_FOUND;
694 return 0;
695 }
696
697 display->last_error = UWAC_SUCCESS;
698 return display->shm_formats_nb;
699}
700
701UwacReturnCode UwacDisplayQueryShmFormats(const UwacDisplay* display, enum wl_shm_format* formats,
702 int formats_size, int* filled)
703{
704 if (!display)
705 return UWAC_ERROR_INVALID_DISPLAY;
706
707 *filled = min((int64_t)display->shm_formats_nb, formats_size);
708 memcpy(formats, (const void*)display->shm_formats, *filled * sizeof(enum wl_shm_format));
709 return UWAC_SUCCESS;
710}
711
712uint32_t UwacDisplayGetNbOutputs(const UwacDisplay* display)
713{
714 return wl_list_length(&display->outputs);
715}
716
717const UwacOutput* UwacDisplayGetOutput(UwacDisplay* display, int index)
718{
719 int i = 0;
720 int display_count = 0;
721 UwacOutput* ret = nullptr;
722
723 if (!display)
724 return nullptr;
725
726 display_count = wl_list_length(&display->outputs);
727 if (display_count <= index)
728 return nullptr;
729
730 wl_list_for_each(ret, &display->outputs, link)
731 {
732 if (i == index)
733 break;
734 i++;
735 }
736
737 if (!ret)
738 {
739 display->last_error = UWAC_NOT_FOUND;
740 return nullptr;
741 }
742
743 display->last_error = UWAC_SUCCESS;
744 return ret;
745}
746
747UwacReturnCode UwacOutputGetResolution(const UwacOutput* output, UwacSize* resolution)
748{
749 if ((output->resolution.height <= 0) || (output->resolution.width <= 0))
750 return UWAC_ERROR_INTERNAL;
751
752 *resolution = output->resolution;
753 return UWAC_SUCCESS;
754}
755
756UwacReturnCode UwacOutputGetPosition(const UwacOutput* output, UwacPosition* pos)
757{
758 *pos = output->position;
759 return UWAC_SUCCESS;
760}
761
762UwacEvent* UwacDisplayNewEvent(UwacDisplay* display, int type)
763{
764 UwacEventListItem* ret = nullptr;
765
766 if (!display)
767 {
768 return nullptr;
769 }
770
771 ret = xzalloc(sizeof(UwacEventListItem));
772
773 if (!ret)
774 {
775 assert(uwacErrorHandler(display, UWAC_ERROR_NOMEMORY, "unable to allocate a '%s' event",
776 event_names[type]));
777 display->last_error = UWAC_ERROR_NOMEMORY;
778 return nullptr;
779 }
780
781 ret->event.type = type;
782 ret->tail = display->push_queue;
783
784 if (ret->tail)
785 ret->tail->head = ret;
786 else
787 display->pop_queue = ret;
788
789 display->push_queue = ret;
790 return &ret->event;
791}
792
793bool UwacHasEvent(UwacDisplay* display)
794{
795 return display->pop_queue != nullptr;
796}
797
798UwacReturnCode UwacNextEvent(UwacDisplay* display, UwacEvent* event)
799{
800 UwacEventListItem* prevItem = nullptr;
801 int ret = 0;
802
803 if (!display)
804 return UWAC_ERROR_INVALID_DISPLAY;
805
806 while (!display->pop_queue)
807 {
808 ret = UwacDisplayDispatch(display, 1 * 1000);
809
810 if (ret < 0)
811 return UWAC_ERROR_INTERNAL;
812 else if (ret == 0)
813 return UWAC_ERROR_CLOSED;
814 }
815
816 prevItem = display->pop_queue->head;
817 *event = display->pop_queue->event;
818 free(display->pop_queue);
819 display->pop_queue = prevItem;
820
821 if (prevItem)
822 prevItem->tail = nullptr;
823 else
824 display->push_queue = nullptr;
825
826 return UWAC_SUCCESS;
827}