23#include "uwac-utils.h"
36#include "wayland-cursor.h"
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
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
54static bool uwac_default_error_handler(UwacDisplay* display, UwacReturnCode code,
const char* msg,
57 va_list args = WINPR_C_ARRAY_INIT;
59 (void)vfprintf(stderr,
"%s", args);
64UwacErrorHandler uwacErrorHandler = uwac_default_error_handler;
66void UwacInstallErrorHandler(UwacErrorHandler handler)
69 uwacErrorHandler = handler;
71 uwacErrorHandler = uwac_default_error_handler;
74static void cb_shm_format(
void* data,
struct wl_shm* wl_shm, uint32_t format)
76 UwacDisplay* d = data;
78 if (format == WL_SHM_FORMAT_RGB565)
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;
87static struct wl_shm_listener shm_listener = { cb_shm_format };
89static void xdg_shell_ping(
void* data,
struct xdg_wm_base* xdg_wm_base, uint32_t serial)
91 xdg_wm_base_pong(xdg_wm_base, serial);
94static const struct xdg_wm_base_listener xdg_wm_base_listener = {
98#ifdef BUILD_FULLSCREEN_SHELL
99static void fullscreen_capability(
void* data,
100 struct zwp_fullscreen_shell_v1* zwp_fullscreen_shell_v1,
105static const struct zwp_fullscreen_shell_v1_listener fullscreen_shell_listener = {
106 fullscreen_capability,
110static void display_destroy_seat(UwacDisplay* d, uint32_t name)
112 UwacSeat* seat =
nullptr;
113 UwacSeat* tmp =
nullptr;
114 wl_list_for_each_safe(seat, tmp, &d->seats, link)
116 if (seat->seat_id == name)
118 UwacSeatDestroy(seat);
123static void UwacSeatRegisterDDM(UwacSeat* seat)
125 UwacDisplay* d = seat->display;
126 if (!d->data_device_manager)
129 if (!seat->data_device)
131 wl_data_device_manager_get_data_device(d->data_device_manager, seat->seat);
134static void UwacRegisterCursor(UwacSeat* seat)
136 if (!seat || !seat->display || !seat->display->compositor)
139 if (!seat->pointer_surface)
140 seat->pointer_surface = wl_compositor_create_surface(seat->display->compositor);
143static void registry_handle_global(
void* data,
struct wl_registry* registry, uint32_t
id,
144 const char* interface, uint32_t version)
146 UwacDisplay* d = data;
147 UwacGlobal* global =
nullptr;
148 global = xzalloc(
sizeof *global);
150 global->interface = xstrdup(interface);
151 global->version = version;
152 wl_list_insert(d->globals.prev, &global->link);
154 if (strcmp(interface,
"wl_compositor") == 0)
156 d->compositor = wl_registry_bind(registry,
id, &wl_compositor_interface,
157 min(TARGET_COMPOSITOR_INTERFACE, version));
160 UwacSeat* seat =
nullptr;
161 UwacSeat* tmp =
nullptr;
162 wl_list_for_each_safe(seat, tmp, &d->seats, link) UwacRegisterCursor(seat);
164 else if (strcmp(interface,
"wl_shm") == 0)
167 wl_registry_bind(registry,
id, &wl_shm_interface, min(TARGET_SHM_INTERFACE, version));
168 wl_shm_add_listener(d->shm, &shm_listener, d);
170 else if (strcmp(interface,
"wl_output") == 0)
172 UwacOutput* output =
nullptr;
173 UwacOutputNewEvent* ev =
nullptr;
174 output = UwacCreateOutput(d,
id, version);
178 assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY,
"unable to create output\n"));
182 ev = (UwacOutputNewEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_OUTPUT);
187 else if (strcmp(interface,
"wl_seat") == 0)
189 UwacSeatNewEvent* ev =
nullptr;
190 UwacSeat* seat =
nullptr;
191 seat = UwacSeatNew(d,
id, min(version, TARGET_SEAT_INTERFACE));
195 assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY,
"unable to create new seat\n"));
199 UwacSeatRegisterDDM(seat);
200 UwacSeatRegisterClipboard(seat);
201 UwacRegisterCursor(seat);
202 ev = (UwacSeatNewEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_SEAT);
206 assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY,
"unable to create new seat event\n"));
212 else if (strcmp(interface,
"wl_data_device_manager") == 0)
214 UwacSeat* seat =
nullptr;
215 UwacSeat* tmp =
nullptr;
217 d->data_device_manager = wl_registry_bind(registry,
id, &wl_data_device_manager_interface,
218 min(TARGET_DDM_INTERFACE, version));
220 wl_list_for_each_safe(seat, tmp, &d->seats, link)
222 UwacSeatRegisterDDM(seat);
223 UwacSeatRegisterClipboard(seat);
224 UwacRegisterCursor(seat);
227 else if (strcmp(interface,
"wl_shell") == 0)
229 d->shell = wl_registry_bind(registry,
id, &wl_shell_interface,
230 min(TARGET_SHELL_INTERFACE, version));
232 else if (strcmp(interface,
"xdg_wm_base") == 0)
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);
237 else if (strcmp(interface,
"wp_viewporter") == 0)
239 d->viewporter = wl_registry_bind(registry,
id, &wp_viewporter_interface, 1);
241 else if (strcmp(interface,
"zwp_keyboard_shortcuts_inhibit_manager_v1") == 0)
243 d->keyboard_inhibit_manager =
244 wl_registry_bind(registry,
id, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
246 else if (strcmp(interface,
"zxdg_decoration_manager_v1") == 0)
248 d->deco_manager = wl_registry_bind(registry,
id, &zxdg_decoration_manager_v1_interface, 1);
250 else if (strcmp(interface,
"org_kde_kwin_server_decoration_manager") == 0)
252 d->kde_deco_manager =
253 wl_registry_bind(registry,
id, &org_kde_kwin_server_decoration_manager_interface, 1);
256 else if (strcmp(interface,
"ivi_application") == 0)
258 d->ivi_application = wl_registry_bind(registry,
id, &ivi_application_interface, 1);
261#if BUILD_FULLSCREEN_SHELL
262 else if (strcmp(interface,
"zwp_fullscreen_shell_v1") == 0)
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);
269 else if (strcmp(interface,
"text_cursor_position") == 0)
271 d->text_cursor_position = wl_registry_bind(registry,
id, &text_cursor_position_interface, 1);
273 else if (strcmp(interface,
"workspace_manager") == 0)
277 else if (strcmp(interface,
"wl_subcompositor") == 0)
279 d->subcompositor = wl_registry_bind(registry,
id, &wl_subcompositor_interface, 1);
283static void registry_handle_global_remove(
void* data,
struct wl_registry* registry, uint32_t name)
285 UwacDisplay* d = data;
286 UwacGlobal* global =
nullptr;
287 UwacGlobal* tmp =
nullptr;
288 wl_list_for_each_safe(global, tmp, &d->globals, link)
290 if (global->name != name)
295 if (strcmp(global->interface,
"wl_output") == 0)
296 display_destroy_output(d, name);
300 if (strcmp(global->interface,
"wl_seat") == 0)
302 UwacSeatRemovedEvent* ev =
nullptr;
303 display_destroy_seat(d, name);
304 ev = (UwacSeatRemovedEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_REMOVED_SEAT);
310 wl_list_remove(&global->link);
311 free(global->interface);
316static void UwacDestroyGlobal(UwacGlobal* global)
318 free(global->interface);
319 wl_list_remove(&global->link);
323static void* display_bind(UwacDisplay* display, uint32_t name,
const struct wl_interface* interface,
326 return wl_registry_bind(display->registry, name, interface, version);
329static const struct wl_registry_listener registry_listener = { registry_handle_global,
330 registry_handle_global_remove };
332int UwacDisplayWatchFd(UwacDisplay* display,
int fd, uint32_t events, UwacTask* task)
334 struct epoll_event ep;
337 return epoll_ctl(display->epoll_fd, EPOLL_CTL_ADD, fd, &ep);
340static void UwacDisplayUnwatchFd(UwacDisplay* display,
int fd)
342 epoll_ctl(display->epoll_fd, EPOLL_CTL_DEL, fd,
nullptr);
345static void display_exit(UwacDisplay* display)
347 display->running =
false;
350static void display_dispatch_events(UwacTask* task, uint32_t events)
352 UwacDisplay* display = container_of(task, UwacDisplay, dispatch_fd_task);
353 struct epoll_event ep;
355 display->display_fd_events = events;
357 if ((events & EPOLLERR) || (events & EPOLLHUP))
359 display_exit(display);
363 if (events & EPOLLIN)
365 ret = wl_display_dispatch(display->display);
369 display_exit(display);
374 if (events & EPOLLOUT)
376 ret = wl_display_flush(display->display);
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);
384 else if (ret == -1 && errno != EAGAIN)
386 display_exit(display);
392UwacDisplay* UwacOpenDisplay(
const char* name, UwacReturnCode* err)
394 UwacDisplay* ret =
nullptr;
395 ret = (UwacDisplay*)xzalloc(
sizeof(*ret));
399 *err = UWAC_ERROR_NOMEMORY;
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);
409 if (ret->display ==
nullptr)
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;
418 ret->epoll_fd = uwac_os_epoll_create_cloexec();
420 if (ret->epoll_fd < 0)
422 *err = UWAC_NOT_ENOUGH_RESOURCES;
426 ret->display_fd = wl_display_get_fd(ret->display);
427 ret->registry = wl_display_get_registry(ret->display);
431 *err = UWAC_ERROR_NOMEMORY;
432 goto out_close_epoll;
435 wl_registry_add_listener(ret->registry, ®istry_listener, ret);
437 if ((wl_display_roundtrip(ret->display) < 0) || (wl_display_roundtrip(ret->display) < 0))
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;
445 ret->dispatch_fd_task.run = display_dispatch_events;
447 if (UwacDisplayWatchFd(ret, ret->display_fd, EPOLLIN | EPOLLERR | EPOLLHUP,
448 &ret->dispatch_fd_task) < 0)
450 uwacErrorHandler(ret, UWAC_ERROR_INTERNAL,
"unable to watch display fd: %m\n");
451 *err = UWAC_ERROR_INTERNAL;
452 goto out_free_registry;
456 ret->last_error = *err = UWAC_SUCCESS;
459 wl_registry_destroy(ret->registry);
461 close(ret->epoll_fd);
463 wl_display_disconnect(ret->display);
469int UwacDisplayDispatch(UwacDisplay* display,
int timeout)
473 UwacTask* task =
nullptr;
474 struct epoll_event ep[16];
475 wl_display_dispatch_pending(display->display);
477 if (!display->running)
480 ret = wl_display_flush(display->display);
482 if (ret < 0 && errno == EAGAIN)
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]);
493 count = epoll_wait(display->epoll_fd, ep, ARRAY_LENGTH(ep), timeout);
495 for (
int i = 0; i < count; i++)
497 task = ep[i].data.ptr;
498 task->run(task, ep[i].events);
504UwacReturnCode UwacDisplayGetLastError(
const UwacDisplay* display)
506 return display->last_error;
509UwacReturnCode UwacCloseDisplay(UwacDisplay** pdisplay)
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;
524 return UWAC_ERROR_INVALID_DISPLAY;
527 wl_list_for_each_safe(window, tmpWindow, &display->windows, link)
529 UwacDestroyWindow(&window);
532 wl_list_for_each_safe(seat, tmpSeat, &display->seats, link)
534 UwacSeatDestroy(seat);
537 wl_list_for_each_safe(output, tmpOutput, &display->outputs, link)
539 UwacDestroyOutput(output);
542 wl_list_for_each_safe(global, tmpGlobal, &display->globals, link)
544 UwacDestroyGlobal(global);
547 if (display->compositor)
548 wl_compositor_destroy(display->compositor);
550 if (display->keyboard_inhibit_manager)
551 zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(display->keyboard_inhibit_manager);
553 if (display->deco_manager)
554 zxdg_decoration_manager_v1_destroy(display->deco_manager);
556 if (display->kde_deco_manager)
557 org_kde_kwin_server_decoration_manager_destroy(display->kde_deco_manager);
559#ifdef BUILD_FULLSCREEN_SHELL
561 if (display->fullscreen_shell)
562 zwp_fullscreen_shell_v1_destroy(display->fullscreen_shell);
567 if (display->ivi_application)
568 ivi_application_destroy(display->ivi_application);
572 if (display->xdg_toplevel)
573 xdg_toplevel_destroy(display->xdg_toplevel);
575 if (display->xdg_base)
576 xdg_wm_base_destroy(display->xdg_base);
579 wl_shell_destroy(display->shell);
582 wl_shm_destroy(display->shm);
584 if (display->viewporter)
585 wp_viewporter_destroy(display->viewporter);
587 if (display->subcompositor)
588 wl_subcompositor_destroy(display->subcompositor);
590 if (display->data_device_manager)
591 wl_data_device_manager_destroy(display->data_device_manager);
593 free(display->shm_formats);
594 wl_registry_destroy(display->registry);
595 close(display->epoll_fd);
596 wl_display_disconnect(display->display);
599 while (display->push_queue)
601 UwacEventListItem* item = display->push_queue;
602 display->push_queue = item->tail;
611int UwacDisplayGetFd(UwacDisplay* display)
613 return display->epoll_fd;
616static const char* errorStrings[] = {
618 "out of memory error",
619 "unable to connect to wayland display",
620 "invalid UWAC display",
621 "not enough resources",
629const char* UwacErrorString(UwacReturnCode error)
631 if (error < UWAC_SUCCESS || error >= UWAC_ERROR_LAST)
632 return "invalid error code";
634 return errorStrings[error];
637UwacReturnCode UwacDisplayQueryInterfaceVersion(
const UwacDisplay* display,
const char* name,
640 const UwacGlobal* global =
nullptr;
641 const UwacGlobal* tmp =
nullptr;
644 return UWAC_ERROR_INVALID_DISPLAY;
646 wl_list_for_each_safe(global, tmp, &display->globals, link)
648 if (strcmp(global->interface, name) == 0)
651 *version = global->version;
656 return UWAC_NOT_FOUND;
659uint32_t UwacDisplayQueryGetNbShmFormats(UwacDisplay* display)
668 display->last_error = UWAC_NOT_FOUND;
672 display->last_error = UWAC_SUCCESS;
673 return display->shm_formats_nb;
676UwacReturnCode UwacDisplayQueryShmFormats(
const UwacDisplay* display,
enum wl_shm_format* formats,
677 int formats_size,
int* filled)
680 return UWAC_ERROR_INVALID_DISPLAY;
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));
687uint32_t UwacDisplayGetNbOutputs(
const UwacDisplay* display)
689 return wl_list_length(&display->outputs);
692const UwacOutput* UwacDisplayGetOutput(UwacDisplay* display,
int index)
695 int display_count = 0;
696 UwacOutput* ret =
nullptr;
701 display_count = wl_list_length(&display->outputs);
702 if (display_count <= index)
705 wl_list_for_each(ret, &display->outputs, link)
714 display->last_error = UWAC_NOT_FOUND;
718 display->last_error = UWAC_SUCCESS;
722UwacReturnCode UwacOutputGetResolution(
const UwacOutput* output, UwacSize* resolution)
724 if ((output->resolution.height <= 0) || (output->resolution.width <= 0))
725 return UWAC_ERROR_INTERNAL;
727 *resolution = output->resolution;
731UwacReturnCode UwacOutputGetPosition(
const UwacOutput* output, UwacPosition* pos)
733 *pos = output->position;
737UwacEvent* UwacDisplayNewEvent(UwacDisplay* display,
int type)
739 UwacEventListItem* ret =
nullptr;
746 ret = xzalloc(
sizeof(UwacEventListItem));
750 assert(uwacErrorHandler(display, UWAC_ERROR_NOMEMORY,
"unable to allocate a '%s' event",
752 display->last_error = UWAC_ERROR_NOMEMORY;
756 ret->event.type = type;
757 ret->tail = display->push_queue;
760 ret->tail->head = ret;
762 display->pop_queue = ret;
764 display->push_queue = ret;
768bool UwacHasEvent(UwacDisplay* display)
770 return display->pop_queue !=
nullptr;
773UwacReturnCode UwacNextEvent(UwacDisplay* display, UwacEvent* event)
775 UwacEventListItem* prevItem =
nullptr;
779 return UWAC_ERROR_INVALID_DISPLAY;
781 while (!display->pop_queue)
783 ret = UwacDisplayDispatch(display, 1 * 1000);
786 return UWAC_ERROR_INTERNAL;
788 return UWAC_ERROR_CLOSED;
791 prevItem = display->pop_queue->head;
792 *
event = display->pop_queue->event;
793 free(display->pop_queue);
794 display->pop_queue = prevItem;
797 prevItem->tail =
nullptr;
799 display->push_queue =
nullptr;