diff --git a/src/wl_init.c b/src/wl_init.c index 29509ae91..4d36dbd64 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -41,6 +42,76 @@ static inline int min(int n1, int n2) return n1 < n2 ? n1 : n2; } +static void dataOfferOffer(void* data, + struct wl_data_offer* offer, + const char* type) +{ + if (_glfw.wl.dataOffer) + { + wl_data_offer_destroy(_glfw.wl.dataOffer); + _glfw.wl.dataOffer = NULL; + } + if (strcmp(type, _glfw.wl.clipboardMimeType) == 0) + { + _glfw.wl.dataOffer = offer; + } +} + +static const struct wl_data_offer_listener dataOfferListener = { + dataOfferOffer, +}; + +static void dataDeviceHandleDataOffer(void* data, + struct wl_data_device* dataDevice, + struct wl_data_offer* offer) +{ + wl_data_offer_add_listener(offer, &dataOfferListener, NULL); +} + +static void dataDeviceHandleEnter(void* data, + struct wl_data_device* dataDevice, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t sx, + wl_fixed_t sf, + struct wl_data_offer *offer) +{ + _glfw.wl.serial = serial; +} + +static void dataDeviceHandleLeave(void *data, + struct wl_data_device* dataDevice) +{ +} + +static void dataDeviceHandleMotion(void *data, + struct wl_data_device* dataDevice, + uint32_t time, + wl_fixed_t sx, + wl_fixed_t sf) +{ +} + +static void dataDeviceHandleDrop(void *data, + struct wl_data_device* dataDevice) +{ +} + +static void dataDeviceHandleSelection(void *data, + struct wl_data_device *dataDevice, + struct wl_data_offer *offer) +{ +} + +static const struct wl_data_device_listener dataDeviceListener = { + dataDeviceHandleDataOffer, + dataDeviceHandleEnter, + dataDeviceHandleLeave, + dataDeviceHandleMotion, + dataDeviceHandleDrop, + dataDeviceHandleSelection +}; + static void pointerHandleEnter(void* data, struct wl_pointer* pointer, uint32_t serial, @@ -50,7 +121,7 @@ static void pointerHandleEnter(void* data, { _GLFWwindow* window = wl_surface_get_user_data(surface); - _glfw.wl.pointerSerial = serial; + _glfw.wl.serial = serial; _glfw.wl.pointerFocus = window; _glfwPlatformSetCursor(window, window->wl.currentCursor); @@ -67,7 +138,7 @@ static void pointerHandleLeave(void* data, if (!window) return; - _glfw.wl.pointerSerial = serial; + _glfw.wl.serial = serial; _glfw.wl.pointerFocus = NULL; _glfwInputCursorEnter(window, GLFW_FALSE); } @@ -106,6 +177,8 @@ static void pointerHandleButton(void* data, _GLFWwindow* window = _glfw.wl.pointerFocus; int glfwButton; + _glfw.wl.serial = serial; + if (!window) return; @@ -232,6 +305,7 @@ static void keyboardHandleEnter(void* data, { _GLFWwindow* window = wl_surface_get_user_data(surface); + _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = window; _glfwInputWindowFocus(window, GLFW_TRUE); } @@ -246,6 +320,7 @@ static void keyboardHandleLeave(void* data, if (!window) return; + _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = NULL; _glfwInputWindowFocus(window, GLFW_FALSE); } @@ -272,6 +347,8 @@ static void keyboardHandleKey(void* data, const xkb_keysym_t *syms; _GLFWwindow* window = _glfw.wl.keyboardFocus; + _glfw.wl.serial = serial; + if (!window) return; @@ -308,6 +385,8 @@ static void keyboardHandleModifiers(void* data, xkb_mod_mask_t mask; unsigned int modifiers = 0; + _glfw.wl.serial = serial; + if (!_glfw.wl.xkb.keymap) return; @@ -422,6 +501,14 @@ static void registryHandleGlobal(void* data, &zwp_pointer_constraints_v1_interface, 1); } + else if (strcmp(interface, "wl_data_device_manager") == 0) + { + _glfw.wl.dataDeviceManager = + wl_registry_bind(registry, + name, + &wl_data_device_manager_interface, + 1); + } } static void registryHandleGlobalRemove(void *data, @@ -608,6 +695,17 @@ int _glfwPlatformInit(void) _glfwInitTimerPOSIX(); + if (_glfw.wl.dataDeviceManager && _glfw.wl.seat) + { + _glfw.wl.clipboardMimeType = "text/plain;charset=utf-8"; + _glfw.wl.dataDevice = + wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, + _glfw.wl.seat); + wl_data_device_add_listener(_glfw.wl.dataDevice, + &dataDeviceListener, + NULL); + } + if (_glfw.wl.pointer && _glfw.wl.shm) { _glfw.wl.cursorTheme = wl_cursor_theme_load(NULL, 32, _glfw.wl.shm); @@ -630,6 +728,20 @@ void _glfwPlatformTerminate(void) _glfwTerminateJoysticksLinux(); _glfwTerminateThreadLocalStoragePOSIX(); + if (_glfw.wl.dataOffer) + wl_data_offer_destroy(_glfw.wl.dataOffer); + if (_glfw.wl.clipboardString) + free(_glfw.wl.clipboardString); + if (_glfw.wl.dataSource) + { + free(wl_data_source_get_user_data(_glfw.wl.dataSource)); + wl_data_source_destroy(_glfw.wl.dataSource); + } + if (_glfw.wl.dataDevice) + wl_data_device_destroy(_glfw.wl.dataDevice); + if (_glfw.wl.dataDeviceManager) + wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager); + if (_glfw.wl.cursorTheme) wl_cursor_theme_destroy(_glfw.wl.cursorTheme); if (_glfw.wl.cursorSurface) diff --git a/src/wl_platform.h b/src/wl_platform.h index 100b12bc0..c297eca34 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -115,6 +115,7 @@ typedef struct _GLFWlibraryWayland struct wl_shell* shell; struct wl_shm* shm; struct wl_seat* seat; + struct wl_data_device_manager* dataDeviceManager; struct wl_pointer* pointer; struct wl_keyboard* keyboard; struct zwp_relative_pointer_manager_v1* relativePointerManager; @@ -122,9 +123,10 @@ typedef struct _GLFWlibraryWayland int wl_compositor_version; + struct wl_data_device* dataDevice; struct wl_cursor_theme* cursorTheme; struct wl_surface* cursorSurface; - uint32_t pointerSerial; + uint32_t serial; _GLFWmonitor** monitors; int monitorsCount; @@ -143,6 +145,11 @@ typedef struct _GLFWlibraryWayland unsigned int modifiers; } xkb; + struct wl_data_offer* dataOffer; + struct wl_data_source* dataSource; + char* clipboardString; + const char* clipboardMimeType; + _GLFWwindow* pointerFocus; _GLFWwindow* keyboardFocus; diff --git a/src/wl_window.c b/src/wl_window.c index 6a86f9e9c..e97e808b2 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -40,6 +40,7 @@ #include #include +#define BUFFER_LEN 500 static void handlePing(void* data, struct wl_shell_surface* shellSurface, @@ -149,6 +150,43 @@ static const struct wl_surface_listener surfaceListener = { handleLeave }; +static void dataSourceTarget(void* data, + struct wl_data_source* source, + const char* mimeType) +{ +} + +static void dataSourceSend(void* data, + struct wl_data_source* source, + const char* mimeType, + int32_t fd) +{ + ssize_t len; + char* string = wl_data_source_get_user_data(source); + + do + { + len = write(fd, string, strlen(string)); + string += len; + } + while(len < strlen(string)); +} + +static void dataSourceCancel(void* data, + struct wl_data_source* source) +{ + if (_glfw.wl.dataSource == source) + _glfw.wl.dataSource = NULL; + free(wl_data_source_get_user_data(source)); + wl_data_source_destroy(source); +} + +static const struct wl_data_source_listener dataSourceListener = { + dataSourceTarget, + dataSourceSend, + dataSourceCancel +}; + static GLFWbool createSurface(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) { @@ -749,7 +787,7 @@ static void lockPointer(_GLFWwindow* window) window->wl.pointerLock.relativePointer = relativePointer; window->wl.pointerLock.lockedPointer = lockedPointer; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0); } @@ -801,7 +839,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) buffer = wl_cursor_image_get_buffer(image); if (!buffer) return; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, surface, image->hotspot_x, image->hotspot_y); @@ -812,7 +850,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } else { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, surface, cursor->wl.xhot, cursor->wl.yhot); @@ -829,22 +867,82 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } else if (window->cursorMode == GLFW_CURSOR_HIDDEN) { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, - NULL, 0, 0); + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0); } } void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) { - // TODO - fprintf(stderr, "_glfwPlatformSetClipboardString not implemented yet\n"); + if (!_glfw.wl.dataDevice) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Clipboard could not be set."\ + "Data device was not initialized"); + return; + } + if (_glfw.wl.dataSource) + { + free(wl_data_source_get_user_data(_glfw.wl.dataSource)); + wl_data_source_destroy(_glfw.wl.dataSource); + } + _glfw.wl.dataSource = + wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); + wl_data_source_offer(_glfw.wl.dataSource, + _glfw.wl.clipboardMimeType); + wl_data_source_add_listener(_glfw.wl.dataSource, + &dataSourceListener, + strdup(string)); + wl_data_device_set_selection(_glfw.wl.dataDevice, + _glfw.wl.dataSource, + _glfw.wl.serial); } const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) { - // TODO - fprintf(stderr, "_glfwPlatformGetClipboardString not implemented yet\n"); - return NULL; + int32_t p[2]; + int len, allocatedLen = 0; + char *aux; + + if (!_glfw.wl.dataOffer) + return NULL; + + if (pipe2(p, O_CLOEXEC) == -1) + return NULL; + + wl_data_offer_receive(_glfw.wl.dataOffer, + _glfw.wl.clipboardMimeType, + p[1]); + close(p[1]); + + _glfwPlatformWaitEvents(); + + if (_glfw.wl.clipboardString) + { + free(_glfw.wl.clipboardString); + _glfw.wl.clipboardString = NULL; + } + + do + { + allocatedLen += BUFFER_LEN; + _glfw.wl.clipboardString = + realloc(_glfw.wl.clipboardString, + allocatedLen * sizeof(*_glfw.wl.clipboardString)); + aux = _glfw.wl.clipboardString + (allocatedLen - BUFFER_LEN); + memset(aux, 0, BUFFER_LEN); + len = read(p[0], aux, BUFFER_LEN); + } + while (len == BUFFER_LEN); + + close(p[0]); + + if (len < 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Clipboard read error: %d", errno); + return NULL; + } + return _glfw.wl.clipboardString; } char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count)