Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,108 @@
# UWAC: Using Wayland As Client
#
# Copyright 2015 David FORT <contact@hardening-consulting.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(MODULE_NAME "uwac")
set(MODULE_PREFIX "UWAC")
set(GENERATED_SOURCES "")
macro(generate_protocol_file PROTO)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/protocols/${PROTO}-protocol.c" COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_CURRENT_BINARY_DIR}/protocols
COMMAND ${WAYLAND_SCANNER} code < ${CMAKE_CURRENT_SOURCE_DIR}/../protocols/${PROTO}.xml >
${CMAKE_CURRENT_BINARY_DIR}/protocols/${PROTO}-protocol.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../protocols/${PROTO}.xml
)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/protocols/${PROTO}-client-protocol.h"
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/protocols
COMMAND ${WAYLAND_SCANNER} client-header < ${CMAKE_CURRENT_SOURCE_DIR}/../protocols/${PROTO}.xml >
${CMAKE_CURRENT_BINARY_DIR}/protocols/${PROTO}-client-protocol.h
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../protocols/${PROTO}.xml
)
list(APPEND GENERATED_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/protocols/${PROTO}-client-protocol.h)
list(APPEND GENERATED_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/protocols/${PROTO}-protocol.c)
endmacro()
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
generate_protocol_file(xdg-shell)
generate_protocol_file(viewporter)
generate_protocol_file(xdg-decoration-unstable-v1)
generate_protocol_file(server-decoration)
generate_protocol_file(ivi-application)
generate_protocol_file(fullscreen-shell-unstable-v1)
generate_protocol_file(keyboard-shortcuts-inhibit-unstable-v1)
if(FREEBSD)
include_directories(SYSTEM ${EPOLLSHIM_INCLUDE_DIR})
endif()
include_directories(SYSTEM ${WAYLAND_INCLUDE_DIR})
include_directories(SYSTEM ${XKBCOMMON_INCLUDE_DIR})
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include")
include_directories("${CMAKE_CURRENT_BINARY_DIR}/../include")
include_directories("${CMAKE_CURRENT_BINARY_DIR}/protocols")
add_compile_definitions(BUILD_IVI BUILD_FULLSCREEN_SHELL ENABLE_XKBCOMMON)
set(${MODULE_PREFIX}_SRCS
${GENERATED_SOURCES}
uwac-display.c
uwac-input.c
uwac-clipboard.c
uwac-os.c
uwac-os.h
uwac-output.c
uwac-priv.h
uwac-tools.c
uwac-utils.c
uwac-window.c
)
add_library(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
set_target_properties(${MODULE_NAME} PROPERTIES LINKER_LANGUAGE C)
set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${UWAC_API_VERSION})
if(WITH_LIBRARY_VERSIONING)
set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${UWAC_VERSION} SOVERSION ${UWAC_API_VERSION})
endif()
target_link_libraries(
${MODULE_NAME} ${${MODULE_PREFIX}_LIBS} PRIVATE ${WAYLAND_LIBS} ${XKBCOMMON_LIBS} ${EPOLLSHIM_LIBS}
)
if(UWAC_HAVE_PIXMAN_REGION)
target_link_libraries(${MODULE_NAME} PRIVATE ${pixman_LINK_LIBRARIES})
else()
target_link_libraries(${MODULE_NAME} PRIVATE freerdp)
endif()
target_link_libraries(${MODULE_NAME} PRIVATE m)
if(NOT UWAC_FORCE_STATIC_BUILD)
target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include/uwac${UWAC_API_VERSION}>)
install(TARGETS ${MODULE_NAME} COMPONENT libraries EXPORT uwac ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "uwac")
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
# add_subdirectory(test)
endif()

View File

@@ -0,0 +1,282 @@
/*
* Copyright © 2018 Armin Novak <armin.novak@thincast.com>
* Copyright © 2018 Thincast Technologies GmbH
*
* 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 <assert.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
/* paste */
static void data_offer_offer(void* data, struct wl_data_offer* data_offer,
const char* offered_mime_type)
{
UwacSeat* seat = (UwacSeat*)data;
assert(seat);
if (!seat->ignore_announcement)
{
UwacClipboardEvent* event =
(UwacClipboardEvent*)UwacDisplayNewEvent(seat->display, UWAC_EVENT_CLIPBOARD_OFFER);
if (!event)
{
assert(uwacErrorHandler(seat->display, UWAC_ERROR_INTERNAL,
"failed to allocate a clipboard event\n"));
}
else
{
event->seat = seat;
(void)snprintf(event->mime, sizeof(event->mime), "%s", offered_mime_type);
}
}
}
static const struct wl_data_offer_listener data_offer_listener = { .offer = data_offer_offer };
static void data_device_data_offer(void* data, struct wl_data_device* data_device,
struct wl_data_offer* data_offer)
{
UwacSeat* seat = (UwacSeat*)data;
assert(seat);
if (!seat->ignore_announcement)
{
UwacClipboardEvent* event =
(UwacClipboardEvent*)UwacDisplayNewEvent(seat->display, UWAC_EVENT_CLIPBOARD_SELECT);
if (!event)
{
assert(uwacErrorHandler(seat->display, UWAC_ERROR_INTERNAL,
"failed to allocate a close event\n"));
}
else
event->seat = seat;
wl_data_offer_add_listener(data_offer, &data_offer_listener, data);
seat->offer = data_offer;
}
else
seat->offer = nullptr;
}
static void data_device_selection(void* data, struct wl_data_device* data_device,
struct wl_data_offer* data_offer)
{
}
static const struct wl_data_device_listener data_device_listener = {
.data_offer = data_device_data_offer, .selection = data_device_selection
};
/* copy */
static void data_source_target_handler(void* data, struct wl_data_source* data_source,
const char* mime_type)
{
}
static void data_source_send_handler(void* data, struct wl_data_source* data_source,
const char* mime_type, int fd)
{
UwacSeat* seat = (UwacSeat*)data;
seat->transfer_data(seat, seat->data_context, mime_type, fd);
}
static void data_source_cancelled_handler(void* data, struct wl_data_source* data_source)
{
UwacSeat* seat = (UwacSeat*)data;
seat->cancel_data(seat, seat->data_context);
}
static const struct wl_data_source_listener data_source_listener = {
.target = data_source_target_handler,
.send = data_source_send_handler,
.cancelled = data_source_cancelled_handler
};
static void UwacRegisterDeviceListener(UwacSeat* s)
{
wl_data_device_add_listener(s->data_device, &data_device_listener, s);
}
static UwacReturnCode UwacCreateDataSource(UwacSeat* s)
{
if (!s)
return UWAC_ERROR_INTERNAL;
s->data_source = wl_data_device_manager_create_data_source(s->display->data_device_manager);
wl_data_source_add_listener(s->data_source, &data_source_listener, s);
return UWAC_SUCCESS;
}
UwacReturnCode UwacSeatRegisterClipboard(UwacSeat* s)
{
UwacClipboardEvent* event = nullptr;
if (!s)
return UWAC_ERROR_INTERNAL;
if (!s->display->data_device_manager || !s->data_device)
return UWAC_NOT_ENOUGH_RESOURCES;
UwacRegisterDeviceListener(s);
UwacReturnCode rc = UwacCreateDataSource(s);
if (rc != UWAC_SUCCESS)
return rc;
event = (UwacClipboardEvent*)UwacDisplayNewEvent(s->display, UWAC_EVENT_CLIPBOARD_AVAILABLE);
if (!event)
{
assert(uwacErrorHandler(s->display, UWAC_ERROR_INTERNAL,
"failed to allocate a clipboard event\n"));
return UWAC_ERROR_INTERNAL;
}
event->seat = s;
return UWAC_SUCCESS;
}
UwacReturnCode UwacClipboardOfferDestroy(UwacSeat* seat)
{
if (!seat)
return UWAC_ERROR_INTERNAL;
if (seat->data_source)
wl_data_source_destroy(seat->data_source);
return UwacCreateDataSource(seat);
}
UwacReturnCode UwacClipboardOfferCreate(UwacSeat* seat, const char* mime)
{
if (!seat || !mime)
return UWAC_ERROR_INTERNAL;
wl_data_source_offer(seat->data_source, mime);
return UWAC_SUCCESS;
}
static void callback_done(void* data, struct wl_callback* callback, uint32_t serial)
{
*(uint32_t*)data = serial;
}
static const struct wl_callback_listener callback_listener = { .done = callback_done };
static uint32_t get_serial(UwacSeat* s)
{
struct wl_callback* callback = nullptr;
uint32_t serial = 0;
callback = wl_display_sync(s->display->display);
wl_callback_add_listener(callback, &callback_listener, &serial);
while (serial == 0)
{
wl_display_dispatch(s->display->display);
}
return serial;
}
UwacReturnCode UwacClipboardOfferAnnounce(UwacSeat* seat, void* context,
UwacDataTransferHandler transfer,
UwacCancelDataTransferHandler cancel)
{
if (!seat)
return UWAC_ERROR_INTERNAL;
seat->data_context = context;
seat->transfer_data = transfer;
seat->cancel_data = cancel;
seat->ignore_announcement = true;
wl_data_device_set_selection(seat->data_device, seat->data_source, get_serial(seat));
wl_display_roundtrip(seat->display->display);
seat->ignore_announcement = false;
return UWAC_SUCCESS;
}
void* UwacClipboardDataGet(UwacSeat* seat, const char* mime, size_t* size)
{
ssize_t r = 0;
size_t alloc = 0;
size_t pos = 0;
char* data = nullptr;
int pipefd[2] = WINPR_C_ARRAY_INIT;
if (!seat || !mime || !size || !seat->offer)
return nullptr;
*size = 0;
if (pipe(pipefd) != 0)
return nullptr;
wl_data_offer_receive(seat->offer, mime, pipefd[1]);
close(pipefd[1]);
wl_display_roundtrip(seat->display->display);
wl_display_flush(seat->display->display);
do
{
if (alloc >= SIZE_MAX - 1024)
goto fail;
alloc += 1024;
void* tmp = xrealloc(data, alloc);
if (!tmp)
goto fail;
data = tmp;
if (pos >= alloc)
goto fail;
r = read(pipefd[0], &data[pos], alloc - pos);
if (r > 0)
pos += r;
if (r < 0)
goto fail;
} while (r > 0);
close(pipefd[0]);
if (alloc > 0)
{
data[pos] = '\0';
*size = pos + 1;
}
return data;
fail:
free(data);
close(pipefd[0]);
return nullptr;
}

