mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-07-03 11:12:30 +08:00
Properly pass source actions to drop events
Implemented for Wayland. Needs implementation for X11 and macOS
This commit is contained in:
2
glfw/glfw3.h
vendored
2
glfw/glfw3.h
vendored
@@ -1407,7 +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;
|
||||
struct { GLFWDragOperationType preferred; int allowed, source_actions; } 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
|
||||
|
||||
1
glfw/input.c
vendored
1
glfw/input.c
vendored
@@ -416,6 +416,7 @@ size_t _glfwInputDropEvent(_GLFWwindow *window, GLFWDropEventType type, double x
|
||||
.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,
|
||||
.operation.source_actions = window->drop_operation.source_actions,
|
||||
};
|
||||
window->callbacks.drop_event((GLFWwindow*)window, &ev);
|
||||
window->drop_operation.preferred = ev.operation.preferred; window->drop_operation.allowed = ev.operation.allowed;
|
||||
|
||||
2
glfw/internal.h
vendored
2
glfw/internal.h
vendored
@@ -461,7 +461,7 @@ struct _GLFWwindow
|
||||
#else
|
||||
const bool swaps_disallowed;
|
||||
#endif
|
||||
struct { GLFWDragOperationType preferred; int allowed; } drop_operation;
|
||||
struct { GLFWDragOperationType preferred; int allowed, source_actions; } drop_operation;
|
||||
|
||||
struct {
|
||||
GLFWwindowposfun pos;
|
||||
|
||||
10
glfw/wl_window.c
vendored
10
glfw/wl_window.c
vendored
@@ -2548,6 +2548,14 @@ update_drop_state(_GLFWWaylandDataOffer *d, _GLFWwindow* window, size_t accepted
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_drop_source_actions(_GLFWwindow *window, _GLFWWaylandDataOffer *offer) {
|
||||
window->drop_operation.source_actions = GLFW_DRAG_OPERATION_NONE;
|
||||
if (offer->source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) window->drop_operation.source_actions |= GLFW_DRAG_OPERATION_COPY;
|
||||
if (offer->source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) window->drop_operation.source_actions |= GLFW_DRAG_OPERATION_MOVE;
|
||||
if (offer->source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) window->drop_operation.source_actions |= GLFW_DRAG_OPERATION_GENERIC;
|
||||
}
|
||||
|
||||
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, wl_fixed_t y, struct wl_data_offer *id) {
|
||||
debug_input("Drop entered\n");
|
||||
@@ -2565,6 +2573,7 @@ drag_enter(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, uint
|
||||
double xpos = wl_fixed_to_double(x);
|
||||
double ypos = wl_fixed_to_double(y);
|
||||
if (reset_copy_mimes(offer)) {
|
||||
update_drop_source_actions(window, offer);
|
||||
size_t mime_count = _glfwInputDropEvent(
|
||||
window, GLFW_DROP_ENTER, xpos, ypos,
|
||||
offer->copy_mimes, offer->copy_mimes_count, offer->is_self_offer);
|
||||
@@ -2713,6 +2722,7 @@ motion(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, uint32_t
|
||||
double xpos = wl_fixed_to_double(x);
|
||||
double ypos = wl_fixed_to_double(y);
|
||||
if (reset_copy_mimes(offer)) {
|
||||
update_drop_source_actions(window, offer);
|
||||
size_t mime_count = _glfwInputDropEvent(
|
||||
window, GLFW_DROP_MOVE, xpos, ypos, offer->copy_mimes, offer->copy_mimes_count, offer->is_self_offer);
|
||||
update_drop_state(offer, window, mime_count);
|
||||
|
||||
21
kitty/dnd.c
21
kitty/dnd.c
@@ -505,7 +505,7 @@ drop_register_machine_id(Window *w, const uint8_t *machine_id, size_t sz) {
|
||||
}
|
||||
|
||||
void
|
||||
drop_move_on_child(Window *w, const char** mimes, size_t num_mimes, bool is_drop, int allowed_ops) {
|
||||
drop_move_on_child(Window *w, const char** mimes, size_t num_mimes, bool is_drop) {
|
||||
if (!w->drop.hovered) {
|
||||
reset_drop(w);
|
||||
w->drop.hovered = true;
|
||||
@@ -531,14 +531,13 @@ drop_move_on_child(Window *w, const char** mimes, size_t num_mimes, bool is_drop
|
||||
if (w->drop.pending.count && !is_drop) return;
|
||||
char buf[128];
|
||||
int header_size;
|
||||
if (allowed_ops)
|
||||
header_size = snprintf(buf, sizeof(buf), "\x1b]%d;t=%c:x=%u:y=%u:X=%d:Y=%d:o=%d", DND_CODE,
|
||||
is_drop ? 'M' : 'm', w->mouse_pos.cell_x, w->mouse_pos.cell_y,
|
||||
(int)w->mouse_pos.global_x, (int)w->mouse_pos.global_y, allowed_ops);
|
||||
else
|
||||
header_size = snprintf(buf, sizeof(buf), "\x1b]%d;t=%c:x=%u:y=%u:X=%d:Y=%d", DND_CODE,
|
||||
is_drop ? 'M' : 'm', w->mouse_pos.cell_x, w->mouse_pos.cell_y,
|
||||
(int)w->mouse_pos.global_x, (int)w->mouse_pos.global_y);
|
||||
int allowed_ops = 0;
|
||||
if (global_state.drop_dest.allowed_ops & GLFW_DRAG_OPERATION_COPY) allowed_ops |= 1;
|
||||
if (global_state.drop_dest.allowed_ops & GLFW_DRAG_OPERATION_MOVE) allowed_ops |= 2;
|
||||
if (global_state.drop_dest.allowed_ops & GLFW_DRAG_OPERATION_GENERIC) allowed_ops |= 3;
|
||||
header_size = snprintf(buf, sizeof(buf), "\x1b]%d;t=%c:x=%u:y=%u:X=%d:Y=%d:o=%d", DND_CODE,
|
||||
is_drop ? 'M' : 'm', w->mouse_pos.cell_x, w->mouse_pos.cell_y,
|
||||
(int)w->mouse_pos.global_x, (int)w->mouse_pos.global_y, allowed_ops);
|
||||
if (w->drop.offered_mimes_total_size) {
|
||||
const size_t mimes_total_size = 1 + w->drop.offered_mimes_total_size;
|
||||
RAII_ALLOC(char, mbuf, malloc(mimes_total_size));
|
||||
@@ -2434,7 +2433,9 @@ dnd_test_fake_drop_event(PyObject *self UNUSED, PyObject *args) {
|
||||
}
|
||||
if (x > -1) w->mouse_pos.cell_x = x;
|
||||
if (y > -1) w->mouse_pos.cell_y = y;
|
||||
drop_move_on_child(w, mimes, (size_t)num_mimes, is_drop ? true : false, allowed_ops);
|
||||
global_state.drop_dest.allowed_ops = allowed_ops;
|
||||
drop_move_on_child(w, mimes, (size_t)num_mimes, is_drop ? true : false);
|
||||
global_state.drop_dest.allowed_ops = 0;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ void dnd_query(Window *w, uint32_t client_id);
|
||||
|
||||
void drop_register_window(Window *w, const uint8_t *payload, size_t payload_sz, bool on, uint32_t client_id, bool more);
|
||||
void drop_register_machine_id(Window *w, const uint8_t *machine_id, size_t sz);
|
||||
void drop_move_on_child(Window *w, const char **mimes, size_t num_mimes, bool is_drop, int allowed_ops);
|
||||
void drop_move_on_child(Window *w, const char **mimes, size_t num_mimes, bool is_drop);
|
||||
void drop_left_child(Window *w);
|
||||
void drop_free_data(Window *w);
|
||||
void drop_send_einval(Window *w, const char *desc);
|
||||
|
||||
2
kitty/glfw-wrapper.h
generated
2
kitty/glfw-wrapper.h
generated
@@ -1135,7 +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;
|
||||
struct { GLFWDragOperationType preferred; int allowed, source_actions; } 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
|
||||
|
||||
21
kitty/glfw.c
21
kitty/glfw.c
@@ -800,12 +800,13 @@ drop_dest_callback(GLFWwindow *window, GLFWDropEvent *ev) {
|
||||
os_window->last_drag_event.x = (int)(ev->xpos * os_window->viewport_x_ratio);
|
||||
os_window->last_drag_event.y = (int)(ev->ypos * os_window->viewport_y_ratio);
|
||||
on_mouse_position_update(ev->xpos, ev->ypos);
|
||||
global_state.drop_dest.allowed_ops = ev->operation.source_actions;
|
||||
if (is_client_drop) {
|
||||
if (ev->type == GLFW_DROP_ENTER) w->drop.accepted_operation = GLFW_DRAG_OPERATION_NONE;
|
||||
int kitty_allowed_ops = 0;
|
||||
if (ev->operation.allowed & GLFW_DRAG_OPERATION_COPY) kitty_allowed_ops |= 1;
|
||||
if (ev->operation.allowed & GLFW_DRAG_OPERATION_MOVE) kitty_allowed_ops |= 2;
|
||||
drop_move_on_child(w, ev->mimes, ev->num_mimes, false, kitty_allowed_ops);
|
||||
if (!w->drop.initialized) {
|
||||
w->drop.initialized = true;
|
||||
w->drop.accepted_operation = GLFW_DRAG_OPERATION_NONE;
|
||||
}
|
||||
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;
|
||||
@@ -854,10 +855,7 @@ drop_dest_callback(GLFWwindow *window, GLFWDropEvent *ev) {
|
||||
global_state.drop_dest.client_window_data_request = 0;
|
||||
global_state.drop_dest.os_window_id = os_window->id;
|
||||
if (is_client_drop) {
|
||||
int kitty_allowed_ops = 0;
|
||||
if (ev->operation.allowed & GLFW_DRAG_OPERATION_COPY) kitty_allowed_ops |= 1;
|
||||
if (ev->operation.allowed & GLFW_DRAG_OPERATION_MOVE) kitty_allowed_ops |= 2;
|
||||
drop_move_on_child(w, ev->mimes, ev->num_mimes, true, kitty_allowed_ops);
|
||||
drop_move_on_child(w, ev->mimes, ev->num_mimes, true);
|
||||
ev->num_mimes = 0; // we wait for the client to request MIMEs
|
||||
} else {
|
||||
if (ev->from_self) {
|
||||
@@ -3121,7 +3119,10 @@ start_window_drag(Window *w, bool in_test_mode) {
|
||||
global_state.drag_source.thumbnail_idx = w->drag_source.img_idx < num_images ? (int)w->drag_source.img_idx : -1;
|
||||
global_state.tracked_drag_in_window = 0; // this is now an OS global drag
|
||||
if (in_test_mode) return 0;
|
||||
int ret = glfwStartDrag(osw->handle, items, w->drag_source.num_mimes, thumbnail.pixels ? &thumbnail : NULL, w->drag_source.allowed_operations, true);
|
||||
GLFWDragOperationType ops = GLFW_DRAG_OPERATION_NONE;
|
||||
if (w->drag_source.allowed_operations & 1) ops |= GLFW_DRAG_OPERATION_COPY;
|
||||
if (w->drag_source.allowed_operations & 2) ops |= GLFW_DRAG_OPERATION_MOVE;
|
||||
int ret = glfwStartDrag(osw->handle, items, w->drag_source.num_mimes, thumbnail.pixels ? &thumbnail : NULL, ops, true);
|
||||
if (ret != 0) free_drag_source();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ typedef struct Window {
|
||||
bool is_hovering;
|
||||
} scrollbar;
|
||||
struct {
|
||||
bool wanted, hovered, dropped, is_remote_client;
|
||||
bool wanted, hovered, dropped, is_remote_client, initialized;
|
||||
uint32_t client_id;
|
||||
char *registered_mimes;
|
||||
char *uri_list; size_t uri_list_sz;
|
||||
@@ -299,7 +299,7 @@ typedef struct Window {
|
||||
const char **offerred_mimes; size_t num_offerred_mimes, offered_mimes_total_size;
|
||||
|
||||
char *accepted_mimes; size_t accepted_mimes_sz;
|
||||
int accepted_operation; bool accept_in_progress;
|
||||
int accepted_operation, allowed_operations; bool accept_in_progress;
|
||||
char *getting_data_for_mime;
|
||||
|
||||
DirHandle *dir_handles; size_t num_dir_handles, dir_handles_capacity;
|
||||
@@ -507,6 +507,7 @@ typedef struct GlobalState {
|
||||
double x, y;
|
||||
size_t num_left;
|
||||
bool drop_has_happened;
|
||||
int allowed_ops;
|
||||
} drop_dest;
|
||||
|
||||
struct {
|
||||
|
||||
@@ -197,35 +197,6 @@ class TestDnDKitten(BaseTest):
|
||||
self.screen = None
|
||||
self.pty = None
|
||||
|
||||
def test_dnd_kitten_drop_allowed_ops(self):
|
||||
# Test that the drop destination respects the drag source's allowed operations.
|
||||
# When the drag source only allows copy (allowed_ops=1), dropping on the move
|
||||
# box must be rejected, and vice versa.
|
||||
self.finish_setup()
|
||||
copy, move = self.get_button_geometry()
|
||||
mimes = ['text/uri-list']
|
||||
wid = self.capture.window_id
|
||||
# allowed_ops=1 means copy-only in kitten format
|
||||
dnd_test_fake_drop_event(wid, False, mimes, copy[0] + 1, copy[1] + 1, 1)
|
||||
self.wait_for_state('drop_action', GLFW_DRAG_OPERATION_COPY)
|
||||
dnd_test_fake_drop_event(wid, False)
|
||||
dnd_test_fake_drop_event(wid, False, mimes, move[0] + 1, move[1] + 1, 1)
|
||||
self.wait_for_state('drop_action', 0) # GLFW_DRAG_OPERATION_NONE: source allows copy only, move box must reject
|
||||
dnd_test_fake_drop_event(wid, False)
|
||||
# allowed_ops=2 means move-only in kitten format
|
||||
dnd_test_fake_drop_event(wid, False, mimes, move[0] + 1, move[1] + 1, 2)
|
||||
self.wait_for_state('drop_action', GLFW_DRAG_OPERATION_MOVE)
|
||||
dnd_test_fake_drop_event(wid, False)
|
||||
dnd_test_fake_drop_event(wid, False, mimes, copy[0] + 1, copy[1] + 1, 2)
|
||||
self.wait_for_state('drop_action', 0) # GLFW_DRAG_OPERATION_NONE: source allows move only, copy box must reject
|
||||
dnd_test_fake_drop_event(wid, False)
|
||||
# allowed_ops=3 means both copy and move allowed (default)
|
||||
for b, expected in ((copy, GLFW_DRAG_OPERATION_COPY), (move, GLFW_DRAG_OPERATION_MOVE)):
|
||||
dnd_test_fake_drop_event(wid, False, mimes, b[0] + 1, b[1] + 1, 3)
|
||||
self.wait_for_state('drop_action', expected)
|
||||
dnd_test_fake_drop_event(wid, False)
|
||||
self.exit_kitten()
|
||||
|
||||
def test_dnd_kitten_drop(self):
|
||||
img_drop_path = 'images/image.png'
|
||||
self.finish_setup(cli_args=(f'--drop=image/png:{img_drop_path}', '--confirm-drop-overwrite'))
|
||||
|
||||
Reference in New Issue
Block a user