the curses library itself.
+.. function:: erasewchar()
+
+ Return the user's current erase character as a one-character string.
+ This is the wide-character variant of :func:`erasechar`. Availability
+ depends on building Python against a wide-character-aware version of the
+ underlying curses library.
+
+ .. versionadded:: next
+
+
.. function:: filter()
The :func:`.filter` routine, if used, must be called before :func:`initscr` is
by the curses library itself.
+.. function:: killwchar()
+
+ Return the user's current line kill character as a one-character string.
+ This is the wide-character variant of :func:`killchar`. Availability
+ depends on building Python against a wide-character-aware version of the
+ underlying curses library.
+
+ .. versionadded:: next
+
+
.. function:: longname()
Return a bytes object containing the terminfo long name field describing the current
example as ``b'^C'``. Printing characters are left as they are.
+.. function:: wunctrl(ch)
+
+ Return a string which is a printable representation of the wide character *ch*.
+ Control characters are represented as a caret followed by the character, for
+ example as ``'^C'``. Printing characters are left as they are. This is the
+ wide-character variant of :func:`unctrl`, returning a :class:`str` rather than
+ :class:`bytes`. Availability depends on building Python against a
+ wide-character-aware version of the underlying curses library.
+
+ .. versionadded:: next
+
+
.. function:: ungetch(ch)
Push *ch* so the next :meth:`~window.getch` will return it.
character previously painted at that location. By default, the character
position and attributes are the current settings for the window object.
+ *ch* may be a single character, optionally followed by combining
+ characters, that together occupy one character cell.
+
.. note::
Writing outside the window, subwindow, or pad raises a :exc:`curses.error`.
Attempting to write to the lower-right corner of a window, subwindow,
or pad will cause an exception to be raised after the character is printed.
+ .. versionchanged:: next
+ A character may now be given as a string of a base character followed
+ by combining characters, instead of only a single character.
+
.. method:: window.addnstr(str, n[, attr])
window.addnstr(y, x, str, n[, attr])
* Wherever the former background character appears, it is changed to the new
background character.
+ .. versionchanged:: next
+ Wide and combining characters are now accepted.
+
.. method:: window.bkgdset(ch[, attr])
characters. The background becomes a property of the character and moves with
the character through any scrolling and insert/delete line/character operations.
+ .. versionchanged:: next
+ Wide and combining characters are now accepted.
+
.. method:: window.border([ls[, rs[, ts[, bs[, tl[, tr[, bl[, br]]]]]]]])
| *br* | Bottom-right corner | :const:`ACS_LRCORNER` |
+-----------+---------------------+-----------------------+
+ .. versionchanged:: next
+ Wide and combining characters are now accepted. A single call cannot mix
+ them with integer or byte characters.
+
.. method:: window.box([vertch, horch])
Similar to :meth:`border`, but both *ls* and *rs* are *vertch* and both *ts* and
*bs* are *horch*. The default corner characters are always used by this function.
+ .. versionchanged:: next
+ Wide and combining characters are now accepted. A single call cannot mix
+ them with integer or byte characters.
+
.. method:: window.chgat(attr)
window.chgat(num, attr)
Add character *ch* with attribute *attr*, and immediately call :meth:`refresh`
on the window.
+ .. versionchanged:: next
+ Wide and combining characters are now accepted.
+
.. method:: window.enclose(y, x)
The maximum value for *n* was increased from 1023 to 2047.
+.. method:: window.get_wstr()
+ window.get_wstr(n)
+ window.get_wstr(y, x)
+ window.get_wstr(y, x, n)
+
+ Read a string from the user, with primitive line editing capacity.
+ This is the wide-character variant of :meth:`getstr`: it returns a
+ :class:`str` rather than a :class:`bytes` object, so it can return
+ characters that are not representable in the window's encoding.
+ At most *n* characters are read; *n* defaults to and cannot exceed 2047.
+
+ .. versionadded:: next
+
+
.. method:: window.getyx()
Return a tuple ``(y, x)`` of current cursor position relative to the window's
the character *ch* with attributes *attr*. The line stops at the right edge
of the window if fewer than *n* cells are available.
+ .. versionchanged:: next
+ Wide and combining characters are now accepted.
+
.. method:: window.idcok(flag)
cursor are shifted one position right, with the rightmost character on the
line being lost. The cursor position does not change.
+ .. versionchanged:: next
+ Wide and combining characters are now accepted.
+
.. method:: window.insdelln(nlines)
The maximum value for *n* was increased from 1023 to 2047.
+.. method:: window.in_wstr([n])
+ window.in_wstr(y, x[, n])
+
+ Return a string of characters, extracted from the window starting at the
+ current cursor position, or at *y*, *x* if specified. This is the
+ wide-character variant of :meth:`instr`: it returns a :class:`str` rather
+ than a :class:`bytes` object, so it can return characters that are not
+ representable in the window's encoding. Attributes and color information
+ are stripped from the characters. The maximum value for *n* is 2047.
+
+ .. versionadded:: next
+
+
.. method:: window.is_linetouched(line)
Return ``True`` if the specified line was modified since the last call to
Display a vertical line starting at ``(y, x)`` with length *n* consisting of the
character *ch* with attributes *attr*.
+ .. versionchanged:: next
+ Wide and combining characters are now accepted.
+
Constants
---------
curses
------
+* The :mod:`curses` character-cell window methods now accept a full character
+ cell --- a spacing character optionally followed by combining characters ---
+ in addition to a single integer or byte character. This affects
+ :meth:`~curses.window.addch`, :meth:`~curses.window.bkgd`,
+ :meth:`~curses.window.bkgdset`, :meth:`~curses.window.border`,
+ :meth:`~curses.window.box`, :meth:`~curses.window.echochar`,
+ :meth:`~curses.window.hline`, :meth:`~curses.window.insch` and
+ :meth:`~curses.window.vline`.
+ Also add the wide-character read methods :meth:`~curses.window.get_wstr` and
+ :meth:`~curses.window.in_wstr`, the counterparts of
+ :meth:`~curses.window.getstr` and :meth:`~curses.window.instr` that return a
+ :class:`str` rather than :class:`bytes`,
+ and the module functions :func:`curses.erasewchar`, :func:`curses.killwchar`
+ and :func:`curses.wunctrl`, the wide-character counterparts of
+ :func:`curses.erasechar`, :func:`curses.killchar` and :func:`curses.unctrl`.
+ These features are only available when built against the wide-character
+ ncursesw library.
+ (Contributed by Serhiy Storchaka in :gh:`151757`.)
+
* Add :func:`curses.nofilter`, which undoes the effect of :func:`curses.filter`.
(Contributed by Serhiy Storchaka in :gh:`151744`.)
self.assertIs(win.is_wintouched(), syncok)
self.assertIs(stdscr.is_wintouched(), syncok)
+ @requires_curses_window_meth('get_wch')
+ def test_addch_combining(self):
+ # A character cell may hold a spacing char plus combining marks.
+ stdscr = self.stdscr
+ stdscr.move(0, 0)
+ stdscr.addch('e\u0301') # 'e' + COMBINING ACUTE ACCENT
+ stdscr.addch(1, 0, 'a\u0323\u0300') # base plus two combining marks
+ # Too many code points to fit in a single character cell.
+ self.assertRaises(TypeError, stdscr.addch, 'e' + '\u0301' * 10)
+ # Only the first code point may be a spacing character.
+ self.assertRaises(ValueError, stdscr.addch, 'ab')
+ self.assertRaises(ValueError, stdscr.addch, 'a\u0301b')
+ # A lone control character is allowed (like addch(ord('\n'))), but it
+ # cannot be combined with other characters, as base or otherwise.
+ stdscr.addch('\n')
+ self.assertRaises(ValueError, stdscr.addch, 'a\n')
+ self.assertRaises(ValueError, stdscr.addch, '\n\u0301')
+ self.assertRaises(ValueError, stdscr.addch, '\ne\u0301')
+
+ @requires_curses_window_meth('get_wch')
+ def test_addch_emoji(self):
+ # curses has no grapheme-cluster support: a cell holds one spacing
+ # character plus zero-width combining characters. A lone emoji fits,
+ # as does an emoji with a zero-width variation selector.
+ stdscr = self.stdscr
+ stdscr.addch(0, 0, '\U0001f600') # single emoji
+ stdscr.addch(1, 0, '\u263a\ufe0f') # WHITE SMILING FACE + VS-16
+ # An emoji ZWJ sequence or an emoji with a modifier is more than one
+ # spacing character and cannot share a single cell.
+ self.assertRaises(ValueError, stdscr.addch,
+ '\U0001f44d\U0001f3fd') # thumbs up + skin tone
+ self.assertRaises(ValueError, stdscr.addch,
+ '\U0001f468\u200d\U0001f469') # man ZWJ woman
+
+ @requires_curses_window_meth('get_wch')
+ def test_wide_characters(self):
+ # Wide and combining characters in the character-cell methods.
+ stdscr = self.stdscr
+ combining = 'e\u0301' # 'e' + COMBINING ACUTE ACCENT
+ vline, hline = '\u2502', '\u2500' # box-drawing vertical/horizontal
+ stdscr.move(0, 0)
+ stdscr.echochar(combining)
+ stdscr.insch(1, 0, combining)
+ stdscr.hline(2, 0, hline, 5)
+ stdscr.vline(3, 0, vline, 3)
+ stdscr.bkgdset(combining)
+ stdscr.bkgd(combining)
+ stdscr.border(vline, vline, hline, hline)
+ stdscr.box(vline, hline)
+ # border() and box() cannot mix integer and wide-string characters.
+ self.assertRaises(TypeError, stdscr.box, vline, ord('-'))
+
+
+ @requires_curses_window_meth('in_wstr')
+ def test_in_wstr(self):
+ # The wide-character window read returns a str (instr returns bytes).
+ stdscr = self.stdscr
+ s = 'a\u00e9\u2502z' # 'a', 'e'+acute (precomposed), box vline, 'z'
+ stdscr.addstr(0, 0, s)
+ self.assertEqual(stdscr.in_wstr(0, 0, len(s)), s)
+ self.assertIsInstance(stdscr.instr(0, 0, len(s)), bytes)
+
+
def test_output_character(self):
stdscr = self.stdscr
encoding = stdscr.encoding
stdscr.echochar('A')
stdscr.echochar(b'A')
stdscr.echochar(65)
- with self.assertRaises((UnicodeEncodeError, OverflowError)):
- # Unicode is not fully supported yet, but at least it does
- # not crash.
- # It is supposed to fail because either the character is
- # not encodable with the current encoding, or it is encoded to
- # a multibyte sequence.
- stdscr.echochar('\u0114')
+ c = '\u0114'
+ try:
+ stdscr.echochar(c)
+ except UnicodeEncodeError:
+ # The character is not encodable with the current encoding.
+ self.assertRaises(UnicodeEncodeError, c.encode, encoding)
+ except OverflowError:
+ # The character is encoded to a multibyte sequence.
+ encoded = c.encode(encoding)
+ self.assertNotEqual(len(encoded), 1, repr(encoded))
stdscr.echochar('A', curses.A_BOLD)
self.assertIs(stdscr.is_wintouched(), False)
self.assertEqual(win.inch(3, 1), b'a'[0])
def test_unctrl(self):
- # TODO: wunctrl()
self.assertEqual(curses.unctrl(b'A'), b'A')
self.assertEqual(curses.unctrl('A'), b'A')
self.assertEqual(curses.unctrl(65), b'A')
self.assertRaises(TypeError, curses.unctrl, b'AB')
self.assertRaises(TypeError, curses.unctrl, '')
self.assertRaises(TypeError, curses.unctrl, 'AB')
+
+ @requires_curses_func('wunctrl')
+ def test_wunctrl(self):
+ # The wide-character variant of unctrl() returns a str.
+ self.assertEqual(curses.wunctrl(b'A'), 'A')
+ self.assertEqual(curses.wunctrl('A'), 'A')
+ self.assertEqual(curses.wunctrl(65), 'A')
+ self.assertEqual(curses.wunctrl('\n'), '^J')
+ self.assertEqual(curses.wunctrl(10), '^J')
+ self.assertEqual(curses.wunctrl('é'), 'é') # printable
+ self.assertRaises(TypeError, curses.wunctrl, b'')
+ self.assertRaises(TypeError, curses.wunctrl, b'AB')
+ self.assertRaises(TypeError, curses.wunctrl, '')
+ # More than one spacing character is not a single cell.
+ self.assertRaises(ValueError, curses.wunctrl, 'AB')
self.assertRaises(OverflowError, curses.unctrl, 2**64)
def test_endwin(self):
curses.newpad(50, 50)
def test_env_queries(self):
- # TODO: term_attrs(), erasewchar(), killwchar()
+ # TODO: term_attrs()
self.assertIsInstance(curses.termname(), bytes)
self.assertIsInstance(curses.longname(), bytes)
self.assertIsInstance(curses.baudrate(), int)
self.assertIsInstance(c, bytes)
self.assertEqual(len(c), 1)
+ # The erase and kill characters are a property of the controlling
+ # terminal: the wide variants report ERR (raising curses.error) without
+ # one, while the narrow variants above return an unspecified byte.
+ try:
+ tty_fd = os.open(os.ctermid(), os.O_RDONLY)
+ except OSError:
+ tty_fd = None
+ if tty_fd is not None:
+ os.close(tty_fd)
+ if hasattr(curses, 'erasewchar'):
+ c = curses.erasewchar()
+ self.assertIsInstance(c, str)
+ self.assertEqual(len(c), 1)
+ if hasattr(curses, 'killwchar'):
+ c = curses.killwchar()
+ self.assertIsInstance(c, str)
+ self.assertEqual(len(c), 1)
+
def test_output_options(self):
stdscr = self.stdscr
--- /dev/null
+The :mod:`curses` character-cell window methods now accept a full character
+cell -- a spacing character optionally followed by combining characters -- in
+addition to a single integer or byte character. Add the wide-character read
+methods :meth:`curses.window.get_wstr` and :meth:`curses.window.in_wstr`, and
+the functions :func:`curses.erasewchar`, :func:`curses.killwchar` and
+:func:`curses.wunctrl`. These features are only available when built against
+the wide-character ncursesw library.
- int
- bytes of length 1
- - str of length 1
+ - str of length 1, or a spacing character followed by up to
+ CCHARW_MAX - 1 combining characters
Return:
)
{
long value;
-#ifdef HAVE_NCURSESW
- wchar_t buffer[2];
-#endif
if (PyUnicode_Check(obj)) {
#ifdef HAVE_NCURSESW
- if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) {
+ /* A character cell may hold a spacing character plus up to
+ CCHARW_MAX - 1 combining characters; wch must point to a buffer
+ of at least CCHARW_MAX + 1 wide characters. */
+ Py_ssize_t nch = PyUnicode_AsWideChar(obj, wch, CCHARW_MAX + 1);
+ if (nch < 0) {
+ return 0;
+ }
+ if (nch == 0 || nch > CCHARW_MAX) {
PyErr_Format(PyExc_TypeError,
- "expect int or bytes or str of length 1, "
- "got a str of length %zi",
- PyUnicode_GET_LENGTH(obj));
+ "expect int or bytes or a string of 1 to %d "
+ "characters, got a str of length %zi",
+ (int)CCHARW_MAX, PyUnicode_GET_LENGTH(obj));
return 0;
}
- *wch = buffer[0];
+ /* A character cell is a single spacing character optionally followed
+ by combining characters. A lone control character is still allowed
+ (like addch(ord('\n'))), but in a multi-character cell the base must
+ be a printable character and the rest must be zero-width combining
+ characters. Validate this explicitly: otherwise setcchar() would
+ silently drop a trailing spacing character, or fail with a generic
+ error for a control character used as the base. */
+ if (nch > 1) {
+ int bad = wcwidth(wch[0]) < 0;
+ for (Py_ssize_t i = 1; !bad && i < nch; i++) {
+ bad = wcwidth(wch[i]) != 0;
+ }
+ if (bad) {
+ PyErr_SetString(PyExc_ValueError,
+ "a character cell must be a single spacing "
+ "character optionally followed by combining "
+ "characters");
+ return 0;
+ }
+ }
return 2;
#else
return PyCurses_ConvertToChtype(win, obj, ch);
return 0;
}
+#ifdef HAVE_NCURSESW
+/* Build a single character cell from obj.
+
+ On success return 1 and store the raw chtype (without *attr*) in *pch when
+ obj is an int or bytes, or return 2 and store a cchar_t (with *attr*
+ applied) in *pwc when obj is a str -- a spacing character optionally
+ followed by combining characters. Return 0 and set an exception on error.
+
+ This lets a method use the wide *_set functions (which accept combining
+ characters) for string arguments while still accepting integer chtype
+ values. */
+static int
+PyCurses_ConvertToCell(PyCursesWindowObject *win, PyObject *obj, long attr,
+ const char *funcname, chtype *pch, cchar_t *pwc)
+{
+ wchar_t wstr[CCHARW_MAX + 1];
+ int type = PyCurses_ConvertToCchar_t(win, obj, pch, wstr);
+ if (type == 2) {
+ if (setcchar(pwc, wstr, (attr_t)attr, PAIR_NUMBER(attr), NULL) == ERR) {
+ curses_window_set_error(win, "setcchar", funcname);
+ return 0;
+ }
+ }
+ return type;
+}
+#endif
+
static int
color_allow_default_converter(PyObject *arg, void *ptr)
{
int type;
chtype cch = 0;
#ifdef HAVE_NCURSESW
- wchar_t wstr[2];
+ wchar_t wstr[CCHARW_MAX + 1];
cchar_t wcval;
#endif
const char *funcname;
#ifdef HAVE_NCURSESW
type = PyCurses_ConvertToCchar_t(self, ch, &cch, wstr);
if (type == 2) {
- wstr[1] = L'\0';
rtn = setcchar(&wcval, wstr, attr, PAIR_NUMBER(attr), NULL);
if (rtn == ERR) {
curses_window_set_error(self, "setcchar", "addch");
/*[clinic end generated code: output=058290afb2cf4034 input=634015bcb339283d]*/
{
chtype bkgd;
-
+#ifdef HAVE_NCURSESW
+ cchar_t wch;
+ int type = PyCurses_ConvertToCell(self, ch, attr, "bkgd", &bkgd, &wch);
+ if (type == 0) {
+ return NULL;
+ }
+ if (type == 2) {
+ int rtn = wbkgrnd(self->win, &wch);
+ return curses_window_check_err(self, rtn, "wbkgrnd", "bkgd");
+ }
+#else
if (!PyCurses_ConvertToChtype(self, ch, &bkgd))
return NULL;
+#endif
- int rtn = wbkgd(self->win, bkgd | attr);
+ int rtn = wbkgd(self->win, bkgd | (attr_t)attr);
return curses_window_check_err(self, rtn, "wbkgd", "bkgd");
}
/*[clinic end generated code: output=8cb994fc4d7e2496 input=e09c682425c9e45b]*/
{
chtype bkgd;
-
+#ifdef HAVE_NCURSESW
+ cchar_t wch;
+ int type = PyCurses_ConvertToCell(self, ch, attr, "bkgdset", &bkgd, &wch);
+ if (type == 0) {
+ return NULL;
+ }
+ if (type == 2) {
+ wbkgrndset(self->win, &wch);
+ Py_RETURN_NONE;
+ }
+#else
if (!PyCurses_ConvertToChtype(self, ch, &bkgd))
return NULL;
+#endif
- wbkgdset(self->win, bkgd | attr);
+ wbkgdset(self->win, bkgd | (attr_t)attr);
Py_RETURN_NONE;
}
{
chtype ch[8];
int i, rtn;
+ PyObject *objs[8] = {ls, rs, ts, bs, tl, tr, bl, br};
/* Clear the array of parameters */
- for(i=0; i<8; i++)
+ for (i = 0; i < 8; i++)
ch[i] = 0;
-#define CONVERTTOCHTYPE(obj, i) \
- if ((obj) != NULL && !PyCurses_ConvertToChtype(self, (obj), &ch[(i)])) \
- return NULL;
-
- CONVERTTOCHTYPE(ls, 0);
- CONVERTTOCHTYPE(rs, 1);
- CONVERTTOCHTYPE(ts, 2);
- CONVERTTOCHTYPE(bs, 3);
- CONVERTTOCHTYPE(tl, 4);
- CONVERTTOCHTYPE(tr, 5);
- CONVERTTOCHTYPE(bl, 6);
- CONVERTTOCHTYPE(br, 7);
-
-#undef CONVERTTOCHTYPE
+#ifdef HAVE_NCURSESW
+ cchar_t wch[8];
+ const cchar_t *wch_p[8];
+ int use_wide = 0;
+ int types[8];
+ for (i = 0; i < 8; i++) {
+ types[i] = 0;
+ if (objs[i] != NULL) {
+ types[i] = PyCurses_ConvertToCell(self, objs[i], A_NORMAL,
+ "border", &ch[i], &wch[i]);
+ if (types[i] == 0) {
+ return NULL;
+ }
+ if (types[i] == 2) {
+ use_wide = 1;
+ }
+ }
+ }
+ if (use_wide) {
+ for (i = 0; i < 8; i++) {
+ if (objs[i] == NULL) {
+ wch_p[i] = NULL; /* use the default character */
+ }
+ else if (types[i] == 2) {
+ wch_p[i] = &wch[i];
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "border() cannot mix integer or bytes "
+ "characters with wide string characters");
+ return NULL;
+ }
+ }
+ rtn = wborder_set(self->win,
+ wch_p[0], wch_p[1], wch_p[2], wch_p[3],
+ wch_p[4], wch_p[5], wch_p[6], wch_p[7]);
+ return curses_window_check_err(self, rtn, "wborder_set", "border");
+ }
+#else
+ for (i = 0; i < 8; i++) {
+ if (objs[i] != NULL && !PyCurses_ConvertToChtype(self, objs[i], &ch[i]))
+ return NULL;
+ }
+#endif
rtn = wborder(self->win,
ch[0], ch[1], ch[2], ch[3],
/*[clinic end generated code: output=f3fcb038bb287192 input=e11acb7dbf6790b6]*/
{
chtype ch1 = 0, ch2 = 0;
+#ifdef HAVE_NCURSESW
+ cchar_t wch1, wch2;
+ int t1 = 0, t2 = 0;
+ if (group_right_1) {
+ t1 = PyCurses_ConvertToCell(self, verch, A_NORMAL, "box", &ch1, &wch1);
+ if (t1 == 0) {
+ return NULL;
+ }
+ t2 = PyCurses_ConvertToCell(self, horch, A_NORMAL, "box", &ch2, &wch2);
+ if (t2 == 0) {
+ return NULL;
+ }
+ }
+ if (t1 == 2 || t2 == 2) {
+ if (t1 != 2 || t2 != 2) {
+ PyErr_SetString(PyExc_TypeError,
+ "box() cannot mix integer or bytes characters "
+ "with wide string characters");
+ return NULL;
+ }
+ int rtn = wborder_set(self->win, &wch1, &wch1, &wch2, &wch2,
+ NULL, NULL, NULL, NULL);
+ return curses_window_check_err(self, rtn, "wborder_set", "box");
+ }
+#else
if (group_right_1) {
if (!PyCurses_ConvertToChtype(self, verch, &ch1)) {
return NULL;
return NULL;
}
}
+#endif
return curses_window_check_err(self, box(self->win, ch1, ch2), "box", NULL);
}
/*[clinic end generated code: output=13e7dd875d4b9642 input=e7f34b964e92b156]*/
{
chtype ch_;
-
+#ifdef HAVE_NCURSESW
+ cchar_t wch;
+ int type = PyCurses_ConvertToCell(self, ch, attr, "echochar", &ch_, &wch);
+ if (type == 0) {
+ return NULL;
+ }
+ if (type == 2) {
+ int rtn;
+ const char *funcname;
+#ifdef py_is_pad
+ if (py_is_pad(self->win)) {
+ rtn = pecho_wchar(self->win, &wch);
+ funcname = "pecho_wchar";
+ }
+ else
+#endif
+ {
+ rtn = wecho_wchar(self->win, &wch);
+ funcname = "wecho_wchar";
+ }
+ return curses_window_check_err(self, rtn, funcname, "echochar");
+ }
+#else
if (!PyCurses_ConvertToChtype(self, ch, &ch_))
return NULL;
+#endif
int rtn;
const char *funcname;
/*[clinic end generated code: output=c00d489d61fc9eef input=81a4dea47268163e]*/
{
chtype ch_;
-
+#ifdef HAVE_NCURSESW
+ cchar_t wch;
+ int type = PyCurses_ConvertToCell(self, ch, attr, "hline", &ch_, &wch);
+ if (type == 0) {
+ return NULL;
+ }
+#else
if (!PyCurses_ConvertToChtype(self, ch, &ch_))
return NULL;
+#endif
if (group_left_1) {
if (wmove(self->win, y, x) == ERR) {
curses_window_set_error(self, "wmove", "hline");
return NULL;
}
}
+#ifdef HAVE_NCURSESW
+ if (type == 2) {
+ int rtn = whline_set(self->win, &wch, n);
+ return curses_window_check_err(self, rtn, "whline_set", "hline");
+ }
+#endif
int rtn = whline(self->win, ch_ | (attr_t)attr, n);
return curses_window_check_err(self, rtn, "whline", "hline");
}
{
int rtn;
chtype ch_ = 0;
-
+ const char *funcname;
+#ifdef HAVE_NCURSESW
+ cchar_t wch;
+ int type = PyCurses_ConvertToCell(self, ch, attr, "insch", &ch_, &wch);
+ if (type == 0) {
+ return NULL;
+ }
+ if (type == 2) {
+ if (!group_left_1) {
+ rtn = wins_wch(self->win, &wch);
+ funcname = "wins_wch";
+ }
+ else {
+ rtn = mvwins_wch(self->win, y, x, &wch);
+ funcname = "mvwins_wch";
+ }
+ return curses_window_check_err(self, rtn, funcname, "insch");
+ }
+#else
if (!PyCurses_ConvertToChtype(self, ch, &ch_))
return NULL;
+#endif
- const char *funcname;
if (!group_left_1) {
rtn = winsch(self->win, ch_ | (attr_t)attr);
funcname = "winsch";
return PyBytesWriter_FinishWithSize(writer, strlen(buf));
}
+#ifdef HAVE_NCURSESW
+PyDoc_STRVAR(_curses_window_get_wstr__doc__,
+"get_wstr([[y, x,] n=2047])\n"
+"Read a string from the user, with primitive line editing capacity.\n"
+"\n"
+" y\n"
+" Y-coordinate.\n"
+" x\n"
+" X-coordinate.\n"
+" n\n"
+" Maximal number of characters.\n"
+"\n"
+"This is the wide-character variant of getstr(); it returns a str.");
+
+static PyObject *
+PyCursesWindow_get_wstr(PyObject *op, PyObject *args)
+{
+ PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op);
+ int rtn, use_xy = 0, y = 0, x = 0;
+ unsigned int max_buf_size = 2048;
+ unsigned int n = max_buf_size - 1;
+
+ if (!curses_clinic_parse_optional_xy_n(args, &y, &x, &n, &use_xy,
+ "_curses.window.get_wstr"))
+ {
+ return NULL;
+ }
+
+ n = Py_MIN(n, max_buf_size - 1);
+ wint_t *buf = PyMem_New(wint_t, n + 1);
+ if (buf == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ if (use_xy) {
+ Py_BEGIN_ALLOW_THREADS
+ rtn = mvwgetn_wstr(self->win, y, x, buf, n);
+ Py_END_ALLOW_THREADS
+ }
+ else {
+ Py_BEGIN_ALLOW_THREADS
+ rtn = wgetn_wstr(self->win, buf, n);
+ Py_END_ALLOW_THREADS
+ }
+
+ if (rtn == ERR) {
+ PyMem_Free(buf);
+ return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
+ }
+
+ /* wgetn_wstr() fills a wint_t buffer; copy it to a wchar_t buffer. */
+ Py_ssize_t len = 0;
+ while (buf[len]) {
+ len++;
+ }
+ wchar_t *wbuf = PyMem_New(wchar_t, len + 1);
+ if (wbuf == NULL) {
+ PyMem_Free(buf);
+ return PyErr_NoMemory();
+ }
+ for (Py_ssize_t i = 0; i < len; i++) {
+ wbuf[i] = (wchar_t)buf[i];
+ }
+ PyObject *res = PyUnicode_FromWideChar(wbuf, len);
+ PyMem_Free(wbuf);
+ PyMem_Free(buf);
+ return res;
+}
+
+PyDoc_STRVAR(_curses_window_in_wstr__doc__,
+"in_wstr([y, x,] n=2047)\n"
+"Return a string of characters, extracted from the window.\n"
+"\n"
+" y\n"
+" Y-coordinate.\n"
+" x\n"
+" X-coordinate.\n"
+" n\n"
+" Maximal number of characters.\n"
+"\n"
+"This is the wide-character variant of instr(); it returns a str.");
+
+static PyObject *
+PyCursesWindow_in_wstr(PyObject *op, PyObject *args)
+{
+ PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op);
+ int rtn, use_xy = 0, y = 0, x = 0;
+ unsigned int max_buf_size = 2048;
+ unsigned int n = max_buf_size - 1;
+
+ if (!curses_clinic_parse_optional_xy_n(args, &y, &x, &n, &use_xy,
+ "_curses.window.in_wstr"))
+ {
+ return NULL;
+ }
+
+ n = Py_MIN(n, max_buf_size - 1);
+ wchar_t *buf = PyMem_New(wchar_t, n + 1);
+ if (buf == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ if (use_xy) {
+ rtn = mvwinnwstr(self->win, y, x, buf, n);
+ }
+ else {
+ rtn = winnwstr(self->win, buf, n);
+ }
+
+ if (rtn == ERR) {
+ PyMem_Free(buf);
+ return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
+ }
+ PyObject *res = PyUnicode_FromWideChar(buf, -1);
+ PyMem_Free(buf);
+ return res;
+}
+#endif /* HAVE_NCURSESW */
+
/*[clinic input]
_curses.window.insstr
/*[clinic end generated code: output=287ad1cc8982217f input=a6f2dc86a4648b32]*/
{
chtype ch_;
-
+#ifdef HAVE_NCURSESW
+ cchar_t wch;
+ int type = PyCurses_ConvertToCell(self, ch, attr, "vline", &ch_, &wch);
+ if (type == 0) {
+ return NULL;
+ }
+#else
if (!PyCurses_ConvertToChtype(self, ch, &ch_))
return NULL;
+#endif
if (group_left_1) {
if (wmove(self->win, y, x) == ERR) {
curses_window_set_error(self, "wmove", "vline");
return NULL;
}
}
+#ifdef HAVE_NCURSESW
+ if (type == 2) {
+ int rtn = wvline_set(self->win, &wch, n);
+ return curses_window_check_err(self, rtn, "wvline_set", "vline");
+ }
+#endif
int rtn = wvline(self->win, ch_ | (attr_t)attr, n);
return curses_window_check_err(self, rtn, "wvline", "vline");
}
"getstr", PyCursesWindow_getstr, METH_VARARGS,
_curses_window_getstr__doc__
},
+#ifdef HAVE_NCURSESW
+ {
+ "get_wstr", PyCursesWindow_get_wstr, METH_VARARGS,
+ _curses_window_get_wstr__doc__
+ },
+#endif
{"getyx", PyCursesWindow_getyx, METH_NOARGS,
"getyx($self, /)\n--\n\n"
"Return a tuple (y, x) of the current cursor position."},
"instr", PyCursesWindow_instr, METH_VARARGS,
_curses_window_instr__doc__
},
+#ifdef HAVE_NCURSESW
+ {
+ "in_wstr", PyCursesWindow_in_wstr, METH_VARARGS,
+ _curses_window_in_wstr__doc__
+ },
+#endif
_CURSES_WINDOW_IS_LINETOUCHED_METHODDEF
{"is_wintouched", PyCursesWindow_is_wintouched, METH_NOARGS,
"is_wintouched($self, /)\n--\n\n"
return PyBytes_FromStringAndSize(&ch, 1);
}
+#ifdef HAVE_NCURSESW
+/*[clinic input]
+_curses.erasewchar
+
+Return the user's current wide-character erase character.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_erasewchar_impl(PyObject *module)
+/*[clinic end generated code: output=7f3bd8c9097ac456 input=f7e9a3893b4df2f8]*/
+{
+ wchar_t ch;
+
+ PyCursesStatefulInitialised(module);
+
+ if (erasewchar(&ch) == ERR) {
+ curses_set_error(module, "erasewchar", NULL);
+ return NULL;
+ }
+ return PyUnicode_FromWideChar(&ch, 1);
+}
+#endif /* HAVE_NCURSESW */
+
/*[clinic input]
_curses.flash
return PyBytes_FromStringAndSize(&ch, 1);
}
+#ifdef HAVE_NCURSESW
+/*[clinic input]
+_curses.killwchar
+
+Return the user's current wide-character line kill character.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_killwchar_impl(PyObject *module)
+/*[clinic end generated code: output=eac1fd72a0c88d42 input=5c2d7d1ab2f24eb7]*/
+{
+ wchar_t ch;
+
+ if (killwchar(&ch) == ERR) {
+ curses_set_error(module, "killwchar", NULL);
+ return NULL;
+ }
+ return PyUnicode_FromWideChar(&ch, 1);
+}
+#endif /* HAVE_NCURSESW */
+
/*[clinic input]
_curses.longname
return PyBytes_FromString(res);
}
+#ifdef HAVE_NCURSESW
+/*[clinic input]
+_curses.wunctrl
+
+ ch: object
+ /
+
+Return a printable representation of the wide character ch.
+
+Control characters are displayed as a caret followed by the character,
+for example as ^C. Printing characters are left as they are.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_wunctrl(PyObject *module, PyObject *ch)
+/*[clinic end generated code: output=7b16d5534ff05728 input=9ceb6749118bd07c]*/
+{
+ chtype ch_;
+ wchar_t wstr[CCHARW_MAX + 1];
+ cchar_t wcval;
+
+ PyCursesStatefulInitialised(module);
+
+ int type = PyCurses_ConvertToCchar_t(NULL, ch, &ch_, wstr);
+ if (type == 0) {
+ return NULL;
+ }
+ if (type == 1) {
+ /* A narrow character is the spacing character of the cell. */
+ wstr[0] = (wchar_t)(ch_ & A_CHARTEXT);
+ wstr[1] = L'\0';
+ }
+ if (setcchar(&wcval, wstr, A_NORMAL, 0, NULL) == ERR) {
+ curses_set_error(module, "setcchar", "wunctrl");
+ return NULL;
+ }
+
+ wchar_t *res = wunctrl(&wcval);
+ if (res == NULL) {
+ curses_set_null_error(module, "wunctrl", NULL);
+ return NULL;
+ }
+ return PyUnicode_FromWideChar(res, -1);
+}
+#endif /* HAVE_NCURSESW */
+
/*[clinic input]
_curses.ungetch
_CURSES_ECHO_METHODDEF
_CURSES_ENDWIN_METHODDEF
_CURSES_ERASECHAR_METHODDEF
+ _CURSES_ERASEWCHAR_METHODDEF
_CURSES_FILTER_METHODDEF
_CURSES_NOFILTER_METHODDEF
_CURSES_FLASH_METHODDEF
_CURSES_IS_TERM_RESIZED_METHODDEF
_CURSES_KEYNAME_METHODDEF
_CURSES_KILLCHAR_METHODDEF
+ _CURSES_KILLWCHAR_METHODDEF
_CURSES_LONGNAME_METHODDEF
_CURSES_META_METHODDEF
_CURSES_MOUSEINTERVAL_METHODDEF
_CURSES_TPARM_METHODDEF
_CURSES_TYPEAHEAD_METHODDEF
_CURSES_UNCTRL_METHODDEF
+ _CURSES_WUNCTRL_METHODDEF
_CURSES_UNGETCH_METHODDEF
_CURSES_UPDATE_LINES_COLS_METHODDEF
_CURSES_UNGET_WCH_METHODDEF
return _curses_erasechar_impl(module);
}
+#if defined(HAVE_NCURSESW)
+
+PyDoc_STRVAR(_curses_erasewchar__doc__,
+"erasewchar($module, /)\n"
+"--\n"
+"\n"
+"Return the user\'s current wide-character erase character.");
+
+#define _CURSES_ERASEWCHAR_METHODDEF \
+ {"erasewchar", (PyCFunction)_curses_erasewchar, METH_NOARGS, _curses_erasewchar__doc__},
+
+static PyObject *
+_curses_erasewchar_impl(PyObject *module);
+
+static PyObject *
+_curses_erasewchar(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_erasewchar_impl(module);
+}
+
+#endif /* defined(HAVE_NCURSESW) */
+
PyDoc_STRVAR(_curses_flash__doc__,
"flash($module, /)\n"
"--\n"
return _curses_killchar_impl(module);
}
+#if defined(HAVE_NCURSESW)
+
+PyDoc_STRVAR(_curses_killwchar__doc__,
+"killwchar($module, /)\n"
+"--\n"
+"\n"
+"Return the user\'s current wide-character line kill character.");
+
+#define _CURSES_KILLWCHAR_METHODDEF \
+ {"killwchar", (PyCFunction)_curses_killwchar, METH_NOARGS, _curses_killwchar__doc__},
+
+static PyObject *
+_curses_killwchar_impl(PyObject *module);
+
+static PyObject *
+_curses_killwchar(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_killwchar_impl(module);
+}
+
+#endif /* defined(HAVE_NCURSESW) */
+
PyDoc_STRVAR(_curses_longname__doc__,
"longname($module, /)\n"
"--\n"
#define _CURSES_UNCTRL_METHODDEF \
{"unctrl", (PyCFunction)_curses_unctrl, METH_O, _curses_unctrl__doc__},
+#if defined(HAVE_NCURSESW)
+
+PyDoc_STRVAR(_curses_wunctrl__doc__,
+"wunctrl($module, ch, /)\n"
+"--\n"
+"\n"
+"Return a printable representation of the wide character ch.\n"
+"\n"
+"Control characters are displayed as a caret followed by the character,\n"
+"for example as ^C. Printing characters are left as they are.");
+
+#define _CURSES_WUNCTRL_METHODDEF \
+ {"wunctrl", (PyCFunction)_curses_wunctrl, METH_O, _curses_wunctrl__doc__},
+
+#endif /* defined(HAVE_NCURSESW) */
+
PyDoc_STRVAR(_curses_ungetch__doc__,
"ungetch($module, ch, /)\n"
"--\n"
#define _CURSES_NOFILTER_METHODDEF
#endif /* !defined(_CURSES_NOFILTER_METHODDEF) */
+#ifndef _CURSES_ERASEWCHAR_METHODDEF
+ #define _CURSES_ERASEWCHAR_METHODDEF
+#endif /* !defined(_CURSES_ERASEWCHAR_METHODDEF) */
+
#ifndef _CURSES_GETSYX_METHODDEF
#define _CURSES_GETSYX_METHODDEF
#endif /* !defined(_CURSES_GETSYX_METHODDEF) */
#define _CURSES_IS_TERM_RESIZED_METHODDEF
#endif /* !defined(_CURSES_IS_TERM_RESIZED_METHODDEF) */
+#ifndef _CURSES_KILLWCHAR_METHODDEF
+ #define _CURSES_KILLWCHAR_METHODDEF
+#endif /* !defined(_CURSES_KILLWCHAR_METHODDEF) */
+
#ifndef _CURSES_MOUSEINTERVAL_METHODDEF
#define _CURSES_MOUSEINTERVAL_METHODDEF
#endif /* !defined(_CURSES_MOUSEINTERVAL_METHODDEF) */
#define _CURSES_TYPEAHEAD_METHODDEF
#endif /* !defined(_CURSES_TYPEAHEAD_METHODDEF) */
+#ifndef _CURSES_WUNCTRL_METHODDEF
+ #define _CURSES_WUNCTRL_METHODDEF
+#endif /* !defined(_CURSES_WUNCTRL_METHODDEF) */
+
#ifndef _CURSES_UNGET_WCH_METHODDEF
#define _CURSES_UNGET_WCH_METHODDEF
#endif /* !defined(_CURSES_UNGET_WCH_METHODDEF) */
#ifndef _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF
#define _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF
#endif /* !defined(_CURSES_ASSUME_DEFAULT_COLORS_METHODDEF) */
-/*[clinic end generated code: output=7494804bf2c4d1f5 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=0bce70b538541c9e input=a9049054013a1b77]*/