Wayland: set drop accepted operations

This commit is contained in:
Kovid Goyal 2026-04-07 08:33:12 +05:30
parent 7fd0475c20
commit dc55052f0c
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
8 changed files with 48 additions and 13 deletions

1
glfw/glfw3.h vendored
View file

@ -1407,6 +1407,7 @@ typedef struct GLFWDropEvent {
// Positions are only valid for GLFW_DROP_ENTER and GLFW_DROP_MOVE.
// They are in window co-ordinates same as for mouse events
double xpos, ypos;
struct { GLFWDragOperationType preferred; int allowed; } operation;
bool from_self; // Only valid upto GLFW_DROP_DROP
ssize_t (*read_data)(GLFWwindow *w, struct GLFWDropEvent* ev, char *buffer, size_t sz); // Only valid for GLFW_DROP_DATA_AVAILABLE
void (*finish_drop)(GLFWwindow *w, GLFWDragOperationType op); // Only valid for GLFW_DROP_DROP and GLFW_DROP_DATA_AVAILABLE

2
glfw/input.c vendored
View file

@ -415,8 +415,10 @@ size_t _glfwInputDropEvent(_GLFWwindow *window, GLFWDropEventType type, double x
.mimes=mimes, .type=type, .xpos=xpos, .ypos=ypos, .num_mimes=num_mimes, .from_self=from_self,
.read_data=type == GLFW_DROP_DATA_AVAILABLE ? _glfwPlatformReadAvailableDropData : NULL,
.finish_drop=type == GLFW_DROP_DATA_AVAILABLE || type == GLFW_DROP_DROP ? _glfwPlatformEndDrop : NULL,
.operation.allowed = window->drop_operation.allowed, .operation.preferred = window->drop_operation.preferred,
};
window->callbacks.drop_event((GLFWwindow*)window, &ev);
window->drop_operation.preferred = ev.operation.preferred; window->drop_operation.allowed = ev.operation.allowed;
return ev.num_mimes;
}

1
glfw/internal.h vendored
View file

