Skip to content

Commit d283a86

Browse files
[3.13] gh-153862: Fix spurious color pair in curses window.inch() on a wide build (GH-154703) (GH-154733)
winch() returns the whole code point, so inch() replacing only its low 8 bits left the high bits in the color field. Rebuild from getcchar()'s attributes and color pair. (cherry picked from commit b618874) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 501e911 commit d283a86

3 files changed

Lines changed: 17 additions & 13 deletions

File tree

Doc/library/curses.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,9 @@ Window objects
10651065
and the color pair with :func:`pair_number`.
10661066
The character byte is the locale-encoded byte of the cell's character,
10671067
consistent with :meth:`instr`.
1068+
On a wide-character build, a character that does not fit in a single byte
1069+
in the current locale has a character byte of ``0``;
1070+
use :meth:`instr` to read such characters.
10681071

10691072

10701073
.. method:: window.insch(ch[, attr])

Lib/test/test_curses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ def test_read_from_window(self):
523523
with self.subTest(ch=ch):
524524
stdscr.addstr(2, 0, ch)
525525
self.assertEqual(stdscr.instr(2, 0, 1), b)
526-
self.assertEqual(stdscr.inch(2, 0) & curses.A_CHARTEXT, b[0])
526+
self.assertEqual(stdscr.inch(2, 0), b[0])
527527

528528
def test_coordinate_errors(self):
529529
# Addressing a cell outside the window raises curses.error.

Modules/_cursesmodule.c

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,10 +1736,10 @@ _curses_window_insch_impl(PyCursesWindowObject *self, int group_left_1,
17361736
}
17371737

17381738
#ifdef HAVE_NCURSESW
1739-
/* winch() returns the low 8 bits of the character's code point with no locale
1740-
conversion, unlike instr(), so recover the locale byte from the wide cell
1741-
when the character maps to exactly one byte, keeping the attribute and color
1742-
bits in RTN. A character with no single-byte form is left to winch(). */
1739+
/* ncursesw's winch() returns the character's whole code point instead of its
1740+
locale byte, overflowing the chtype's 8-bit character field into the color
1741+
and attribute bits, so rebuild the value from the locale byte plus the
1742+
attributes and color pair reported by getcchar(). */
17431743
static chtype
17441744
curses_cell_locale_byte(chtype rtn, const cchar_t *cell)
17451745
{
@@ -1748,18 +1748,19 @@ curses_cell_locale_byte(chtype rtn, const cchar_t *cell)
17481748
short pair;
17491749
/* getcchar() is not guaranteed to write the text of an empty cell. */
17501750
wstr[0] = L'\0';
1751-
if (getcchar(cell, wstr, &attrs, &pair, NULL) == ERR
1752-
|| wstr[0] == L'\0' || wstr[1] != L'\0')
1753-
{
1751+
if (getcchar(cell, wstr, &attrs, &pair, NULL) == ERR) {
17541752
return rtn;
17551753
}
17561754
/* wctob() mirrors ncurses' own _nc_to_char(): the single-byte form, or EOF
1757-
when the character has none in this locale. */
1758-
int byte = wctob(wstr[0]);
1759-
if (byte != EOF) {
1760-
rtn = (rtn & ~(chtype)A_CHARTEXT) | (unsigned char)byte;
1755+
when the character has none in this locale (then use 0). */
1756+
int byte = 0;
1757+
if (wstr[0] != L'\0' && wstr[1] == L'\0') {
1758+
byte = wctob(wstr[0]);
1759+
if (byte == EOF) {
1760+
byte = 0;
1761+
}
17611762
}
1762-
return rtn;
1763+
return (chtype)byte | (attrs & ~(attr_t)A_COLOR) | COLOR_PAIR(pair);
17631764
}
17641765
#endif
17651766

0 commit comments

Comments
 (0)