csd: track pointer when rendering and handling CSD button clicks

* Render button as highlighted only when pointer is above them
* Releasing the mouse button while *not* on the button does *not*
  activate the button

When pressing, and holding, a mouse button, the compositor keeps
sending motion events for the surface where the button was pressed,
even if the mouse has moved outside it.

We also don't get any surface leave/enter events.

This meant that the button was rendered as highlighted, and a click
registered, if the user pressed the mouse button while on the button,
and then moved the cursor away from the button before releasing the
button.

This patch fixes it, by checking if the current cursor coordinates
fall within the button surface.
This commit is contained in:
Daniel Eklöf 2024-08-09 08:20:59 +02:00
parent 7ec9ca2b95
commit bef613e656
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 98 additions and 12 deletions

75
input.c
View file

@ -2053,6 +2053,25 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
}
}
static bool
pointer_is_on_button(const struct terminal *term, const struct seat *seat,
enum csd_surface csd_surface)
{
if (seat->mouse.x < 0)
return false;
if (seat->mouse.y < 0)
return false;
struct csd_data info = get_csd_data(term, csd_surface);
if (seat->mouse.x > info.width)
return false;
if (seat->mouse.y > info.height)
return false;
return true;
}
static void
wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
@ -2085,16 +2104,42 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
int x = wl_fixed_to_int(surface_x) * term->scale;
int y = wl_fixed_to_int(surface_y) * term->scale;
enum term_surface surf_kind = term->active_surface;
int button = 0;
bool send_to_client = false;
bool is_on_button = false;
/* If current surface is a button, check if pointer was on it
*before* the motion event */
switch (surf_kind) {
case TERM_SURF_BUTTON_MINIMIZE:
is_on_button = pointer_is_on_button(term, seat, CSD_SURF_MINIMIZE);
break;
case TERM_SURF_BUTTON_MAXIMIZE:
is_on_button = pointer_is_on_button(term, seat, CSD_SURF_MAXIMIZE);
break;
case TERM_SURF_BUTTON_CLOSE:
is_on_button = pointer_is_on_button(term, seat, CSD_SURF_CLOSE);
break;
case TERM_SURF_NONE:
case TERM_SURF_GRID:
case TERM_SURF_TITLE:
case TERM_SURF_BORDER_LEFT:
case TERM_SURF_BORDER_RIGHT:
case TERM_SURF_BORDER_TOP:
case TERM_SURF_BORDER_BOTTOM:
break;
}
seat->pointer.hidden = false;
seat->mouse.x = x;
seat->mouse.y = y;
term_xcursor_update_for_seat(term, seat);
enum term_surface surf_kind = term->active_surface;
int button = 0;
bool send_to_client = false;
if (tll_length(seat->mouse.buttons) > 0) {
const struct button_tracker *tracker = &tll_front(seat->mouse.buttons);
surf_kind = tracker->surf_kind;
@ -2104,9 +2149,21 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
switch (surf_kind) {
case TERM_SURF_NONE:
break;
case TERM_SURF_BUTTON_MINIMIZE:
if (pointer_is_on_button(term, seat, CSD_SURF_MINIMIZE) != is_on_button)
render_refresh_csd(term);
break;
case TERM_SURF_BUTTON_MAXIMIZE:
if (pointer_is_on_button(term, seat, CSD_SURF_MAXIMIZE) != is_on_button)
render_refresh_csd(term);
break;
case TERM_SURF_BUTTON_CLOSE:
if (pointer_is_on_button(term, seat, CSD_SURF_CLOSE) != is_on_button)
render_refresh_csd(term);
break;
case TERM_SURF_TITLE:
@ -2246,8 +2303,8 @@ fdm_csd_move(struct fdm *fdm, int fd, int events, void *data)
}
static const struct key_binding *
match_mouse_binding(const struct seat *seat, const struct terminal *term,
int button)
match_mouse_binding(const struct seat *seat, const struct terminal *term,
int button)
{
if (seat->wl_keyboard != NULL && seat->kbd.xkb_state != NULL) {
/* Seat has keyboard - use mouse bindings *with* modifiers */
@ -2578,7 +2635,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
case TERM_SURF_BUTTON_MINIMIZE:
if (button == BTN_LEFT &&
term->active_surface == TERM_SURF_BUTTON_MINIMIZE &&
pointer_is_on_button(term, seat, CSD_SURF_MINIMIZE) &&
state == WL_POINTER_BUTTON_STATE_RELEASED)
{
xdg_toplevel_set_minimized(term->window->xdg_toplevel);
@ -2587,7 +2644,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
case TERM_SURF_BUTTON_MAXIMIZE:
if (button == BTN_LEFT &&
term->active_surface == TERM_SURF_BUTTON_MAXIMIZE &&
pointer_is_on_button(term, seat, CSD_SURF_MAXIMIZE) &&
state == WL_POINTER_BUTTON_STATE_RELEASED)
{
if (term->window->is_maximized)
@ -2599,7 +2656,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
case TERM_SURF_BUTTON_CLOSE:
if (button == BTN_LEFT &&
term->active_surface == TERM_SURF_BUTTON_CLOSE &&
pointer_is_on_button(term, seat, CSD_SURF_CLOSE) &&
state == WL_POINTER_BUTTON_STATE_RELEASED)
{
term_shutdown(term);

View file

@ -2627,6 +2627,32 @@ render_csd_button_close(struct terminal *term, struct buffer *buf)
pixman_image_unref(src);
}
static bool
any_pointer_is_on_button(const struct terminal *term, enum csd_surface csd_surface)
{
if (unlikely(tll_length(term->wl->seats) == 0))
return false;
tll_foreach(term->wl->seats, it) {
const struct seat *seat = &it->item;
if (seat->mouse.x < 0)
continue;
if (seat->mouse.y < 0)
continue;
struct csd_data info = get_csd_data(term, csd_surface);
if (seat->mouse.x > info.width)
continue;
if (seat->mouse.y > info.height)
continue;
return true;
}
return false;
}
static void
render_csd_button(struct terminal *term, enum csd_surface surf_idx,
const struct csd_data *info, struct buffer *buf)
@ -2650,21 +2676,24 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx,
_color = term->conf->colors.table[4]; /* blue */
is_set = term->conf->csd.color.minimize_set;
conf_color = &term->conf->csd.color.minimize;
is_active = term->active_surface == TERM_SURF_BUTTON_MINIMIZE;
is_active = term->active_surface == TERM_SURF_BUTTON_MINIMIZE &&
any_pointer_is_on_button(term, CSD_SURF_MINIMIZE);
break;
case CSD_SURF_MAXIMIZE:
_color = term->conf->colors.table[2]; /* green */
is_set = term->conf->csd.color.maximize_set;
conf_color = &term->conf->csd.color.maximize;
is_active = term->active_surface == TERM_SURF_BUTTON_MAXIMIZE;
is_active = term->active_surface == TERM_SURF_BUTTON_MAXIMIZE &&
any_pointer_is_on_button(term, CSD_SURF_MAXIMIZE);
break;
case CSD_SURF_CLOSE:
_color = term->conf->colors.table[1]; /* red */
is_set = term->conf->csd.color.close_set;
conf_color = &term->conf->csd.color.quit;
is_active = term->active_surface == TERM_SURF_BUTTON_CLOSE;
is_active = term->active_surface == TERM_SURF_BUTTON_CLOSE &&
any_pointer_is_on_button(term, CSD_SURF_CLOSE);
break;
default: