st

fork of suckless's simple terminal
Index Commits Files Refs README LICENSE
commit ea7cd7b62fdfa6a1fbd882d1565d557577f2cf32
parent 7fb0c0cc681f36be2ad12091ef93a41671f32738
Author: robert <robertrussell.72001@gmail.com>
Date:   Sat,  8 Jan 2022 11:40:34 -0800

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.

Diffstat:
Mx.c | 86+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
1 file changed, 51 insertions(+), 35 deletions(-)
diff --git a/x.c b/x.c
@@ -252,7 +252,7 @@ static char *opt_line  = NULL;
 static char *opt_name  = NULL;
 static char *opt_title = NULL;
 
-static int oldbutton = 3; /* button event on startup: 3 = release */
+static uint buttons; /* bit field of pressed buttons */
 
 void
 clipcopy(const Arg *dummy)
@@ -364,61 +364,68 @@ 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)
-            + ((state & Mod4Mask   ) ? 8  : 0)
-            + ((state & ControlMask) ? 16 : 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;
     }
@@ -461,9 +468,13 @@ mouseaction(XEvent *e, uint release)
 void
 bpress(XEvent *e)
 {
+    int btn = e->xbutton.button;
     struct timespec now;
     int snap;
 
+    if (1 <= btn && btn <= 11)
+        buttons |= 1 << (btn-1);
+
     if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
         mousereport(e);
         return;
@@ -472,7 +483,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.
@@ -686,6 +697,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;
@@ -693,7 +709,7 @@ brelease(XEvent *e)
 
     if (mouseaction(e, 1))
         return;
-    if (e->xbutton.button == Button1)
+    if (btn == Button1)
         mousesel(e, 1);
 }