Implement sending drag start message to client

This commit is contained in:
Kovid Goyal 2026-04-03 09:42:57 +05:30
parent cf52a8289c
commit 0584cbfadc
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
8 changed files with 60 additions and 15 deletions

View file

@ -257,7 +257,7 @@ image by sending::
Where ``idx`` is now a zero based index with zero being the first image and so on.
Once the terminal program has present all data and images for the drag
Once the terminal program has sent all data and images for the drag
operation, it indicates the drag should be started by sending ``t=P:x=-1``. At
this time if the user has already cancelled the drag or the terminal determines
the drag operation is not allowed, it must respond with ``t=R ; EPERM``. If any

View file

@ -854,3 +854,8 @@ drop_left_child(Window *w) {
queue_payload_to_child(w->id, w->drop.client_id, &w->drop.pending, buf, header_size, NULL, 0, false);
}
}
void
drag_free_offer(Window *w) {
(void)w;
}

View file

@ -21,3 +21,6 @@ size_t drop_update_mimes(Window *w, const char **allowed_mimes, size_t allowed_m
void drop_dispatch_data(Window *w, const char *mime_type, const char *data, ssize_t sz);
void drop_finish(Window *w);
void dnd_set_test_write_func(PyObject *func);
void drag_free_offer(Window *w);

View file

@ -1056,6 +1056,12 @@ free_drag_source(void) {
zero_at_ptr(&ds);
}
void
cancel_current_drag_source(void) {
if (!ds.from_os_window) return; OSWindow *w = os_window_for_id(ds.from_os_window); if (!w || !w->handle) return;
glfwStartDrag(w->handle, NULL, 0, NULL, -3, false);
}
static void
drag_source_callback(GLFWwindow *window UNUSED, GLFWDragEvent *ev) {
#define finish \
@ -3115,6 +3121,8 @@ change_drag_thumbnail(PyObject *self UNUSED, PyObject *args) {
if (!get_thumbnail(global_state.drag_source.thumbnails, &thumbnail, idx)) return NULL;
global_state.drag_source.thumbnail_idx = idx;
} else global_state.drag_source.thumbnail_idx = -1;
global_state.drag_source.from_os_window = w->id;
global_state.drag_source.from_window = 0;
errno = glfwStartDrag(w->handle, NULL, 0, thumbnail.pixels ? &thumbnail : NULL, -2, false);
if (errno != 0) {
PyErr_SetFromErrno(PyExc_OSError);

View file

@ -643,6 +643,10 @@ handle_scrollbar_mouse(Window *w, int button, MouseAction action, int modifiers
}
// }}}
static double
distance(double x1, double y1, double x2, double y2) {
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
HANDLER(handle_move_event) {
modifiers &= ~GLFW_LOCK_MASK;
@ -672,11 +676,13 @@ HANDLER(handle_move_event) {
int sz = encode_mouse_button(w, button, button >=0 ? DRAG : MOVE, modifiers);
if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf); }
}
}
static double
distance(double x1, double y1, double x2, double y2) {
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
if (w->drag_source.can_offer && w->drag_source.initial_left_press.at && distance(w->mouse_pos.global_x, w->mouse_pos.global_y, w->drag_source.initial_left_press.x, w->drag_source.initial_left_press.y) > OPT(drag_threshold)) {
zero_at_ptr(&w->drag_source.initial_left_press);
snprintf(mouse_event_buf, sizeof(mouse_event_buf), "%d;t=o:x=%d:y=%d:X=%d:Y=%d",
DND_CODE, w->mouse_pos.cell_x, w->mouse_pos.cell_y, (int)w->mouse_pos.global_x, (int)w->mouse_pos.global_y);
write_escape_code_to_child(screen, ESC_OSC, mouse_event_buf);
debug("Sent drag start event to child\n");
}
}
static void
@ -870,6 +876,15 @@ HANDLER(handle_button_event) {
bool a, b;
if (!set_mouse_position(w, &a, &b)) return;
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if (is_release) {
zero_at_ptr(&w->drag_source.initial_left_press);
} else if (w->drag_source.can_offer) {
w->drag_source.initial_left_press.x = w->mouse_pos.global_x;
w->drag_source.initial_left_press.y = w->mouse_pos.global_x;
w->drag_source.initial_left_press.at = monotonic();
}
}
id_type wid = w->id;
if (!dispatch_mouse_event(w, button, is_release ? -1 : 1, modifiers, screen->modes.mouse_tracking_mode != 0)) {
if (!suppress_child_forwarding && screen->modes.mouse_tracking_mode != 0) {

View file

@ -884,7 +884,7 @@
'''
)
opt('drag_threshold', '5', option_type='positive_int', long_text='''
opt('drag_threshold', '5', option_type='positive_int', ctype='int', long_text='''
The threshold distance the mouse must move to start a drag and drop. Dragging
works for tabs and windows. You can drag tabs to re-order them, detach
them into new OS Windows or move them to another OS Window. Similarly,

View file

@ -1530,16 +1530,23 @@ screen_handle_dnd_command(Screen *self, const DnDCommand *cmd, const uint8_t *pa
}
} break;
case 's': {
if (cmd->payload_sz)
drop_request_uri_data(w, (const char*)payload, cmd->payload_sz);
else
drop_send_einval(w);
if (cmd->payload_sz) drop_request_uri_data(w, (const char*)payload, cmd->payload_sz);
else drop_send_einval(w);
} break;
case 'd': {
if (cmd->cell_x > 0)
drop_handle_dir_request(w, (uint32_t)cmd->cell_x, cmd->cell_y);
else
drop_send_einval(w);
if (cmd->cell_x > 0) drop_handle_dir_request(w, (uint32_t)cmd->cell_x, cmd->cell_y);
else drop_send_einval(w);
} break;
case 'o': {
if (cmd->payload_sz > 0) ;
else w->drag_source.can_offer = true;
} break;
case 'O': {
drag_free_offer(w);
w->drag_source.can_offer = false;
if (global_state.drag_source.is_active && global_state.drag_source.from_window == w->id) {
cancel_current_drag_source();
}
} break;
}
}

View file

@ -153,6 +153,7 @@ typedef struct Options {
double momentum_scroll;
double window_drag_tolerance;
bool generate_256_palette;
int drag_threshold;
} Options;
typedef struct WindowLogoRenderData {
@ -284,6 +285,10 @@ typedef struct Window {
monotonic_t last_file_send_at; /* time of last successful file chunk write */
id_type file_send_timer; /* pending file-send retry timer, 0 = none */
} drop;
struct {
bool can_offer;
struct { double x, y; monotonic_t at; } initial_left_press;
} drag_source;
} Window;
typedef struct BorderRect {
@ -441,6 +446,7 @@ typedef struct GlobalState {
struct {
bool is_active, was_dropped, was_canceled, needs_toplevel_on_wayland;
id_type from_window, from_os_window;
char *accepted_mime_type;
int action, thumbnail_idx;
PyObject *drag_data, *thumbnails;
@ -571,3 +577,4 @@ bool current_framebuffer_is_ok(void);
void request_drop_status_update(OSWindow *osw);
void register_mimes_for_drop(OSWindow *w, const char **mimes, size_t sz);
void request_drop_data(OSWindow *w, id_type wid, const char* mime);
void cancel_current_drag_source(void);