On Sat, Jan 08, 2022 at 11:40:34AM -0800, robert wrote:
> 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.
> ---
>  x.c | 86 ++++++++++++++++++++++++++++++++++++-------------------------
>  1 file changed, 51 insertions(+), 35 deletions(-)
> 
> diff --git a/x.c b/x.c
> index 8a16faa..5f4ea73 100644
> --- 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);
>  }
>  
> -- 
> 2.17.1
> 
> 

Hi Robert,

Thanks for the patch, I've pushed it to the repo. I've tried to test all cases
and it works great.

-- 
Kind regards,
Hiltjo

Reply via email to