View 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, &registry_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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,300 @@
/*
* Copyright © 2012 Collabora, Ltd.
*
* 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.
*/
/*
* This file is an adaptation of src/wayland-os.h from the wayland project and
* shared/os-compatiblity.h from the weston project.
*
* Functions have been renamed just to prevent name clashes.
*/
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#endif
#define _GNU_SOURCE // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
#define USE_SHM
#endif
/* uClibc and uClibc-ng don't provide O_TMPFILE */
#if !defined(O_TMPFILE) && !defined(__FreeBSD__)
#define O_TMPFILE (020000000 | O_DIRECTORY)
#endif
#include <sys/types.h>
#include <sys/socket.h>
#ifdef USE_SHM
#include <sys/mman.h>
#endif
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <winpr/wtypes.h>
#include <uwac/config.h>
#include "uwac-os.h"
#include "uwac-utils.h"
static int set_cloexec_or_close(int fd)
{
long flags = 0;
if (fd == -1)
return -1;
flags = fcntl(fd, F_GETFD);
if (flags == -1)
goto err;
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
goto err;
return fd;
err:
close(fd);
return -1;
}
int uwac_os_socket_cloexec(int domain, int type, int protocol)
{
int fd = 0;
fd = socket(domain, type | SOCK_CLOEXEC, protocol);
if (fd >= 0)
return fd;
if (errno != EINVAL)
return -1;
fd = socket(domain, type, protocol);
return set_cloexec_or_close(fd);
}
int uwac_os_dupfd_cloexec(int fd, long minfd)
{
int newfd = 0;
newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
if (newfd >= 0)
return newfd;
if (errno != EINVAL)
return -1;
newfd = fcntl(fd, F_DUPFD, minfd);
return set_cloexec_or_close(newfd);
}
static ssize_t recvmsg_cloexec_fallback(int sockfd, struct msghdr* msg, int flags)
{
ssize_t len = 0;
struct cmsghdr* cmsg = nullptr;
unsigned char* data = nullptr;
int* end = nullptr;
len = recvmsg(sockfd, msg, flags);
if (len == -1)
return -1;
if (!msg->msg_control || msg->msg_controllen == 0)
return len;
cmsg = CMSG_FIRSTHDR(msg);
for (; cmsg != nullptr; cmsg = CMSG_NXTHDR(msg, cmsg))
{
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
continue;
data = CMSG_DATA(cmsg);
end = (int*)(data + cmsg->cmsg_len - CMSG_LEN(0));
for (int* fd = (int*)data; fd < end; ++fd)
*fd = set_cloexec_or_close(*fd);
}
return len;
}
ssize_t uwac_os_recvmsg_cloexec(int sockfd, struct msghdr* msg, int flags)
{
ssize_t len = 0;
len = recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC);
if (len >= 0)
return len;
if (errno != EINVAL)
return -1;
return recvmsg_cloexec_fallback(sockfd, msg, flags);
}
int uwac_os_epoll_create_cloexec(void)
{
int fd = 0;
#ifdef EPOLL_CLOEXEC
fd = epoll_create1(EPOLL_CLOEXEC);
if (fd >= 0)
return fd;
if (errno != EINVAL)
return -1;
#endif
fd = epoll_create(1);
return set_cloexec_or_close(fd);
}
static int secure_mkstemp(char* tmpname)
{
const mode_t mask = umask(S_IRWXU);
int fd = mkstemp(tmpname);
(void)umask(mask);
return fd;
}
static int create_tmpfile_cloexec(char* tmpname)
{
int fd = 0;
#ifdef USE_SHM
fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
#elif defined(UWAC_HAVE_MKOSTEMP)
fd = mkostemp(tmpname, O_CLOEXEC);
if (fd >= 0)
unlink(tmpname);
#else
fd = secure_mkstemp(tmpname);
if (fd >= 0)
{
fd = set_cloexec_or_close(fd);
unlink(tmpname);
}
#endif
return fd;
}
/*
* Create a new, unique, anonymous file of the given size, and
* return the file descriptor for it. The file descriptor is set
* CLOEXEC. The file is immediately suitable for mmap()'ing
* the given size at offset zero.
*
* The file should not have a permanent backing store like a disk,
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
*
* The file name is deleted from the file system.
*
* The file is suitable for buffer sharing between processes by
* transmitting the file descriptor over Unix sockets using the
* SCM_RIGHTS methods.
*
* If the C library implements posix_fallocate(), it is used to
* guarantee that disk space is available for the file at the
* given size. If disk space is insufficient, errno is set to ENOSPC.
* If posix_fallocate() is not supported, program may receive
* SIGBUS on accessing mmap()'ed file contents instead.
*/
int uwac_create_anonymous_file(off_t size)
{
static const char template[] = "/weston-shared-XXXXXX";
size_t length = 0;
char* name = nullptr;
int fd = 0;
int ret = 0;
// NOLINTNEXTLINE(concurrency-mt-unsafe)
const char* path = getenv("XDG_RUNTIME_DIR");
if (!path)
{
errno = ENOENT;
return -1;
}
#ifdef O_TMPFILE
fd = open(path, O_TMPFILE | O_RDWR | O_EXCL, 0600);
#else
/*
* Some platforms (e.g. FreeBSD) won't support O_TMPFILE and can't
* reasonably emulate it at first blush. Opt to make them rely on
* the create_tmpfile_cloexec() path instead.
*/
fd = -1;
#endif
if (fd < 0)
{
length = strlen(path) + sizeof(template);
name = xmalloc(length);
if (!name)
return -1;
(void)snprintf(name, length, "%s%s", path, template);
fd = create_tmpfile_cloexec(name);
free(name);
}
if (fd < 0)
return -1;
#ifdef UWAC_HAVE_POSIX_FALLOCATE
ret = posix_fallocate(fd, 0, size);
if (ret != 0)
{
close(fd);
errno = ret;
return -1;
}
#else
ret = ftruncate(fd, size);
if (ret < 0)
{
close(fd);
return -1;
}
#endif
return fd;
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright © 2012 Collabora, Ltd.
* 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.
*/
/*
* This file is an adaptation of src/wayland-os.h from the wayland project and
* shared/os-compatiblity.h from the weston project.
*
* Functions have been renamed just to prevent name clashes.
*/
#ifndef UWAC_OS_H
#define UWAC_OS_H
#include <sys/socket.h>
int uwac_os_socket_cloexec(int domain, int type, int protocol);
int uwac_os_dupfd_cloexec(int fd, long minfd);
ssize_t uwac_os_recvmsg_cloexec(int sockfd, struct msghdr* msg, int flags);
int uwac_os_epoll_create_cloexec(void);
int uwac_create_anonymous_file(off_t size);
#endif /* UWAC_OS_H */

View File

@@ -0,0 +1,183 @@
/*
* 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 <assert.h>
#define TARGET_OUTPUT_INTERFACE 2U
static bool dupstr(char** dst, const char* src)
{
assert(dst);
free(*dst);
*dst = nullptr;
if (!src)
return true;
*dst = strdup(src);
return *dst != nullptr;
}
static void output_handle_geometry(void* data, struct wl_output* wl_output, int x, int y,
int physical_width, int physical_height, int subpixel,
const char* make, const char* model, int transform)
{
UwacOutput* output = data;
assert(output);
output->position.x = x;
output->position.y = y;
output->transform = transform;
if (!dupstr(&output->make, make))
{
assert(uwacErrorHandler(output->display, UWAC_ERROR_NOMEMORY, "%s: unable to strdup make\n",
__func__));
}
if (!dupstr(&output->model, model))
{
assert(uwacErrorHandler(output->display, UWAC_ERROR_NOMEMORY,
"%s: unable to strdup model\n", __func__));
}
UwacEvent* event = UwacDisplayNewEvent(output->display, UWAC_EVENT_OUTPUT_GEOMETRY);
event->output_geometry.output = output;
event->output_geometry.x = x;
event->output_geometry.y = y;
event->output_geometry.physical_width = physical_width;
event->output_geometry.physical_height = physical_height;
event->output_geometry.subpixel = subpixel;
event->output_geometry.make = output->make;
event->output_geometry.model = output->model;
event->output_geometry.transform = transform;
}
static void output_handle_done(void* data, struct wl_output* wl_output)
{
UwacOutput* output = data;
assert(output);
output->doneReceived = true;
}
static void output_handle_scale(void* data, struct wl_output* wl_output, int32_t scale)
{
UwacOutput* output = data;
assert(output);
output->scale = scale;
if (scale > output->display->actual_scale)
output->display->actual_scale = scale;
}
static void output_handle_name(void* data, struct wl_output* wl_output, const char* name)
{
UwacOutput* output = data;
assert(output);
if (!dupstr(&output->name, name))
{
assert(uwacErrorHandler(output->display, UWAC_ERROR_NOMEMORY, "%s: unable to strdup make\n",
__func__));
}
}
static void output_handle_description(void* data, struct wl_output* wl_output,
const char* description)
{
UwacOutput* output = data;
assert(output);
if (!dupstr(&output->description, description))
{
assert(uwacErrorHandler(output->display, UWAC_ERROR_NOMEMORY, "%s: unable to strdup make\n",
__func__));
}
}
static void output_handle_mode(void* data, struct wl_output* wl_output, uint32_t flags, int width,
int height, int refresh)
{
UwacOutput* output = data;
assert(output);
// UwacDisplay *display = output->display;
if (output->doneNeeded && output->doneReceived)
{
/* TODO: we should clear the mode list */
}
if (flags & WL_OUTPUT_MODE_CURRENT)
{
output->resolution.width = width;
output->resolution.height = height;
/* output->allocation.width = width;
output->allocation.height = height;
if (display->output_configure_handler)
(*display->output_configure_handler)(
output, display->user_data);*/
}
}
static const struct wl_output_listener output_listener = {
output_handle_geometry, output_handle_mode, output_handle_done,
output_handle_scale, output_handle_name, output_handle_description
};
UwacOutput* UwacCreateOutput(UwacDisplay* d, uint32_t id, uint32_t version)
{
UwacOutput* o = xzalloc(sizeof *o);
if (!o)
return nullptr;
o->display = d;
o->server_output_id = id;
o->doneNeeded = (version > 1);
o->doneReceived = false;
o->output = wl_registry_bind(d->registry, id, &wl_output_interface,
min(TARGET_OUTPUT_INTERFACE, version));
wl_output_add_listener(o->output, &output_listener, o);
wl_list_insert(d->outputs.prev, &o->link);
return o;
}
int UwacDestroyOutput(UwacOutput* output)
{
if (!output)
return UWAC_SUCCESS;
free(output->make);
free(output->model);
free(output->name);
free(output->description);
wl_output_destroy(output->output);
wl_list_remove(&output->link);
free(output);
return UWAC_SUCCESS;
}

