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, &registry_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