Hello, here are the mouse-related fixes I was discussing earlier. See commit messages for more details.
Is there a real version control system for ncurses or are you working will tarballs and patch series only? 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
signature.asc
Description: PGP signature