View File

@@ -0,0 +1,291 @@
/*
* 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.
*/
#ifndef UWAC_PRIV_H_
#define UWAC_PRIV_H_
#include <uwac/config.h>
#include <stdbool.h>
#include <wayland-client.h>
#include "xdg-shell-client-protocol.h"
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "xdg-decoration-unstable-v1-client-protocol.h"
#include "server-decoration-client-protocol.h"
#include "viewporter-client-protocol.h"
#ifdef BUILD_IVI
#include "ivi-application-client-protocol.h"
#endif
#ifdef BUILD_FULLSCREEN_SHELL
#include "fullscreen-shell-unstable-v1-client-protocol.h"
#endif
#ifdef UWAC_HAVE_PIXMAN_REGION
#include <pixman-1/pixman.h>
#else
#include <freerdp/codec/region.h>
#endif
#include <xkbcommon/xkbcommon.h>
#include <uwac/uwac.h>
extern UwacErrorHandler uwacErrorHandler;
typedef struct uwac_task UwacTask;
/** @brief task struct
*/
struct uwac_task
{
void (*run)(UwacTask* task, uint32_t events);
struct wl_list link;
};
/** @brief a global registry object
*/
struct uwac_global
{
uint32_t name;
char* interface;
uint32_t version;
struct wl_list link;
};
typedef struct uwac_global UwacGlobal;
struct uwac_event_list_item;
typedef struct uwac_event_list_item UwacEventListItem;
/** @brief double linked list element
*/
struct uwac_event_list_item
{
UwacEvent event;
UwacEventListItem *tail, *head;
};
/** @brief main connection object to a wayland display
*/
struct uwac_display
{
struct wl_list globals;
struct wl_display* display;
struct wl_registry* registry;
struct wl_compositor* compositor;
struct wp_viewporter* viewporter;
struct wl_subcompositor* subcompositor;
struct wl_shell* shell;
struct xdg_toplevel* xdg_toplevel;
struct xdg_wm_base* xdg_base;
struct wl_data_device_manager* devicemanager;
struct zwp_keyboard_shortcuts_inhibit_manager_v1* keyboard_inhibit_manager;
struct zxdg_decoration_manager_v1* deco_manager;
struct org_kde_kwin_server_decoration_manager* kde_deco_manager;
#ifdef BUILD_IVI
struct ivi_application* ivi_application;
#endif
#ifdef BUILD_FULLSCREEN_SHELL
struct zwp_fullscreen_shell_v1* fullscreen_shell;
#endif
struct wl_shm* shm;
enum wl_shm_format* shm_formats;
uint32_t shm_formats_nb;
bool has_rgb565;
struct wl_data_device_manager* data_device_manager;
struct text_cursor_position* text_cursor_position;
struct workspace_manager* workspace_manager;
struct wl_list seats;
int display_fd;
UwacReturnCode last_error;
uint32_t display_fd_events;
int epoll_fd;
bool running;
UwacTask dispatch_fd_task;
uint32_t serial;
uint32_t pointer_focus_serial;
int actual_scale;
struct wl_list windows;
struct wl_list outputs;
UwacEventListItem *push_queue, *pop_queue;
};
/** @brief an output on a wayland display */
struct uwac_output
{
UwacDisplay* display;
bool doneNeeded;
bool doneReceived;
UwacPosition position;
UwacSize resolution;
int transform;
int scale;
char* make;
char* model;
uint32_t server_output_id;
struct wl_output* output;
struct wl_list link;
char* name;
char* description;
};
/** @brief a seat attached to a wayland display */
struct uwac_seat
{
UwacDisplay* display;
char* name;
struct wl_seat* seat;
uint32_t seat_id;
uint32_t seat_version;
struct wl_data_device* data_device;
struct wl_data_source* data_source;
struct wl_pointer* pointer;
struct wl_surface* pointer_surface;
struct wl_cursor_image* pointer_image;
struct wl_cursor_theme* cursor_theme;
struct wl_cursor* default_cursor;
void* pointer_data;
size_t pointer_size;
int pointer_type;
struct wl_keyboard* keyboard;
struct wl_touch* touch;
struct wl_data_offer* offer;
struct xkb_context* xkb_context;
struct zwp_keyboard_shortcuts_inhibitor_v1* keyboard_inhibitor;
struct
{
struct xkb_keymap* keymap;
struct xkb_state* state;
xkb_mod_mask_t control_mask;
xkb_mod_mask_t alt_mask;
xkb_mod_mask_t shift_mask;
xkb_mod_mask_t caps_mask;
xkb_mod_mask_t num_mask;
} xkb;
uint32_t modifiers;
int32_t repeat_rate_sec, repeat_rate_nsec;
int32_t repeat_delay_sec, repeat_delay_nsec;
uint32_t repeat_sym, repeat_key, repeat_time;
struct wl_array pressed_keys;
UwacWindow* pointer_focus;
UwacWindow* keyboard_focus;
UwacWindow* touch_focus;
bool touch_frame_started;
int repeat_timer_fd;
UwacTask repeat_task;
double sx, sy;
struct wl_list link;
void* data_context;
UwacDataTransferHandler transfer_data;
UwacCancelDataTransferHandler cancel_data;
bool ignore_announcement;
};
/** @brief a buffer used for drawing a surface frame */
struct uwac_buffer
{
bool used;
bool dirty;
#ifdef UWAC_HAVE_PIXMAN_REGION
pixman_region32_t damage;
#else
REGION16 damage;
#endif
struct wl_buffer* wayland_buffer;
void* data;
size_t size;
};
typedef struct uwac_buffer UwacBuffer;
/** @brief a window */
struct uwac_window
{
UwacDisplay* display;
int width, height, stride;
int surfaceStates;
enum wl_shm_format format;
size_t nbuffers;
UwacBuffer* buffers;
struct wl_region* opaque_region;
struct wl_region* input_region;
ssize_t drawingBufferIdx;
ssize_t pendingBufferIdx;
struct wl_surface* surface;
struct wp_viewport* viewport;
struct wl_shell_surface* shell_surface;
struct xdg_surface* xdg_surface;
struct xdg_toplevel* xdg_toplevel;
struct zxdg_toplevel_decoration_v1* deco;
struct org_kde_kwin_server_decoration* kde_deco;
#ifdef BUILD_IVI
struct ivi_surface* ivi_surface;
#endif
struct wl_list link;
uint32_t pointer_enter_serial;
uint32_t pointer_cursor_serial;
int pointer_current_cursor;
};
/**@brief data to pass to wl_buffer release listener */
struct uwac_buffer_release_data
{
UwacWindow* window;
size_t bufferIdx;
};
typedef struct uwac_buffer_release_data UwacBufferReleaseData;
/* in uwa-display.c */
UwacEvent* UwacDisplayNewEvent(UwacDisplay* d, int type);
int UwacDisplayWatchFd(UwacDisplay* display, int fd, uint32_t events, UwacTask* task);
/* in uwac-input.c */
UwacSeat* UwacSeatNew(UwacDisplay* d, uint32_t id, uint32_t version);
void UwacSeatDestroy(UwacSeat* s);
/* in uwac-output.c */
UwacOutput* UwacCreateOutput(UwacDisplay* d, uint32_t id, uint32_t version);
int UwacDestroyOutput(UwacOutput* output);
UwacReturnCode UwacSeatRegisterClipboard(UwacSeat* s);
#endif /* UWAC_PRIV_H_ */

