parent
bbe8bcb28d
commit
128b9e1cd0
@ -15,6 +15,8 @@ Changelog
|
||||
|
||||
- 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 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"
|
||||
#endif
|
||||
|
||||
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
#if defined(GLFW_INCLUDE_GLCOREARB) || \
|
||||
defined(GLFW_INCLUDE_ES1) || \
|
||||
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);
|
||||
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)
|
||||
{
|
||||
@ -599,6 +602,9 @@ static void registryHandleGlobal(void* data,
|
||||
wl_registry_bind(registry, name,
|
||||
&wl_data_device_manager_interface,
|
||||
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);
|
||||
if (_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)
|
||||
wl_data_device_destroy(_glfw.wl.dataDevice);
|
||||
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]));
|
||||
free(_glfw.wl.clipboardString); _glfw.wl.clipboardString = NULL;
|
||||
free(_glfw.wl.clipboardSourceString); _glfw.wl.clipboardSourceString = NULL;
|
||||
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetVersionString(void)
|
||||
|
||||
13
glfw/wl_platform.h
vendored
13
glfw/wl_platform.h
vendored
@ -175,6 +175,14 @@ typedef struct _GLFWwindowWayland
|
||||
|
||||
} _GLFWwindowWayland;
|
||||
|
||||
typedef struct _GLFWWaylandDataOffer
|
||||
{
|
||||
struct wl_data_offer *id;
|
||||
const char *mime;
|
||||
int offer_type;
|
||||
size_t idx;
|
||||
} _GLFWWaylandDataOffer;
|
||||
|
||||
// Wayland-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryWayland
|
||||
@ -236,6 +244,10 @@ typedef struct _GLFWlibraryWayland
|
||||
|
||||
EventLoopData eventLoopData;
|
||||
char* clipboardString;
|
||||
char* clipboardSourceString;
|
||||
struct wl_data_offer* clipboardSourceOffer;
|
||||
size_t dataOffersCounter;
|
||||
_GLFWWaylandDataOffer dataOffers[8];
|
||||
} _GLFWlibraryWayland;
|
||||
|
||||
// Wayland-specific per-monitor data
|
||||
@ -264,3 +276,4 @@ typedef struct _GLFWcursorWayland
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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
|
||||
copy_callback_done(void *data, struct wl_callback *callback, uint32_t serial) {
|
||||
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
|
||||
};
|
||||
|
||||
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() {
|
||||
if (!_glfw.wl.dataDeviceManager)
|
||||
{
|
||||
_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;
|
||||
}
|
||||
|
||||
@ -1486,10 +1607,9 @@ static inline GLFWbool _glfwEnsureDataDevice() {
|
||||
if (!_glfw.wl.seat)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Cannot use clipboard seat is not ready");
|
||||
"Wayland: Cannot use clipboard, seat is not ready");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
_glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat);
|
||||
if (!_glfw.wl.dataDevice)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
@ -1526,9 +1646,11 @@ void _glfwPlatformSetClipboardString(const char* string)
|
||||
|
||||
const char* _glfwPlatformGetClipboardString(void)
|
||||
{
|
||||
// TODO
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Clipboard getting not implemented yet");
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].id && _glfw.wl.dataOffers[i].mime && _glfw.wl.dataOffers[i].offer_type == 1) {
|
||||
return _glfwReceiveClipboardText(_glfw.wl.dataOffers[i].id, _glfw.wl.dataOffers[i].mime);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user