On Tue, Sep 09, 2025 at 11:12:48PM +0000, Robin Haberkorn wrote:
> Hello,
> 
> here are the mouse-related fixes I was discussing earlier.
> See commit messages for more details.

thanks - will review and see where we're disagreeing :-)
 
> Is there a real version control system for ncurses or are you working
> will tarballs and patch series only?

https://invisible-island.net/personal/git-exports.html
 
> These fixes will probably take many years to become widespread on many
> installations. Unfortunately, I did not find workarounds that will fully
> solve the issues. You should not reset the mouse mask unless you have to to
> any avoid additional bogus BUTTON3_PRESSED events when scrolling
> horizontally.
> Also, you could ignore BUTTON2_PRESSED and react only to BUTTON2_RELEASED.
> You will inevitably loose the separation between PRESSED and RELEASED events
> even on terminal emulators that do deliver them separately.
> 
> Best regards,
> Robin
> 
> -- 
> @ii._._.*.._+__.+_+.+...+.+.++..+*+.+._.+...*_*.*.__+__._._.++..+_*.++__+__
> .+_..*...+.+_+__.+._.+...*_+_+__._ ...*_ +.+._.+.._+*+_+__._._ .+_..+.+***_
> . *_+_+__.+.*.++..+_+.*.__+_ _.+...*_*_+__.++*.+...++..+* +.+.._+__._+_.+..
> .++..+*_.*...+*+.+.*_ +*+i2^rj.u#__%uu#_.%uu#_+%uu#_*!+!0a"t1010^t^c^c'0a^#
> 1010"=d'0a-100000"=d'0auuqq*100+q[_^euu]uq-rq:^/100@oo,+,+,+oqq^t0uq@o*+*!!

