Wayland: Work on supporting drop of file paths
This commit is contained in:
parent
c0c8e1ce5e
commit
6a51ce5dc4
51
glfw/backend_utils.c
vendored
51
glfw/backend_utils.c
vendored
@ -261,3 +261,54 @@ closeFds(int *fds, size_t count) {
|
|||||||
fds++;
|
fds++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Splits and translates a text/uri-list into separate file paths
|
||||||
|
// NOTE: This function destroys the provided string
|
||||||
|
//
|
||||||
|
char** parseUriList(char* text, int* count)
|
||||||
|
{
|
||||||
|
const char* prefix = "file://";
|
||||||
|
char** paths = NULL;
|
||||||
|
char* line;
|
||||||
|
|
||||||
|
*count = 0;
|
||||||
|
|
||||||
|
while ((line = strtok(text, "\r\n")))
|
||||||
|
{
|
||||||
|
text = NULL;
|
||||||
|
|
||||||
|
if (line[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strncmp(line, prefix, strlen(prefix)) == 0)
|
||||||
|
{
|
||||||
|
line += strlen(prefix);
|
||||||
|
// TODO: Validate hostname
|
||||||
|
while (*line != '/')
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*count)++;
|
||||||
|
|
||||||
|
char* path = calloc(strlen(line) + 1, 1);
|
||||||
|
paths = realloc(paths, *count * sizeof(char*));
|
||||||
|
paths[*count - 1] = path;
|
||||||
|
|
||||||
|
while (*line)
|
||||||
|
{
|
||||||
|
if (line[0] == '%' && line[1] && line[2])
|
||||||
|
{
|
||||||
|
const char digits[3] = { line[1], line[2], '\0' };
|
||||||
|
*path = strtol(digits, NULL, 16);
|
||||||
|
line += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*path = *line;
|
||||||
|
|
||||||
|
path++;
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|||||||
1
glfw/backend_utils.h
vendored
1
glfw/backend_utils.h
vendored
@ -71,3 +71,4 @@ int pollForEvents(EventLoopData *eld, double timeout);
|
|||||||
unsigned dispatchTimers(EventLoopData *eld);
|
unsigned dispatchTimers(EventLoopData *eld);
|
||||||
void closeFds(int *fds, size_t count);
|
void closeFds(int *fds, size_t count);
|
||||||
void initPollData(EventLoopData *eld, int wakeup_fd, int display_fd);
|
void initPollData(EventLoopData *eld, int wakeup_fd, int display_fd);
|
||||||
|
char** parseUriList(char* text, int* count);
|
||||||
|
|||||||
2
glfw/wl_platform.h
vendored
2
glfw/wl_platform.h
vendored
@ -182,8 +182,10 @@ typedef struct _GLFWWaylandDataOffer
|
|||||||
int offer_type;
|
int offer_type;
|
||||||
size_t idx;
|
size_t idx;
|
||||||
int is_self_offer;
|
int is_self_offer;
|
||||||
|
int has_uri_list;
|
||||||
uint32_t source_actions;
|
uint32_t source_actions;
|
||||||
uint32_t dnd_action;
|
uint32_t dnd_action;
|
||||||
|
struct wl_surface *surface;
|
||||||
} _GLFWWaylandDataOffer;
|
} _GLFWWaylandDataOffer;
|
||||||
|
|
||||||
// Wayland-specific global data
|
// Wayland-specific global data
|
||||||
|
|||||||
95
glfw/wl_window.c
vendored
95
glfw/wl_window.c
vendored
@ -38,6 +38,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#define KITTY_CLIPBOARD_MIME "application/kitty+clipboard-data"
|
#define KITTY_CLIPBOARD_MIME "application/kitty+clipboard-data"
|
||||||
|
#define URI_LIST_MIME "text/uri-list"
|
||||||
|
|
||||||
|
|
||||||
static void handlePing(void* data,
|
static void handlePing(void* data,
|
||||||
@ -1460,25 +1461,21 @@ static void _glfwSendClipboardText(void *data, struct wl_data_source *data_sourc
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* _glfwReceiveClipboardText(struct wl_data_offer *data_offer, const char *mime)
|
static char* read_data_offer(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];
|
int pipefd[2];
|
||||||
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
||||||
wl_data_offer_receive(data_offer, mime, pipefd[1]);
|
wl_data_offer_receive(data_offer, mime, pipefd[1]);
|
||||||
close(pipefd[1]);
|
close(pipefd[1]);
|
||||||
wl_display_flush(_glfw.wl.display);
|
wl_display_flush(_glfw.wl.display);
|
||||||
size_t sz = 0, capacity = 0;
|
size_t sz = 0, capacity = 0;
|
||||||
free(_glfw.wl.clipboardSourceString);
|
char *buf = NULL;
|
||||||
_glfw.wl.clipboardSourceString = NULL;
|
|
||||||
struct pollfd fds;
|
struct pollfd fds;
|
||||||
fds.fd = pipefd[0];
|
fds.fd = pipefd[0];
|
||||||
fds.events = POLLIN;
|
fds.events = POLLIN;
|
||||||
double start = glfwGetTime();
|
double start = glfwGetTime();
|
||||||
#define bail(...) { \
|
#define bail(...) { \
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); \
|
_glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); \
|
||||||
free(_glfw.wl.clipboardSourceString); _glfw.wl.clipboardSourceString = NULL; \
|
free(buf); buf = NULL; \
|
||||||
close(pipefd[0]); \
|
close(pipefd[0]); \
|
||||||
return NULL; \
|
return NULL; \
|
||||||
}
|
}
|
||||||
@ -1494,22 +1491,32 @@ static const char* _glfwReceiveClipboardText(struct wl_data_offer *data_offer, c
|
|||||||
}
|
}
|
||||||
if (capacity <= sz || capacity - sz <= 64) {
|
if (capacity <= sz || capacity - sz <= 64) {
|
||||||
capacity += 4096;
|
capacity += 4096;
|
||||||
_glfw.wl.clipboardSourceString = realloc(_glfw.wl.clipboardSourceString, capacity);
|
buf = realloc(buf, capacity);
|
||||||
if (!_glfw.wl.clipboardSourceString) {
|
if (!buf) {
|
||||||
bail("Wayland: Failed to allocate memory to read clipboard data");
|
bail("Wayland: Failed to allocate memory to read clipboard data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = read(pipefd[0], _glfw.wl.clipboardSourceString + sz, capacity - sz - 1);
|
ret = read(pipefd[0], buf + sz, capacity - sz - 1);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;
|
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;
|
||||||
bail("Wayland: Failed to read clipboard data from pipe with error: %s", strerror(errno));
|
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; }
|
if (ret == 0) { close(pipefd[0]); buf[sz] = 0; return buf; }
|
||||||
sz += ret;
|
sz += ret;
|
||||||
start = glfwGetTime();
|
start = glfwGetTime();
|
||||||
}
|
}
|
||||||
bail("Wayland: Failed to read clipboard data from pipe (timed out)");
|
bail("Wayland: Failed to read clipboard data from pipe (timed out)");
|
||||||
#undef bail
|
#undef bail
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
free(_glfw.wl.clipboardSourceString);
|
||||||
|
_glfw.wl.clipboardSourceString = read_data_offer(data_offer, mime);
|
||||||
|
return _glfw.wl.clipboardSourceString;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void data_source_canceled(void *data, struct wl_data_source *wl_data_source) {
|
static void data_source_canceled(void *data, struct wl_data_source *wl_data_source) {
|
||||||
@ -1557,6 +1564,8 @@ static void handle_offer_mimetype(void *data, struct wl_data_offer* id, const ch
|
|||||||
_glfw.wl.dataOffers[i].mime = "text/plain";
|
_glfw.wl.dataOffers[i].mime = "text/plain";
|
||||||
else if (strcmp(mime, KITTY_CLIPBOARD_MIME) == 0)
|
else if (strcmp(mime, KITTY_CLIPBOARD_MIME) == 0)
|
||||||
_glfw.wl.dataOffers[i].is_self_offer = 1;
|
_glfw.wl.dataOffers[i].is_self_offer = 1;
|
||||||
|
else if (strcmp(mime, URI_LIST_MIME) == 0)
|
||||||
|
_glfw.wl.dataOffers[i].has_uri_list = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1608,9 +1617,73 @@ end:
|
|||||||
wl_data_offer_add_listener(id, &data_offer_listener, NULL);
|
wl_data_offer_add_listener(id, &data_offer_listener, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void drag_enter(void *data, struct wl_data_device *wl_data_device, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id) {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].id == id) {
|
||||||
|
_glfw.wl.dataOffers[i].offer_type = 2;
|
||||||
|
_glfw.wl.dataOffers[i].surface = surface;
|
||||||
|
const char *mime = _glfw.wl.dataOffers[i].has_uri_list ? URI_LIST_MIME : NULL;
|
||||||
|
wl_data_offer_accept(id, serial, mime);
|
||||||
|
} else if (_glfw.wl.dataOffers[i].offer_type == 2) {
|
||||||
|
_glfw.wl.dataOffers[i].offer_type = 0; // previous drag offer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prune_unclaimed_data_offers();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drag_leave(void *data, struct wl_data_device *wl_data_device) {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].offer_type == 2) {
|
||||||
|
wl_data_offer_destroy(_glfw.wl.dataOffers[i].id);
|
||||||
|
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void drop(void *data, struct wl_data_device *wl_data_device) {
|
||||||
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
|
if (_glfw.wl.dataOffers[i].offer_type == 2) {
|
||||||
|
char *uri_list = read_data_offer(_glfw.wl.dataOffers[i].id, URI_LIST_MIME);
|
||||||
|
if (uri_list) {
|
||||||
|
wl_data_offer_finish(_glfw.wl.dataOffers[i].id);
|
||||||
|
int count;
|
||||||
|
char** paths = parseUriList(data, &count);
|
||||||
|
|
||||||
|
_GLFWwindow* window = _glfw.windowListHead;
|
||||||
|
while (window)
|
||||||
|
{
|
||||||
|
if (window->wl.surface == _glfw.wl.dataOffers[i].surface) {
|
||||||
|
_glfwInputDrop(window, count, (const char**) paths);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
window = window->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int k = 0; k < count; k++)
|
||||||
|
free(paths[k]);
|
||||||
|
free(paths);
|
||||||
|
free(uri_list);
|
||||||
|
}
|
||||||
|
wl_data_offer_destroy(_glfw.wl.dataOffers[i].id);
|
||||||
|
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void motion(void *data, struct wl_data_device *wl_data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y) {
|
||||||
|
}
|
||||||
|
|
||||||
const static struct wl_data_device_listener data_device_listener = {
|
const static struct wl_data_device_listener data_device_listener = {
|
||||||
.data_offer = handle_data_offer,
|
.data_offer = handle_data_offer,
|
||||||
.selection = mark_selection_offer,
|
.selection = mark_selection_offer,
|
||||||
|
.enter = drag_enter,
|
||||||
|
.motion = motion,
|
||||||
|
.drop = drop,
|
||||||
|
.leave = drag_leave,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
50
glfw/x11_window.c
vendored
50
glfw/x11_window.c
vendored
@ -350,56 +350,6 @@ static void updateWindowMode(_GLFWwindow* window)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Splits and translates a text/uri-list into separate file paths
|
|
||||||
// NOTE: This function destroys the provided string
|
|
||||||
//
|
|
||||||
static char** parseUriList(char* text, int* count)
|
|
||||||
{
|
|
||||||
const char* prefix = "file://";
|
|
||||||
char** paths = NULL;
|
|
||||||
char* line;
|
|
||||||
|
|
||||||
*count = 0;
|
|
||||||
|
|
||||||
while ((line = strtok(text, "\r\n")))
|
|
||||||
{
|
|
||||||
text = NULL;
|
|
||||||
|
|
||||||
if (line[0] == '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (strncmp(line, prefix, strlen(prefix)) == 0)
|
|
||||||
{
|
|
||||||
line += strlen(prefix);
|
|
||||||
// TODO: Validate hostname
|
|
||||||
while (*line != '/')
|
|
||||||
line++;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*count)++;
|
|
||||||
|
|
||||||
char* path = calloc(strlen(line) + 1, 1);
|
|
||||||
paths = realloc(paths, *count * sizeof(char*));
|
|
||||||
paths[*count - 1] = path;
|
|
||||||
|
|
||||||
while (*line)
|
|
||||||
{
|
|
||||||
if (line[0] == '%' && line[1] && line[2])
|
|
||||||
{
|
|
||||||
const char digits[3] = { line[1], line[2], '\0' };
|
|
||||||
*path = strtol(digits, NULL, 16);
|
|
||||||
line += 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
*path = *line;
|
|
||||||
|
|
||||||
path++;
|
|
||||||
line++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode a Unicode code point to a UTF-8 stream
|
// Encode a Unicode code point to a UTF-8 stream
|
||||||
// Based on cutef8 by Jeff Bezanson (Public Domain)
|
// Based on cutef8 by Jeff Bezanson (Public Domain)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user