Fix mousereport

This patch replaces the previous one I sent.

The following changes are made in this patch:
 - Fix tracking of pressed buttons. Previously, pressing two buttons and
   then releasing one would make st think no buttons are pressed, which
   in particular broke MODE_MOUSEMOTION.
 - Always send the lowest-numbered pressed button on motion events; when
   no button is pressed for a motion event in MODE_MOUSEMANY, then send
   a release. This matches the behaviour of xterm. (Previously, st sent
   the most recently pressed button in the motion report.)
 - Remove UB (?) access to potentially inactive struct member
   e->xbutton.button of XEvent union.
 - Fix (unlikely) possibility of overflow for large button numbers.

The one discrepancy I found between st and xterm is that xterm sometimes
encodes buttons with large numbers (>5) strangely. E.g., xterm reports
presses of buttons 8 and 9 as releases, whereas st properly (?) encodes
them as presses.

Ref.
   - https://git.suckless.org/st/commit/ea7cd7b62fdfa6a1fbd882d1565d557577f2cf32.html
This commit is contained in:
bakkeby 2022-02-24 13:38:01 +01:00
parent b2dffe8a25
commit 5bf86e1ca2
1 changed files with 59 additions and 42 deletions

85
x.c
View File

@ -209,7 +209,7 @@ static char *opt_dir = NULL;
static int focused = 0;
#endif // ALPHA_FOCUS_HIGHLIGHT_PATCH
static int oldbutton = 3; /* button event on startup: 3 = release */
static uint buttons; /* bit field of pressed buttons */
#if BLINKING_CURSOR_PATCH
static int cursorblinks = 0;
#endif // BLINKING_CURSOR_PATCH
@ -395,61 +395,69 @@ mousesel(XEvent *e, int done)
void
mousereport(XEvent *e)
{
int len, x = evcol(e), y = evrow(e),
button = e->xbutton.button, state = e->xbutton.state;
int len, btn, code;
int x = evcol(e), y = evrow(e);
int state = e->xbutton.state;
char buf[40];
static int ox, oy;
/* from urxvt */
if (e->xbutton.type == MotionNotify) {
if (e->type == MotionNotify) {
if (x == ox && y == oy)
return;
if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY))
return;
/* MOUSE_MOTION: no reporting if no button is pressed */
if (IS_SET(MODE_MOUSEMOTION) && oldbutton == 3)
/* MODE_MOUSEMOTION: no reporting if no button is pressed */
if (IS_SET(MODE_MOUSEMOTION) && buttons == 0)
return;
button = oldbutton + 32;
ox = x;
oy = y;
/* Set btn to lowest-numbered pressed button, or 12 if no
* buttons are pressed. */
for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++)
;
code = 32;
} else {
if (!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) {
button = 3;
} else {
button -= Button1;
if (button >= 7)
button += 128 - 7;
else if (button >= 3)
button += 64 - 3;
}
if (e->xbutton.type == ButtonPress) {
oldbutton = button;
ox = x;
oy = y;
} else if (e->xbutton.type == ButtonRelease) {
oldbutton = 3;
btn = e->xbutton.button;
/* Only buttons 1 through 11 can be encoded */
if (btn < 1 || btn > 11)
return;
if (e->type == ButtonRelease) {
/* MODE_MOUSEX10: no button release reporting */
if (IS_SET(MODE_MOUSEX10))
return;
if (button == 64 || button == 65)
/* Don't send release events for the scroll wheel */
if (btn == 4 || btn == 5)
return;
}
code = 0;
}
ox = x;
oy = y;
/* Encode btn into code. If no button is pressed for a motion event in
* MODE_MOUSEMANY, then encode it as a release. */
if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12)
code += 3;
else if (btn >= 8)
code += 128 + btn - 8;
else if (btn >= 4)
code += 64 + btn - 4;
else
code += btn - 1;
if (!IS_SET(MODE_MOUSEX10)) {
button += ((state & ShiftMask ) ? 4 : 0)
code += ((state & ShiftMask ) ? 4 : 0)
+ ((state & Mod4Mask ) ? 8 : 0)
+ ((state & ControlMask) ? 16 : 0);
}
if (IS_SET(MODE_MOUSESGR)) {
len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
button, x+1, y+1,
e->xbutton.type == ButtonRelease ? 'm' : 'M');
code, x+1, y+1,
e->type == ButtonRelease ? 'm' : 'M');
} else if (x < 223 && y < 223) {
len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
32+button, 32+x+1, 32+y+1);
32+code, 32+x+1, 32+y+1);
} else {
return;
}
@ -460,11 +468,15 @@ mousereport(XEvent *e)
void
bpress(XEvent *e)
{
int btn = e->xbutton.button;
struct timespec now;
#if !VIM_BROWSE_PATCH
int snap;
#endif // VIM_BROWSE_PATCH
if (1 <= btn && btn <= 11)
buttons |= 1 << (btn-1);
if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
mousereport(e);
return;
@ -473,7 +485,7 @@ bpress(XEvent *e)
if (mouseaction(e, 0))
return;
if (e->xbutton.button == Button1) {
if (btn == Button1) {
/*
* If the user clicks below predefined timeouts specific
* snapping behaviour is exposed.
@ -722,6 +734,11 @@ xsetsel(char *str)
void
brelease(XEvent *e)
{
int btn = e->xbutton.button;
if (1 <= btn && btn <= 11)
buttons &= ~(1 << (btn-1));
if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
mousereport(e);
return;
@ -730,14 +747,14 @@ brelease(XEvent *e)
if (mouseaction(e, 1))
return;
#if VIM_BROWSE_PATCH
if (e->xbutton.button == Button1 && !IS_SET(MODE_NORMAL)) {
if (btn == Button1 && !IS_SET(MODE_NORMAL)) {
mousesel(e, 1);
#if OPENURLONCLICK_PATCH
openUrlOnClick(evcol(e), evrow(e), url_opener);
#endif // OPENURLONCLICK_PATCH
}
#else
if (e->xbutton.button == Button1) {
if (btn == Button1) {
mousesel(e, 1);
#if OPENURLONCLICK_PATCH
openUrlOnClick(evcol(e), evrow(e), url_opener);
@ -745,7 +762,7 @@ brelease(XEvent *e)
}
#endif // VIM_BROWSE_PATCH
#if RIGHTCLICKTOPLUMB_PATCH
else if (e->xbutton.button == Button3)
else if (btn == Button3)
plumb(xsel.primary);
#endif // RIGHTCLICKTOPLUMB_PATCH
}