parent
bbe8bcb28d
commit
128b9e1cd0
@ -15,6 +15,8 @@ Changelog
|
|||||||
|
|
||||||
- Linux: Fix numpad arrow keys not working when num lock is off (:iss:`857`)
|
- Linux: Fix numpad arrow keys not working when num lock is off (:iss:`857`)
|
||||||
|
|
||||||
|
- Wayland: Implement support for clipboard copy/paste (:iss:`855`)
|
||||||
|
|
||||||
- Allow mapping shortcuts using the raw key code from the OS (:iss:`848`)
|
- Allow mapping shortcuts using the raw key code from the OS (:iss:`848`)
|
||||||
|
|
||||||
- Allow mapping of individual keypresses without modifiers as shortcuts
|
- Allow mapping of individual keypresses without modifiers as shortcuts
|
||||||
|
|||||||
2
glfw/internal.h
vendored
2
glfw/internal.h
vendored
@ -31,6 +31,8 @@
|
|||||||
#include "glfw_config.h"
|
#include "glfw_config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
||||||
|
|
||||||
#if defined(GLFW_INCLUDE_GLCOREARB) || \
|
#if defined(GLFW_INCLUDE_GLCOREARB) || \
|
||||||
defined(GLFW_INCLUDE_ES1) || \
|
defined(GLFW_INCLUDE_ES1) || \
|
||||||
defined(GLFW_INCLUDE_ES2) || \
|
defined(GLFW_INCLUDE_ES2) || \
|
||||||
|
|||||||
13
glfw/wl_init.c
vendored
13
glfw/wl_init.c
vendored
@ -560,6 +560,9 @@ static void registryHandleGlobal(void* data,
|
|||||||
_glfw.wl.seatVersion);
|
_glfw.wl.seatVersion);
|
||||||
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
||||||
}
|
}
|
||||||
|
if (_glfw.wl.seat && _glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) {
|
||||||
|
_glfwSetupWaylandDataDevice();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(interface, "xdg_wm_base") == 0)
|
else if (strcmp(interface, "xdg_wm_base") == 0)
|
||||||
{
|
{
|
||||||
@ -599,6 +602,9 @@ static void registryHandleGlobal(void* data,
|
|||||||
wl_registry_bind(registry, name,
|
wl_registry_bind(registry, name,
|
||||||
&wl_data_device_manager_interface,
|
&wl_data_device_manager_interface,
|
||||||
1);
|
1);
|
||||||
|
if (_glfw.wl.seat && _glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) {
|
||||||
|
_glfwSetupWaylandDataDevice();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -771,6 +777,11 @@ void _glfwPlatformTerminate(void)
|
|||||||
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
|
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
|
||||||
if (_glfw.wl.dataSourceForClipboard)
|
if (_glfw.wl.dataSourceForClipboard)
|
||||||
wl_data_source_destroy(_glfw.wl.dataSourceForClipboard);
|
wl_data_source_destroy(_glfw.wl.dataSourceForClipboard);
|
||||||
|
for (size_t doi=0; doi < arraysz(_glfw.wl.dataOffers); doi++) {
|
||||||
|
if (_glfw.wl.dataOffers[doi].id) {
|
||||||
|
wl_data_offer_destroy(_glfw.wl.dataOffers[doi].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (_glfw.wl.dataDevice)
|
if (_glfw.wl.dataDevice)
|
||||||
wl_data_device_destroy(_glfw.wl.dataDevice);
|
wl_data_device_destroy(_glfw.wl.dataDevice);
|
||||||
if (_glfw.wl.dataDeviceManager)
|
if (_glfw.wl.dataDeviceManager)
|
||||||
@ -784,6 +795,8 @@ void _glfwPlatformTerminate(void)
|
|||||||
}
|
}
|
||||||
closeFds(_glfw.wl.eventLoopData.wakeupFds, sizeof(_glfw.wl.eventLoopData.wakeupFds)/sizeof(_glfw.wl.eventLoopData.wakeupFds[0]));
|
closeFds(_glfw.wl.eventLoopData.wakeupFds, sizeof(_glfw.wl.eventLoopData.wakeupFds)/sizeof(_glfw.wl.eventLoopData.wakeupFds[0]));
|
||||||
free(_glfw.wl.clipboardString); _glfw.wl.clipboardString = NULL;
|
free(_glfw.wl.clipboardString); _glfw.wl.clipboardString = NULL;
|
||||||
|
free(_glfw.wl.clipboardSourceString); _glfw.wl.clipboardSourceString = NULL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* _glfwPlatformGetVersionString(void)
|
const char* _glfwPlatformGetVersionString(void)
|
||||||
|
|||||||
15
glfw/wl_platform.h
vendored
15
glfw/wl_platform.h
vendored
@ -175,6 +175,14 @@ typedef struct _GLFWwindowWayland
|
|||||||
|
|
||||||
} _GLFWwindowWayland;
|
} _GLFWwindowWayland;
|
||||||
|
|
||||||
|
typedef struct _GLFWWaylandDataOffer
|
||||||
|
{
|
||||||
|
struct wl_data_offer *id;
|
||||||
|
const char *mime;
|
||||||
|
int offer_type;
|
||||||
|
size_t idx;
|
||||||
|
} _GLFWWaylandDataOffer;
|
||||||
|
|
||||||
// Wayland-specific global data
|
// Wayland-specific global data
|
||||||
//
|
//
|
||||||
typedef struct _GLFWlibraryWayland
|
typedef struct _GLFWlibraryWayland
|
||||||
@ -235,7 +243,11 @@ typedef struct _GLFWlibraryWayland
|
|||||||
} egl;
|
} egl;
|
||||||
|
|
||||||
EventLoopData eventLoopData;
|
EventLoopData eventLoopData;
|
||||||
char *clipboardString;
|
char* clipboardString;
|
||||||
|
char* clipboardSourceString;
|
||||||
|
struct wl_data_offer* clipboardSourceOffer;
|
||||||
|
size_t dataOffersCounter;
|
||||||
|
_GLFWWaylandDataOffer dataOffers[8];
|
||||||
} _GLFWlibraryWayland;
|
} _GLFWlibraryWayland;
|
||||||
|
|
||||||
// Wayland-specific per-monitor data
|
// Wayland-specific per-monitor data
|
||||||
@ -264,3 +276,4 @@ typedef struct _GLFWcursorWayland
|
|||||||
|
|
||||||
|
|
||||||
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
||||||
|
void _glfwSetupWaylandDataDevice();
|
||||||
|
|||||||
138
glfw/wl_window.c
vendored
138
glfw/wl_window.c
vendored
@ -1457,10 +1457,126 @@ static void _glfwSendClipboardText(void *data, struct wl_data_source *data_sourc
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct wl_data_source_listener data_source_listener = {
|
static const char* _glfwReceiveClipboardText(struct wl_data_offer *data_offer, const char *mime)
|
||||||
|
{
|
||||||
|
if (_glfw.wl.clipboardSourceOffer == data_offer && _glfw.wl.clipboardSourceString)
|
||||||
|
return _glfw.wl.clipboardSourceString;
|
||||||
|
int pipefd[2];
|
||||||
|
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
||||||
|
wl_data_offer_receive(data_offer, mime, pipefd[1]);
|
||||||
|
close(pipefd[1]);
|
||||||
|
wl_display_flush(_glfw.wl.display);
|
||||||
|
size_t sz = 0, capacity = 0;
|
||||||
|
free(_glfw.wl.clipboardSourceString);
|
||||||
|
_glfw.wl.clipboardSourceString = NULL;
|
||||||
|
struct pollfd fds;
|
||||||
|
fds.fd = pipefd[0];
|
||||||
|
fds.events = POLLIN;
|
||||||
|
double start = glfwGetTime();
|
||||||
|
#define bail(...) { \
|
||||||
|
_glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); \
|
||||||
|
free(_glfw.wl.clipboardSourceString); _glfw.wl.clipboardSourceString = NULL; \
|
||||||
|
close(pipefd[0]); \
|
||||||
|
return NULL; \
|
||||||
|
}
|
||||||
|
|
||||||
|
while (glfwGetTime() - start < 2) {
|
||||||
|
int ret = poll(&fds, 1, 2000);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
bail("Wayland: Failed to poll clipboard data from pipe with error: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
bail("Wayland: Failed to read clipboard data from pipe (timed out)");
|
||||||
|
}
|
||||||
|
if (capacity <= sz || capacity - sz <= 64) {
|
||||||
|
capacity += 4096;
|
||||||
|
_glfw.wl.clipboardSourceString = realloc(_glfw.wl.clipboardSourceString, capacity);
|
||||||
|
if (!_glfw.wl.clipboardSourceString) {
|
||||||
|
bail("Wayland: Failed to allocate memory to read clipboard data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = read(pipefd[0], _glfw.wl.clipboardSourceString + sz, capacity - sz - 1);
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;
|
||||||
|
bail("Wayland: Failed to read clipboard data from pipe with error: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
if (ret == 0) { close(pipefd[0]); _glfw.wl.clipboardSourceString[sz] = 0; return _glfw.wl.clipboardSourceString; }
|
||||||
|
sz += ret;
|
||||||
|
start = glfwGetTime();
|
||||||
|
}
|
||||||
|
bail("Wayland: Failed to read clipboard data from pipe (timed out)");
|
||||||
|
#undef bail
|
||||||
|
}
|
||||||
|
|
||||||
|
const static struct wl_data_source_listener data_source_listener = {
|
||||||
.send = _glfwSendClipboardText,
|
.send = _glfwSendClipboardText,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void prune_unclaimed_data_offers() {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].id && !_glfw.wl.dataOffers[i].offer_type) {
|
||||||
|
wl_data_offer_destroy(_glfw.wl.dataOffers[i].id);
|
||||||
|
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mark_selection_offer(void *data, struct wl_data_device *data_device, struct wl_data_offer *data_offer)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].id == data_offer) {
|
||||||
|
_glfw.wl.dataOffers[i].offer_type = 1;
|
||||||
|
} else if (_glfw.wl.dataOffers[i].offer_type == 1) {
|
||||||
|
_glfw.wl.dataOffers[i].offer_type = 0; // previous selection offer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prune_unclaimed_data_offers();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_offer_mimetype(void *data, struct wl_data_offer* id, const char *mime) {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].id == id) {
|
||||||
|
if (strcmp(mime, "text/plain;charset=utf-8") == 0)
|
||||||
|
_glfw.wl.dataOffers[i].mime = "text/plain;charset=utf-8";
|
||||||
|
else if (!_glfw.wl.dataOffers[i].mime && strcmp(mime, "text/plain"))
|
||||||
|
_glfw.wl.dataOffers[i].mime = "text/plain";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_data_offer_listener data_offer_listener = {
|
||||||
|
.offer = handle_offer_mimetype,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id) {
|
||||||
|
size_t smallest_idx = SIZE_MAX, pos = 0;
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].idx && _glfw.wl.dataOffers[i].idx < smallest_idx) {
|
||||||
|
smallest_idx = _glfw.wl.dataOffers[i].idx;
|
||||||
|
pos = i;
|
||||||
|
}
|
||||||
|
if (_glfw.wl.dataOffers[i].id == NULL) {
|
||||||
|
_glfw.wl.dataOffers[i].id = id;
|
||||||
|
_glfw.wl.dataOffers[i].idx = ++_glfw.wl.dataOffersCounter;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_glfw.wl.dataOffers[pos].id) wl_data_offer_destroy(_glfw.wl.dataOffers[pos].id);
|
||||||
|
memset(_glfw.wl.dataOffers + pos, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||||
|
_glfw.wl.dataOffers[pos].id = id;
|
||||||
|
_glfw.wl.dataOffers[pos].idx = ++_glfw.wl.dataOffersCounter;
|
||||||
|
end:
|
||||||
|
wl_data_offer_add_listener(id, &data_offer_listener, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
const static struct wl_data_device_listener data_device_listener = {
|
||||||
|
.data_offer = handle_data_offer,
|
||||||
|
.selection = mark_selection_offer,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
copy_callback_done(void *data, struct wl_callback *callback, uint32_t serial) {
|
copy_callback_done(void *data, struct wl_callback *callback, uint32_t serial) {
|
||||||
if (!_glfw.wl.dataDevice) return;
|
if (!_glfw.wl.dataDevice) return;
|
||||||
@ -1469,15 +1585,20 @@ copy_callback_done(void *data, struct wl_callback *callback, uint32_t serial) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct wl_callback_listener copy_callback_listener = {
|
const static struct wl_callback_listener copy_callback_listener = {
|
||||||
.done = copy_callback_done
|
.done = copy_callback_done
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void _glfwSetupWaylandDataDevice() {
|
||||||
|
_glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat);
|
||||||
|
if (_glfw.wl.dataDevice) wl_data_device_add_listener(_glfw.wl.dataDevice, &data_device_listener, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static inline GLFWbool _glfwEnsureDataDevice() {
|
static inline GLFWbool _glfwEnsureDataDevice() {
|
||||||
if (!_glfw.wl.dataDeviceManager)
|
if (!_glfw.wl.dataDeviceManager)
|
||||||
{
|
{
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
"Wayland: Cannot use clipboard data device manager is not ready");
|
"Wayland: Cannot use clipboard, data device manager is not ready");
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1486,10 +1607,9 @@ static inline GLFWbool _glfwEnsureDataDevice() {
|
|||||||
if (!_glfw.wl.seat)
|
if (!_glfw.wl.seat)
|
||||||
{
|
{
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
"Wayland: Cannot use clipboard seat is not ready");
|
"Wayland: Cannot use clipboard, seat is not ready");
|
||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
}
|
}
|
||||||
_glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat);
|
|
||||||
if (!_glfw.wl.dataDevice)
|
if (!_glfw.wl.dataDevice)
|
||||||
{
|
{
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
@ -1526,9 +1646,11 @@ void _glfwPlatformSetClipboardString(const char* string)
|
|||||||
|
|
||||||
const char* _glfwPlatformGetClipboardString(void)
|
const char* _glfwPlatformGetClipboardString(void)
|
||||||
{
|
{
|
||||||
// TODO
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
if (_glfw.wl.dataOffers[i].id && _glfw.wl.dataOffers[i].mime && _glfw.wl.dataOffers[i].offer_type == 1) {
|
||||||
"Wayland: Clipboard getting not implemented yet");
|
return _glfwReceiveClipboardText(_glfw.wl.dataOffers[i].id, _glfw.wl.dataOffers[i].mime);
|
||||||
|
}
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user