From a1a0b489eec73b8e4823cf210fbebfe1fd1d1efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 11 Dec 2020 20:48:43 +0100 Subject: [PATCH] input: report mouse drag events also when the pointer is outside the grid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As long as the mouse button was *pressed* while the pointer was inside the grid, we want to keep reporting motion events until the button is released. Even when the pointer moves outside the grid (but in this case, the reported coordinates are bounded by the grid size). This patch also tries to improve multi-button handling (i.e. multiple buttons pressed at the same time), and the events we report to the client for these, in the following ways: * Motion events now report the *initial* button. That is, if you start a drag operation with the LEFT button, then press RIGHT (before releasing LEFT), keep reporting LEFT in the motion events. * Mouse release events are reported for *any* button, as long as the pointer is *inside* the grid, *or*, the button released was the button used to start a drag operation. The last point is important; if we have reported a button press followed by motion events (i.e. a drag operation), we need to report the button release, *even* if the pointer is outside the grid. Note that the client may receive unbalanced button press/release events in the following ways if the user pressed one, and then a second button *inside* the grid, then releases the *first* button (possibly outside the grid), and finally releases the *second* button *outside* the grid. In this case, both buttons will report press events. The first button will report a release event since it is the initial button in the drag operation. However, we don’t track the fact that the second button is being pressed, and thus if it is released outside the grid, it wont generate a release event. --- CHANGELOG.md | 2 ++ input.c | 30 ++++++++++++++++++++++++++---- wayland.h | 12 ++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ea6b8aa..caf1a73d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -123,6 +123,8 @@ means foot can be PGO:d in e.g. sandboxed build scripts. See (https://codeberg.org/dnkl/foot/issues/233). * `\E[s`+`\E[u` (save/restore cursor) now saves and restores attributes and charset configuration, just like `\E7`+`\E8`. +* Report mouse motion events to the client application also while + dragging the cursor outside the grid. ### Security diff --git a/input.c b/input.c index d457fb4c..bee0bbbd 100644 --- a/input.c +++ b/input.c @@ -1250,7 +1250,7 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, /* Reset mouse state */ seat->mouse.x = seat->mouse.y = 0; seat->mouse.col = seat->mouse.row = 0; - seat->mouse.button = seat->mouse.last_button = seat->mouse.count = 0; + seat->mouse.button = seat->mouse.last_button = seat->mouse.button_for_motion_events = seat->mouse.count = 0; seat->mouse.consumed = false; memset(&seat->mouse.last_time, 0, sizeof(seat->mouse.last_time)); seat->mouse.axis_aggregated = 0.0; @@ -1383,6 +1383,16 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, selection_row = seat->mouse.row; } + /* + * If client is receiving events (because the button was + * pressed while the cursor was inside the grid area), then + * make sure it receives valid coordinates. + */ + if (seat->mouse.button_for_motion_events > 0) { + seat->mouse.col = selection_col; + seat->mouse.row = selection_row; + } + assert(seat->mouse.col == -1 || (seat->mouse.col >= 0 && seat->mouse.col < term->cols)); assert(seat->mouse.row == -1 || (seat->mouse.row >= 0 && seat->mouse.row < term->rows)); @@ -1440,13 +1450,18 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, /* Send mouse event to client application */ if (!seat->mouse.consumed && !term_mouse_grabbed(term, seat) && - cursor_is_on_new_cell && cursor_is_on_grid) + cursor_is_on_new_cell && + (seat->mouse.button_for_motion_events > 0 || + (seat->mouse.button == 0 && cursor_is_on_grid))) { assert(seat->mouse.col < term->cols); assert(seat->mouse.row < term->rows); term_mouse_motion( - term, seat->mouse.button, seat->mouse.row, seat->mouse.col, + term, + (seat->mouse.button_for_motion_events > 0 + ? seat->mouse.button_for_motion_events : seat->mouse.button), + seat->mouse.row, seat->mouse.col, seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl); } break; @@ -1702,6 +1717,9 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, term_mouse_down( term, button, seat->mouse.row, seat->mouse.col, seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl); + + if (seat->mouse.button_for_motion_events == 0) + seat->mouse.button_for_motion_events = button; } break; } @@ -1711,11 +1729,15 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, if (!seat->mouse.consumed && !term_mouse_grabbed(term, seat) && - cursor_is_on_grid) + ((cursor_is_on_grid && seat->mouse.button_for_motion_events > 0) || + seat->mouse.button_for_motion_events == button)) { term_mouse_up( term, button, seat->mouse.row, seat->mouse.col, seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl); + + if (seat->mouse.button_for_motion_events == button) + seat->mouse.button_for_motion_events = 0; } seat->mouse.consumed = false; diff --git a/wayland.h b/wayland.h index ac61c6ff..16678496 100644 --- a/wayland.h +++ b/wayland.h @@ -202,6 +202,18 @@ struct seat { int col; int row; int button; + + /* + * Button to send in motion events to the client. This is + * always the *first* button pressed on the grid. If multiple + * buttons are pressed, the first button is still the one used + * in motion events. + * + * A non-zero value of this *also* indicates that the client + * should receive events even if the pointer is outside the + * grid. + */ + int button_for_motion_events; bool consumed; /* True if a button press was consumed - i.e. if a binding claimed it */ int count;