Implement drag and drop of text/plain for Wayland as well
This commit is contained in:
parent
e827e6fa21
commit
2458c3a7c6
@ -77,7 +77,7 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
- Workaround for bug in less that causes colors to reset at wrapped lines
|
||||
(:iss:`2381`)
|
||||
|
||||
- X11: Allow drag and drop of text/plain in addition to text/uri-list
|
||||
- X11/Wayland: Allow drag and drop of text/plain in addition to text/uri-list
|
||||
(:iss:`2441`)
|
||||
|
||||
- Dont strip :code:`&` and :code:`-` from the end of URLs (:iss:`2436`)
|
||||
|
||||
51
glfw/backend_utils.c
vendored
51
glfw/backend_utils.c
vendored
@ -332,54 +332,3 @@ pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func displ
|
||||
}
|
||||
return read_ok;
|
||||
}
|
||||
|
||||
// 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
@ -94,5 +94,4 @@ int pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func);
|
||||
unsigned dispatchTimers(EventLoopData *eld);
|
||||
void finalizePollData(EventLoopData *eld);
|
||||
bool initPollData(EventLoopData *eld, int display_fd);
|
||||
char** parseUriList(char* text, int* count);
|
||||
void wakeupEventLoop(EventLoopData *eld);
|
||||
|
||||
17
glfw/glfw3.h
vendored
17
glfw/glfw3.h
vendored
@ -1593,19 +1593,22 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int);
|
||||
*/
|
||||
typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
|
||||
|
||||
/*! @brief The function pointer type for path drop callbacks.
|
||||
/*! @brief The function pointer type for drag and drop callbacks.
|
||||
*
|
||||
* This is the function pointer type for path drop callbacks. A path drop
|
||||
* This is the function pointer type for drop callbacks. A drop
|
||||
* callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int path_count, const char* paths[])
|
||||
* int function_name(GLFWwindow* window, const char* mime, const char* text)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] path_count The number of dropped paths.
|
||||
* @param[in] paths The UTF-8 encoded file and/or directory path names.
|
||||
* @param[in] mime The UTF-8 encoded drop mime-type
|
||||
* @param[in] data The dropped data or NULL for drag enter events
|
||||
* @param[in] sz The size of the dropped data
|
||||
* @return For drag events should return the priority for the specified mime type. A priority of zero
|
||||
* or lower means the mime type is not accepted. Highest priority will be the finally accepted mime-type.
|
||||
*
|
||||
* @pointer_lifetime The path array and its strings are valid until the
|
||||
* @pointer_lifetime The text is valid until the
|
||||
* callback function returns.
|
||||
*
|
||||
* @sa @ref path_drop
|
||||
@ -1615,7 +1618,7 @@ typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWdropfun)(GLFWwindow*,int,const char*[]);
|
||||
typedef int (* GLFWdropfun)(GLFWwindow*, const char *, const char*, size_t);
|
||||
|
||||
typedef void (* GLFWliveresizefun)(GLFWwindow*, bool);
|
||||
|
||||
|
||||
5
glfw/input.c
vendored
5
glfw/input.c
vendored
@ -350,10 +350,11 @@ void _glfwInputCursorEnter(_GLFWwindow* window, bool entered)
|
||||
|
||||
// Notifies shared code of files or directories dropped on a window
|
||||
//
|
||||
void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths)
|
||||
int _glfwInputDrop(_GLFWwindow* window, const char *mime, const char *text, size_t sz)
|
||||
{
|
||||
if (window->callbacks.drop)
|
||||
window->callbacks.drop((GLFWwindow*) window, count, paths);
|
||||
return window->callbacks.drop((GLFWwindow*) window, mime, text, sz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Notifies shared code of a joystick connection or disconnection
|
||||
|
||||
2
glfw/internal.h
vendored
2
glfw/internal.h
vendored
@ -759,7 +759,7 @@ void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int f
|
||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
||||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||
void _glfwInputCursorEnter(_GLFWwindow* window, bool entered);
|
||||
void _glfwInputDrop(_GLFWwindow* window, int count, const char** names);
|
||||
int _glfwInputDrop(_GLFWwindow* window, const char *mime, const char *text, size_t sz);
|
||||
void _glfwInputJoystick(_GLFWjoystick* js, int event);
|
||||
void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value);
|
||||
void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value);
|
||||
|
||||
2
glfw/wl_init.c
vendored
2
glfw/wl_init.c
vendored
@ -852,7 +852,7 @@ void _glfwPlatformTerminate(void)
|
||||
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);
|
||||
destroy_data_offer(&_glfw.wl.dataOffers[doi]);
|
||||
}
|
||||
}
|
||||
if (_glfw.wl.dataDevice)
|
||||
|
||||
24
glfw/wl_platform.h
vendored
24
glfw/wl_platform.h
vendored
@ -195,28 +195,19 @@ typedef enum _GLFWWaylandOfferType
|
||||
|
||||
typedef struct _GLFWWaylandDataOffer
|
||||
{
|
||||
struct wl_data_offer *id;
|
||||
const char *mime;
|
||||
void *id;
|
||||
_GLFWWaylandOfferType offer_type;
|
||||
size_t idx;
|
||||
int is_self_offer;
|
||||
int has_uri_list;
|
||||
bool is_self_offer;
|
||||
bool is_primary;
|
||||
const char *plain_text_mime, *mime_for_drop;
|
||||
uint32_t source_actions;
|
||||
uint32_t dnd_action;
|
||||
struct wl_surface *surface;
|
||||
const char **mimes;
|
||||
size_t mimes_capacity, mimes_count;
|
||||
} _GLFWWaylandDataOffer;
|
||||
|
||||
typedef struct _GLFWWaylandPrimaryOffer
|
||||
{
|
||||
struct zwp_primary_selection_offer_v1 *id;
|
||||
const char *mime;
|
||||
_GLFWWaylandOfferType offer_type;
|
||||
size_t idx;
|
||||
int is_self_offer;
|
||||
int has_uri_list;
|
||||
struct wl_surface *surface;
|
||||
} _GLFWWaylandPrimaryOffer;
|
||||
|
||||
// Wayland-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryWayland
|
||||
@ -287,8 +278,6 @@ typedef struct _GLFWlibraryWayland
|
||||
size_t dataOffersCounter;
|
||||
_GLFWWaylandDataOffer dataOffers[8];
|
||||
char* primarySelectionString;
|
||||
size_t primarySelectionOffersCounter;
|
||||
_GLFWWaylandPrimaryOffer primarySelectionOffers[8];
|
||||
} _GLFWlibraryWayland;
|
||||
|
||||
// Wayland-specific per-monitor data
|
||||
@ -322,3 +311,4 @@ void _glfwSetupWaylandDataDevice(void);
|
||||
void _glfwSetupWaylandPrimarySelectionDevice(void);
|
||||
void animateCursorImage(id_type timer_id, void *data);
|
||||
struct wl_cursor* _glfwLoadCursor(GLFWCursorShape);
|
||||
void destroy_data_offer(_GLFWWaylandDataOffer*);
|
||||
|
||||
190
glfw/wl_window.c
vendored
190
glfw/wl_window.c
vendored
@ -42,7 +42,7 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
|
||||
#define URI_LIST_MIME "text/uri-list"
|
||||
|
||||
|
||||
|
||||
static bool checkScaleChange(_GLFWwindow* window)
|
||||
@ -1509,10 +1509,11 @@ static void _glfwSendPrimarySelectionText(void *data UNUSED, struct zwp_primary_
|
||||
send_text(_glfw.wl.primarySelectionString, fd);
|
||||
}
|
||||
|
||||
static char* read_offer_string(int data_pipe) {
|
||||
static char* read_offer_string(int data_pipe, size_t *data_sz) {
|
||||
wl_display_flush(_glfw.wl.display);
|
||||
size_t sz = 0, capacity = 0;
|
||||
size_t capacity = 0;
|
||||
char *buf = NULL;
|
||||
*data_sz = 0;
|
||||
struct pollfd fds;
|
||||
fds.fd = data_pipe;
|
||||
fds.events = POLLIN;
|
||||
@ -1533,20 +1534,20 @@ static char* read_offer_string(int data_pipe) {
|
||||
if (!ret) {
|
||||
bail("Wayland: Failed to read clipboard data from pipe (timed out)");
|
||||
}
|
||||
if (capacity <= sz || capacity - sz <= 64) {
|
||||
if (capacity <= *data_sz || capacity - *data_sz <= 64) {
|
||||
capacity += 4096;
|
||||
buf = realloc(buf, capacity);
|
||||
if (!buf) {
|
||||
bail("Wayland: Failed to allocate memory to read clipboard data");
|
||||
}
|
||||
}
|
||||
ret = read(data_pipe, buf + sz, capacity - sz - 1);
|
||||
ret = read(data_pipe, buf + *data_sz, capacity - *data_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(data_pipe); buf[sz] = 0; return buf; }
|
||||
sz += ret;
|
||||
if (ret == 0) { close(data_pipe); buf[*data_sz] = 0; return buf; }
|
||||
*data_sz += ret;
|
||||
start = glfwGetTime();
|
||||
}
|
||||
bail("Wayland: Failed to read clipboard data from pipe (timed out)");
|
||||
@ -1559,15 +1560,16 @@ static char* read_primary_selection_offer(struct zwp_primary_selection_offer_v1
|
||||
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
||||
zwp_primary_selection_offer_v1_receive(primary_selection_offer, mime, pipefd[1]);
|
||||
close(pipefd[1]);
|
||||
return read_offer_string(pipefd[0]);
|
||||
size_t sz = 0;
|
||||
return read_offer_string(pipefd[0], &sz);
|
||||
}
|
||||
|
||||
static char* read_data_offer(struct wl_data_offer *data_offer, const char *mime) {
|
||||
static char* read_data_offer(struct wl_data_offer *data_offer, const char *mime, size_t *sz) {
|
||||
int pipefd[2];
|
||||
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
||||
wl_data_offer_receive(data_offer, mime, pipefd[1]);
|
||||
close(pipefd[1]);
|
||||
return read_offer_string(pipefd[0]);
|
||||
return read_offer_string(pipefd[0], sz);
|
||||
}
|
||||
|
||||
static void data_source_canceled(void *data UNUSED, struct wl_data_source *wl_data_source) {
|
||||
@ -1596,20 +1598,23 @@ static const struct zwp_primary_selection_source_v1_listener primary_selection_s
|
||||
.cancelled = primary_selection_source_canceled,
|
||||
};
|
||||
|
||||
void
|
||||
destroy_data_offer(_GLFWWaylandDataOffer *offer) {
|
||||
if (offer->id) {
|
||||
if (offer->is_primary) zwp_primary_selection_offer_v1_destroy(offer->id);
|
||||
else wl_data_offer_destroy(offer->id);
|
||||
}
|
||||
if (offer->mimes) {
|
||||
for (size_t i = 0; i < offer->mimes_count; i++) free((char*)offer->mimes[i]);
|
||||
free(offer->mimes);
|
||||
}
|
||||
memset(offer, 0, sizeof(_GLFWWaylandDataOffer));
|
||||
}
|
||||
|
||||
static void prune_unclaimed_data_offers(void) {
|
||||
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 prune_unclaimed_primary_selection_offers(void) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.primarySelectionOffers); i++) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].id && !_glfw.wl.dataOffers[i].offer_type) {
|
||||
zwp_primary_selection_offer_v1_destroy(_glfw.wl.primarySelectionOffers[i].id);
|
||||
memset(_glfw.wl.primarySelectionOffers + i, 0, sizeof(_glfw.wl.primarySelectionOffers[0]));
|
||||
destroy_data_offer(&_glfw.wl.dataOffers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1628,25 +1633,32 @@ static void mark_selection_offer(void *data UNUSED, struct wl_data_device *data_
|
||||
|
||||
static void mark_primary_selection_offer(void *data UNUSED, struct zwp_primary_selection_device_v1* primary_selection_device UNUSED,
|
||||
struct zwp_primary_selection_offer_v1 *primary_selection_offer) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.primarySelectionOffers); i++) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].id == primary_selection_offer) {
|
||||
_glfw.wl.primarySelectionOffers[i].offer_type = PRIMARY_SELECTION;
|
||||
} else if (_glfw.wl.primarySelectionOffers[i].offer_type == PRIMARY_SELECTION) {
|
||||
_glfw.wl.primarySelectionOffers[i].offer_type = EXPIRED; // previous selection offer
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].id == primary_selection_offer) {
|
||||
_glfw.wl.dataOffers[i].offer_type = PRIMARY_SELECTION;
|
||||
} else if (_glfw.wl.dataOffers[i].offer_type == PRIMARY_SELECTION) {
|
||||
_glfw.wl.dataOffers[i].offer_type = EXPIRED; // previous selection offer
|
||||
}
|
||||
}
|
||||
prune_unclaimed_primary_selection_offers();
|
||||
prune_unclaimed_data_offers();
|
||||
}
|
||||
|
||||
static void set_offer_mimetype(struct _GLFWWaylandDataOffer* offer, const char* mime) {
|
||||
if (strcmp(mime, "text/plain;charset=utf-8") == 0)
|
||||
offer->mime = "text/plain;charset=utf-8";
|
||||
else if (!offer->mime && strcmp(mime, "text/plain") == 0)
|
||||
offer->mime = "text/plain";
|
||||
else if (strcmp(mime, clipboard_mime()) == 0)
|
||||
offer->is_self_offer = 1;
|
||||
else if (strcmp(mime, URI_LIST_MIME) == 0)
|
||||
offer->has_uri_list = 1;
|
||||
static void
|
||||
set_offer_mimetype(_GLFWWaylandDataOffer* offer, const char* mime) {
|
||||
if (strcmp(mime, "text/plain;charset=utf-8") == 0) {
|
||||
offer->plain_text_mime = "text/plain;charset=utf-8";
|
||||
} else if (!offer->plain_text_mime && strcmp(mime, "text/plain")) {
|
||||
offer->plain_text_mime = "text/plain";
|
||||
}
|
||||
if (strcmp(mime, clipboard_mime()) == 0) {
|
||||
offer->is_self_offer = true;
|
||||
}
|
||||
if (!offer->mimes || offer->mimes_count >= offer->mimes_capacity - 1) {
|
||||
offer->mimes = realloc(offer->mimes, sizeof(char*) * (offer->mimes_capacity + 64));
|
||||
if (offer->mimes) offer->mimes_capacity += 64;
|
||||
else return;
|
||||
}
|
||||
offer->mimes[offer->mimes_count++] = _glfw_strdup(mime);
|
||||
}
|
||||
|
||||
static void handle_offer_mimetype(void *data UNUSED, struct wl_data_offer* id, const char *mime) {
|
||||
@ -1659,9 +1671,9 @@ static void handle_offer_mimetype(void *data UNUSED, struct wl_data_offer* id, c
|
||||
}
|
||||
|
||||
static void handle_primary_selection_offer_mimetype(void *data UNUSED, struct zwp_primary_selection_offer_v1* id, const char *mime) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.primarySelectionOffers); i++) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].id == id) {
|
||||
set_offer_mimetype((struct _GLFWWaylandDataOffer*)&_glfw.wl.primarySelectionOffers[i], mime);
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].id == id) {
|
||||
set_offer_mimetype((_GLFWWaylandDataOffer*)&_glfw.wl.dataOffers[i], mime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1696,7 +1708,8 @@ static const struct zwp_primary_selection_offer_v1_listener primary_selection_of
|
||||
.offer = handle_primary_selection_offer_mimetype,
|
||||
};
|
||||
|
||||
static void handle_data_offer(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, struct wl_data_offer *id) {
|
||||
static size_t
|
||||
handle_data_offer_generic(void *id, bool is_primary) {
|
||||
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) {
|
||||
@ -1704,47 +1717,48 @@ static void handle_data_offer(void *data UNUSED, struct wl_data_device *wl_data_
|
||||
pos = i;
|
||||
}
|
||||
if (_glfw.wl.dataOffers[i].id == NULL) {
|
||||
_glfw.wl.dataOffers[i].id = id;
|
||||
_glfw.wl.dataOffers[i].idx = ++_glfw.wl.dataOffersCounter;
|
||||
pos = i;
|
||||
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;
|
||||
if (_glfw.wl.dataOffers[pos].id) destroy_data_offer(&_glfw.wl.dataOffers[pos]);
|
||||
end:
|
||||
_glfw.wl.dataOffers[pos].id = id;
|
||||
_glfw.wl.dataOffers[pos].is_primary = is_primary;
|
||||
_glfw.wl.dataOffers[pos].idx = ++_glfw.wl.dataOffersCounter;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void handle_data_offer(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, struct wl_data_offer *id) {
|
||||
handle_data_offer_generic(id, false);
|
||||
wl_data_offer_add_listener(id, &data_offer_listener, NULL);
|
||||
}
|
||||
|
||||
static void handle_primary_selection_offer(void *data UNUSED, struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1 UNUSED, struct zwp_primary_selection_offer_v1 *id) {
|
||||
size_t smallest_idx = SIZE_MAX, pos = 0;
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.primarySelectionOffers); i++) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].idx && _glfw.wl.primarySelectionOffers[i].idx < smallest_idx) {
|
||||
smallest_idx = _glfw.wl.primarySelectionOffers[i].idx;
|
||||
pos = i;
|
||||
}
|
||||
if (_glfw.wl.primarySelectionOffers[i].id == NULL) {
|
||||
_glfw.wl.primarySelectionOffers[i].id = id;
|
||||
_glfw.wl.primarySelectionOffers[i].idx = ++_glfw.wl.primarySelectionOffersCounter;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
if (_glfw.wl.primarySelectionOffers[pos].id) zwp_primary_selection_offer_v1_destroy(_glfw.wl.primarySelectionOffers[pos].id);
|
||||
memset(_glfw.wl.primarySelectionOffers + pos, 0, sizeof(_glfw.wl.primarySelectionOffers[0]));
|
||||
_glfw.wl.primarySelectionOffers[pos].id = id;
|
||||
_glfw.wl.primarySelectionOffers[pos].idx = ++_glfw.wl.primarySelectionOffersCounter;
|
||||
end:
|
||||
handle_data_offer_generic(id, true);
|
||||
zwp_primary_selection_offer_v1_add_listener(id, &primary_selection_offer_listener, NULL);
|
||||
}
|
||||
|
||||
static void drag_enter(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, uint32_t serial, struct wl_surface *surface, wl_fixed_t x UNUSED, wl_fixed_t y UNUSED, 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 = DRAG_AND_DROP;
|
||||
_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);
|
||||
_GLFWWaylandDataOffer *d = _glfw.wl.dataOffers + i;
|
||||
if (d->id == id) {
|
||||
d->offer_type = DRAG_AND_DROP;
|
||||
d->surface = surface;
|
||||
_GLFWwindow* window = _glfw.windowListHead;
|
||||
int format_priority = 0;
|
||||
while (window)
|
||||
{
|
||||
if (window->wl.surface == surface) {
|
||||
for (size_t x = 0; x < d->mimes_count; x++) {
|
||||
int prio = _glfwInputDrop(window, d->mimes[x], NULL, 0);
|
||||
if (prio > format_priority) d->mime_for_drop = d->mimes[x];
|
||||
}
|
||||
break;
|
||||
}
|
||||
window = window->next;
|
||||
}
|
||||
wl_data_offer_accept(id, serial, d->mime_for_drop);
|
||||
} else if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP) {
|
||||
_glfw.wl.dataOffers[i].offer_type = EXPIRED; // previous drag offer
|
||||
}
|
||||
@ -1755,41 +1769,34 @@ static void drag_enter(void *data UNUSED, struct wl_data_device *wl_data_device
|
||||
static void drag_leave(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP) {
|
||||
wl_data_offer_destroy(_glfw.wl.dataOffers[i].id);
|
||||
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||
destroy_data_offer(&_glfw.wl.dataOffers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drop(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP) {
|
||||
char *uri_list = read_data_offer(_glfw.wl.dataOffers[i].id, URI_LIST_MIME);
|
||||
if (uri_list) {
|
||||
if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP && _glfw.wl.dataOffers[i].mime_for_drop) {
|
||||
size_t sz = 0;
|
||||
char *data = read_data_offer(_glfw.wl.dataOffers[i].id, _glfw.wl.dataOffers[i].mime_for_drop, &sz);
|
||||
if (data) {
|
||||
// We dont do finish as this requires version 3 for wl_data_device_manager
|
||||
// which then requires more work with calling set_actions for drag and drop to function
|
||||
// wl_data_offer_finish(_glfw.wl.dataOffers[i].id);
|
||||
int count;
|
||||
char** paths = parseUriList(uri_list, &count);
|
||||
|
||||
_GLFWwindow* window = _glfw.windowListHead;
|
||||
while (window)
|
||||
{
|
||||
if (window->wl.surface == _glfw.wl.dataOffers[i].surface) {
|
||||
_glfwInputDrop(window, count, (const char**) paths);
|
||||
_glfwInputDrop(window, _glfw.wl.dataOffers[i].mime_for_drop, data, sz);
|
||||
break;
|
||||
}
|
||||
window = window->next;
|
||||
}
|
||||
|
||||
|
||||
for (int k = 0; k < count; k++)
|
||||
free(paths[k]);
|
||||
free(paths);
|
||||
free(uri_list);
|
||||
free(data);
|
||||
}
|
||||
wl_data_offer_destroy(_glfw.wl.dataOffers[i].id);
|
||||
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||
destroy_data_offer(&_glfw.wl.dataOffers[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1894,10 +1901,12 @@ void _glfwPlatformSetClipboardString(const char* string)
|
||||
const char* _glfwPlatformGetClipboardString(void)
|
||||
{
|
||||
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 == CLIPBOARD) {
|
||||
if (_glfw.wl.dataOffers[i].is_self_offer) return _glfw.wl.clipboardString;
|
||||
_GLFWWaylandDataOffer *d = _glfw.wl.dataOffers + i;
|
||||
if (d->id && d->offer_type == CLIPBOARD && d->plain_text_mime) {
|
||||
if (d->is_self_offer) return _glfw.wl.clipboardString;
|
||||
free(_glfw.wl.pasteString);
|
||||
_glfw.wl.pasteString = read_data_offer(_glfw.wl.dataOffers[i].id, _glfw.wl.dataOffers[i].mime);
|
||||
size_t sz = 0;
|
||||
_glfw.wl.pasteString = read_data_offer(d->id, d->plain_text_mime, &sz);
|
||||
return _glfw.wl.pasteString;
|
||||
}
|
||||
}
|
||||
@ -1944,13 +1953,14 @@ const char* _glfwPlatformGetPrimarySelectionString(void)
|
||||
{
|
||||
if (_glfw.wl.dataSourceForPrimarySelection != NULL) return _glfw.wl.primarySelectionString;
|
||||
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.primarySelectionOffers); i++) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].id && _glfw.wl.primarySelectionOffers[i].mime && _glfw.wl.primarySelectionOffers[i].offer_type == PRIMARY_SELECTION) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].is_self_offer) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
_GLFWWaylandDataOffer *d = _glfw.wl.dataOffers + i;
|
||||
if (d->id && d->is_primary && d->offer_type == PRIMARY_SELECTION && d->plain_text_mime) {
|
||||
if (d->is_self_offer) {
|
||||
return _glfw.wl.primarySelectionString;
|
||||
}
|
||||
free(_glfw.wl.pasteString);
|
||||
_glfw.wl.pasteString = read_primary_selection_offer(_glfw.wl.primarySelectionOffers[i].id, _glfw.wl.primarySelectionOffers[i].mime);
|
||||
_glfw.wl.pasteString = read_primary_selection_offer(d->id, d->plain_text_mime);
|
||||
return _glfw.wl.pasteString;
|
||||
}
|
||||
}
|
||||
|
||||
3
glfw/x11_init.c
vendored
3
glfw/x11_init.c
vendored
@ -384,9 +384,6 @@ static bool initExtensions(void)
|
||||
_glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False);
|
||||
_glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False);
|
||||
_glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False);
|
||||
_glfw.x11.text_uri_list = XInternAtom(_glfw.x11.display, "text/uri-list", False);
|
||||
_glfw.x11.text_plain = XInternAtom(_glfw.x11.display, "text/plain", False);
|
||||
_glfw.x11.text_plain_utf8 = XInternAtom(_glfw.x11.display, "text/plain;charset=utf-8", False);
|
||||
|
||||
// ICCCM, EWMH and Motif window property atoms
|
||||
// These can be set safely even without WM support
|
||||
|
||||
6
glfw/x11_platform.h
vendored
6
glfw/x11_platform.h
vendored
@ -266,9 +266,6 @@ typedef struct _GLFWlibraryX11
|
||||
Atom XdndFinished;
|
||||
Atom XdndSelection;
|
||||
Atom XdndTypeList;
|
||||
Atom text_uri_list;
|
||||
Atom text_plain;
|
||||
Atom text_plain_utf8;
|
||||
|
||||
// Selection (clipboard) atoms
|
||||
Atom TARGETS;
|
||||
@ -329,7 +326,8 @@ typedef struct _GLFWlibraryX11
|
||||
struct {
|
||||
int version;
|
||||
Window source;
|
||||
Atom format;
|
||||
char format[128];
|
||||
int format_priority;
|
||||
} xdnd;
|
||||
|
||||
struct {
|
||||
|
||||
40
glfw/x11_window.c
vendored
40
glfw/x11_window.c
vendored
@ -1454,7 +1454,8 @@ static void processEvent(XEvent *event)
|
||||
|
||||
_glfw.x11.xdnd.source = event->xclient.data.l[0];
|
||||
_glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
|
||||
_glfw.x11.xdnd.format = None;
|
||||
memset(_glfw.x11.xdnd.format, 0, sizeof(_glfw.x11.xdnd.format));
|
||||
_glfw.x11.xdnd.format_priority = 0;
|
||||
|
||||
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
|
||||
return;
|
||||
@ -1471,18 +1472,22 @@ static void processEvent(XEvent *event)
|
||||
count = 3;
|
||||
formats = (Atom*) event->xclient.data.l + 2;
|
||||
}
|
||||
char **atom_names = calloc(count, sizeof(char**));
|
||||
if (atom_names) {
|
||||
XGetAtomNames(_glfw.x11.display, formats, count, atom_names);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (formats[i] == _glfw.x11.text_uri_list)
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
_glfw.x11.xdnd.format = _glfw.x11.text_uri_list;
|
||||
break;
|
||||
} else if (formats[i] == _glfw.x11.text_plain_utf8 && (_glfw.x11.xdnd.format == None || _glfw.x11.xdnd.format == _glfw.x11.text_plain)) {
|
||||
_glfw.x11.xdnd.format = _glfw.x11.text_plain_utf8;
|
||||
} else if (formats[i] == _glfw.x11.text_plain && _glfw.x11.xdnd.format == None) {
|
||||
_glfw.x11.xdnd.format = _glfw.x11.text_plain;
|
||||
if (atom_names[i]) {
|
||||
int prio = _glfwInputDrop(window, atom_names[i], NULL, 0);
|
||||
if (prio > _glfw.x11.xdnd.format_priority) {
|
||||
_glfw.x11.xdnd.format_priority = prio;
|
||||
strncpy(_glfw.x11.xdnd.format, atom_names[i], arraysz(_glfw.x11.xdnd.format) - 1);
|
||||
}
|
||||
XFree(atom_names[i]);
|
||||
}
|
||||
}
|
||||
free(atom_names);
|
||||
}
|
||||
|
||||
if (list && formats)
|
||||
@ -1496,7 +1501,7 @@ static void processEvent(XEvent *event)
|
||||
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
|
||||
return;
|
||||
|
||||
if (_glfw.x11.xdnd.format)
|
||||
if (_glfw.x11.xdnd.format_priority > 0)
|
||||
{
|
||||
if (_glfw.x11.xdnd.version >= 1)
|
||||
time = event->xclient.data.l[2];
|
||||
@ -1504,7 +1509,7 @@ static void processEvent(XEvent *event)
|
||||
// Request the chosen format from the source window
|
||||
XConvertSelection(_glfw.x11.display,
|
||||
_glfw.x11.XdndSelection,
|
||||
_glfw.x11.xdnd.format,
|
||||
XInternAtom(_glfw.x11.display, _glfw.x11.xdnd.format, 0),
|
||||
_glfw.x11.XdndSelection,
|
||||
window->x11.handle,
|
||||
time);
|
||||
@ -1552,7 +1557,7 @@ static void processEvent(XEvent *event)
|
||||
reply.xclient.data.l[2] = 0; // Specify an empty rectangle
|
||||
reply.xclient.data.l[3] = 0;
|
||||
|
||||
if (_glfw.x11.xdnd.format)
|
||||
if (_glfw.x11.xdnd.format_priority > 0)
|
||||
{
|
||||
// Reply that we are ready to copy the dragged data
|
||||
reply.xclient.data.l[1] = 1; // Accept with no rectangle
|
||||
@ -1582,14 +1587,7 @@ static void processEvent(XEvent *event)
|
||||
|
||||
if (result)
|
||||
{
|
||||
int i, count;
|
||||
char** paths = parseUriList(data, &count);
|
||||
|
||||
_glfwInputDrop(window, count, (const char**) paths);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
free(paths[i]);
|
||||
free(paths);
|
||||
_glfwInputDrop(window, _glfw.x11.xdnd.format, data, result);
|
||||
}
|
||||
|
||||
if (data)
|
||||
|
||||
@ -46,8 +46,9 @@ from .tabs import (
|
||||
from .typing import PopenType, TypedDict
|
||||
from .utils import (
|
||||
func_name, get_editor, get_primary_selection, is_path_in_temp_dir,
|
||||
log_error, open_url, parse_address_spec, remove_socket_file, safe_print,
|
||||
set_primary_selection, single_instance, startup_notification_handler
|
||||
log_error, open_url, parse_address_spec, parse_uri_list,
|
||||
remove_socket_file, safe_print, set_primary_selection, single_instance,
|
||||
startup_notification_handler
|
||||
)
|
||||
from .window import MatchPatternType, Window
|
||||
|
||||
@ -686,12 +687,15 @@ class Boss:
|
||||
if tm is not None:
|
||||
tm.update_tab_bar_data()
|
||||
|
||||
def on_drop(self, os_window_id: int, strings: Iterable[str]) -> None:
|
||||
def on_drop(self, os_window_id: int, mime: str, data: bytes) -> None:
|
||||
tm = self.os_window_map.get(os_window_id)
|
||||
if tm is not None:
|
||||
w = tm.active_window
|
||||
if w is not None:
|
||||
w.paste('\n'.join(strings))
|
||||
text = data.decode('utf-8', 'replace')
|
||||
if mime == 'text/uri-list':
|
||||
text = '\n'.join(parse_uri_list(text))
|
||||
w.paste(text)
|
||||
|
||||
def on_os_window_closed(self, os_window_id: int, viewport_width: int, viewport_height: int) -> None:
|
||||
self.cached_values['window-size'] = viewport_width, viewport_height
|
||||
|
||||
6
kitty/glfw-wrapper.c
generated
6
kitty/glfw-wrapper.c
generated
@ -137,12 +137,12 @@ load_glfw(const char* path) {
|
||||
*(void **) (&glfwSetWindowSizeLimits_impl) = dlsym(handle, "glfwSetWindowSizeLimits");
|
||||
if (glfwSetWindowSizeLimits_impl == NULL) fail("Failed to load glfw function glfwSetWindowSizeLimits with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwSetWindowAspectRatio_impl) = dlsym(handle, "glfwSetWindowAspectRatio");
|
||||
if (glfwSetWindowAspectRatio_impl == NULL) fail("Failed to load glfw function glfwSetWindowAspectRatio with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwSetWindowSizeIncrements_impl) = dlsym(handle, "glfwSetWindowSizeIncrements");
|
||||
if (glfwSetWindowSizeIncrements_impl == NULL) fail("Failed to load glfw function glfwSetWindowSizeIncrements with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwSetWindowAspectRatio_impl) = dlsym(handle, "glfwSetWindowAspectRatio");
|
||||
if (glfwSetWindowAspectRatio_impl == NULL) fail("Failed to load glfw function glfwSetWindowAspectRatio with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwSetWindowSize_impl) = dlsym(handle, "glfwSetWindowSize");
|
||||
if (glfwSetWindowSize_impl == NULL) fail("Failed to load glfw function glfwSetWindowSize with error: %s", dlerror());
|
||||
|
||||
|
||||
25
kitty/glfw-wrapper.h
generated
25
kitty/glfw-wrapper.h
generated
@ -1353,19 +1353,22 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int);
|
||||
*/
|
||||
typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
|
||||
|
||||
/*! @brief The function pointer type for path drop callbacks.
|
||||
/*! @brief The function pointer type for drag and drop callbacks.
|
||||
*
|
||||
* This is the function pointer type for path drop callbacks. A path drop
|
||||
* This is the function pointer type for drop callbacks. A drop
|
||||
* callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int path_count, const char* paths[])
|
||||
* int function_name(GLFWwindow* window, const char* mime, const char* text)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] path_count The number of dropped paths.
|
||||
* @param[in] paths The UTF-8 encoded file and/or directory path names.
|
||||
* @param[in] mime The UTF-8 encoded drop mime-type
|
||||
* @param[in] data The dropped data or NULL for drag enter events
|
||||
* @param[in] sz The size of the dropped data
|
||||
* @return For drag events should return the priority for the specified mime type. A priority of zero
|
||||
* or lower means the mime type is not accepted. Highest priority will be the finally accepted mime-type.
|
||||
*
|
||||
* @pointer_lifetime The path array and its strings are valid until the
|
||||
* @pointer_lifetime The text is valid until the
|
||||
* callback function returns.
|
||||
*
|
||||
* @sa @ref path_drop
|
||||
@ -1375,7 +1378,7 @@ typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWdropfun)(GLFWwindow*,int,const char*[]);
|
||||
typedef int (* GLFWdropfun)(GLFWwindow*, const char *, const char*, size_t);
|
||||
|
||||
typedef void (* GLFWliveresizefun)(GLFWwindow*, bool);
|
||||
|
||||
@ -1747,14 +1750,14 @@ typedef void (*glfwSetWindowSizeLimits_func)(GLFWwindow*, int, int, int, int);
|
||||
glfwSetWindowSizeLimits_func glfwSetWindowSizeLimits_impl;
|
||||
#define glfwSetWindowSizeLimits glfwSetWindowSizeLimits_impl
|
||||
|
||||
typedef void (*glfwSetWindowAspectRatio_func)(GLFWwindow*, int, int);
|
||||
glfwSetWindowAspectRatio_func glfwSetWindowAspectRatio_impl;
|
||||
#define glfwSetWindowAspectRatio glfwSetWindowAspectRatio_impl
|
||||
|
||||
typedef void (*glfwSetWindowSizeIncrements_func)(GLFWwindow*, int, int);
|
||||
glfwSetWindowSizeIncrements_func glfwSetWindowSizeIncrements_impl;
|
||||
#define glfwSetWindowSizeIncrements glfwSetWindowSizeIncrements_impl
|
||||
|
||||
typedef void (*glfwSetWindowAspectRatio_func)(GLFWwindow*, int, int);
|
||||
glfwSetWindowAspectRatio_func glfwSetWindowAspectRatio_impl;
|
||||
#define glfwSetWindowAspectRatio glfwSetWindowAspectRatio_impl
|
||||
|
||||
typedef void (*glfwSetWindowSize_func)(GLFWwindow*, int, int);
|
||||
glfwSetWindowSize_func glfwSetWindowSize_impl;
|
||||
#define glfwSetWindowSize glfwSetWindowSize_impl
|
||||
|
||||
27
kitty/glfw.c
27
kitty/glfw.c
@ -320,17 +320,26 @@ window_focus_callback(GLFWwindow *w, int focused) {
|
||||
global_state.callback_os_window = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
drop_callback(GLFWwindow *w, int count, const char **strings) {
|
||||
if (!set_callback_window(w)) return;
|
||||
PyObject *s = PyTuple_New(count);
|
||||
if (s) {
|
||||
for (int i = 0; i < count; i++) PyTuple_SET_ITEM(s, i, PyUnicode_FromString(strings[i]));
|
||||
WINDOW_CALLBACK(on_drop, "O", s);
|
||||
Py_CLEAR(s);
|
||||
request_tick_callback();
|
||||
static int
|
||||
drop_callback(GLFWwindow *w, const char *mime, const char *data, size_t sz) {
|
||||
if (!set_callback_window(w)) return 0;
|
||||
if (!data) {
|
||||
if (strcmp(mime, "text/uri-list") == 0) return 3;
|
||||
if (strcmp(mime, "text/plain;charset=utf-8") == 0) return 2;
|
||||
if (strcmp(mime, "text/plain") == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
WINDOW_CALLBACK(on_drop, "sy#", mime, data, (int)sz);
|
||||
request_tick_callback();
|
||||
/* PyObject *s = PyTuple_New(count); */
|
||||
/* if (s) { */
|
||||
/* for (int i = 0; i < count; i++) PyTuple_SET_ITEM(s, i, PyUnicode_FromString(strings[i])); */
|
||||
/* WINDOW_CALLBACK(on_drop, "O", s); */
|
||||
/* Py_CLEAR(s); */
|
||||
/* request_tick_callback(); */
|
||||
/* } */
|
||||
global_state.callback_os_window = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
@ -532,3 +532,21 @@ def read_shell_environment(opts: Optional[Options] = None) -> Dict[str, str]:
|
||||
else:
|
||||
log_error('Failed to run shell to read its environment')
|
||||
return ans
|
||||
|
||||
|
||||
def parse_uri_list(text: str) -> Generator[str, None, None]:
|
||||
' Get paths from file:// URLs '
|
||||
from urllib.parse import urlparse, unquote
|
||||
for line in text.splitlines():
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
if not line.startswith('file://'):
|
||||
yield line
|
||||
continue
|
||||
try:
|
||||
purl = urlparse(line, allow_fragments=False)
|
||||
except Exception:
|
||||
yield line
|
||||
continue
|
||||
if purl.path:
|
||||
yield unquote(purl.path)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user