Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
796
third_party/FreeRDP/uwac/libuwac/uwac-display.c
vendored
Normal file
796
third_party/FreeRDP/uwac/libuwac/uwac-display.c
vendored
Normal file
@@ -0,0 +1,796 @@
|
||||
/*
|
||||
* Copyright © 2014 David FORT <contact@hardening-consulting.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that copyright
|
||||
* notice and this permission notice appear in supporting documentation, and
|
||||
* that the name of the copyright holders not be used in advertising or
|
||||
* publicity pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no representations
|
||||
* about the suitability of this software for any purpose. It is provided "as
|
||||
* is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THIS SOFTWARE.
|
||||
*/
|
||||
#include "uwac-priv.h"
|
||||
#include "uwac-utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include "uwac-os.h"
|
||||
#include "wayland-cursor.h"
|
||||
|
||||
#define TARGET_COMPOSITOR_INTERFACE 3U
|
||||
#define TARGET_SHM_INTERFACE 1U
|
||||
#define TARGET_SHELL_INTERFACE 1U
|
||||
#define TARGET_DDM_INTERFACE 1U
|
||||
#define TARGET_SEAT_INTERFACE 5U
|
||||
#define TARGET_XDG_VERSION 5U /* The version of xdg-shell that we implement */
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
static const char* event_names[] = {
|
||||
"new seat", "removed seat", "new output", "configure", "pointer enter",
|
||||
"pointer leave", "pointer motion", "pointer buttons", "pointer axis", "keyboard enter",
|
||||
"key", "touch frame begin", "touch up", "touch down", "touch motion",
|
||||
"touch cancel", "touch frame end", "frame done", "close", nullptr
|
||||
};
|
||||
#endif
|
||||
|
||||
static bool uwac_default_error_handler(UwacDisplay* display, UwacReturnCode code, const char* msg,
|
||||
...)
|
||||
{
|
||||
va_list args = WINPR_C_ARRAY_INIT;
|
||||
va_start(args, msg);
|
||||
(void)vfprintf(stderr, "%s", args);
|
||||
va_end(args);
|
||||
return false;
|
||||
}
|
||||
|
||||
UwacErrorHandler uwacErrorHandler = uwac_default_error_handler;
|
||||
|
||||
void UwacInstallErrorHandler(UwacErrorHandler handler)
|
||||
{
|
||||
if (handler)
|
||||
uwacErrorHandler = handler;
|
||||
else
|
||||
uwacErrorHandler = uwac_default_error_handler;
|
||||
}
|
||||
|
||||
static void cb_shm_format(void* data, struct wl_shm* wl_shm, uint32_t format)
|
||||
{
|
||||
UwacDisplay* d = data;
|
||||
|
||||
if (format == WL_SHM_FORMAT_RGB565)
|
||||
d->has_rgb565 = true;
|
||||
|
||||
d->shm_formats_nb++;
|
||||
d->shm_formats =
|
||||
xrealloc((void*)d->shm_formats, sizeof(enum wl_shm_format) * d->shm_formats_nb);
|
||||
d->shm_formats[d->shm_formats_nb - 1] = format;
|
||||
}
|
||||
|
||||
static struct wl_shm_listener shm_listener = { cb_shm_format };
|
||||
|
||||
static void xdg_shell_ping(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial)
|
||||
{
|
||||
xdg_wm_base_pong(xdg_wm_base, serial);
|
||||
}
|
||||
|
||||
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
||||
xdg_shell_ping,
|
||||
};
|
||||
|
||||
#ifdef BUILD_FULLSCREEN_SHELL
|
||||
static void fullscreen_capability(void* data,
|
||||
struct zwp_fullscreen_shell_v1* zwp_fullscreen_shell_v1,
|
||||
uint32_t capability)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct zwp_fullscreen_shell_v1_listener fullscreen_shell_listener = {
|
||||
fullscreen_capability,
|
||||
};
|
||||
#endif
|
||||
|
||||
static void display_destroy_seat(UwacDisplay* d, uint32_t name)
|
||||
{
|
||||
UwacSeat* seat = nullptr;
|
||||
UwacSeat* tmp = nullptr;
|
||||
wl_list_for_each_safe(seat, tmp, &d->seats, link)
|
||||
{
|
||||
if (seat->seat_id == name)
|
||||
{
|
||||
UwacSeatDestroy(seat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UwacSeatRegisterDDM(UwacSeat* seat)
|
||||
{
|
||||
UwacDisplay* d = seat->display;
|
||||
if (!d->data_device_manager)
|
||||
return;
|
||||
|
||||
if (!seat->data_device)
|
||||
seat->data_device =
|
||||
wl_data_device_manager_get_data_device(d->data_device_manager, seat->seat);
|
||||
}
|
||||
|
||||
static void UwacRegisterCursor(UwacSeat* seat)
|
||||
{
|
||||
if (!seat || !seat->display || !seat->display->compositor)
|
||||
return;
|
||||
|
||||
seat->pointer_surface = wl_compositor_create_surface(seat->display->compositor);
|
||||
}
|
||||
|
||||
static void registry_handle_global(void* data, struct wl_registry* registry, uint32_t id,
|
||||
const char* interface, uint32_t version)
|
||||
{
|
||||
UwacDisplay* d = data;
|
||||
UwacGlobal* global = nullptr;
|
||||
global = xzalloc(sizeof *global);
|
||||
global->name = id;
|
||||
global->interface = xstrdup(interface);
|
||||
global->version = version;
|
||||
wl_list_insert(d->globals.prev, &global->link);
|
||||
|
||||
if (strcmp(interface, "wl_compositor") == 0)
|
||||
{
|
||||
d->compositor = wl_registry_bind(registry, id, &wl_compositor_interface,
|
||||
min(TARGET_COMPOSITOR_INTERFACE, version));
|
||||
}
|
||||
else if (strcmp(interface, "wl_shm") == 0)
|
||||
{
|
||||
d->shm =
|
||||
wl_registry_bind(registry, id, &wl_shm_interface, min(TARGET_SHM_INTERFACE, version));
|
||||
wl_shm_add_listener(d->shm, &shm_listener, d);
|
||||
}
|
||||
else if (strcmp(interface, "wl_output") == 0)
|
||||
{
|
||||
UwacOutput* output = nullptr;
|
||||
UwacOutputNewEvent* ev = nullptr;
|
||||
output = UwacCreateOutput(d, id, version);
|
||||
|
||||
if (!output)
|
||||
{
|
||||
assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to create output\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
ev = (UwacOutputNewEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_OUTPUT);
|
||||
|
||||
if (ev)
|
||||
ev->output = output;
|
||||
}
|
||||
else if (strcmp(interface, "wl_seat") == 0)
|
||||
{
|
||||
UwacSeatNewEvent* ev = nullptr;
|
||||
UwacSeat* seat = nullptr;
|
||||
seat = UwacSeatNew(d, id, min(version, TARGET_SEAT_INTERFACE));
|
||||
|
||||
if (!seat)
|
||||
{
|
||||
assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to create new seat\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
UwacSeatRegisterDDM(seat);
|
||||
UwacSeatRegisterClipboard(seat);
|
||||
UwacRegisterCursor(seat);
|
||||
ev = (UwacSeatNewEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_SEAT);
|
||||
|
||||
if (!ev)
|
||||
{
|
||||
assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to create new seat event\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
ev->seat = seat;
|
||||
}
|
||||
else if (strcmp(interface, "wl_data_device_manager") == 0)
|
||||
{
|
||||
UwacSeat* seat = nullptr;
|
||||
UwacSeat* tmp = nullptr;
|
||||
|
||||
d->data_device_manager = wl_registry_bind(registry, id, &wl_data_device_manager_interface,
|
||||
min(TARGET_DDM_INTERFACE, version));
|
||||
|
||||
wl_list_for_each_safe(seat, tmp, &d->seats, link)
|
||||
{
|
||||
UwacSeatRegisterDDM(seat);
|
||||
UwacSeatRegisterClipboard(seat);
|
||||
UwacRegisterCursor(seat);
|
||||
}
|
||||
}
|
||||
else if (strcmp(interface, "wl_shell") == 0)
|
||||
{
|
||||
d->shell = wl_registry_bind(registry, id, &wl_shell_interface,
|
||||
min(TARGET_SHELL_INTERFACE, version));
|
||||
}
|
||||
else if (strcmp(interface, "xdg_wm_base") == 0)
|
||||
{
|
||||
d->xdg_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1);
|
||||
xdg_wm_base_add_listener(d->xdg_base, &xdg_wm_base_listener, d);
|
||||
}
|
||||
else if (strcmp(interface, "wp_viewporter") == 0)
|
||||
{
|
||||
d->viewporter = wl_registry_bind(registry, id, &wp_viewporter_interface, 1);
|
||||
}
|
||||
else if (strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0)
|
||||
{
|
||||
d->keyboard_inhibit_manager =
|
||||
wl_registry_bind(registry, id, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
|
||||
}
|
||||
else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0)
|
||||
{
|
||||
d->deco_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1);
|
||||
}
|
||||
else if (strcmp(interface, "org_kde_kwin_server_decoration_manager") == 0)
|
||||
{
|
||||
d->kde_deco_manager =
|
||||
wl_registry_bind(registry, id, &org_kde_kwin_server_decoration_manager_interface, 1);
|
||||
}
|
||||
#if BUILD_IVI
|
||||
else if (strcmp(interface, "ivi_application") == 0)
|
||||
{
|
||||
d->ivi_application = wl_registry_bind(registry, id, &ivi_application_interface, 1);
|
||||
}
|
||||
#endif
|
||||
#if BUILD_FULLSCREEN_SHELL
|
||||
else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0)
|
||||
{
|
||||
d->fullscreen_shell = wl_registry_bind(registry, id, &zwp_fullscreen_shell_v1_interface, 1);
|
||||
zwp_fullscreen_shell_v1_add_listener(d->fullscreen_shell, &fullscreen_shell_listener, d);
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
else if (strcmp(interface, "text_cursor_position") == 0)
|
||||
{
|
||||
d->text_cursor_position = wl_registry_bind(registry, id, &text_cursor_position_interface, 1);
|
||||
}
|
||||
else if (strcmp(interface, "workspace_manager") == 0)
|
||||
{
|
||||
//init_workspace_manager(d, id);
|
||||
}
|
||||
else if (strcmp(interface, "wl_subcompositor") == 0)
|
||||
{
|
||||
d->subcompositor = wl_registry_bind(registry, id, &wl_subcompositor_interface, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void registry_handle_global_remove(void* data, struct wl_registry* registry, uint32_t name)
|
||||
{
|
||||
UwacDisplay* d = data;
|
||||
UwacGlobal* global = nullptr;
|
||||
UwacGlobal* tmp = nullptr;
|
||||
wl_list_for_each_safe(global, tmp, &d->globals, link)
|
||||
{
|
||||
if (global->name != name)
|
||||
continue;
|
||||
|
||||
#if 0
|
||||
|
||||
if (strcmp(global->interface, "wl_output") == 0)
|
||||
display_destroy_output(d, name);
|
||||
|
||||
#endif
|
||||
|
||||
if (strcmp(global->interface, "wl_seat") == 0)
|
||||
{
|
||||
UwacSeatRemovedEvent* ev = nullptr;
|
||||
display_destroy_seat(d, name);
|
||||
ev = (UwacSeatRemovedEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_REMOVED_SEAT);
|
||||
|
||||
if (ev)
|
||||
ev->id = name;
|
||||
}
|
||||
|
||||
wl_list_remove(&global->link);
|
||||
free(global->interface);
|
||||
free(global);
|
||||
}
|
||||
}
|
||||
|
||||
static void UwacDestroyGlobal(UwacGlobal* global)
|
||||
{
|
||||
free(global->interface);
|
||||
wl_list_remove(&global->link);
|
||||
free(global);
|
||||
}
|
||||
|
||||
static void* display_bind(UwacDisplay* display, uint32_t name, const struct wl_interface* interface,
|
||||
uint32_t version)
|
||||
{
|
||||
return wl_registry_bind(display->registry, name, interface, version);
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = { registry_handle_global,
|
||||
registry_handle_global_remove };
|
||||
|
||||
int UwacDisplayWatchFd(UwacDisplay* display, int fd, uint32_t events, UwacTask* task)
|
||||
{
|
||||
struct epoll_event ep;
|
||||
ep.events = events;
|
||||
ep.data.ptr = task;
|
||||
return epoll_ctl(display->epoll_fd, EPOLL_CTL_ADD, fd, &ep);
|
||||
}
|
||||
|
||||
static void UwacDisplayUnwatchFd(UwacDisplay* display, int fd)
|
||||
{
|
||||
epoll_ctl(display->epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
|
||||
}
|
||||
|
||||
static void display_exit(UwacDisplay* display)
|
||||
{
|
||||
display->running = false;
|
||||
}
|
||||
|
||||
static void display_dispatch_events(UwacTask* task, uint32_t events)
|
||||
{
|
||||
UwacDisplay* display = container_of(task, UwacDisplay, dispatch_fd_task);
|
||||
struct epoll_event ep;
|
||||
int ret = 0;
|
||||
display->display_fd_events = events;
|
||||
|
||||
if ((events & EPOLLERR) || (events & EPOLLHUP))
|
||||
{
|
||||
display_exit(display);
|
||||
return;
|
||||
}
|
||||
|
||||
if (events & EPOLLIN)
|
||||
{
|
||||
ret = wl_display_dispatch(display->display);
|
||||
|
||||
if (ret == -1)
|
||||
{
|
||||
display_exit(display);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (events & EPOLLOUT)
|
||||
{
|
||||
ret = wl_display_flush(display->display);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
ep.events = EPOLLIN | EPOLLERR | EPOLLHUP;
|
||||
ep.data.ptr = &display->dispatch_fd_task;
|
||||
epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, display->display_fd, &ep);
|
||||
}
|
||||
else if (ret == -1 && errno != EAGAIN)
|
||||
{
|
||||
display_exit(display);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UwacDisplay* UwacOpenDisplay(const char* name, UwacReturnCode* err)
|
||||
{
|
||||
UwacDisplay* ret = nullptr;
|
||||
ret = (UwacDisplay*)xzalloc(sizeof(*ret));
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
*err = UWAC_ERROR_NOMEMORY;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wl_list_init(&ret->globals);
|
||||
wl_list_init(&ret->seats);
|
||||
wl_list_init(&ret->outputs);
|
||||
wl_list_init(&ret->windows);
|
||||
ret->display = wl_display_connect(name);
|
||||
|
||||
if (ret->display == nullptr)
|
||||
{
|
||||
char buffer[256] = WINPR_C_ARRAY_INIT;
|
||||
(void)fprintf(stderr, "failed to connect to Wayland display %s: %s\n", name,
|
||||
uwac_strerror(errno, buffer, sizeof(buffer)));
|
||||
*err = UWAC_ERROR_UNABLE_TO_CONNECT;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
ret->epoll_fd = uwac_os_epoll_create_cloexec();
|
||||
|
||||
if (ret->epoll_fd < 0)
|
||||
{
|
||||
*err = UWAC_NOT_ENOUGH_RESOURCES;
|
||||
goto out_disconnect;
|
||||
}
|
||||
|
||||
ret->display_fd = wl_display_get_fd(ret->display);
|
||||
ret->registry = wl_display_get_registry(ret->display);
|
||||
|
||||
if (!ret->registry)
|
||||
{
|
||||
*err = UWAC_ERROR_NOMEMORY;
|
||||
goto out_close_epoll;
|
||||
}
|
||||
|
||||
wl_registry_add_listener(ret->registry, ®istry_listener, ret);
|
||||
|
||||
if ((wl_display_roundtrip(ret->display) < 0) || (wl_display_roundtrip(ret->display) < 0))
|
||||
{
|
||||
uwacErrorHandler(ret, UWAC_ERROR_UNABLE_TO_CONNECT,
|
||||
"Failed to process Wayland connection: %m\n");
|
||||
*err = UWAC_ERROR_UNABLE_TO_CONNECT;
|
||||
goto out_free_registry;
|
||||
}
|
||||
|
||||
ret->dispatch_fd_task.run = display_dispatch_events;
|
||||
|
||||
if (UwacDisplayWatchFd(ret, ret->display_fd, EPOLLIN | EPOLLERR | EPOLLHUP,
|
||||
&ret->dispatch_fd_task) < 0)
|
||||
{
|
||||
uwacErrorHandler(ret, UWAC_ERROR_INTERNAL, "unable to watch display fd: %m\n");
|
||||
*err = UWAC_ERROR_INTERNAL;
|
||||
goto out_free_registry;
|
||||
}
|
||||
|
||||
ret->running = true;
|
||||
ret->last_error = *err = UWAC_SUCCESS;
|
||||
return ret;
|
||||
out_free_registry:
|
||||
wl_registry_destroy(ret->registry);
|
||||
out_close_epoll:
|
||||
close(ret->epoll_fd);
|
||||
out_disconnect:
|
||||
wl_display_disconnect(ret->display);
|
||||
out_free:
|
||||
free(ret);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int UwacDisplayDispatch(UwacDisplay* display, int timeout)
|
||||
{
|
||||
int ret = 0;
|
||||
int count = 0;
|
||||
UwacTask* task = nullptr;
|
||||
struct epoll_event ep[16];
|
||||
wl_display_dispatch_pending(display->display);
|
||||
|
||||
if (!display->running)
|
||||
return 0;
|
||||
|
||||
ret = wl_display_flush(display->display);
|
||||
|
||||
if (ret < 0 && errno == EAGAIN)
|
||||
{
|
||||
ep[0].events = (EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP);
|
||||
ep[0].data.ptr = &display->dispatch_fd_task;
|
||||
epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, display->display_fd, &ep[0]);
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
count = epoll_wait(display->epoll_fd, ep, ARRAY_LENGTH(ep), timeout);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
task = ep[i].data.ptr;
|
||||
task->run(task, ep[i].events);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
UwacReturnCode UwacDisplayGetLastError(const UwacDisplay* display)
|
||||
{
|
||||
return display->last_error;
|
||||
}
|
||||
|
||||
UwacReturnCode UwacCloseDisplay(UwacDisplay** pdisplay)
|
||||
{
|
||||
UwacDisplay* display = nullptr;
|
||||
UwacSeat* seat = nullptr;
|
||||
UwacSeat* tmpSeat = nullptr;
|
||||
UwacWindow* window = nullptr;
|
||||
UwacWindow* tmpWindow = nullptr;
|
||||
UwacOutput* output = nullptr;
|
||||
UwacOutput* tmpOutput = nullptr;
|
||||
UwacGlobal* global = nullptr;
|
||||
UwacGlobal* tmpGlobal = nullptr;
|
||||
assert(pdisplay);
|
||||
display = *pdisplay;
|
||||
|
||||
if (!display)
|
||||
return UWAC_ERROR_INVALID_DISPLAY;
|
||||
|
||||
/* destroy windows */
|
||||
wl_list_for_each_safe(window, tmpWindow, &display->windows, link)
|
||||
{
|
||||
UwacDestroyWindow(&window);
|
||||
}
|
||||
/* destroy seats */
|
||||
wl_list_for_each_safe(seat, tmpSeat, &display->seats, link)
|
||||
{
|
||||
UwacSeatDestroy(seat);
|
||||
}
|
||||
/* destroy output */
|
||||
wl_list_for_each_safe(output, tmpOutput, &display->outputs, link)
|
||||
{
|
||||
UwacDestroyOutput(output);
|
||||
}
|
||||
/* destroy globals */
|
||||
wl_list_for_each_safe(global, tmpGlobal, &display->globals, link)
|
||||
{
|
||||
UwacDestroyGlobal(global);
|
||||
}
|
||||
|
||||
if (display->compositor)
|
||||
wl_compositor_destroy(display->compositor);
|
||||
|
||||
if (display->keyboard_inhibit_manager)
|
||||
zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(display->keyboard_inhibit_manager);
|
||||
|
||||
if (display->deco_manager)
|
||||
zxdg_decoration_manager_v1_destroy(display->deco_manager);
|
||||
|
||||
if (display->kde_deco_manager)
|
||||
org_kde_kwin_server_decoration_manager_destroy(display->kde_deco_manager);
|
||||
|
||||
#ifdef BUILD_FULLSCREEN_SHELL
|
||||
|
||||
if (display->fullscreen_shell)
|
||||
zwp_fullscreen_shell_v1_destroy(display->fullscreen_shell);
|
||||
|
||||
#endif
|
||||
#ifdef BUILD_IVI
|
||||
|
||||
if (display->ivi_application)
|
||||
ivi_application_destroy(display->ivi_application);
|
||||
|
||||
#endif
|
||||
|
||||
if (display->xdg_toplevel)
|
||||
xdg_toplevel_destroy(display->xdg_toplevel);
|
||||
|
||||
if (display->xdg_base)
|
||||
xdg_wm_base_destroy(display->xdg_base);
|
||||
|
||||
if (display->shell)
|
||||
wl_shell_destroy(display->shell);
|
||||
|
||||
if (display->shm)
|
||||
wl_shm_destroy(display->shm);
|
||||
|
||||
if (display->viewporter)
|
||||
wp_viewporter_destroy(display->viewporter);
|
||||
|
||||
if (display->subcompositor)
|
||||
wl_subcompositor_destroy(display->subcompositor);
|
||||
|
||||
if (display->data_device_manager)
|
||||
wl_data_device_manager_destroy(display->data_device_manager);
|
||||
|
||||
free(display->shm_formats);
|
||||
wl_registry_destroy(display->registry);
|
||||
close(display->epoll_fd);
|
||||
wl_display_disconnect(display->display);
|
||||
|
||||
/* cleanup the event queue */
|
||||
while (display->push_queue)
|
||||
{
|
||||
UwacEventListItem* item = display->push_queue;
|
||||
display->push_queue = item->tail;
|
||||
free(item);
|
||||
}
|
||||
|
||||
free(display);
|
||||
*pdisplay = nullptr;
|
||||
return UWAC_SUCCESS;
|
||||
}
|
||||
|
||||
int UwacDisplayGetFd(UwacDisplay* display)
|
||||
{
|
||||
return display->epoll_fd;
|
||||
}
|
||||
|
||||
static const char* errorStrings[] = {
|
||||
"success",
|
||||
"out of memory error",
|
||||
"unable to connect to wayland display",
|
||||
"invalid UWAC display",
|
||||
"not enough resources",
|
||||
"timed out",
|
||||
"not found",
|
||||
"closed connection",
|
||||
|
||||
"internal error",
|
||||
};
|
||||
|
||||
const char* UwacErrorString(UwacReturnCode error)
|
||||
{
|
||||
if (error < UWAC_SUCCESS || error >= UWAC_ERROR_LAST)
|
||||
return "invalid error code";
|
||||
|
||||
return errorStrings[error];
|
||||
}
|
||||
|
||||
UwacReturnCode UwacDisplayQueryInterfaceVersion(const UwacDisplay* display, const char* name,
|
||||
uint32_t* version)
|
||||
{
|
||||
const UwacGlobal* global = nullptr;
|
||||
const UwacGlobal* tmp = nullptr;
|
||||
|
||||
if (!display)
|
||||
return UWAC_ERROR_INVALID_DISPLAY;
|
||||
|
||||
wl_list_for_each_safe(global, tmp, &display->globals, link)
|
||||
{
|
||||
if (strcmp(global->interface, name) == 0)
|
||||
{
|
||||
if (version)
|
||||
*version = global->version;
|
||||
|
||||
return UWAC_SUCCESS;
|
||||
}
|
||||
}
|
||||
return UWAC_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint32_t UwacDisplayQueryGetNbShmFormats(UwacDisplay* display)
|
||||
{
|
||||
if (!display)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!display->shm)
|
||||
{
|
||||
display->last_error = UWAC_NOT_FOUND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
display->last_error = UWAC_SUCCESS;
|
||||
return display->shm_formats_nb;
|
||||
}
|
||||
|
||||
UwacReturnCode UwacDisplayQueryShmFormats(const UwacDisplay* display, enum wl_shm_format* formats,
|
||||
int formats_size, int* filled)
|
||||
{
|
||||
if (!display)
|
||||
return UWAC_ERROR_INVALID_DISPLAY;
|
||||
|
||||
*filled = min((int64_t)display->shm_formats_nb, formats_size);
|
||||
memcpy(formats, (const void*)display->shm_formats, *filled * sizeof(enum wl_shm_format));
|
||||
return UWAC_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t UwacDisplayGetNbOutputs(const UwacDisplay* display)
|
||||
{
|
||||
return wl_list_length(&display->outputs);
|
||||
}
|
||||
|
||||
const UwacOutput* UwacDisplayGetOutput(UwacDisplay* display, int index)
|
||||
{
|
||||
int i = 0;
|
||||
int display_count = 0;
|
||||
UwacOutput* ret = nullptr;
|
||||
|
||||
if (!display)
|
||||
return nullptr;
|
||||
|
||||
display_count = wl_list_length(&display->outputs);
|
||||
if (display_count <= index)
|
||||
return nullptr;
|
||||
|
||||
wl_list_for_each(ret, &display->outputs, link)
|
||||
{
|
||||
if (i == index)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
display->last_error = UWAC_NOT_FOUND;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
display->last_error = UWAC_SUCCESS;
|
||||
return ret;
|
||||
}
|
||||
|
||||
UwacReturnCode UwacOutputGetResolution(const UwacOutput* output, UwacSize* resolution)
|
||||
{
|
||||
if ((output->resolution.height <= 0) || (output->resolution.width <= 0))
|
||||
return UWAC_ERROR_INTERNAL;
|
||||
|
||||
*resolution = output->resolution;
|
||||
return UWAC_SUCCESS;
|
||||
}
|
||||
|
||||
UwacReturnCode UwacOutputGetPosition(const UwacOutput* output, UwacPosition* pos)
|
||||
{
|
||||
*pos = output->position;
|
||||
return UWAC_SUCCESS;
|
||||
}
|
||||
|
||||
UwacEvent* UwacDisplayNewEvent(UwacDisplay* display, int type)
|
||||
{
|
||||
UwacEventListItem* ret = nullptr;
|
||||
|
||||
if (!display)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret = xzalloc(sizeof(UwacEventListItem));
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
assert(uwacErrorHandler(display, UWAC_ERROR_NOMEMORY, "unable to allocate a '%s' event",
|
||||
event_names[type]));
|
||||
display->last_error = UWAC_ERROR_NOMEMORY;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret->event.type = type;
|
||||
ret->tail = display->push_queue;
|
||||
|
||||
if (ret->tail)
|
||||
ret->tail->head = ret;
|
||||
else
|
||||
display->pop_queue = ret;
|
||||
|
||||
display->push_queue = ret;
|
||||
return &ret->event;
|
||||
}
|
||||
|
||||
bool UwacHasEvent(UwacDisplay* display)
|
||||
{
|
||||
return display->pop_queue != nullptr;
|
||||
}
|
||||
|
||||
UwacReturnCode UwacNextEvent(UwacDisplay* display, UwacEvent* event)
|
||||
{
|
||||
UwacEventListItem* prevItem = nullptr;
|
||||
int ret = 0;
|
||||
|
||||
if (!display)
|
||||
return UWAC_ERROR_INVALID_DISPLAY;
|
||||
|
||||
while (!display->pop_queue)
|
||||
{
|
||||
ret = UwacDisplayDispatch(display, 1 * 1000);
|
||||
|
||||
if (ret < 0)
|
||||
return UWAC_ERROR_INTERNAL;
|
||||
else if (ret == 0)
|
||||
return UWAC_ERROR_CLOSED;
|
||||
}
|
||||
|
||||
prevItem = display->pop_queue->head;
|
||||
*event = display->pop_queue->event;
|
||||
free(display->pop_queue);
|
||||
display->pop_queue = prevItem;
|
||||
|
||||
if (prevItem)
|
||||
prevItem->tail = nullptr;
|
||||
else
|
||||
display->push_queue = nullptr;
|
||||
|
||||
return UWAC_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user