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