Wayland: Work on supporting drop of file paths

This commit is contained in:
Kovid Goyal 2018-09-05 21:41:47 +05:30
parent c0c8e1ce5e
commit 6a51ce5dc4
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 138 additions and 61 deletions

51
glfw/backend_utils.c vendored
View File

@ -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;
}

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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)