> From 7a335729a68f24192be14c73f1b00f3bc69702df Mon Sep 17 00:00:00 2001
> From: Robin Haberkorn <[email protected]>
> Date: Wed, 10 Sep 2025 00:25:17 +0300
> Subject: [PATCH 1/2] fixed horizontal mouse scroll events
> 
> These can be delivered by Xterm and GNOME Terminal
> in SGR mouse mode.
> They were erroneously reported as BUTTON3_PRESSED.
> ---
>  ncurses/base/lib_mouse.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/ncurses/base/lib_mouse.c b/ncurses/base/lib_mouse.c
> index 65cb005f..06748890 100644
> --- a/ncurses/base/lib_mouse.c
> +++ b/ncurses/base/lib_mouse.c
> @@ -984,7 +984,12 @@ handle_wheel(SCREEN *sp, MEVENT * eventp, int button, 
> int wheel)
>       }
>       break;
>      case 2:
> -     PRESS_POSITION(3);
> +     if (wheel) {
> +         /* Ignore this event as it is not a true press of the button */
> +         eventp->bstate = REPORT_MOUSE_POSITION;
> +     } else {
> +         PRESS_POSITION(3);
> +     }
>       break;
>      default:
>       /*
> -- 
> 2.50.1
> 

> From 315c4b332bee8535077a9dfca8e7d5d5ba547ec4 Mon Sep 17 00:00:00 2001
> From: Robin Haberkorn <[email protected]>
> Date: Wed, 10 Sep 2025 01:36:36 +0300
> Subject: [PATCH 2/2] fixed processing of multiple simultaneous mouse events,
>  ie. middle clicks in certain terminal emulators
> 
> * At least on Xterm and simpleterm (st) pressing the middle mouse button
>   sends a PRESSED and RELEASED event immediately when releasing the button.
>   These will have to be read with two getmouse() calls.
>   This is a rare case where the _mouse_events queue is actually made use of.
> * Unfortunately, the queue was rather a stack. We need separate
>   read and write pointers for a proper circular queue.
>   The order of events returned by getmouse() was therefore reversed.
> * Also, if you mask for CLICKED events, but disabled click detection
>   via mouseinterval(0), _nc_mouse_parse() would kick in and synthesize CLICKED
>   events when the queue actually has more than 1 entry as happens when 
> pressing
>   the middle button.
>   This had to be guarded against explicitly.
> ---
>  ncurses/base/lib_mouse.c | 53 +++++++++++++++++++++-------------------
>  ncurses/curses.priv.h    |  3 ++-
>  2 files changed, 30 insertions(+), 26 deletions(-)
> 
> diff --git a/ncurses/base/lib_mouse.c b/ncurses/base/lib_mouse.c
> index 06748890..8e9cce74 100644
> --- a/ncurses/base/lib_mouse.c
> +++ b/ncurses/base/lib_mouse.c
> @@ -786,7 +786,7 @@ _nc_mouse_init(SCREEN *sp)
>  
>           TR(MY_TRACE, ("set _mouse_initialized"));
>  
> -         sp->_mouse_eventp = FirstEV(sp);
> +         sp->_mouse_readp = sp->_mouse_writep = FirstEV(sp);
>           for (i = 0; i < EV_MAX; i++)
>               Invalidate(sp->_mouse_events + i);
>  
> @@ -806,7 +806,7 @@ _nc_mouse_init(SCREEN *sp)
>  static bool
>  _nc_mouse_event(SCREEN *sp)
>  {
> -    MEVENT *eventp = sp->_mouse_eventp;
> +    MEVENT *eventp = sp->_mouse_writep;
>      bool result = FALSE;
>  
>      (void) eventp;
> @@ -874,7 +874,7 @@ _nc_mouse_event(SCREEN *sp)
>               eventp->z = 0;
>  
>               /* bump the next-free pointer into the circular list */
> -             sp->_mouse_eventp = NEXT(eventp);
> +             sp->_mouse_writep = NEXT(eventp);
>               result = TRUE;
>               break;
>           }
> @@ -899,7 +899,7 @@ _nc_mouse_event(SCREEN *sp)
>           }
>  
>           /* bump the next-free pointer into the circular list */
> -         sp->_mouse_eventp = eventp = NEXT(eventp);
> +         sp->_mouse_writep = eventp = NEXT(eventp);
>           result = TRUE;
>       }
>       break;
> @@ -921,7 +921,7 @@ _nc_mouse_event(SCREEN *sp)
>           }
>  
>           /* bump the next-free pointer into the circular list */
> -         sp->_mouse_eventp = eventp = NEXT(eventp);
> +         sp->_mouse_writep = eventp = NEXT(eventp);
>           result = TRUE;
>       }
>       break;
> @@ -1360,7 +1360,7 @@ _nc_mouse_inline(SCREEN *sp)
>  /* mouse report received in the keyboard stream -- parse its info */
>  {
>      bool result = FALSE;
> -    MEVENT *eventp = sp->_mouse_eventp;
> +    MEVENT *eventp = sp->_mouse_writep;
>  
>      TR(MY_TRACE, ("_nc_mouse_inline() called"));
>  
> @@ -1385,7 +1385,7 @@ _nc_mouse_inline(SCREEN *sp)
>           (long) IndexEV(sp, eventp)));
>  
>       /* bump the next-free pointer into the circular list */
> -     sp->_mouse_eventp = NEXT(eventp);
> +     sp->_mouse_writep = NEXT(eventp);
>  
>       if (!result) {
>           /* If this event is from a wheel-mouse, treat it like position
> @@ -1506,7 +1506,7 @@ static bool
>  _nc_mouse_parse(SCREEN *sp, int runcount)
>  /* parse a run of atomic mouse events into a gesture */
>  {
> -    MEVENT *eventp = sp->_mouse_eventp;
> +    MEVENT *eventp = sp->_mouse_writep;
>      MEVENT *next, *ep;
>      MEVENT *first_valid = NULL;
>      MEVENT *first_invalid = NULL;
> @@ -1517,6 +1517,10 @@ _nc_mouse_parse(SCREEN *sp, int runcount)
>  
>      TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount));
>  
> +    if (!sp->_maxclick)
> +     return sp->_mouse_readp && ValidEvent(sp->_mouse_readp) &&
> +             ((sp->_mouse_readp->bstate & sp->_mouse_mask) != 0);
> +
>      /*
>       * When we enter this routine, the event list next-free pointer
>       * points just past a run of mouse events that we know were separated
> @@ -1552,6 +1556,7 @@ _nc_mouse_parse(SCREEN *sp, int runcount)
>       Invalidate(ep);
>       ep = NEXT(ep);
>      }
> +    assert(ep == sp->_mouse_readp);
>  
>  #ifdef TRACE
>      if (USE_TRACEF(TRACE_IEVENT)) {
> @@ -1728,7 +1733,7 @@ _nc_mouse_parse(SCREEN *sp, int runcount)
>      if (first_invalid == NULL) {
>       first_invalid = eventp;
>      }
> -    sp->_mouse_eventp = first_invalid;
> +    sp->_mouse_writep = first_invalid;
>  
>  #ifdef TRACE
>      if (first_valid != NULL) {
> @@ -1835,36 +1840,34 @@ NCURSES_EXPORT(int)
>  NCURSES_SP_NAME(getmouse) (NCURSES_SP_DCLx MEVENT * aevent)
>  {
>      int result = ERR;
> -    MEVENT *eventp;
> +    MEVENT *readp;
>  
>      T((T_CALLED("getmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent));
>  
>      if ((aevent != NULL) &&
>       (SP_PARM != NULL) &&
>       (SP_PARM->_mouse_type != M_NONE) &&
> -     (eventp = SP_PARM->_mouse_eventp) != NULL) {
> -     /* compute the current-event pointer */
> -     MEVENT *prev = PREV(eventp);
> -
> +     (readp = SP_PARM->_mouse_readp) != NULL) {
>       /*
>        * Discard events not matching mask (there could be still some if
>        * _nc_mouse_parse was not called, e.g., when _nc_mouse_inline returns
>        * false).
>        */
> -     while (ValidEvent(prev) && (!(prev->bstate & SP_PARM->_mouse_mask2))) {
> -         Invalidate(prev);
> -         prev = PREV(prev);
> +     while (readp != SP_PARM->_mouse_writep &&
> +            (!ValidEvent(readp) || !(readp->bstate & 
> SP_PARM->_mouse_mask2))) {
> +         Invalidate(readp);
> +         readp = NEXT(readp);
>       }
> -     if (ValidEvent(prev)) {
> +     if (readp != SP_PARM->_mouse_writep && ValidEvent(readp)) {
>           /* copy the event we find there */
> -         *aevent = *prev;
> +         *aevent = *readp;
>  
>           TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld",
> -                           _nc_tracemouse(SP_PARM, prev),
> -                           (long) IndexEV(SP_PARM, prev)));
> +                           _nc_tracemouse(SP_PARM, readp),
> +                           (long) IndexEV(SP_PARM, readp)));
>  
> -         Invalidate(prev);   /* so the queue slot becomes free */
> -         SP_PARM->_mouse_eventp = prev;
> +         Invalidate(readp);  /* so the queue slot becomes free */
> +         SP_PARM->_mouse_readp = NEXT(readp);
>           result = OK;
>       } else {
>           /* Reset the provided event */
> @@ -1897,13 +1900,13 @@ NCURSES_SP_NAME(ungetmouse) (NCURSES_SP_DCLx MEVENT * 
> aevent)
>  
>      if (aevent != NULL &&
>       SP_PARM != NULL &&
> -     (eventp = SP_PARM->_mouse_eventp) != NULL) {
> +     (eventp = SP_PARM->_mouse_writep) != NULL) {
>  
>       /* stick the given event in the next-free slot */
>       *eventp = *aevent;
>  
>       /* bump the next-free pointer into the circular list */
> -     SP_PARM->_mouse_eventp = NEXT(eventp);
> +     SP_PARM->_mouse_writep = NEXT(eventp);
>  
>       /* push back the notification event on the keyboard queue */
>       result = NCURSES_SP_NAME(ungetch) (NCURSES_SP_ARGx KEY_MOUSE);
> diff --git a/ncurses/curses.priv.h b/ncurses/curses.priv.h
> index 4fed9b97..9536cc51 100644
> --- a/ncurses/curses.priv.h
> +++ b/ncurses/curses.priv.h
> @@ -1152,7 +1152,8 @@ typedef struct screen {
>       MouseFormat     _mouse_format;  /* type of xterm mouse protocol */
>       NCURSES_CONST char *_mouse_xtermcap; /* string to enable/disable mouse 
> */
>       MEVENT          _mouse_events[EV_MAX];  /* hold the last mouse event 
> seen */
> -     MEVENT          *_mouse_eventp; /* next free slot in event queue */
> +     MEVENT          *_mouse_readp;  /* read pointer into event queue */
> +     MEVENT          *_mouse_writep; /* write pointer into event queue */
>  
>       /*
>        * These are data that support the proper handling of the panel stack 
> on an
> -- 
> 2.50.1
> 




-- 
Thomas E. Dickey <[email protected]>
https://invisible-island.net

Attachment: signature.asc
Description: PGP signature

Reply via email to