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