hi-

    I'm seeing issues with pkgsrc mail/mutt under NetBSD 11
displaying text with the wrong colors.  The terminal/xterm
default colors are leaking through in places where they are
not supposed to!   If I recompile mail/mutt to use ncurses,
then it displays properly -- so the bugs are in NetBSD libcurses.

    Working with NetBSD libcurses from trunk, I've tracked the
the issue down to two main issues:

 - the handling of "orig_pair" in color.c
 - the handling of "exit_attribute_mode" in refresh.c

I have fixes for both that I'm hoping someone more familiar
with NetBSD curses can double check so we can get this fixed.


for "orig_pair"/color.c:
  - tput() of "orig_pair" resets the colors to terminal default.
    the only time you'd need to do this is when assume_default_colors()
    is used to change curses' default white on black (from
    __default_pair) with -1 to access one or both of the
    terminal default colors.

    start_colors():
      I removed tput() of "orig_pair" from start_color() and
      replaced it with a call to __unset_color(curscr) right 
      after setting __using_color = 1 (to ensure we are in a
      known state).   I also moved the tput() of "orig_colors"
      later (after we know we are not going to return(ERR)).

    __unset_color(): I added a helper function unset_color_pair()
      to centralize the handling of "orig_pair" across the
      COLOR_ANSI and COLOR_OTHER cases.  It only tputs() "orig_pair"
      if either __default_pair.fore or __default_pair.back is -1.

for "exit_attribute_mode"/refresh.c:
  - tput() of "exit_attribute_mode" resets the colors to terminal default.
    we need to add "_cursesi_screen->curpair = -1" to let curses know the
    currently set color has changed when "exit_attribute_mode" is used.


Also, I think init_pair() is supposed to fail if you call it
with -1 for a color and you have not called assume_default_colors().
At least that is what ncurses' init_pair() does.  I didn't address this.

diff relative to trunk below.

chuck


Index: color.c
===================================================================
RCS file: /cvsroot/src/lib/libcurses/color.c,v
retrieving revision 1.51
diff -u -r1.51 color.c
--- color.c     2 Sep 2026 04:20:20 -0000       1.51
+++ color.c     31 Oct 2026 21:16:45 -0000
@@ -124,14 +124,6 @@
        _cursesi_screen->COLOR_PAIRS = COLOR_PAIRS;
        _cursesi_screen->curpair = -1;
 
-       /* Reset terminal colour and colour pairs. */
-       if (orig_colors != NULL)
-               tputs(orig_colors, 0, __cputchar);
-       if (orig_pair != NULL) {
-               tputs(orig_pair, 0, __cputchar);
-               curscr->wattr &= _cursesi_screen->mask_op;
-       }
-
        /* Type of colour manipulation - ANSI/TEK/HP/other */
        if (set_a_foreground != NULL && set_a_background != NULL)
                _cursesi_screen->color_type = COLOR_ANSI;
@@ -144,6 +136,10 @@
        else
                return(ERR);            /* Unsupported colour method */
 
+       /* Reset terminal colour and colour pairs. */
+       if (orig_colors != NULL)
+               tputs(orig_colors, 0, __cputchar);
+
 #ifdef DEBUG
        __CTRACE(__CTRACE_COLOR, "start_color: COLORS = %d, COLOR_PAIRS = %d",
            COLORS, COLOR_PAIRS);
@@ -240,6 +236,7 @@
            __default_pair.flags;
 
        __using_color = 1;
+       __unset_color(curscr);
 
        /* Set all positions on all windows to curses default colours. */
        for (wlp = _cursesi_screen->winlistp; wlp != NULL; wlp = wlp->nextp) {
@@ -585,6 +582,42 @@
        curscr->wattr &= ~__COLOR;
        curscr->wattr |= attr & __COLOR;
 }
+/*
+ * unset_color_pair --
+ *     Reset colors to the defaults.  If start_color() has been
+ *     called, then we use the colors stored in __default_pair
+ *     (white on black, if not changed by the application).  If
+ *     either color is -1 (indicates we should use the terminal
+ *     default for that value) we emit orig_pair to reset both
+ *     foreground and background to the terminal default and then
+ *     override the terminal default for any color that is not -1.
+ *     (-1 values can be established with the use_default_colors()
+ *      or assume_default_colors() APIs.)
+ *
+ *     If start_color() has not been called, we just use orig_pair.
+ */
+static void
+unset_color_pair(WINDOW *win, const char *set_fg, const char *set_bg)
+{
+       if (__using_color) {
+               /* emit orig_pair if terminal default is used */
+               if (__default_pair.fore == -1 || __default_pair.back == -1) {
+                       if (orig_pair)
+                               tputs(orig_pair, 0, __cputchar);
+               }
+               if (__default_pair.fore >= 0 && set_fg)
+                       tputs(tiparm(set_fg, (int)__default_pair.fore),
+                               0, __cputchar);
+               if (__default_pair.back >= 0 && set_bg)
+                       tputs(tiparm(set_bg, (int)__default_pair.back),
+                               0, __cputchar);
+       } else {
+               if (orig_pair)
+                       tputs(orig_pair, 0, __cputchar);
+       }
+       win->wattr &= __mask_op;
+       curscr->wattr &= __mask_op;
+}
 
 /*
  * __unset_color --
@@ -597,11 +630,9 @@
        switch (_cursesi_screen->color_type) {
        /* Clear ANSI foreground and background colours */
        case COLOR_ANSI:
-               if (orig_pair != NULL) {
-                       tputs(orig_pair, 0, __cputchar);
-                       win->wattr &= __mask_op;
-                       curscr->wattr &= __mask_op;
-               }
+               unset_color_pair(win,
+                       t_set_a_foreground(_cursesi_screen->term),
+                       t_set_a_background(_cursesi_screen->term));
                break;
        case COLOR_HP:
                /* XXX: need to support HP style */
@@ -610,10 +641,9 @@
                /* XXX: need to support Tek style */
                break;
        case COLOR_OTHER:
-               if (orig_pair != NULL) {
-                       tputs(orig_pair, 0, __cputchar);
-                       win->wattr &= __mask_op;
-               }
+               unset_color_pair(win,
+                       t_set_foreground(_cursesi_screen->term),
+                       t_set_background(_cursesi_screen->term));
                break;
        }
 
Index: refresh.c
===================================================================
RCS file: /cvsroot/src/lib/libcurses/refresh.c,v
retrieving revision 1.137
diff -u -r1.137 refresh.c
--- refresh.c   2 Sep 2026 04:20:20 -0000       1.137
+++ refresh.c   31 Oct 2026 21:16:46 -0000
@@ -880,6 +880,8 @@
         */
        if (off & __TERMATTR && exit_attribute_mode != NULL) {
                tputs(exit_attribute_mode, 0, __cputchar);
+               /* exit_attribute_mode resets the color to terminal default */
+               _cursesi_screen->curpair = -1;
                curscr->wattr &= __mask_me;
                off &= __mask_me;
        }
@@ -2093,6 +2095,7 @@
         */
        if (curscr->wattr & __TERMATTR && isms) {
                tputs(exit_attribute_mode, 0, __cputchar);
+               _cursesi_screen->curpair = -1;  /* tputs just reset it */
                curscr->wattr &= __mask_me;
        }
        /* Don't leave the screen with altcharset set (don't check ms). */

Reply via email to