mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-06-09 17:41:36 +00:00
Fix dnd kitten: restrict drop to boxes matching drag source allowed operations
Agent-Logs-Url: https://github.com/kovidgoyal/kitty/sessions/41b8254d-fc79-4f41-9775-67d1ddfceb5b Co-authored-by: kovidgoyal <1308621+kovidgoyal@users.noreply.github.com>
This commit is contained in:
parent
bfc3646868
commit
66dca3cde1
6 changed files with 73 additions and 20 deletions
|
|
@ -379,14 +379,15 @@ func parse_uri_list(src string) (ans []string, err error) {
|
|||
}
|
||||
|
||||
type drop_status struct {
|
||||
offered_mimes []string
|
||||
accepted_mimes []string
|
||||
uri_list []string
|
||||
cell_x, cell_y int
|
||||
action int
|
||||
in_window bool
|
||||
reading_data bool
|
||||
is_remote_client bool
|
||||
offered_mimes []string
|
||||
accepted_mimes []string
|
||||
uri_list []string
|
||||
cell_x, cell_y int
|
||||
action int
|
||||
in_window bool
|
||||
reading_data bool
|
||||
is_remote_client bool
|
||||
source_allowed_ops int
|
||||
|
||||
dropping_to *dir_handle
|
||||
root_remote_dir *remote_dir_entry
|
||||
|
|
@ -594,7 +595,7 @@ func (dnd *dnd) request_mime_data() {
|
|||
|
||||
var offered_mimes_buf strings.Builder
|
||||
|
||||
func (dnd *dnd) on_drop_move(cell_x, cell_y int, has_more bool, offered_mimes string, is_drop bool) (needs_rerender bool) {
|
||||
func (dnd *dnd) on_drop_move(cell_x, cell_y int, has_more bool, offered_mimes string, is_drop bool, source_allowed_ops int) (needs_rerender bool) {
|
||||
prev_status := dnd.drop_status
|
||||
dnd.drop_status.cell_x, dnd.drop_status.cell_y = cell_x, cell_y
|
||||
if offered_mimes != "" {
|
||||
|
|
@ -614,6 +615,9 @@ func (dnd *dnd) on_drop_move(cell_x, cell_y int, has_more bool, offered_mimes st
|
|||
}
|
||||
}
|
||||
offered_mimes_buf.Reset()
|
||||
if source_allowed_ops != 0 {
|
||||
dnd.drop_status.source_allowed_ops = source_allowed_ops
|
||||
}
|
||||
if dnd.copy_button_region.has(cell_x, cell_y) {
|
||||
dnd.drop_status.action = copy_on_drop
|
||||
} else if dnd.move_button_region.has(cell_x, cell_y) {
|
||||
|
|
@ -629,6 +633,13 @@ func (dnd *dnd) on_drop_move(cell_x, cell_y int, has_more bool, offered_mimes st
|
|||
dnd.drop_status.action = move_on_drop
|
||||
}
|
||||
}
|
||||
// Restrict to operations allowed by the drag source.
|
||||
if sao := dnd.drop_status.source_allowed_ops; sao != 0 && dnd.drop_status.action != 0 {
|
||||
if sao&dnd.drop_status.action == 0 {
|
||||
dnd.drop_status.action = 0
|
||||
dnd.drop_status.accepted_mimes = nil
|
||||
}
|
||||
}
|
||||
dnd.drop_status.in_window = cell_x > -1 && cell_y > -1
|
||||
if !dnd.drop_status.in_window || dnd.drag_status.active { // disallow self drag and drop
|
||||
dnd.reset_drop()
|
||||
|
|
|
|||
|
|
@ -259,11 +259,11 @@ func (dnd *dnd) run_loop() (err error) {
|
|||
if cmd.Payload != nil {
|
||||
payload = utils.UnsafeBytesToString(cmd.Payload)
|
||||
}
|
||||
if dnd.on_drop_move(cmd.X, cmd.Y, cmd.Has_more, payload, false) {
|
||||
if dnd.on_drop_move(cmd.X, cmd.Y, cmd.Has_more, payload, false, cmd.Operation) {
|
||||
dnd.render_screen()
|
||||
}
|
||||
case 'M':
|
||||
if dnd.on_drop_move(cmd.X, cmd.Y, cmd.Has_more, utils.UnsafeBytesToString(cmd.Payload), true) {
|
||||
if dnd.on_drop_move(cmd.X, cmd.Y, cmd.Has_more, utils.UnsafeBytesToString(cmd.Payload), true, cmd.Operation) {
|
||||
dnd.render_screen()
|
||||
}
|
||||
case 'R':
|
||||
|
|
|
|||
19
kitty/dnd.c
19
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) {
|
||||
drop_move_on_child(Window *w, const char** mimes, size_t num_mimes, bool is_drop, int allowed_ops) {
|
||||
if (!w->drop.hovered) {
|
||||
reset_drop(w);
|
||||
w->drop.hovered = true;
|
||||
|
|
@ -530,9 +530,15 @@ drop_move_on_child(Window *w, const char** mimes, size_t num_mimes, bool is_drop
|
|||
// we simply drop this event if there is too much data being written to the child
|
||||
if (w->drop.pending.count && !is_drop) return;
|
||||
char buf[128];
|
||||
int 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 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);
|
||||
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));
|
||||
|
|
@ -2409,7 +2415,8 @@ dnd_test_fake_drop_event(PyObject *self UNUSED, PyObject *args) {
|
|||
int is_drop;
|
||||
PyObject *mimes_seq = Py_None;
|
||||
int x = -2, y = -2;
|
||||
if (!PyArg_ParseTuple(args, "Kp|Oii", &window_id, &is_drop, &mimes_seq, &x, &y)) return NULL;
|
||||
int allowed_ops = 0;
|
||||
if (!PyArg_ParseTuple(args, "Kp|Oiii", &window_id, &is_drop, &mimes_seq, &x, &y, &allowed_ops)) return NULL;
|
||||
Window *w = window_for_window_id((id_type)window_id);
|
||||
if (!w) { PyErr_SetString(PyExc_ValueError, "Window not found"); return NULL; }
|
||||
if (mimes_seq == Py_None) {
|
||||
|
|
@ -2427,7 +2434,7 @@ 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);
|
||||
drop_move_on_child(w, mimes, (size_t)num_mimes, is_drop ? true : false, allowed_ops);
|
||||
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);
|
||||
void drop_move_on_child(Window *w, const char **mimes, size_t num_mimes, bool is_drop, int allowed_ops);
|
||||
void drop_left_child(Window *w);
|
||||
void drop_free_data(Window *w);
|
||||
void drop_send_einval(Window *w, const char *desc);
|
||||
|
|
|
|||
10
kitty/glfw.c
10
kitty/glfw.c
|
|
@ -802,7 +802,10 @@ drop_dest_callback(GLFWwindow *window, GLFWDropEvent *ev) {
|
|||
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_NONE;
|
||||
drop_move_on_child(w, ev->mimes, ev->num_mimes, false);
|
||||
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);
|
||||
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;
|
||||
|
|
@ -851,7 +854,10 @@ 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) {
|
||||
drop_move_on_child(w, ev->mimes, ev->num_mimes, true);
|
||||
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);
|
||||
ev->num_mimes = 0; // we wait for the client to request MIMEs
|
||||
} else {
|
||||
if (ev->from_self) {
|
||||
|
|
|
|||
|
|
@ -197,6 +197,35 @@ 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
|
||||
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
|
||||
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'))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue