input: report mouse drag events also when the pointer is outside the grid

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.
This commit is contained in:
Daniel Eklöf 2020-12-11 20:48:43 +01:00
parent 975e70dae1
commit a1a0b489ee
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
3 changed files with 40 additions and 4 deletions

30
input.c
View file

@ -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;