From 23fa6b918217fc1831f3c6d876c83b0ad022469a Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Fri, 25 Jan 2019 13:05:51 +0100 Subject: [PATCH] Added mouse cursor callback stubs. --- client/Wayland/CMakeLists.txt | 2 + client/Wayland/wlf_pointer.c | 136 +++++++++++++++++++++++++++++ client/Wayland/wlf_pointer.h | 28 ++++++ client/Wayland/wlfreerdp.c | 12 +++ client/Wayland/wlfreerdp.h | 1 + cmake/FindWayland.cmake | 12 ++- uwac/include/uwac/uwac.h | 19 +++++ uwac/libuwac/uwac-display.c | 26 ++++++ uwac/libuwac/uwac-input.c | 155 ++++++++++++++++++++++++++++++++++ uwac/libuwac/uwac-priv.h | 6 +- 10 files changed, 395 insertions(+), 2 deletions(-) create mode 100644 client/Wayland/wlf_pointer.c create mode 100644 client/Wayland/wlf_pointer.h diff --git a/client/Wayland/CMakeLists.txt b/client/Wayland/CMakeLists.txt index d98d2d4ab..eb1cce7ff 100644 --- a/client/Wayland/CMakeLists.txt +++ b/client/Wayland/CMakeLists.txt @@ -27,6 +27,8 @@ set(${MODULE_PREFIX}_SRCS wlfreerdp.h wlf_disp.c wlf_disp.h + wlf_pointer.c + wlf_pointer.h wlf_input.c wlf_input.h wlf_cliprdr.c diff --git a/client/Wayland/wlf_pointer.c b/client/Wayland/wlf_pointer.c new file mode 100644 index 000000000..b7982743b --- /dev/null +++ b/client/Wayland/wlf_pointer.c @@ -0,0 +1,136 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Wayland Mouse Pointer + * + * Copyright 2019 Armin Novak + * Copyright 2019 Thincast Technologies GmbH + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "wlf_pointer.h" +#include "wlfreerdp.h" + +#include +#define TAG CLIENT_TAG("wayland.pointer") + +struct wlf_pointer +{ + rdpPointer pointer; + size_t size; + void* data; +}; +typedef struct wlf_pointer wlfPointer; + +static BOOL wlf_Pointer_New(rdpContext* context, rdpPointer* pointer) +{ + wlfPointer* ptr = (wlfPointer*)pointer; + if (!ptr) + return FALSE; + + ptr->size = pointer->width * pointer->height * 4; + ptr->data = _aligned_malloc(ptr->size, 16); + if (!ptr->data) + return FALSE; + + if (!freerdp_image_copy_from_pointer_data( + ptr->data, PIXEL_FORMAT_ARGB32, + 0, 0, 0, pointer->width, pointer->height, + pointer->xorMaskData, pointer->lengthXorMask, + pointer->andMaskData, pointer->lengthAndMask, + pointer->xorBpp, &context->gdi->palette)) + { + _aligned_free(ptr->data); + return FALSE; + } + + return TRUE; +} + +static void wlf_Pointer_Free(rdpContext* context, rdpPointer* pointer) +{ + wlfPointer* ptr = (wlfPointer*)pointer; + WINPR_UNUSED(context); + if (ptr) + _aligned_free(ptr->data); +} + +static BOOL wlf_Pointer_Set(rdpContext* context, + const rdpPointer* pointer) +{ + wlfContext* wlf = (wlfContext*)context; + wlfPointer* ptr = (wlfPointer*)pointer; + + if (!wlf || !wlf->seat) + return FALSE; + + // TODO: Scale according to SmartSizing + if (UwacSeatSetMouseCursor(wlf->seat, ptr->data, ptr->size, pointer->width, pointer->height, pointer->xPos, pointer->yPos) != UWAC_SUCCESS) + return FALSE; + + return TRUE; +} + +static BOOL wlf_Pointer_SetNull(rdpContext* context) +{ + wlfContext* wlf = (wlfContext*)context; + if (!wlf || !wlf->seat) + return FALSE; + + if (UwacSeatSetMouseCursor(wlf->seat, NULL, 0, 0, 0, 0, 0) != UWAC_SUCCESS) + return FALSE; + + return TRUE; +} + +static BOOL wlf_Pointer_SetDefault(rdpContext* context) +{ + wlfContext* wlf = (wlfContext*)context; + if (!wlf || !wlf->seat) + return FALSE; + + if (UwacSeatSetMouseCursor(wlf->seat, NULL, 1, 0, 0, 0, 0) != UWAC_SUCCESS) + return FALSE; + + return TRUE; +} + +static BOOL wlf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y) +{ + // TODO + WLog_WARN(TAG, "%s not implemented", __FUNCTION__); + return TRUE; +} + +BOOL wlf_register_pointer(rdpGraphics* graphics) +{ + rdpPointer* pointer = NULL; + + if (!(pointer = (rdpPointer*) calloc(1, sizeof(rdpPointer)))) + return FALSE; + + pointer->size = sizeof(wlfPointer); + pointer->New = wlf_Pointer_New; + pointer->Free = wlf_Pointer_Free; + pointer->Set = wlf_Pointer_Set; + pointer->SetNull = wlf_Pointer_SetNull; + pointer->SetDefault = wlf_Pointer_SetDefault; + pointer->SetPosition = wlf_Pointer_SetPosition; + graphics_register_pointer(graphics, pointer); + free(pointer); + return TRUE; +} diff --git a/client/Wayland/wlf_pointer.h b/client/Wayland/wlf_pointer.h new file mode 100644 index 000000000..8ae82e1d5 --- /dev/null +++ b/client/Wayland/wlf_pointer.h @@ -0,0 +1,28 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Wayland Mouse Pointer + * + * Copyright 2019 Armin Novak + * Copyright 2019 Thincast Technologies GmbH + * + * 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. + */ + +#ifndef FREERDP_CLIENT_WAYLAND_POINTER_H +#define FREERDP_CLIENT_WAYLAND_POINTER_H + +#include + +BOOL wlf_register_pointer(rdpGraphics* graphics); + +#endif /* FREERDP_CLIENT_WAYLAND_POINTER_H */ diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c index 3856be4b6..ed73d91b0 100644 --- a/client/Wayland/wlfreerdp.c +++ b/client/Wayland/wlfreerdp.c @@ -39,6 +39,7 @@ #include "wlf_cliprdr.h" #include "wlf_disp.h" #include "wlf_channels.h" +#include "wlf_pointer.h" static BOOL wl_begin_paint(rdpContext* context) { @@ -222,6 +223,9 @@ static BOOL wl_post_connect(freerdp* instance) if (!gdi || (gdi->width < 0) || (gdi->height < 0)) return FALSE; + if (!wlf_register_pointer(instance->context->graphics)) + return FALSE; + w = (UINT32)gdi->width; h = (UINT32)gdi->height; context = (wlfContext*) instance->context; @@ -286,6 +290,14 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display) /*printf("UWAC event type %d\n", event.type);*/ switch (event.type) { + case UWAC_EVENT_NEW_SEAT: + context->seat = event.seat_new.seat; + break; + + case UWAC_EVENT_REMOVED_SEAT: + context->seat = NULL; + break; + case UWAC_EVENT_FRAME_DONE: break; diff --git a/client/Wayland/wlfreerdp.h b/client/Wayland/wlfreerdp.h index dd294bcd4..24b117d22 100644 --- a/client/Wayland/wlfreerdp.h +++ b/client/Wayland/wlfreerdp.h @@ -41,6 +41,7 @@ struct wlf_context UwacDisplay* display; HANDLE displayHandle; UwacWindow* window; + UwacSeat* seat; BOOL fullscreen; diff --git a/cmake/FindWayland.cmake b/cmake/FindWayland.cmake index ee217f61d..6128c1433 100644 --- a/cmake/FindWayland.cmake +++ b/cmake/FindWayland.cmake @@ -30,6 +30,7 @@ include(FindPkgConfig) if(PKG_CONFIG_FOUND) pkg_check_modules(WAYLAND_SCANNER_PC wayland-scanner) pkg_check_modules(WAYLAND_CLIENT_PC wayland-client) + pkg_check_modules(WAYLAND_CURSOR_PC wayland-cursor) pkg_check_modules(XKBCOMMON_PC xkbcommon) endif() @@ -41,11 +42,20 @@ find_path(WAYLAND_INCLUDE_DIR wayland-client.h HINTS ${WAYLAND_CLIENT_PC_INCLUDE_DIRS} ) -find_library(WAYLAND_LIBS +find_library(WAYLAND_CLIENT_LIB NAMES "wayland-client" HINTS "${WAYLAND_CLIENT_PC_LIBRARY_DIRS}" ) +find_library(WAYLAND_CURSOR_LIB + NAMES "wayland-cursor" + HINTS "${WAYLAND_CURSOR_PC_LIBRARY_DIRS}" +) + +if (WAYLAND_CLIENT_LIB AND WAYLAND_CURSOR_LIB) + list(INSERT WAYLAND_LIBS ${WAYLAND_CLIENT_LIB} ${WAYLAND_CURSOR_LIB}) +endif (WAYLAND_CLIENT_LIB AND WAYLAND_CURSOR_LIB) + find_path(XKBCOMMON_INCLUDE_DIR xkbcommon/xkbcommon.h HINTS ${XKBCOMMON_PC_INCLUDE_DIRS} ) diff --git a/uwac/include/uwac/uwac.h b/uwac/include/uwac/uwac.h index 8466b1125..65684e526 100644 --- a/uwac/include/uwac/uwac.h +++ b/uwac/include/uwac/uwac.h @@ -564,6 +564,25 @@ UWAC_API void* UwacClipboardDataGet(UwacSeat* seat, const char* mime, size_t* si */ UWAC_API UwacReturnCode UwacSeatInhibitShortcuts(UwacSeat* seat, bool inhibit); +/** + * @brief UwacSeatSetMouseCursor Sets the specified image as the new mouse cursor. + * Special values: If data == NULL && lenght == 0 + * the cursor is hidden, if data == NULL && length != 0 + * the default system cursor is used. + * + * @param seat The UwacSeat to apply the cursor image to + * @param data A pointer to the image data + * @param length The size of the image data + * @param width The image width in pixel + * @param height The image height in pixel + * @param hot_x The hotspot horizontal offset in pixel + * @param hot_y The hotspot vertical offset in pixel + * + * @return UWAC_SUCCESS if successful, an appropriate error otherwise. + */ +UWAC_API UwacReturnCode UwacSeatSetMouseCursor(UwacSeat* seat, const void* data, size_t length, + size_t width, size_t height, size_t hot_x, size_t hot_y); + #ifdef __cplusplus } #endif diff --git a/uwac/libuwac/uwac-display.c b/uwac/libuwac/uwac-display.c index cf621b2dc..dc3fee143 100644 --- a/uwac/libuwac/uwac-display.c +++ b/uwac/libuwac/uwac-display.c @@ -33,6 +33,7 @@ #include #include "uwac-os.h" +#include "wayland-cursor.h" #define TARGET_COMPOSITOR_INTERFACE 3 #define TARGET_SHM_INTERFACE 1 @@ -151,6 +152,14 @@ static void UwacSeatRegisterDDM(UwacSeat *seat) 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) { @@ -171,6 +180,18 @@ static void registry_handle_global(void* data, struct wl_registry* registry, uin { d->shm = wl_registry_bind(registry, id, &wl_shm_interface, min(TARGET_SHM_INTERFACE, version)); wl_shm_add_listener(d->shm, &shm_listener, d); + + d->cursor_theme = wl_cursor_theme_load(NULL, 32, d->shm); + if (!d->cursor_theme) { + assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to get wayland cursor theme\n")); + return; + } + + d->default_cursor = wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr"); + if (!d->default_cursor) { + assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to get wayland cursor left_ptr\n")); + return; + } } else if (strcmp(interface, "wl_output") == 0) { @@ -203,6 +224,7 @@ static void registry_handle_global(void* data, struct wl_registry* registry, uin UwacSeatRegisterDDM(seat); UwacSeatRegisterClipboard(seat); + UwacRegisterCursor(seat); ev = (UwacSeatNewEvent*)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_SEAT); if (!ev) @@ -224,6 +246,7 @@ static void registry_handle_global(void* data, struct wl_registry* registry, uin { UwacSeatRegisterDDM(seat); UwacSeatRegisterClipboard(seat); + UwacRegisterCursor(seat); } } else if (strcmp(interface, "wl_shell") == 0) @@ -573,6 +596,9 @@ UwacReturnCode UwacCloseDisplay(UwacDisplay** pdisplay) if (display->shell) wl_shell_destroy(display->shell); + if (display->cursor_theme) + wl_cursor_theme_destroy(display->cursor_theme); + if (display->shm) wl_shm_destroy(display->shm); diff --git a/uwac/libuwac/uwac-input.c b/uwac/libuwac/uwac-input.c index 3e7799ef2..b0f930c2a 100644 --- a/uwac/libuwac/uwac-input.c +++ b/uwac/libuwac/uwac-input.c @@ -32,6 +32,62 @@ #include #include +#include "uwac-os.h" +#include "wayland-cursor.h" +#include "wayland-client-protocol.h" + +static UwacReturnCode +set_cursor_image(UwacSeat* seat, uint32_t serial) +{ + struct wl_buffer* buffer; + struct wl_cursor* cursor; + struct wl_cursor_image* image; + struct wl_surface* surface = NULL; + int32_t x = 0, y = 0; + + if (!seat || !seat->display || !seat->display->default_cursor || !seat->display->default_cursor->images) + return UWAC_ERROR_INTERNAL; + + switch(seat->pointer_type) { + case 2: /* Custom poiner */ + image = seat->pointer_image; + buffer = seat->pointer_buffer; + surface = seat->pointer_surface; + x = image->hotspot_x; + y = image->hotspot_y; + break; + case 1: /* NULL pointer */ + break; + default: /* Default system pointer */ + cursor = seat->display->default_cursor; + if (!cursor) + return UWAC_ERROR_INTERNAL; + image = cursor->images[0]; + if (!image) + return UWAC_ERROR_INTERNAL; + x = image->hotspot_x; + y = image->hotspot_y; + buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return UWAC_ERROR_INTERNAL; + surface = seat->pointer_surface; + break; + } + + wl_pointer_set_cursor(seat->pointer, + serial, + surface, + x, y); + + if (surface) { + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + image->width, image->height); + wl_surface_commit(surface); + } + + return UWAC_SUCCESS; +} static void keyboard_repeat_func(UwacTask *task, uint32_t events) { @@ -588,6 +644,10 @@ static void pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_ event->window = window; event->x = sx; event->y = sy; + + + /* Apply cursor theme */ + set_cursor_image(input, serial); } static void pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial, @@ -871,6 +931,14 @@ void UwacSeatDestroy(UwacSeat *s) { if (s->data_source) wl_data_source_destroy(s->data_source); + if (s->pointer_surface) + wl_surface_destroy(s->pointer_surface); + + if (s->pointer_buffer) + wl_buffer_destroy(s->pointer_buffer); + + free(s->pointer_image); + wl_list_remove(&s->link); free(s); } @@ -898,3 +966,90 @@ UwacReturnCode UwacSeatInhibitShortcuts(UwacSeat* s, bool inhibit) return UWAC_ERROR_INTERNAL; return UWAC_SUCCESS; } + +UwacReturnCode create_pointer_buffer(UwacSeat* seat, const void* src, size_t size) +{ + UwacReturnCode ret = UWAC_SUCCESS; + UwacBuffer* newBuffers; + int fd; + void* data; + struct wl_shm_pool* pool; + + if (!newBuffers) + return UWAC_ERROR_NOMEMORY; + + fd = uwac_create_anonymous_file(size); + + if (fd < 0) + { + return UWAC_ERROR_INTERNAL; + } + + data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (data == MAP_FAILED) + { + ret = UWAC_ERROR_NOMEMORY; + goto error_mmap; + } + memcpy(data, src, size); + + pool = wl_shm_create_pool(seat->display->shm, fd, size * 2); + + if (!pool) + { + munmap(data, size); + ret = UWAC_ERROR_NOMEMORY; + goto error_mmap; + } + + seat->pointer_buffer = wl_shm_pool_create_buffer(pool, size, + seat->pointer_image->width, + seat->pointer_image->height, + seat->pointer_image->width * 4, + WL_SHM_FORMAT_XRGB8888); + wl_shm_pool_destroy(pool); + +error_mmap: + close(fd); + return ret; +} + +UwacReturnCode UwacSeatSetMouseCursor(UwacSeat* seat, const void* data, size_t length, + size_t width, size_t height, + size_t hot_x, size_t hot_y) +{ + if (!seat) + return UWAC_ERROR_CLOSED; + + free(seat->pointer_image); + if (seat->pointer_buffer) + wl_buffer_destroy(seat->pointer_buffer); + seat->pointer_image = NULL; + seat->pointer_buffer = NULL; + + /* There is a cursor provided */ + if ((data != NULL) && (length != 0)) + { + seat->pointer_image = calloc(1, sizeof(struct wl_cursor_image)); + if (!seat->pointer_image) + return UWAC_ERROR_NOMEMORY; + seat->pointer_image->width = width; + seat->pointer_image->height = height; + seat->pointer_image->hotspot_x = hot_x; + seat->pointer_image->hotspot_y = hot_y; + + create_pointer_buffer(seat, data, length); + + seat->pointer_type = 2; + } + /* We want to use the system cursor */ + else if (length != 0) { + seat->pointer_type = 0; + } + /* Hide the cursor */ + else { + seat->pointer_type = 1; + } + return set_cursor_image(seat, seat->display->serial); +} diff --git a/uwac/libuwac/uwac-priv.h b/uwac/libuwac/uwac-priv.h index 540906b10..64dc90607 100644 --- a/uwac/libuwac/uwac-priv.h +++ b/uwac/libuwac/uwac-priv.h @@ -121,7 +121,7 @@ struct uwac_display { uint32_t serial; struct wl_cursor_theme *cursor_theme; - struct wl_cursor **cursors; + struct wl_cursor *default_cursor; struct wl_list windows; @@ -158,6 +158,10 @@ struct uwac_seat { 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; + int pointer_type; + struct wl_buffer *pointer_buffer; struct wl_keyboard *keyboard; struct wl_touch *touch; struct wl_data_offer* offer;