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
44#define TARGET_LINUX_DMABUF_VERSION 1U
47static const char* event_names[] = {
48 "new seat",
"removed seat",
"new output",
"configure",
"pointer enter",
49 "pointer leave",
"pointer motion",
"pointer buttons",
"pointer axis",
"keyboard enter",
50 "key",
"touch frame begin",
"touch up",
"touch down",
"touch motion",
51 "touch cancel",
"touch frame end",
"frame done",
"close",
nullptr
55static bool uwac_default_error_handler(UwacDisplay* display, UwacReturnCode code,
const char* msg,
58 va_list args = WINPR_C_ARRAY_INIT;
60 (void)vfprintf(stderr,
"%s", args);
65UwacErrorHandler uwacErrorHandler = uwac_default_error_handler;
67bool UwacDisplayHasLinuxDmabuf(
const UwacDisplay* display)
69 return display && display->linux_dmabuf;
72void UwacInstallErrorHandler(UwacErrorHandler handler)
75 uwacErrorHandler = handler;
77 uwacErrorHandler = uwac_default_error_handler;
80static void cb_shm_format(
void* data,
struct wl_shm* wl_shm, uint32_t format)
82 UwacDisplay* d = data;
84 if (format == WL_SHM_FORMAT_RGB565)
89 xrealloc((
void*)d->shm_formats,
sizeof(
enum wl_shm_format) * d->shm_formats_nb);
90 d->shm_formats[d->shm_formats_nb - 1] = format;
93static struct wl_shm_listener shm_listener = { cb_shm_format };
95static void cb_linux_dmabuf_format(WINPR_ATTR_UNUSED
void* data,
96 WINPR_ATTR_UNUSED
struct zwp_linux_dmabuf_v1* dmabuf,
97 WINPR_ATTR_UNUSED uint32_t format)
101static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_listener = {
102 cb_linux_dmabuf_format,
105static void xdg_shell_ping(
void* data,
struct xdg_wm_base* xdg_wm_base, uint32_t serial)
107 xdg_wm_base_pong(xdg_wm_base, serial);
110static const struct xdg_wm_base_listener xdg_wm_base_listener = {
114#ifdef BUILD_FULLSCREEN_SHELL
115static void fullscreen_capability(
void* data,
116 struct zwp_fullscreen_shell_v1* zwp_fullscreen_shell_v1,
121static const struct zwp_fullscreen_shell_v1_listener fullscreen_shell_listener = {
122 fullscreen_capability,
126static void display_destroy_seat(UwacDisplay* d, uint32_t name)
128 UwacSeat* seat =
nullptr;
129 UwacSeat* tmp =
nullptr;
130 wl_list_for_each_safe(seat, tmp, &d->seats, link)
132 if (seat->seat_id == name)
134 UwacSeatDestroy(seat);
139static void UwacSeatRegisterDDM(UwacSeat* seat)
141 UwacDisplay* d = seat->display;
142 if (!d->data_device_manager)
145 if (!seat->data_device)
147 wl_data_device_manager_get_data_device(d->data_device_manager, seat->seat);
150static void UwacRegisterCursor(UwacSeat* seat)
152 if (!seat || !seat->display || !seat->display->compositor)
155 if (!seat->pointer_surface)
156 seat->pointer_surface = wl_compositor_create_surface(seat->display->compositor);
159static void registry_handle_global(
void* data,
struct wl_registry* registry, uint32_t
id,
160 const char* interface, uint32_t version)
162 UwacDisplay* d = data;
163 UwacGlobal* global =
nullptr;
164 global = xzalloc(
sizeof *global);
166 global->interface = xstrdup(interface);
167 global->version = version;
168 wl_list_insert(d->globals.prev, &global->link);
170 if (strcmp(interface,
"wl_compositor") == 0)
172 d->compositor = wl_registry_bind(registry,
id, &wl_compositor_interface,
173 min(TARGET_COMPOSITOR_INTERFACE, version));
176 UwacSeat* seat =
nullptr;
177 UwacSeat* tmp =
nullptr;
178 wl_list_for_each_safe(seat, tmp, &d->seats, link) UwacRegisterCursor(seat);
180 else if (strcmp(interface,
"wl_shm") == 0)
183 wl_registry_bind(registry,
id, &wl_shm_interface, min(TARGET_SHM_INTERFACE, version));
184 wl_shm_add_listener(d->shm, &shm_listener, d);
186 else if (strcmp(interface,
"zwp_linux_dmabuf_v1") == 0)
188 d->linux_dmabuf = wl_registry_bind(registry,
id, &zwp_linux_dmabuf_v1_interface,
189 min(TARGET_LINUX_DMABUF_VERSION, version));
190 zwp_linux_dmabuf_v1_add_listener(d->linux_dmabuf, &linux_dmabuf_listener, d);
192 else if (strcmp(interface,
"wl_output") == 0)
194 UwacOutput* output =
nullptr;
195 UwacOutputNewEvent* ev =
nullptr;
196 output = UwacCreateOutput(d,
id, version);
200 assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY,
"unable to create output\n"));
204 ev = (UwacOutputNewEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_OUTPUT);
209 else if (strcmp(interface,
"wl_seat") == 0)
211 UwacSeatNewEvent* ev =
nullptr;
212 UwacSeat* seat =
nullptr;
213 seat = UwacSeatNew(d,
id, min(version, TARGET_SEAT_INTERFACE));
217 assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY,
"unable to create new seat\n"));
221 UwacSeatRegisterDDM(seat);
222 UwacSeatRegisterClipboard(seat);
223 UwacRegisterCursor(seat);
224 ev = (UwacSeatNewEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_SEAT);
228 assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY,
"unable to create new seat event\n"));
234 else if (strcmp(interface,
"wl_data_device_manager") == 0)
236 UwacSeat* seat =
nullptr;
237 UwacSeat* tmp =
nullptr;
239 d->data_device_manager = wl_registry_bind(registry,
id, &wl_data_device_manager_interface,
240 min(TARGET_DDM_INTERFACE, version));
242 wl_list_for_each_safe(seat, tmp, &d->seats, link)
244 UwacSeatRegisterDDM(seat);
245 UwacSeatRegisterClipboard(seat);
246 UwacRegisterCursor(seat);
249 else if (strcmp(interface,
"wl_shell") == 0)
251 d->shell = wl_registry_bind(registry,
id, &wl_shell_interface,
252 min(TARGET_SHELL_INTERFACE, version));
254 else if (strcmp(interface,
"xdg_wm_base") == 0)
256 d->xdg_base = wl_registry_bind(registry,
id, &xdg_wm_base_interface, 1);
257 xdg_wm_base_add_listener(d->xdg_base, &xdg_wm_base_listener, d);
259 else if (strcmp(interface,
"wp_viewporter") == 0)
261 d->viewporter = wl_registry_bind(registry,
id, &wp_viewporter_interface, 1);
263 else if (strcmp(interface,
"zwp_keyboard_shortcuts_inhibit_manager_v1") == 0)
265 d->keyboard_inhibit_manager =
266 wl_registry_bind(registry,
id, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
268 else if (strcmp(interface,
"zxdg_decoration_manager_v1") == 0)
270 d->deco_manager = wl_registry_bind(registry,
id, &zxdg_decoration_manager_v1_interface, 1);
272 else if (strcmp(interface,
"org_kde_kwin_server_decoration_manager") == 0)
274 d->kde_deco_manager =
275 wl_registry_bind(registry,
id, &org_kde_kwin_server_decoration_manager_interface, 1);
278 else if (strcmp(interface,
"ivi_application") == 0)
280 d->ivi_application = wl_registry_bind(registry,
id, &ivi_application_interface, 1);
283#if BUILD_FULLSCREEN_SHELL
284 else if (strcmp(interface,
"zwp_fullscreen_shell_v1") == 0)
286 d->fullscreen_shell = wl_registry_bind(registry,
id, &zwp_fullscreen_shell_v1_interface, 1);
287 zwp_fullscreen_shell_v1_add_listener(d->fullscreen_shell, &fullscreen_shell_listener, d);
291 else if (strcmp(interface,
"text_cursor_position") == 0)
293 d->text_cursor_position = wl_registry_bind(registry,
id, &text_cursor_position_interface, 1);
295 else if (strcmp(interface,
"workspace_manager") == 0)
299 else if (strcmp(interface,
"wl_subcompositor") == 0)
301 d->subcompositor = wl_registry_bind(registry,
id, &wl_subcompositor_interface, 1);
305static void registry_handle_global_remove(
void* data,
struct wl_registry* registry, uint32_t name)
307 UwacDisplay* d = data;
308 UwacGlobal* global =
nullptr;
309 UwacGlobal* tmp =
nullptr;
310 wl_list_for_each_safe(global, tmp, &d->globals, link)
312 if (global->name != name)
317 if (strcmp(global->interface,
"wl_output") == 0)
318 display_destroy_output(d, name);
322 if (strcmp(global->interface,
"wl_seat") == 0)
324 UwacSeatRemovedEvent* ev =
nullptr;
325 display_destroy_seat(d, name);
326 ev = (UwacSeatRemovedEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_REMOVED_SEAT);
332 wl_list_remove(&global->link);
333 free(global->interface);
338static void UwacDestroyGlobal(UwacGlobal* global)
340 free(global->interface);
341 wl_list_remove(&global->link);
345static void* display_bind(UwacDisplay* display, uint32_t name,
const struct wl_interface* interface,
348 return wl_registry_bind(display->registry, name, interface, version);
351static const struct wl_registry_listener registry_listener = { registry_handle_global,
352 registry_handle_global_remove };
354int UwacDisplayWatchFd(UwacDisplay* display,
int fd, uint32_t events, UwacTask* task)
356 struct epoll_event ep;
359 return epoll_ctl(display->epoll_fd, EPOLL_CTL_ADD, fd, &ep);
362static void UwacDisplayUnwatchFd(UwacDisplay* display,
int fd)
364 epoll_ctl(display->epoll_fd, EPOLL_CTL_DEL, fd,
nullptr);
367static void display_exit(UwacDisplay* display)
369 display->running =
false;
372static void display_dispatch_events(UwacTask* task, uint32_t events)
374 UwacDisplay* display = container_of(task, UwacDisplay, dispatch_fd_task);
375 struct epoll_event ep;
377 display->display_fd_events = events;
379 if ((events & EPOLLERR) || (events & EPOLLHUP))
381 display_exit(display);
385 if (events & EPOLLIN)
387 ret = wl_display_dispatch(display->display);
391 display_exit(display);
396 if (events & EPOLLOUT)
398 ret = wl_display_flush(display->display);
402 ep.events = EPOLLIN | EPOLLERR | EPOLLHUP;
403 ep.data.ptr = &display->dispatch_fd_task;
404 epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, display->display_fd, &ep);
406 else if (ret == -1 && errno != EAGAIN)
408 display_exit(display);
414UwacDisplay* UwacOpenDisplay(
const char* name, UwacReturnCode* err)
416 UwacDisplay* ret =
nullptr;
417 ret = (UwacDisplay*)xzalloc(
sizeof(*ret));
421 *err = UWAC_ERROR_NOMEMORY;
425 wl_list_init(&ret->globals);
426 wl_list_init(&ret->seats);
427 wl_list_init(&ret->outputs);
428 wl_list_init(&ret->windows);
429 ret->display = wl_display_connect(name);
431 if (ret->display ==
nullptr)
433 char buffer[256] = WINPR_C_ARRAY_INIT;
434 (void)fprintf(stderr,
"failed to connect to Wayland display %s: %s\n", name,
435 uwac_strerror(errno, buffer,
sizeof(buffer)));
436 *err = UWAC_ERROR_UNABLE_TO_CONNECT;
440 ret->epoll_fd = uwac_os_epoll_create_cloexec();
442 if (ret->epoll_fd < 0)
444 *err = UWAC_NOT_ENOUGH_RESOURCES;
448 ret->display_fd = wl_display_get_fd(ret->display);
449 ret->registry = wl_display_get_registry(ret->display);
453 *err = UWAC_ERROR_NOMEMORY;
454 goto out_close_epoll;
457 wl_registry_add_listener(ret->registry, ®istry_listener, ret);
459 if ((wl_display_roundtrip(ret->display) < 0) || (wl_display_roundtrip(ret->display) < 0))
461 uwacErrorHandler(ret, UWAC_ERROR_UNABLE_TO_CONNECT,
462 "Failed to process Wayland connection: %m\n");
463 *err = UWAC_ERROR_UNABLE_TO_CONNECT;
464 goto out_free_registry;
467 ret->dispatch_fd_task.run = display_dispatch_events;
469 if (UwacDisplayWatchFd(ret, ret->display_fd, EPOLLIN | EPOLLERR | EPOLLHUP,
470 &ret->dispatch_fd_task) < 0)
472 uwacErrorHandler(ret, UWAC_ERROR_INTERNAL,
"unable to watch display fd: %m\n");
473 *err = UWAC_ERROR_INTERNAL;
474 goto out_free_registry;
478 ret->last_error = *err = UWAC_SUCCESS;
481 wl_registry_destroy(ret->registry);
483 close(ret->epoll_fd);
485 wl_display_disconnect(ret->display);
491int UwacDisplayDispatch(UwacDisplay* display,
int timeout)
495 UwacTask* task =
nullptr;
496 struct epoll_event ep[16];
497 wl_display_dispatch_pending(display->display);
499 if (!display->running)
502 ret = wl_display_flush(display->display);
504 if (ret < 0 && errno == EAGAIN)
506 ep[0].events = (EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP);
507 ep[0].data.ptr = &display->dispatch_fd_task;
508 epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, display->display_fd, &ep[0]);
515 count = epoll_wait(display->epoll_fd, ep, ARRAY_LENGTH(ep), timeout);
517 for (
int i = 0; i < count; i++)
519 task = ep[i].data.ptr;
520 task->run(task, ep[i].events);
526UwacReturnCode UwacDisplayGetLastError(
const UwacDisplay* display)
528 return display->last_error;
531UwacReturnCode UwacCloseDisplay(UwacDisplay** pdisplay)
533 UwacDisplay* display =
nullptr;
534 UwacSeat* seat =
nullptr;
535 UwacSeat* tmpSeat =
nullptr;
536 UwacWindow* window =
nullptr;
537 UwacWindow* tmpWindow =
nullptr;
538 UwacOutput* output =
nullptr;
539 UwacOutput* tmpOutput =
nullptr;
540 UwacGlobal* global =
nullptr;
541 UwacGlobal* tmpGlobal =
nullptr;
546 return UWAC_ERROR_INVALID_DISPLAY;
549 wl_list_for_each_safe(window, tmpWindow, &display->windows, link)
551 UwacDestroyWindow(&window);
554 wl_list_for_each_safe(seat, tmpSeat, &display->seats, link)
556 UwacSeatDestroy(seat);
559 wl_list_for_each_safe(output, tmpOutput, &display->outputs, link)
561 UwacDestroyOutput(output);
564 wl_list_for_each_safe(global, tmpGlobal, &display->globals, link)
566 UwacDestroyGlobal(global);
569 if (display->compositor)
570 wl_compositor_destroy(display->compositor);
572 if (display->keyboard_inhibit_manager)
573 zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(display->keyboard_inhibit_manager);
575 if (display->deco_manager)
576 zxdg_decoration_manager_v1_destroy(display->deco_manager);
578 if (display->kde_deco_manager)
579 org_kde_kwin_server_decoration_manager_destroy(display->kde_deco_manager);
581#ifdef BUILD_FULLSCREEN_SHELL
583 if (display->fullscreen_shell)
584 zwp_fullscreen_shell_v1_destroy(display->fullscreen_shell);
589 if (display->ivi_application)
590 ivi_application_destroy(display->ivi_application);
594 if (display->xdg_toplevel)
595 xdg_toplevel_destroy(display->xdg_toplevel);
597 if (display->xdg_base)
598 xdg_wm_base_destroy(display->xdg_base);
601 wl_shell_destroy(display->shell);
604 wl_shm_destroy(display->shm);
606 if (display->linux_dmabuf)
607 zwp_linux_dmabuf_v1_destroy(display->linux_dmabuf);
609 if (display->viewporter)
610 wp_viewporter_destroy(display->viewporter);
612 if (display->subcompositor)
613 wl_subcompositor_destroy(display->subcompositor);
615 if (display->data_device_manager)
616 wl_data_device_manager_destroy(display->data_device_manager);
618 free(display->shm_formats);
619 wl_registry_destroy(display->registry);
620 close(display->epoll_fd);
621 wl_display_disconnect(display->display);
624 while (display->push_queue)
626 UwacEventListItem* item = display->push_queue;
627 display->push_queue = item->tail;
636int UwacDisplayGetFd(UwacDisplay* display)
638 return display->epoll_fd;
641static const char* errorStrings[] = {
643 "out of memory error",
644 "unable to connect to wayland display",
645 "invalid UWAC display",
646 "not enough resources",
654const char* UwacErrorString(UwacReturnCode error)
656 if (error < UWAC_SUCCESS || error >= UWAC_ERROR_LAST)
657 return "invalid error code";
659 return errorStrings[error];
662UwacReturnCode UwacDisplayQueryInterfaceVersion(
const UwacDisplay* display,
const char* name,
665 const UwacGlobal* global =
nullptr;
666 const UwacGlobal* tmp =
nullptr;
669 return UWAC_ERROR_INVALID_DISPLAY;
671 wl_list_for_each_safe(global, tmp, &display->globals, link)
673 if (strcmp(global->interface, name) == 0)
676 *version = global->version;
681 return UWAC_NOT_FOUND;
684uint32_t UwacDisplayQueryGetNbShmFormats(UwacDisplay* display)
693 display->last_error = UWAC_NOT_FOUND;
697 display->last_error = UWAC_SUCCESS;
698 return display->shm_formats_nb;
701UwacReturnCode UwacDisplayQueryShmFormats(
const UwacDisplay* display,
enum wl_shm_format* formats,
702 int formats_size,
int* filled)
705 return UWAC_ERROR_INVALID_DISPLAY;
707 *filled = min((int64_t)display->shm_formats_nb, formats_size);
708 memcpy(formats, (
const void*)display->shm_formats, *filled *
sizeof(
enum wl_shm_format));
712uint32_t UwacDisplayGetNbOutputs(
const UwacDisplay* display)
714 return wl_list_length(&display->outputs);
717const UwacOutput* UwacDisplayGetOutput(UwacDisplay* display,
int index)
720 int display_count = 0;
721 UwacOutput* ret =
nullptr;
726 display_count = wl_list_length(&display->outputs);
727 if (display_count <= index)
730 wl_list_for_each(ret, &display->outputs, link)
739 display->last_error = UWAC_NOT_FOUND;
743 display->last_error = UWAC_SUCCESS;
747UwacReturnCode UwacOutputGetResolution(
const UwacOutput* output, UwacSize* resolution)
749 if ((output->resolution.height <= 0) || (output->resolution.width <= 0))
750 return UWAC_ERROR_INTERNAL;
752 *resolution = output->resolution;
756UwacReturnCode UwacOutputGetPosition(
const UwacOutput* output, UwacPosition* pos)
758 *pos = output->position;
762UwacEvent* UwacDisplayNewEvent(UwacDisplay* display,
int type)
764 UwacEventListItem* ret =
nullptr;
771 ret = xzalloc(
sizeof(UwacEventListItem));
775 assert(uwacErrorHandler(display, UWAC_ERROR_NOMEMORY,
"unable to allocate a '%s' event",
777 display->last_error = UWAC_ERROR_NOMEMORY;
781 ret->event.type = type;
782 ret->tail = display->push_queue;
785 ret->tail->head = ret;
787 display->pop_queue = ret;
789 display->push_queue = ret;
793bool UwacHasEvent(UwacDisplay* display)
795 return display->pop_queue !=
nullptr;
798UwacReturnCode UwacNextEvent(UwacDisplay* display, UwacEvent* event)
800 UwacEventListItem* prevItem =
nullptr;
804 return UWAC_ERROR_INVALID_DISPLAY;
806 while (!display->pop_queue)
808 ret = UwacDisplayDispatch(display, 1 * 1000);
811 return UWAC_ERROR_INTERNAL;
813 return UWAC_ERROR_CLOSED;
816 prevItem = display->pop_queue->head;
817 *
event = display->pop_queue->event;
818 free(display->pop_queue);
819 display->pop_queue = prevItem;
822 prevItem->tail =
nullptr;
824 display->push_queue =
nullptr;