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