View File

@@ -0,0 +1,107 @@
/*
* Copyright © 2015 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 <wayland-util.h>
#include <string.h>
#include <uwac/uwac-tools.h>
#include <winpr/wtypes.h>
struct uwac_touch_automata
{
struct wl_array tp;
};
void UwacTouchAutomataInit(UwacTouchAutomata* automata)
{
wl_array_init(&automata->tp);
}
void UwacTouchAutomataReset(UwacTouchAutomata* automata)
{
automata->tp.size = 0;
}
bool UwacTouchAutomataInjectEvent(UwacTouchAutomata* automata, UwacEvent* event)
{
UwacTouchPoint* tp = nullptr;
switch (event->type)
{
case UWAC_EVENT_TOUCH_FRAME_BEGIN:
break;
case UWAC_EVENT_TOUCH_UP:
{
UwacTouchUp* touchUp = &event->touchUp;
size_t toMove = automata->tp.size - sizeof(UwacTouchPoint);
wl_array_for_each(tp, &automata->tp)
{
if ((int64_t)tp->id == touchUp->id)
{
if (toMove)
memmove(tp, tp + 1, toMove);
return true;
}
toMove -= sizeof(UwacTouchPoint);
}
break;
}
case UWAC_EVENT_TOUCH_DOWN:
{
UwacTouchDown* touchDown = &event->touchDown;
wl_array_for_each(tp, &automata->tp)
{
if ((int64_t)tp->id == touchDown->id)
{
tp->x = touchDown->x;
tp->y = touchDown->y;
return true;
}
}
tp = wl_array_add(&automata->tp, sizeof(UwacTouchPoint));
if (!tp)
return false;
if (touchDown->id < 0)
return false;
tp->id = (uint32_t)touchDown->id;
tp->x = touchDown->x;
tp->y = touchDown->y;
break;
}
case UWAC_EVENT_TOUCH_FRAME_END:
break;
default:
break;
}
return true;
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright © 2012 Collabora, Ltd.
* Copyright © 2008 Kristian Høgsberg
* 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/config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <winpr/wtypes.h>
#include "uwac-utils.h"
/*
* This part is an adaptation of client/window.c from the weston project.
*/
static void* fail_on_null(void* p)
{
if (p == nullptr)
{
(void)fprintf(stderr, "out of memory\n");
// NOLINTNEXTLINE(concurrency-mt-unsafe)
exit(EXIT_FAILURE);
}
return p;
}
void* xmalloc(size_t s)
{
return fail_on_null(malloc(s));
}
void* xzalloc(size_t s)
{
return fail_on_null(zalloc(s));
}
char* xstrdup(const char* s)
{
return fail_on_null(strdup(s));
}
void* xrealloc(void* p, size_t s)
{
return fail_on_null(realloc(p, s));
}

