FreeRDP
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)
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 
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  (void)vfprintf(stderr, "%s", args);
60  va_end(args);
61  return false;
62 }
63 
64 UwacErrorHandler uwacErrorHandler = uwac_default_error_handler;
65 
66 void UwacInstallErrorHandler(UwacErrorHandler handler)
67 {
68  if (handler)
69  uwacErrorHandler = handler;
70  else
71  uwacErrorHandler = uwac_default_error_handler;
72 }
73 
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 static struct wl_shm_listener shm_listener = { cb_shm_format };
88 
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
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 
110 static 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 
123 static 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 
134 static 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 
142 static 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 
277 static 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 
310 static void UwacDestroyGlobal(UwacGlobal* global)
311 {
312  free(global->interface);
313  wl_list_remove(&global->link);
314  free(global);
315 }
316 
317 static 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 
323 static const struct wl_registry_listener registry_listener = { registry_handle_global,
324  registry_handle_global_remove };
325 
326 int 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 
334 static void UwacDisplayUnwatchFd(UwacDisplay* display, int fd)
335 {
336  epoll_ctl(display->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
337 }
338 
339 static void display_exit(UwacDisplay* display)
340 {
341  display->running = false;
342 }
343 
344 static 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 
386 UwacDisplay* 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;
452 out_free_registry:
453  wl_registry_destroy(ret->registry);
454 out_close_epoll:
455  close(ret->epoll_fd);
456 out_disconnect:
457  wl_display_disconnect(ret->display);
458 out_free:
459  free(ret);
460  return NULL;
461 }
462 
463 int 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 
498 UwacReturnCode UwacDisplayGetLastError(const UwacDisplay* display)
499 {
500  return display->last_error;
501 }
502 
503 UwacReturnCode 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 
605 int UwacDisplayGetFd(UwacDisplay* display)
606 {
607  return display->epoll_fd;
608 }
609 
610 static 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 
623 const 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 
631 UwacReturnCode 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 
653 uint32_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 
670 UwacReturnCode 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 
681 uint32_t UwacDisplayGetNbOutputs(const UwacDisplay* display)
682 {
683  return wl_list_length(&display->outputs);
684 }
685 
686 const 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 
716 UwacReturnCode 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 
725 UwacReturnCode UwacOutputGetPosition(const UwacOutput* output, UwacPosition* pos)
726 {
727  *pos = output->position;
728  return UWAC_SUCCESS;
729 }
730 
731 UwacEvent* 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 
762 bool UwacHasEvent(UwacDisplay* display)
763 {
764  return display->pop_queue != NULL;
765 }
766 
767 UwacReturnCode 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 }