@ -461,6 +461,7 @@ struct _GLFWwindow
#else
const bool swaps_disallowed;
#endif
struct { GLFWDragOperationType preferred; int allowed; } drop_operation;
struct {
GLFWwindowposfun pos;

2
glfw/wl_platform.h vendored
View file

@ -314,6 +314,8 @@ typedef struct _GLFWWaylandDataOffer
const char **copy_mimes; // Working copy passed to callbacks; pointers into mimes[]
size_t copy_mimes_count; // Count of entries in copy_mimes (accepted count after callback)
bool drag_accepted, dropped;
enum wl_data_device_manager_dnd_action preferred;
int allowed;
uint32_t serial;
struct {
id_type watch_id;

20
glfw/wl_window.c vendored
View file

@ -2510,14 +2510,25 @@ static void handle_primary_selection_offer(void *data UNUSED, struct zwp_primary
// Helper function to update drop state from callback results
static void
update_drop_state(_GLFWWaylandDataOffer *d, _GLFWwindow* window UNUSED, size_t accepted_count) {
update_drop_state(_GLFWWaylandDataOffer *d, _GLFWwindow* window, size_t accepted_count) {
d->copy_mimes_count = accepted_count;
bool accepted = accepted_count > 0;
bool acceptance_changed = (accepted != d->drag_accepted);
// The first entry in the accepted (sorted) copy is the preferred MIME.
const char* new_preferred_mime = (accepted && d->copy_mimes) ? d->copy_mimes[0] : NULL;
bool mime_changed = false;
enum wl_data_device_manager_dnd_action preferred = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; int allowed = 0;
if (accepted && window->drop_operation.allowed) {
if (window->drop_operation.allowed & GLFW_DRAG_OPERATION_GENERIC) allowed |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
if (window->drop_operation.allowed & GLFW_DRAG_OPERATION_COPY) allowed |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
if (window->drop_operation.allowed & GLFW_DRAG_OPERATION_MOVE) allowed |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
switch (window->drop_operation.preferred) {
case GLFW_DRAG_OPERATION_GENERIC: preferred = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; break;
case GLFW_DRAG_OPERATION_COPY: preferred = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; break;
case GLFW_DRAG_OPERATION_MOVE: preferred = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; break;
case GLFW_DRAG_OPERATION_NONE: break;
}
}
// Check if the preferred MIME changed
if (d->mime_for_drop == NULL && new_preferred_mime != NULL) {
mime_changed = true;
@ -2526,11 +2537,14 @@ update_drop_state(_GLFWWaylandDataOffer *d, _GLFWwindow* window UNUSED, size_t a
} else if (d->mime_for_drop != NULL && new_preferred_mime != NULL) {
mime_changed = (strcmp(d->mime_for_drop, new_preferred_mime) != 0);
}
const bool op_changed = preferred != d->preferred || allowed != d->allowed;
if (acceptance_changed || mime_changed) {
if (acceptance_changed || mime_changed || op_changed) {
d->drag_accepted = accepted;
d->mime_for_drop = new_preferred_mime;
d->preferred = preferred; d->allowed = allowed;
wl_data_offer_accept(d->id, d->serial, d->mime_for_drop);
wl_data_offer_set_actions(d->id, d->allowed, d->preferred);
}
}

View file

@ -305,10 +305,11 @@ drop_move_on_child(Window *w, const char** mimes, size_t num_mimes, bool is_drop
void
drop_set_status(Window *w, int operation, const char *payload, size_t payload_sz, bool more) {
if (!w->drop.accept_in_progress) {
drop_free_accepted_mimes(w); w->drop.accept_in_progress = true; w->drop.accepted_operation = 0;
drop_free_accepted_mimes(w); w->drop.accept_in_progress = true;
switch(operation) {
case 1: case 2: w->drop.accepted_operation = operation; break;
default: w->drop.accepted_operation = 0; break;
case 1: w->drop.accepted_operation = GLFW_DRAG_OPERATION_COPY; break;
case 2: w->drop.accepted_operation = GLFW_DRAG_OPERATION_MOVE; break;
default: w->drop.accepted_operation = GLFW_DRAG_OPERATION_NONE; break;
}
}
if (payload_sz) {
@ -335,17 +336,14 @@ void
drop_finish(Window *w) {
OSWindow *osw = os_window_for_kitty_window(w->id);
if (osw && osw->handle) {
int op = GLFW_DRAG_OPERATION_GENERIC;
if (w->drop.accepted_operation == 1) op = GLFW_DRAG_OPERATION_COPY;
else if (w->drop.accepted_operation == 2) op = GLFW_DRAG_OPERATION_MOVE;
glfwEndDrop(osw->handle, op);
glfwEndDrop(osw->handle, w->drop.accepted_operation);
}
}
size_t
drop_update_mimes(Window *w, const char **allowed_mimes, size_t allowed_mimes_count) {
if (w->drop.accept_in_progress) return allowed_mimes_count;
if (!w->drop.accepted_operation) return 0;
if (w->drop.accepted_operation == GLFW_DRAG_OPERATION_NONE) return 0;
typedef struct mime_sorter { const char *m; ssize_t key; } mime_sorter;
if (!w->drop.accepted_mimes) return allowed_mimes_count;
RAII_ALLOC(mime_sorter, ms, malloc(sizeof(mime_sorter) * allowed_mimes_count));

1
kitty/glfw-wrapper.h generated
View file

@ -1135,6 +1135,7 @@ typedef struct GLFWDropEvent {
// Positions are only valid for GLFW_DROP_ENTER and GLFW_DROP_MOVE.
// They are in window co-ordinates same as for mouse events
double xpos, ypos;
struct { GLFWDragOperationType preferred; int allowed; } operation;
bool from_self; // Only valid upto GLFW_DROP_DROP
ssize_t (*read_data)(GLFWwindow *w, struct GLFWDropEvent* ev, char *buffer, size_t sz); // Only valid for GLFW_DROP_DATA_AVAILABLE
void (*finish_drop)(GLFWwindow *w, GLFWDragOperationType op); // Only valid for GLFW_DROP_DROP and GLFW_DROP_DATA_AVAILABLE

View file

@ -801,8 +801,11 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
os_window->last_drag_event.y = (int)(ev->ypos * os_window->viewport_y_ratio);
on_mouse_position_update(ev->xpos, ev->ypos);
if (is_client_drop) {
if (ev->type == GLFW_DROP_ENTER) w->drop.accepted_operation = GLFW_DRAG_OPERATION_COPY;
drop_move_on_child(w, ev->mimes, ev->num_mimes, false);
ev->num_mimes = drop_update_mimes(w, ev->mimes, ev->num_mimes);
ev->operation.allowed = w->drop.accepted_operation;
ev->operation.preferred = w->drop.accepted_operation;
return;
}
call_boss(on_drop_move, "KiiOO",
@ -810,8 +813,21 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
ev->from_self ? Py_True : Py_False, Py_False);
/* fallthrough */
case GLFW_DROP_STATUS_UPDATE:
if (is_client_drop) ev->num_mimes = drop_update_mimes(w, ev->mimes, ev->num_mimes);
else update_allowed_mimes_for_drop(ev);
if (is_client_drop) {
ev->num_mimes = drop_update_mimes(w, ev->mimes, ev->num_mimes);
ev->operation.allowed = w->drop.accepted_operation;
ev->operation.preferred = w->drop.accepted_operation;
} else {
update_allowed_mimes_for_drop(ev);
if (ev->num_mimes == 0) zero_at_ptr(&ev->operation);
else if (is_kitty_ui_drag) {
ev->operation.preferred = GLFW_DRAG_OPERATION_MOVE;
ev->operation.allowed = GLFW_DRAG_OPERATION_MOVE;
} else {
ev->operation.preferred = GLFW_DRAG_OPERATION_MOVE;
ev->operation.allowed = GLFW_DRAG_OPERATION_MOVE;
}
}
break;
case GLFW_DROP_LEAVE:
for (size_t tc = 0; tc < os_window->num_tabs; tc++) {