View File

@@ -0,0 +1,62 @@
/*
* 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.
*/
#ifndef UWAC_UTILS_H_
#define UWAC_UTILS_H_
#include <stdlib.h>
#include <string.h>
#include <uwac/config.h>
#define min(a, b) (a) < (b) ? (a) : (b)
#define container_of(ptr, type, member) (type*)((char*)(ptr)-offsetof(type, member))
#define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a)[0])
void* xmalloc(size_t s);
static inline void* zalloc(size_t size)
{
return calloc(1, size);
}
void* xzalloc(size_t s);
char* xstrdup(const char* s);
void* xrealloc(void* p, size_t s);
static inline char* uwac_strerror(int dw, char* dmsg, size_t size)
{
#ifdef __STDC_LIB_EXT1__
(void)strerror_s(dw, dmsg, size);
#elif defined(UWAC_HAVE_STRERROR_R)
(void)strerror_r(dw, dmsg, size);
#else
(void)_snprintf(dmsg, size, "%s", strerror(dw));
#endif
return dmsg;
}
#endif /* UWAC_UTILS_H_ */

View File

@@ -0,0 +1,921 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <errno.h>
#include "uwac-priv.h"
#include "uwac-utils.h"
#include "uwac-os.h"
#include <uwac/config.h>
#include <winpr/cast.h>
#define UWAC_INITIAL_BUFFERS 3ull
static int bppFromShmFormat(enum wl_shm_format format)
{
switch (format)
{
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_XRGB8888:
default:
return 4;
}
}
static void buffer_release(void* data, struct wl_buffer* buffer)
{
UwacBufferReleaseData* releaseData = data;
UwacBuffer* uwacBuffer = &releaseData->window->buffers[releaseData->bufferIdx];
uwacBuffer->used = false;
}
static const struct wl_buffer_listener buffer_listener = { buffer_release };
static void UwacWindowDestroyBuffers(UwacWindow* w)
{
for (size_t i = 0; i < w->nbuffers; i++)
{
UwacBuffer* buffer = &w->buffers[i];
#ifdef UWAC_HAVE_PIXMAN_REGION
pixman_region32_fini(&buffer->damage);
#else
region16_uninit(&buffer->damage);
#endif
UwacBufferReleaseData* releaseData =
(UwacBufferReleaseData*)wl_buffer_get_user_data(buffer->wayland_buffer);
wl_buffer_destroy(buffer->wayland_buffer);
free(releaseData);
munmap(buffer->data, buffer->size);
}
w->nbuffers = 0;
free(w->buffers);
w->buffers = nullptr;
}
static int UwacWindowShmAllocBuffers(UwacWindow* w, uint64_t nbuffers, uint64_t allocSize,
uint32_t width, uint32_t height, enum wl_shm_format format);
static void xdg_handle_toplevel_configure(void* data, struct xdg_toplevel* xdg_toplevel,
int32_t width, int32_t height, struct wl_array* states)
{
UwacWindow* window = (UwacWindow*)data;
int scale = window->display->actual_scale;
int32_t actual_width = width;
int32_t actual_height = height;
width *= scale;
height *= scale;
UwacConfigureEvent* event = nullptr;
int ret = 0;
int surfaceState = 0;
enum xdg_toplevel_state* state = nullptr;
surfaceState = 0;
wl_array_for_each(state, states)
{
switch (*state)
{
case XDG_TOPLEVEL_STATE_MAXIMIZED:
surfaceState |= UWAC_WINDOW_MAXIMIZED;
break;
case XDG_TOPLEVEL_STATE_FULLSCREEN:
surfaceState |= UWAC_WINDOW_FULLSCREEN;
break;
case XDG_TOPLEVEL_STATE_ACTIVATED:
surfaceState |= UWAC_WINDOW_ACTIVATED;
break;
case XDG_TOPLEVEL_STATE_RESIZING:
surfaceState |= UWAC_WINDOW_RESIZING;
break;
default:
break;
}
}
window->surfaceStates = surfaceState;
event = (UwacConfigureEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
if (!event)
{
assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY,
"failed to allocate a configure event\n"));
return;
}
event->window = window;
event->states = surfaceState;
if ((width > 0 && height > 0) && (width != window->width || height != window->height))
{
event->width = width;
event->height = height;
UwacWindowDestroyBuffers(window);
window->width = width;
window->stride = width * bppFromShmFormat(window->format);
window->height = height;
ret =
UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, 1ull * window->stride * height,
width, height, window->format);
if (ret != UWAC_SUCCESS)
{
assert(
uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
window->drawingBufferIdx = window->pendingBufferIdx = -1;
return;
}
window->drawingBufferIdx = 0;
if (window->pendingBufferIdx != -1)
window->pendingBufferIdx = window->drawingBufferIdx;
if (window->viewport)
{
wp_viewport_set_source(window->viewport, wl_fixed_from_int(0), wl_fixed_from_int(0),
wl_fixed_from_int(actual_width),
wl_fixed_from_int(actual_height));
wp_viewport_set_destination(window->viewport, actual_width, actual_height);
}
}
else
{
event->width = window->width;
event->height = window->height;
}
}
static void xdg_handle_toplevel_close(void* data, struct xdg_toplevel* xdg_toplevel)
{
UwacCloseEvent* event = nullptr;
UwacWindow* window = (UwacWindow*)data;
event = (UwacCloseEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CLOSE);
if (!event)
{
assert(uwacErrorHandler(window->display, UWAC_ERROR_INTERNAL,
"failed to allocate a close event\n"));
return;
}
event->window = window;
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
xdg_handle_toplevel_configure,
xdg_handle_toplevel_close,
};
static void xdg_handle_surface_configure(void* data, struct xdg_surface* xdg_surface,
uint32_t serial)
{
xdg_surface_ack_configure(xdg_surface, serial);
}
static const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_handle_surface_configure,
};
#if BUILD_IVI
static void ivi_handle_configure(void* data, struct ivi_surface* surface, int32_t width,
int32_t height)
{
UwacWindow* window = (UwacWindow*)data;
UwacConfigureEvent* event = nullptr;
int ret = 0;
event = (UwacConfigureEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
if (!event)
{
assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY,
"failed to allocate a configure event\n"));
return;
}
event->window = window;
event->states = 0;
if (width && height)
{
event->width = width;
event->height = height;
UwacWindowDestroyBuffers(window);
window->width = width;
window->stride = width * bppFromShmFormat(window->format);
window->height = height;
ret =
UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, 1ull * window->stride * height,
width, height, window->format);
if (ret != UWAC_SUCCESS)
{
assert(
uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
window->drawingBufferIdx = window->pendingBufferIdx = -1;
return;
}
window->drawingBufferIdx = 0;
if (window->pendingBufferIdx != -1)
window->pendingBufferIdx = window->drawingBufferIdx;
}
else
{
event->width = window->width;
event->height = window->height;
}
}
static const struct ivi_surface_listener ivi_surface_listener = {
ivi_handle_configure,
};
#endif
static void shell_ping(void* data, struct wl_shell_surface* surface, uint32_t serial)
{
wl_shell_surface_pong(surface, serial);
}
static void shell_configure(void* data, struct wl_shell_surface* surface, uint32_t edges,
int32_t width, int32_t height)
{
UwacWindow* window = (UwacWindow*)data;
UwacConfigureEvent* event = nullptr;
int ret = 0;
event = (UwacConfigureEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
if (!event)
{
assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY,
"failed to allocate a configure event\n"));
return;
}
event->window = window;
event->states = 0;
if (width && height)
{
event->width = width;
event->height = height;
UwacWindowDestroyBuffers(window);
window->width = width;
window->stride = width * bppFromShmFormat(window->format);
window->height = height;
ret =
UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, 1ull * window->stride * height,
width, height, window->format);
if (ret != UWAC_SUCCESS)
{
assert(
uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
window->drawingBufferIdx = window->pendingBufferIdx = -1;
return;
}
window->drawingBufferIdx = 0;
if (window->pendingBufferIdx != -1)
window->pendingBufferIdx = window->drawingBufferIdx;
}
else
{
event->width = window->width;
event->height = window->height;
}
}
static void shell_popup_done(void* data, struct wl_shell_surface* surface)
{
}
static const struct wl_shell_surface_listener shell_listener = { shell_ping, shell_configure,
shell_popup_done };
int UwacWindowShmAllocBuffers(UwacWindow* w, uint64_t nbuffers, uint64_t allocSize, uint32_t width,
uint32_t height, enum wl_shm_format format)
{
int ret = UWAC_SUCCESS;
int fd = 0;
void* data = nullptr;
struct wl_shm_pool* pool = nullptr;
if ((width > INT32_MAX) || (height > INT32_MAX))
return UWAC_ERROR_NOMEMORY;
const int64_t pagesize = sysconf(_SC_PAGESIZE);
if (pagesize <= 0)
return UWAC_ERROR_NOMEMORY;
/* round up to a multiple of PAGESIZE to page align data for each buffer */
const uint64_t test = (1ull * allocSize + (size_t)pagesize - 1ull) & ~((size_t)pagesize - 1);
if (test > INT64_MAX)
return UWAC_ERROR_NOMEMORY;
allocSize = test;
UwacBuffer* newBuffers =
xrealloc(w->buffers, (0ull + w->nbuffers + nbuffers) * sizeof(UwacBuffer));
if (!newBuffers)
return UWAC_ERROR_NOMEMORY;
w->buffers = newBuffers;
memset(w->buffers + w->nbuffers, 0, sizeof(UwacBuffer) * nbuffers);
const size_t allocbuffersize = 1ull * allocSize * nbuffers;
if (allocbuffersize > INT32_MAX)
return UWAC_ERROR_NOMEMORY;
fd = uwac_create_anonymous_file(WINPR_ASSERTING_INT_CAST(off_t, allocbuffersize));
if (fd < 0)
{
return UWAC_ERROR_INTERNAL;
}
data = mmap(nullptr, allocbuffersize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED)
{
ret = UWAC_ERROR_NOMEMORY;
goto error_mmap;
}
pool = wl_shm_create_pool(w->display->shm, fd, (int32_t)allocbuffersize);
if (!pool)
{
munmap(data, allocbuffersize);
ret = UWAC_ERROR_NOMEMORY;
goto error_mmap;
}
for (uint64_t i = 0; i < nbuffers; i++)
{
const size_t idx = (size_t)i;
const size_t bufferIdx = w->nbuffers + idx;
UwacBuffer* buffer = &w->buffers[bufferIdx];
#ifdef UWAC_HAVE_PIXMAN_REGION
pixman_region32_init(&buffer->damage);
#else
region16_init(&buffer->damage);
#endif
const size_t offset = allocSize * idx;
if (offset > INT32_MAX)
goto error_mmap;
buffer->data = &((char*)data)[allocSize * idx];
buffer->size = allocSize;
buffer->wayland_buffer = wl_shm_pool_create_buffer(pool, (int32_t)offset, (int32_t)width,
(int32_t)height, w->stride, format);
UwacBufferReleaseData* listener_data = xmalloc(sizeof(UwacBufferReleaseData));
listener_data->window = w;
listener_data->bufferIdx = bufferIdx;
wl_buffer_add_listener(buffer->wayland_buffer, &buffer_listener, listener_data);
}
wl_shm_pool_destroy(pool);
w->nbuffers += nbuffers;
error_mmap:
close(fd);
return ret;
}
static UwacBuffer* UwacWindowFindFreeBuffer(UwacWindow* w, ssize_t* index)
{
int ret = 0;
if (index)
*index = -1;
size_t i = 0;
for (; i < w->nbuffers; i++)
{
if (!w->buffers[i].used)
{
w->buffers[i].used = true;
if (index)
*index = WINPR_ASSERTING_INT_CAST(ssize_t, i);
return &w->buffers[i];
}
}
ret = UwacWindowShmAllocBuffers(w, 2, 1ull * w->stride * w->height, w->width, w->height,
w->format);
if (ret != UWAC_SUCCESS)
{
w->display->last_error = ret;
return nullptr;
}
w->buffers[i].used = true;
if (index)
*index = WINPR_ASSERTING_INT_CAST(ssize_t, i);
return &w->buffers[i];
}
static UwacReturnCode UwacWindowSetDecorations(UwacWindow* w)
{
if (!w || !w->display)
return UWAC_ERROR_INTERNAL;
if (w->display->deco_manager)
{
w->deco = zxdg_decoration_manager_v1_get_toplevel_decoration(w->display->deco_manager,
w->xdg_toplevel);
if (!w->deco)
{
uwacErrorHandler(w->display, UWAC_NOT_FOUND,
"Current window manager does not allow decorating with SSD");
}
else
zxdg_toplevel_decoration_v1_set_mode(w->deco,
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
else if (w->display->kde_deco_manager)
{
w->kde_deco =
org_kde_kwin_server_decoration_manager_create(w->display->kde_deco_manager, w->surface);
if (!w->kde_deco)
{
uwacErrorHandler(w->display, UWAC_NOT_FOUND,
"Current window manager does not allow decorating with SSD");
}
else
org_kde_kwin_server_decoration_request_mode(w->kde_deco,
ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER);
}
return UWAC_SUCCESS;
}
UwacWindow* UwacCreateWindowShm(UwacDisplay* display, uint32_t width, uint32_t height,
enum wl_shm_format format)
{
UwacWindow* w = nullptr;
int ret = 0;
if (!display)
{
return nullptr;
}
w = xzalloc(sizeof(*w));
if (!w)
{
display->last_error = UWAC_ERROR_NOMEMORY;
return nullptr;
}
w->display = display;
w->format = format;
w->width = WINPR_ASSERTING_INT_CAST(int32_t, width);
w->height = WINPR_ASSERTING_INT_CAST(int32_t, height);
w->stride = WINPR_ASSERTING_INT_CAST(int32_t, width* bppFromShmFormat(format));
const size_t allocSize = 1ULL * w->stride * height;
ret = UwacWindowShmAllocBuffers(w, UWAC_INITIAL_BUFFERS, allocSize, width, height, format);
if (ret != UWAC_SUCCESS)
{
display->last_error = ret;
goto out_error_free;
}
w->buffers[0].used = true;
w->drawingBufferIdx = 0;
w->pendingBufferIdx = -1;
w->surface = wl_compositor_create_surface(display->compositor);
if (!w->surface)
{
display->last_error = UWAC_ERROR_NOMEMORY;
goto out_error_surface;
}
wl_surface_set_user_data(w->surface, w);
#if BUILD_IVI
uint32_t ivi_surface_id = 1;
// NOLINTNEXTLINE(concurrency-mt-unsafe)
char* env = getenv("IVI_SURFACE_ID");
if (env)
{
unsigned long val = 0;
char* endp = nullptr;
errno = 0;
val = strtoul(env, &endp, 10);
if (!errno && val != 0 && val != UINT32_MAX)
ivi_surface_id = val;
}
if (display->ivi_application)
{
w->ivi_surface =
ivi_application_surface_create(display->ivi_application, ivi_surface_id, w->surface);
assert(w->ivi_surface);
ivi_surface_add_listener(w->ivi_surface, &ivi_surface_listener, w);
}
else
#endif
#if BUILD_FULLSCREEN_SHELL
if (display->fullscreen_shell)
{
zwp_fullscreen_shell_v1_present_surface(display->fullscreen_shell, w->surface,
ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_CENTER,
nullptr);
}
else
#endif
if (display->xdg_base)
{
w->xdg_surface = xdg_wm_base_get_xdg_surface(display->xdg_base, w->surface);
if (!w->xdg_surface)
{
display->last_error = UWAC_ERROR_NOMEMORY;
goto out_error_shell;
}
xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w);
w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface);
if (!w->xdg_toplevel)
{
display->last_error = UWAC_ERROR_NOMEMORY;
goto out_error_shell;
}
assert(w->xdg_surface);
xdg_toplevel_add_listener(w->xdg_toplevel, &xdg_toplevel_listener, w);
wl_surface_commit(w->surface);
wl_display_roundtrip(w->display->display);
}
else
{
w->shell_surface = wl_shell_get_shell_surface(display->shell, w->surface);
assert(w->shell_surface);
wl_shell_surface_add_listener(w->shell_surface, &shell_listener, w);
wl_shell_surface_set_toplevel(w->shell_surface);
}
if (display->viewporter)
{
w->viewport = wp_viewporter_get_viewport(display->viewporter, w->surface);
if (display->actual_scale != 1)
wl_surface_set_buffer_scale(w->surface, display->actual_scale);
}
wl_list_insert(display->windows.prev, &w->link);
display->last_error = UWAC_SUCCESS;
UwacWindowSetDecorations(w);
return w;
out_error_shell:
wl_surface_destroy(w->surface);
out_error_surface:
UwacWindowDestroyBuffers(w);
out_error_free:
free(w);
return nullptr;
}
UwacReturnCode UwacDestroyWindow(UwacWindow** pwindow)
{
UwacWindow* w = nullptr;
assert(pwindow);
w = *pwindow;
UwacWindowDestroyBuffers(w);
if (w->deco)
zxdg_toplevel_decoration_v1_destroy(w->deco);
if (w->kde_deco)
org_kde_kwin_server_decoration_destroy(w->kde_deco);
if (w->xdg_surface)
xdg_surface_destroy(w->xdg_surface);
#if BUILD_IVI
if (w->ivi_surface)
ivi_surface_destroy(w->ivi_surface);
#endif
if (w->opaque_region)
wl_region_destroy(w->opaque_region);
if (w->input_region)
wl_region_destroy(w->input_region);
if (w->viewport)
wp_viewport_destroy(w->viewport);
wl_surface_destroy(w->surface);
wl_list_remove(&w->link);
free(w);
*pwindow = nullptr;
return UWAC_SUCCESS;
}
UwacReturnCode UwacWindowSetOpaqueRegion(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
uint32_t height)
{
assert(window);
if (window->opaque_region)
wl_region_destroy(window->opaque_region);
window->opaque_region = wl_compositor_create_region(window->display->compositor);
if (!window->opaque_region)
return UWAC_ERROR_NOMEMORY;
wl_region_add(window->opaque_region, WINPR_ASSERTING_INT_CAST(int32_t, x),
WINPR_ASSERTING_INT_CAST(int32_t, y), WINPR_ASSERTING_INT_CAST(int32_t, width),
WINPR_ASSERTING_INT_CAST(int32_t, height));
wl_surface_set_opaque_region(window->surface, window->opaque_region);
return UWAC_SUCCESS;
}
UwacReturnCode UwacWindowSetInputRegion(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
uint32_t height)
{
assert(window);
if (window->input_region)
wl_region_destroy(window->input_region);
window->input_region = wl_compositor_create_region(window->display->compositor);
if (!window->input_region)
return UWAC_ERROR_NOMEMORY;
wl_region_add(window->input_region, WINPR_ASSERTING_INT_CAST(int32_t, x),
WINPR_ASSERTING_INT_CAST(int32_t, y), WINPR_ASSERTING_INT_CAST(int32_t, width),
WINPR_ASSERTING_INT_CAST(int32_t, height));
wl_surface_set_input_region(window->surface, window->input_region);
return UWAC_SUCCESS;
}
void* UwacWindowGetDrawingBuffer(UwacWindow* window)
{
UwacBuffer* buffer = nullptr;
if (window->drawingBufferIdx < 0)
return nullptr;
buffer = &window->buffers[window->drawingBufferIdx];
if (!buffer)
return nullptr;
return buffer->data;
}
static void frame_done_cb(void* data, struct wl_callback* callback, uint32_t time);
static const struct wl_callback_listener frame_listener = { frame_done_cb };
#ifdef UWAC_HAVE_PIXMAN_REGION
static void damage_surface(UwacWindow* window, UwacBuffer* buffer, int scale)
{
int nrects = 0;
const pixman_box32_t* box = pixman_region32_rectangles(&buffer->damage, &nrects);
for (int i = 0; i < nrects; i++, box++)
{
const int x = ((int)floor(box->x1 / scale)) - 1;
const int y = ((int)floor(box->y1 / scale)) - 1;
const int w = ((int)ceil((box->x2 - box->x1) / scale)) + 2;
const int h = ((int)ceil((box->y2 - box->y1) / scale)) + 2;
wl_surface_damage(window->surface, x, y, w, h);
}
pixman_region32_clear(&buffer->damage);
}
#else
static void damage_surface(UwacWindow* window, UwacBuffer* buffer, int scale)
{
uint32_t nrects = 0;
const RECTANGLE_16* boxes = region16_rects(&buffer->damage, &nrects);
for (UINT32 i = 0; i < nrects; i++)
{
const RECTANGLE_16* box = &boxes[i];
const double dx = floor(1.0 * box->left / scale);
const double dy = floor(1.0 * box->top / scale);
const double dw = ceil(1.0 * (box->right - box->left) / scale);
const double dh = ceil(1.0 * (box->bottom - box->top) / scale);
const int x = ((int)dx) - 1;
const int y = ((int)dy) - 1;
const int w = ((int)dw) + 2;
const int h = ((int)dh) + 2;
wl_surface_damage(window->surface, x, y, w, h);
}
region16_clear(&buffer->damage);
}
#endif
static void UwacSubmitBufferPtr(UwacWindow* window, UwacBuffer* buffer)
{
wl_surface_attach(window->surface, buffer->wayland_buffer, 0, 0);
int scale = window->display->actual_scale;
damage_surface(window, buffer, scale);
struct wl_callback* frame_callback = wl_surface_frame(window->surface);
wl_callback_add_listener(frame_callback, &frame_listener, window);
wl_surface_commit(window->surface);
buffer->dirty = false;
}
static void frame_done_cb(void* data, struct wl_callback* callback, uint32_t time)
{
UwacWindow* window = (UwacWindow*)data;
UwacFrameDoneEvent* event = nullptr;
wl_callback_destroy(callback);
window->pendingBufferIdx = -1;
event = (UwacFrameDoneEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_FRAME_DONE);
if (event)
event->window = window;
}
#ifdef UWAC_HAVE_PIXMAN_REGION
UwacReturnCode UwacWindowAddDamage(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
uint32_t height)
{
UwacBuffer* buf = nullptr;
if (window->drawingBufferIdx < 0)
return UWAC_ERROR_INTERNAL;
buf = &window->buffers[window->drawingBufferIdx];
if (!pixman_region32_union_rect(&buf->damage, &buf->damage, x, y, width, height))
return UWAC_ERROR_INTERNAL;
buf->dirty = true;
return UWAC_SUCCESS;
}
#else
UwacReturnCode UwacWindowAddDamage(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
uint32_t height)
{
RECTANGLE_16 box;
UwacBuffer* buf = nullptr;
box.left = x;
box.top = y;
box.right = x + width;
box.bottom = y + height;
if (window->drawingBufferIdx < 0)
return UWAC_ERROR_INTERNAL;
buf = &window->buffers[window->drawingBufferIdx];
if (!buf)
return UWAC_ERROR_INTERNAL;
if (!region16_union_rect(&buf->damage, &buf->damage, &box))
return UWAC_ERROR_INTERNAL;
buf->dirty = true;
return UWAC_SUCCESS;
}
#endif
UwacReturnCode UwacWindowGetDrawingBufferGeometry(UwacWindow* window, UwacSize* geometry,
size_t* stride)
{
if (!window || (window->drawingBufferIdx < 0))
return UWAC_ERROR_INTERNAL;
if (geometry)
{
geometry->width = window->width;
geometry->height = window->height;
}
if (stride)
*stride = window->stride;
return UWAC_SUCCESS;
}
UwacReturnCode UwacWindowSubmitBuffer(UwacWindow* window, bool copyContentForNextFrame)
{
UwacBuffer* currentDrawingBuffer = nullptr;
UwacBuffer* nextDrawingBuffer = nullptr;
UwacBuffer* pendingBuffer = nullptr;
if (window->drawingBufferIdx < 0)
return UWAC_ERROR_INTERNAL;
currentDrawingBuffer = &window->buffers[window->drawingBufferIdx];
if ((window->pendingBufferIdx >= 0) || !currentDrawingBuffer->dirty)
return UWAC_SUCCESS;
window->pendingBufferIdx = window->drawingBufferIdx;
nextDrawingBuffer = UwacWindowFindFreeBuffer(window, &window->drawingBufferIdx);
pendingBuffer = &window->buffers[window->pendingBufferIdx];
if ((!nextDrawingBuffer) || (window->drawingBufferIdx < 0))
return UWAC_ERROR_NOMEMORY;
if (copyContentForNextFrame)
memcpy(nextDrawingBuffer->data, pendingBuffer->data,
1ull * window->stride * window->height);
UwacSubmitBufferPtr(window, pendingBuffer);
return UWAC_SUCCESS;
}
UwacReturnCode UwacWindowGetGeometry(UwacWindow* window, UwacSize* geometry)
{
assert(window);
assert(geometry);
geometry->width = window->width;
geometry->height = window->height;
return UWAC_SUCCESS;
}
UwacReturnCode UwacWindowSetFullscreenState(UwacWindow* window, UwacOutput* output,
bool isFullscreen)
{
if (window->xdg_toplevel)
{
if (isFullscreen)
{
xdg_toplevel_set_fullscreen(window->xdg_toplevel, output ? output->output : nullptr);
}
else
{
xdg_toplevel_unset_fullscreen(window->xdg_toplevel);
}
}
else if (window->shell_surface)
{
if (isFullscreen)
{
wl_shell_surface_set_fullscreen(window->shell_surface,
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0,
output ? output->output : nullptr);
}
else
{
wl_shell_surface_set_toplevel(window->shell_surface);
}
}
return UWAC_SUCCESS;
}
void UwacWindowSetTitle(UwacWindow* window, const char* name)
{
if (window->xdg_toplevel)
xdg_toplevel_set_title(window->xdg_toplevel, name);
else if (window->shell_surface)
wl_shell_surface_set_title(window->shell_surface, name);
}
void UwacWindowSetAppId(UwacWindow* window, const char* app_id)
{
if (window->xdg_toplevel)
xdg_toplevel_set_app_id(window->xdg_toplevel, app_id);
}