Skip to content

Commit e67966e

Browse files
committed
Add curses.window.in_wch
1 parent d359a76 commit e67966e

5 files changed

Lines changed: 149 additions & 1 deletion

File tree

Doc/library/curses.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,18 @@ the following methods and attributes:
10171017
Return the character at the given position in the window. The bottom 8 bits are
10181018
the character proper, and upper bits are the attributes.
10191019

1020+
.. note::
1021+
1022+
``inch`` only works for ASCII characters. Use :meth:`in_wch` instead
1023+
for unicode support.
1024+
1025+
.. method:: window.in_wch([x, y])
1026+
1027+
Return the wide character at the given position in the window. The return
1028+
value is a 3-tuple ``(character, attributes, color_pair)``.
1029+
1030+
.. versionadded:: 3.14
1031+
10201032

10211033
.. method:: window.insch(ch[, attr])
10221034
window.insch(y, x, ch[, attr])

Lib/test/test_curses.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,39 @@ def test_issue6243(self):
11071107
curses.ungetch(1025)
11081108
self.stdscr.getkey()
11091109

1110+
def test_in_wch(self):
1111+
stdscr = self.stdscr
1112+
1113+
if not hasattr(stdscr, 'in_wch'):
1114+
raise unittest.SkipTest('requires curses.window.in_wch')
1115+
1116+
if curses.has_colors():
1117+
curses.init_pair(1, curses.COLOR_RED, 0)
1118+
expected_pair = 1
1119+
color_attr = curses.color_pair(expected_pair)
1120+
else:
1121+
expected_pair = 0
1122+
color_attr = 0
1123+
1124+
def _norm(ch, attr, color):
1125+
# for some reason, curses returns some color bits here?
1126+
return ch, attr & ~curses.A_COLOR, color
1127+
1128+
for ch in ('a', '\xe9', '\u20ac', '\U0001f643'):
1129+
expected = (ch, curses.A_BOLD, expected_pair)
1130+
1131+
stdscr.insstr(0, 0, ch, color_attr | curses.A_BOLD)
1132+
self.assertEqual(_norm(*stdscr.in_wch()), expected)
1133+
1134+
stdscr.addstr(0, 0, ch, color_attr | curses.A_BOLD)
1135+
self.assertEqual(_norm(*stdscr.in_wch(0, 0)), expected)
1136+
1137+
# in_wch can return multiple characters in the case of zero width
1138+
# curses at max returns 5 characters in a cchar_t
1139+
stdscr.addstr(0, 0, 'a' + '\u200b' * 10)
1140+
expected = ('a\u200b\u200b\u200b\u200b', 0, 0)
1141+
self.assertEqual(stdscr.in_wch(0, 0), expected)
1142+
11101143
@requires_curses_func('unget_wch')
11111144
@unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
11121145
"unget_wch is broken in ncurses 5.7 and earlier")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add :func:`curses.window.in_wch` function - by Anthony Sottile.

Modules/_cursesmodule.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,6 +1736,54 @@ _curses_window_inch_impl(PyCursesWindowObject *self, int group_right_1,
17361736
return rtn;
17371737
}
17381738

1739+
#ifdef HAVE_NCURSESW
1740+
/*[clinic input]
1741+
_curses.window.in_wch
1742+
1743+
[
1744+
y: int
1745+
Starting Y-coordinate.
1746+
x: int
1747+
Starting X-coordinate.
1748+
]
1749+
/
1750+
1751+
Retrieve the wide character at the gven position in the window.
1752+
1753+
The return value is a 3-tuple of (character, attributes, color_pair).
1754+
[clinic start generated code]*/
1755+
1756+
static PyObject *
1757+
_curses_window_in_wch_impl(PyCursesWindowObject *self, int group_right_1,
1758+
int y, int x)
1759+
/*[clinic end generated code: output=846ca8a82f2ecab4 input=5c20d96b592b0e0b]*/
1760+
{
1761+
int ret;
1762+
1763+
cchar_t wcval;
1764+
1765+
wchar_t wstr[CCHARW_MAX + 1];
1766+
attr_t attr;
1767+
short color_pair;
1768+
1769+
if (group_right_1) {
1770+
ret = mvwin_wch(self->win, y, x, &wcval);
1771+
} else {
1772+
ret = win_wch(self->win, &wcval);
1773+
}
1774+
if (PyCursesCheckERR(ret, "in_wch") == NULL) {
1775+
return NULL;
1776+
}
1777+
1778+
ret = getcchar(&wcval, wstr, &attr, &color_pair, NULL);
1779+
if (PyCursesCheckERR(ret, "getcchar") == NULL) {
1780+
return NULL;
1781+
}
1782+
1783+
return Py_BuildValue("ukh", wstr, attr, color_pair);
1784+
}
1785+
#endif
1786+
17391787
/*[-clinic input]
17401788
_curses.window.instr
17411789
@@ -2530,6 +2578,7 @@ static PyMethodDef PyCursesWindow_Methods[] = {
25302578
{"immedok", (PyCFunction)PyCursesWindow_immedok, METH_VARARGS},
25312579
#endif
25322580
_CURSES_WINDOW_INCH_METHODDEF
2581+
_CURSES_WINDOW_IN_WCH_METHODDEF
25332582
_CURSES_WINDOW_INSCH_METHODDEF
25342583
{"insdelln", (PyCFunction)PyCursesWindow_winsdelln, METH_VARARGS},
25352584
{"insertln", (PyCFunction)PyCursesWindow_winsertln, METH_NOARGS},

Modules/clinic/_cursesmodule.c.h

Lines changed: 54 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)