Re: curses inch()/mvinch() mangle non-ASCII characters of an 8-bit locale on a wide build

Serhiy Storchaka <[email protected]> Mon, 3 Aug 2026 14:38:10 +0300
Newsgroups gmane.comp.lib.ncurses.bugs
Message-ID <[email protected]>
One more thing about this, separate from whether the 8-bit limit is
intended: winch() does not do what curs_inch.3x says.

The page says these functions "extract only the low-order eight bits of the
character code from the cell".  On a wide build they extract all of it 
-- the
bits above the eighth are not discarded, they are OR-ed into the attribute
and colour fields.

     setlocale(LC_ALL, "");
     initscr();
     start_color();
     init_pair(1, COLOR_RED, COLOR_BLACK);
     attrset(COLOR_PAIR(1));
     mvaddstr(0, 0, "€");      /* EURO SIGN, in a UTF-8 locale */
     chtype c = mvinch(0, 0);

gives, for a cell that was written with colour pair 1:

     winch()        = 0x000021ac
     & A_CHARTEXT   = 0xac
     & A_COLOR      = 0x00002100
     PAIR_NUMBER()  = 33

0x20 is the high byte of U+20AC; it lands in A_COLOR, so the pair reads back
as 33 rather than 1.

lib_winch.c:

     returnChtype((chtype) CharOf(win->_line[win->_cury].text[win->_
                  | AttrOf(win->_line[win->_cury].text[win->_curx]))

and on a wide build CharOf() is the raw wchar_t (curses.priv.h:1464), while
the narrow ChCharOf() masks with A_CHARTEXT (curses.priv.h:1427).
the wide one the same way would make the code agree with the manual
would leave the rendition bits usable.

This is not about 8-bit locales -- the example above is UTF-8. Eve
that wants nothing but the rendition from inch() gets a wrong answe

We have worked around it in Python's curses binding, so nothing is 
blocked on
our side; I am only reporting that the behaviour and the documentation
disagree.

     https://github.com/python/cpython/issues/153862