.. function:: erasechar()
- Return the user's current erase character as a one-byte bytes object. Under Unix operating systems this
- is a property of the controlling tty of the curses program, and is not set by
- the curses library itself.
+ Return the user's current erase character as a raw byte, a :class:`bytes`
+ object of length 1. See also :func:`erasewchar`.
.. 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.
+ Return the user's current erase character as a one-character :class:`str`.
+ Under Unix operating systems this is a property of the controlling tty of the
+ curses program, and is not set by the curses library itself.
.. versionadded:: next
.. function:: killchar()
- Return the user's current line kill character as a one-byte bytes object. Under Unix operating systems
- this is a property of the controlling tty of the curses program, and is not set
- by the curses library itself.
+ Return the user's current line kill character as a raw byte, a :class:`bytes`
+ object of length 1. See also :func:`killwchar`.
.. 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.
+ Return the user's current line kill character as a one-character :class:`str`.
+ Under Unix operating systems this is a property of the controlling tty of the
+ curses program, and is not set by the curses library itself.
.. versionadded:: next
.. function:: unctrl(ch)
Return a bytes object which is a printable representation of the character *ch*.
- Control characters are represented as a caret followed by the character, for
- example as ``b'^C'``. Printing characters are left as they are.
+ *ch* cannot be a character that does not fit in a single byte; use
+ :func:`wunctrl` for those.
.. 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.
+ Return a string which is a printable representation of the character *ch*;
+ any attributes and color pair are ignored.
+ ASCII control characters are represented as a caret followed by a character,
+ for example as ``'^C'``. Printing characters, including non-ASCII characters
+ printable in the locale, are left as they are. The representation of other
+ characters is defined by the underlying curses library.
.. versionadded:: next
.. function:: ungetch(ch)
- Push *ch* so the next :meth:`~window.getch` will return it.
+ Push *ch* so the next :meth:`~window.getch` or :meth:`~window.get_wch` will
+ return it.
+
+ *ch* may be an integer (a key code or character code), a byte, or a string of
+ length 1. A one-character string is pushed like :func:`unget_wch`; on a
+ narrow build it must encode to a single byte.
.. note::
Only one *ch* can be pushed before :meth:`!getch` is called.
+ .. versionchanged:: next
+ A one-character string argument is no longer required to encode to a single
+ byte, except on a narrow build.
+
.. function:: update_lines_cols()
.. versionadded:: 3.3
+ .. versionchanged:: next
+ Also available on a narrow build, where *ch* must encode to a single byte
+ (an 8-bit locale).
+
.. function:: ungetmouse(id, x, y, z, bstate)
.. method:: window.getbkgd()
Return the given window's current background character/attribute pair.
+ It cannot represent a background set with a wide character or with a color
+ pair outside the :func:`color_pair` range; use :meth:`getbkgrnd` for those.
.. method:: window.getbkgrnd()
Return the given window's current background as a :class:`complexchar`.
- This is the wide-character variant of :meth:`getbkgd`: the returned object
- carries the background character together with its attributes and color pair,
- and the color pair is not limited to the value that fits in a
- :func:`color_pair`.
+ Unlike :meth:`getbkgd`, the returned object carries the background character
+ together with its attributes and color pair, and the color pair is not limited
+ to the value that fits in a :func:`color_pair`.
.. versionadded:: next
range: function keys, keypad keys and so on are represented by numbers higher
than 255. In no-delay mode, return ``-1`` if there is no input, otherwise
wait until a key is pressed.
+ A multibyte character is returned as its encoded bytes one at a time; use
+ :meth:`get_wch` to read it as a single character.
.. method:: window.get_wch([y, x])
Get a wide character. Return a character for most keys, or an integer for
- function keys, keypad keys, and other special keys.
+ function keys, keypad keys, and other special keys. Unlike :meth:`getch`, an
+ ordinary key is returned as a one-character :class:`str`.
In no-delay mode, raise an exception if there is no input.
.. versionadded:: 3.3
+ .. versionchanged:: next
+ Also available on a narrow build, where only a character representable as a
+ single byte (an 8-bit locale) can be returned.
+
.. method:: window.getdelay()
Read a bytes object from the user, with primitive line editing capacity.
At most *n* characters are read;
*n* defaults to and cannot exceed 2047.
+ A multibyte character is returned as its encoded bytes; use :meth:`get_wstr`
+ to read the input as a :class:`str`.
.. versionchanged:: 3.14
The maximum value for *n* was increased from 1023 to 2047.
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.
+ Unlike :meth:`getstr`, 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
Return the character at the given position in the window. The bottom 8 bits are
the character proper, and upper bits are the attributes.
+ It cannot represent a cell holding combining characters, a character that does
+ not fit in a single byte, or a color pair outside the :func:`color_pair`
+ range; use :meth:`in_wch` for those.
.. method:: window.in_wch([y, x])
Return the complex character at the given position in the window as a
- :class:`complexchar`. This is the wide-character variant of :meth:`inch`:
- the returned object carries the cell's text (a spacing character optionally
- followed by combining characters) together with its attributes and color
- pair, none of which :meth:`inch` can represent.
+ :class:`complexchar`. Unlike :meth:`inch`, the returned object carries the
+ cell's text (a spacing character optionally followed by combining characters)
+ together with its attributes and color pair.
.. versionadded:: next
from the characters. If *n* is specified, :meth:`instr` returns a string
at most *n* characters long (exclusive of the trailing NUL).
The maximum value for *n* is 2047.
+ A character not representable in the window's encoding cannot be returned;
+ use :meth:`in_wstr` for those.
.. versionchanged:: 3.14
The maximum value for *n* was increased from 1023 to 2047.
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.
+ current cursor position, or at *y*, *x* if specified. Unlike :meth:`instr`,
+ 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
:func:`curses.erasechar`, :func:`curses.killchar` and :func:`curses.unctrl`.
On a narrow (non-ncursesw) build the character cell holds a single character
without combining marks, representable as one byte in the window's encoding,
- and :meth:`~curses.window.in_wstr` returns its decoded text;
- :meth:`~curses.window.get_wstr` and the :func:`curses.erasewchar`,
- :func:`curses.killwchar` and :func:`curses.wunctrl` functions require the
- wide-character ncursesw library.
+ and :meth:`~curses.window.in_wstr` returns its decoded text.
(Contributed by Serhiy Storchaka in :gh:`151757`.)
+* The wide-character :mod:`curses` functions and methods
+ :meth:`~curses.window.get_wch`, :meth:`~curses.window.get_wstr`,
+ :func:`curses.unget_wch`, :func:`curses.erasewchar`,
+ :func:`curses.killwchar` and :func:`curses.wunctrl` now also work when Python
+ is not built against a wide-character-aware curses library, on an 8-bit
+ locale, where each character is a single byte in the relevant encoding.
+ :func:`curses.ungetch` now also accepts a one-character string, like
+ :func:`curses.unget_wch`; on a wide-character build it can be any character
+ (previously a multibyte character raised :exc:`OverflowError`).
+ (Contributed by Serhiy Storchaka in :gh:`152470`.)
+
* Add :func:`curses.nofilter`, which undoes the effect of :func:`curses.filter`.
(Contributed by Serhiy Storchaka in :gh:`151744`.)
return wrapped
return deco
+def _wide_build():
+ # True on a build that stores wide-character cells (built against ncursesw).
+ # A wide build accepts a spacing character plus a combining mark in a single
+ # cell; a narrow build accepts only one character per cell. This stays a
+ # reliable wide/narrow signal even as the wide-character functions (get_wch()
+ # and friends) become available on narrow builds too, because the
+ # multi-codepoint cell capacity itself is build-specific.
+ if not hasattr(curses, 'complexchar'):
+ return hasattr(curses.window, 'get_wch')
+ try:
+ curses.complexchar('e\u0301') # 'e' + combining acute: two code points
+ except ValueError:
+ return False
+ return True
+
+WIDE_BUILD = _wide_build()
+
+def requires_wide_build(test):
+ @functools.wraps(test)
+ def wrapped(self, *args, **kwargs):
+ if not WIDE_BUILD:
+ raise unittest.SkipTest('requires a wide-character curses build')
+ test(self, *args, **kwargs)
+ return wrapped
+
def requires_colors(test):
@functools.wraps(test)
# one byte.
if not self._encodable(s):
return False
- if hasattr(self.stdscr, 'get_wch'): # wide build
+ if WIDE_BUILD:
return True
return len(s.encode(self.stdscr.encoding)) == len(s)
return str(stdscr.in_wch(y, x))
return stdscr.instr(y, x, 1).decode(stdscr.encoding)
- @requires_curses_window_meth('get_wch')
+ @requires_wide_build
def test_addch_combining(self):
stdscr = self.stdscr
stdscr.move(0, 0)
self.assertRaises(ValueError, stdscr.addch, '\n\u0301')
self.assertRaises(ValueError, stdscr.addch, '\ne\u0301')
- @requires_curses_window_meth('get_wch')
+ @requires_wide_build
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,
self.assertRaises(ValueError, stdscr.addch,
'\U0001f468\u200d\U0001f469') # man ZWJ woman
- @requires_curses_window_meth('get_wch')
+ @requires_wide_build
def test_wide_characters(self):
# Wide and combining characters in the character-cell methods.
stdscr = self.stdscr
# border() and box() cannot mix integer and wide-string characters.
self.assertRaises(TypeError, stdscr.box, vline, ord('-'))
- @requires_curses_func('complexchar')
def test_complexchar_in_cell_methods(self):
# Every single-character-cell method also accepts a complexchar, whose
# attributes and color pair come from the cell itself.
self.assertRaises(TypeError, stdscr.hline, h, 3, curses.A_BOLD)
self.assertRaises(TypeError, stdscr.vline, v, 3, curses.A_BOLD)
- @requires_curses_window_meth('in_wstr')
def test_in_wstr(self):
# The wide-character window read returns a str (instr returns bytes).
# See _encodable for the character set.
self.assertEqual(stdscr.in_wstr(0, 0, len(s)), s)
self.assertIsInstance(stdscr.instr(0, 0, len(s)), bytes)
- @requires_curses_func('complexchar')
def test_complexchar(self):
# A complexchar is a styled wide-character cell: str() is its text,
# and the attr and pair attributes are its rendition.
self.assertRaises(ValueError, curses.complexchar, 'A', 0, -1)
self.assertRaises(ValueError, curses.complexchar, 'ab')
- @requires_curses_window_meth('in_wch')
def test_in_wch(self):
# in_wch() returns the styled wide cell as a complexchar -- something
# inch() (a packed chtype) cannot represent.
stdscr.move(0, 0)
self.assertEqual(str(stdscr.in_wch()), 'A')
- @requires_curses_window_meth('in_wch')
@requires_colors
def test_in_wch_color(self):
# Unlike the chtype methods (which pack the pair into the value via
self.assertEqual(cc.pair, 1)
self.assertEqual(curses.complexchar('A', 0, 1).pair, 1)
- @requires_curses_window_meth('getbkgrnd')
def test_getbkgrnd(self):
# getbkgrnd() returns the background as a complexchar (getbkgd() can
# only return a packed chtype).
self.assertEqual(str(stdscr.getbkgrnd()), ch)
stdscr.bkgd(' ')
- @requires_curses_func('complexstr')
def test_complexstr(self):
# A complexstr is an immutable run of styled wide-character cells: the
# string counterpart of complexchar (as str is to a single character).
base = 'é' # 'e' + combining acute: two code points, one cell
# Combining sequences need wide-character cells (a narrow build stores
# one byte per cell).
- if hasattr(curses.window, 'get_wch') and self._encodable(base):
+ if WIDE_BUILD and self._encodable(base):
self.assertEqual(len(curses.complexstr(base)), 1)
self.assertEqual(curses.complexstr(base)[0], cc(base))
self.assertEqual(len(curses.complexstr('a' + base + 'b')), 3)
self.assertRaises(TypeError,
lambda: curses.complexstr(['A'], pair=0))
- @requires_curses_window_meth('in_wchstr')
def test_in_wchstr(self):
# in_wchstr() returns a complexstr -- the styled-cell counterpart of
# instr() (bytes) and in_wstr() (str), which both strip the rendition.
stdscr.move(0, 0)
self.assertEqual(str(stdscr.in_wchstr())[:3], 'AbC')
- @requires_curses_window_meth('in_wchstr')
def test_complexstr_in_write_methods(self):
# addstr/addnstr/insstr/insnstr also accept a complexstr, written via
# the wide-character functions; a plain str keeps its current meaning.
# str is stored as a wide-character cell on a wide build, so every
# encodable character round-trips, insch() included. A multibyte
# character does not fit a cell on a narrow build and is skipped.
- wide = hasattr(stdscr, 'get_wch')
for c in ('é', '¤', '€', 'є'):
- if not self._encodable(c):
- continue
- if not wide and len(c.encode(encoding)) != 1:
+ if not self._storable(c):
continue
with self.subTest(c=c):
stdscr.addch(0, 0, c)
self.assertEqual(win.getstr(), b'amet')
self.assertEqual(win.instr(1, 0), b'amet dolor ')
- @requires_curses_window_meth('get_wstr')
def test_get_wstr(self):
# get_wstr() reads input as a str (getstr() returns bytes); feed it with
# unget_wch(). See _encodable for the character set.
'naïve ¤', # ISO-8859-1
'soupçon €', # ISO-8859-15
'дяк']: # KOI8-U
- if self._encodable(s):
+ if self._storable(s):
with self.subTest(s=s):
win.erase()
for ch in reversed(s + '\n'):
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(10), '^J')
# See _encodable for the character set (all printable here).
for c in ('A', 'é', '¤', '€', 'є'):
- self.assertEqual(curses.wunctrl(c), c)
+ if self._storable(c):
+ self.assertEqual(curses.wunctrl(c), c)
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')
+ if WIDE_BUILD:
+ # 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):
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)
+ c = curses.erasewchar()
+ self.assertIsInstance(c, str)
+ self.assertEqual(len(c), 1)
+ c = curses.killwchar()
+ self.assertIsInstance(c, str)
+ self.assertEqual(len(c), 1)
@requires_curses_func('define_key')
def test_key_management(self):
curses.ungetch(1025)
self.stdscr.getkey()
- @requires_curses_func('unget_wch')
+ @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
+ "unget_wch is broken in ncurses 5.7 and earlier")
+ def test_ungetch_wch(self):
+ # ungetch() also accepts a character, like unget_wch(), and it
+ # round-trips through get_wch() -- including a character that does not
+ # fit in a single byte.
+ stdscr = self.stdscr
+ for ch in ('a', '\xe9', '\xa4', '€', 'є', '\U0010FFFF'):
+ if not self._storable(ch):
+ continue
+ curses.ungetch(ch)
+ self.assertEqual(stdscr.get_wch(), ch)
+ # An int is a raw keycode, not a character codepoint.
+ curses.ungetch(curses.KEY_LEFT)
+ self.assertEqual(stdscr.getch(), curses.KEY_LEFT)
+
@unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
"unget_wch is broken in ncurses 5.7 and earlier")
def test_unget_wch(self):
stdscr = self.stdscr
encoding = stdscr.encoding
- # See _encodable for the character set, plus a non-BMP character.
+ # See _storable for the character set, plus a non-BMP character.
for ch in ('a', '\xe9', '\xa4', '\u20ac', '\u0454', '\U0010FFFF'):
- try:
- ch.encode(encoding)
- except UnicodeEncodeError:
+ if not self._storable(ch):
continue
try:
curses.unget_wch(ch)
except Exception as err:
self.fail("unget_wch(%a) failed with encoding %s: %s"
- % (ch, stdscr.encoding, err))
- read = stdscr.get_wch()
- self.assertEqual(read, ch)
-
- code = ord(ch)
- curses.unget_wch(code)
- read = stdscr.get_wch()
- self.assertEqual(read, ch)
+ % (ch, encoding, err))
+ self.assertEqual(stdscr.get_wch(), ch)
+
+ curses.unget_wch(ord(ch))
+ self.assertEqual(stdscr.get_wch(), ch)
def test_encoding(self):
stdscr = self.stdscr
:func:`curses.wunctrl`. On a narrow (non-ncursesw) build the character cell
holds a single character without combining marks, representable as one byte in
the window's encoding, and :meth:`curses.window.in_wstr` returns its decoded
-text; :meth:`curses.window.get_wstr` and the :func:`curses.erasewchar`,
-:func:`curses.killwchar` and :func:`curses.wunctrl` functions require the
-wide-character ncursesw library.
+text.
--- /dev/null
+The wide-character :mod:`curses` functions and methods
+:meth:`curses.window.get_wch`, :meth:`curses.window.get_wstr`,
+:func:`curses.unget_wch`, :func:`curses.erasewchar`, :func:`curses.killwchar`
+and :func:`curses.wunctrl` now also work when Python is not built against a
+wide-character-aware curses library, on an 8-bit locale, where each character
+is a single byte in the relevant encoding. :func:`curses.ungetch` now also
+accepts a one-character string, like :func:`curses.unget_wch`; on a
+wide-character build it can be any character (previously a multibyte character
+raised :exc:`OverflowError`).
}
}
-#ifdef HAVE_NCURSESW
/*[clinic input]
_curses.window.get_wch
int y, int x)
/*[clinic end generated code: output=9f4f86e91fe50ef3 input=dd7e5367fb49dc48]*/
{
+#ifdef HAVE_NCURSESW
int ct;
wint_t rtn;
return PyLong_FromLong(rtn);
else
return PyUnicode_FromOrdinal(rtn);
-}
+#else
+ /* Without the wide library, read one key with wgetch(): a value above 255
+ is a function key (returned as an int); a byte is decoded with the
+ window's encoding (8-bit locales). */
+ int rtn;
+ Py_BEGIN_ALLOW_THREADS
+ if (!group_right_1) {
+ rtn = wgetch(self->win);
+ }
+ else {
+ rtn = mvwgetch(self->win, y, x);
+ }
+ Py_END_ALLOW_THREADS
+
+ if (rtn == ERR) {
+ const char *funcname = group_right_1 ? "mvwgetch" : "wgetch";
+ return curses_check_signals_on_input_error(self, funcname, "get_wch");
+ }
+ if (rtn > 255) {
+ return PyLong_FromLong(rtn);
+ }
+ char ch = (char)rtn;
+ return PyUnicode_Decode(&ch, 1, self->encoding, NULL);
#endif
+}
/*
* Helper function for parsing parameters from getstr() and instr().
" n\n"
" Maximal number of characters.");
+/* Read user input into a new bytes object (empty on ERR), with primitive line
+ editing. Shared by getstr() and, without the wide library, by get_wstr(). */
static PyObject *
-PyCursesWindow_getstr(PyObject *op, PyObject *args)
+curses_window_getstr_bytes(PyCursesWindowObject *self, PyObject *args,
+ const char *funcname)
{
- 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.instr"))
+ if (!curses_clinic_parse_optional_xy_n(args, &y, &x, &n, &use_xy, funcname))
{
return NULL;
}
return PyBytesWriter_FinishWithSize(writer, strlen(buf));
}
+static PyObject *
+PyCursesWindow_getstr(PyObject *op, PyObject *args)
+{
+ PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op);
+ return curses_window_getstr_bytes(self, args, "_curses.window.getstr");
+}
+
/*[clinic input]
_curses.window.hline
"instr() returns a string at most n characters long (exclusive of\n"
"the trailing NUL).");
+/* Extract characters from the window into a new bytes object (empty on ERR),
+ with attributes and color stripped. Shared by instr() and, without the wide
+ library, by in_wstr(). */
static PyObject *
-PyCursesWindow_instr(PyObject *op, PyObject *args)
+curses_window_instr_bytes(PyCursesWindowObject *self, PyObject *args,
+ const char *funcname)
{
- 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.instr"))
+ if (!curses_clinic_parse_optional_xy_n(args, &y, &x, &n, &use_xy, funcname))
{
return NULL;
}
return PyBytesWriter_FinishWithSize(writer, strlen(buf));
}
-#ifdef HAVE_NCURSESW
+static PyObject *
+PyCursesWindow_instr(PyObject *op, PyObject *args)
+{
+ PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op);
+ return curses_window_instr_bytes(self, args, "_curses.window.instr");
+}
+
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"
PyCursesWindow_get_wstr(PyObject *op, PyObject *args)
{
PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op);
+#ifdef HAVE_NCURSESW
int rtn, use_xy = 0, y = 0, x = 0;
unsigned int max_buf_size = 2048;
unsigned int n = max_buf_size - 1;
PyMem_Free(wbuf);
PyMem_Free(buf);
return res;
-}
+#else
+ /* Without the wide library, read the bytes as getstr() does and decode them
+ with the window's encoding. */
+ PyObject *bytes = curses_window_getstr_bytes(self, args,
+ "_curses.window.get_wstr");
+ if (bytes == NULL) {
+ return NULL;
+ }
+ PyObject *res = PyUnicode_Decode(PyBytes_AS_STRING(bytes),
+ PyBytes_GET_SIZE(bytes),
+ self->encoding, NULL);
+ Py_DECREF(bytes);
+ return res;
#endif /* HAVE_NCURSESW */
+}
PyDoc_STRVAR(_curses_window_in_wstr__doc__,
"in_wstr([y, x,] n=2047)\n"
PyCursesWindow_in_wstr(PyObject *op, PyObject *args)
{
PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op);
+#ifdef HAVE_NCURSESW
int rtn, use_xy = 0, y = 0, x = 0;
unsigned int max_buf_size = 2048;
unsigned int n = max_buf_size - 1;
}
n = Py_MIN(n, max_buf_size - 1);
-#ifdef HAVE_NCURSESW
wchar_t *buf = PyMem_New(wchar_t, n + 1);
if (buf == NULL) {
return PyErr_NoMemory();
PyMem_Free(buf);
return res;
#else
- /* Without the wide library, read the locale-encoded bytes and decode them
+ /* Without the wide library, read the bytes as instr() does and decode them
with the window's encoding. */
- char *buf = PyMem_New(char, n + 1);
- if (buf == NULL) {
- return PyErr_NoMemory();
- }
-
- if (use_xy) {
- rtn = mvwinnstr(self->win, y, x, buf, n);
- }
- else {
- rtn = winnstr(self->win, buf, n);
- }
-
- if (rtn == ERR) {
- PyMem_Free(buf);
- return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
+ PyObject *bytes = curses_window_instr_bytes(self, args,
+ "_curses.window.in_wstr");
+ if (bytes == NULL) {
+ return NULL;
}
- PyObject *res = PyUnicode_Decode(buf, strlen(buf), self->encoding, NULL);
- PyMem_Free(buf);
+ PyObject *res = PyUnicode_Decode(PyBytes_AS_STRING(bytes),
+ PyBytes_GET_SIZE(bytes),
+ self->encoding, NULL);
+ Py_DECREF(bytes);
return res;
#endif
}
"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."},
return PyBytes_FromStringAndSize(&ch, 1);
}
-#ifdef HAVE_NCURSESW
/*[clinic input]
_curses.erasewchar
_curses_erasewchar_impl(PyObject *module)
/*[clinic end generated code: output=7f3bd8c9097ac456 input=f7e9a3893b4df2f8]*/
{
- wchar_t ch;
-
PyCursesStatefulInitialised(module);
+#ifdef HAVE_NCURSESW
+ wchar_t ch;
+
if (erasewchar(&ch) == ERR) {
curses_set_error(module, "erasewchar", NULL);
return NULL;
}
return PyUnicode_FromWideChar(&ch, 1);
+#else
+ /* Without the wide library, decode the single-byte erase character
+ with the screen's encoding. */
+ char ch = erasechar();
+
+ return PyUnicode_Decode(&ch, 1, curses_screen_encoding, NULL);
+#endif
}
-#endif /* HAVE_NCURSESW */
/*[clinic input]
_curses.flash
return PyBytes_FromStringAndSize(&ch, 1);
}
-#ifdef HAVE_NCURSESW
/*[clinic input]
_curses.killwchar
_curses_killwchar_impl(PyObject *module)
/*[clinic end generated code: output=eac1fd72a0c88d42 input=5c2d7d1ab2f24eb7]*/
{
+#ifdef HAVE_NCURSESW
wchar_t ch;
if (killwchar(&ch) == ERR) {
return NULL;
}
return PyUnicode_FromWideChar(&ch, 1);
+#else
+ /* Without the wide library, decode the single-byte kill character
+ with the screen's encoding. */
+ char ch = killchar();
+
+ return PyUnicode_Decode(&ch, 1, curses_screen_encoding, NULL);
+#endif
}
-#endif /* HAVE_NCURSESW */
/*[clinic input]
_curses.longname
return PyBytes_FromString(res);
}
-#ifdef HAVE_NCURSESW
/*[clinic input]
_curses.wunctrl
_curses_wunctrl(PyObject *module, PyObject *ch)
/*[clinic end generated code: output=7b16d5534ff05728 input=9ceb6749118bd07c]*/
{
+ PyCursesStatefulInitialised(module);
+
+#ifdef HAVE_NCURSESW
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;
return NULL;
}
return PyUnicode_FromWideChar(res, -1);
-}
-#endif /* HAVE_NCURSESW */
-
-/*[clinic input]
-_curses.ungetch
-
- ch: object
- /
-
-Push ch so the next getch() will return it.
-[clinic start generated code]*/
-
-static PyObject *
-_curses_ungetch(PyObject *module, PyObject *ch)
-/*[clinic end generated code: output=9b19d8268376d887 input=6681e6ae4c42e5eb]*/
-{
+#else
+ /* Without the wide library, fall back to the single-byte unctrl() and
+ decode its result with the screen's encoding. */
chtype ch_;
- PyCursesStatefulInitialised(module);
-
- if (!PyCurses_ConvertToChtype(NULL, ch, &ch_))
+ if (!PyCurses_ConvertToChtype(NULL, ch, &ch_)) {
return NULL;
+ }
- return curses_check_err(module, ungetch(ch_), "ungetch", NULL);
+ const char *res = unctrl(ch_);
+ if (res == NULL) {
+ curses_set_null_error(module, "unctrl", "wunctrl");
+ return NULL;
+ }
+ return PyUnicode_Decode(res, strlen(res), curses_screen_encoding, NULL);
+#endif
}
-#ifdef HAVE_NCURSESW
/* Convert an object to a character (wchar_t):
- int
}
}
+/*[clinic input]
+_curses.ungetch
+
+ ch: object
+ /
+
+Push ch so the next getch() will return it.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_ungetch(PyObject *module, PyObject *ch)
+/*[clinic end generated code: output=9b19d8268376d887 input=6681e6ae4c42e5eb]*/
+{
+ PyCursesStatefulInitialised(module);
+
+#ifdef HAVE_NCURSESW
+ /* Push a str through the wide queue, so a character that does not fit in a
+ single byte round-trips to get_wch(). An int stays a raw value (a keycode
+ or a byte) for getch(); use unget_wch() to push it as a character. */
+ if (PyUnicode_Check(ch)) {
+ wchar_t wch;
+ if (!PyCurses_ConvertToWchar_t(ch, &wch))
+ return NULL;
+ return curses_check_err(module, unget_wch(wch), "unget_wch", "ungetch");
+ }
+#endif
+
+ chtype ch_;
+ if (!PyCurses_ConvertToChtype(NULL, ch, &ch_))
+ return NULL;
+
+ return curses_check_err(module, ungetch(ch_), "ungetch", NULL);
+}
+
/*[clinic input]
_curses.unget_wch
if (!PyCurses_ConvertToWchar_t(ch, &wch))
return NULL;
+#ifdef HAVE_NCURSESW
return curses_check_err(module, unget_wch(wch), "unget_wch", NULL);
-}
+#else
+ /* Without the wide library there is no unget_wch(): encode the character as
+ a single screen-encoding byte and push that. Narrow builds support only
+ 8-bit locales, so a character that does not fit in one byte is rejected. */
+ PyObject *str = PyUnicode_FromWideChar(&wch, 1);
+ if (str == NULL) {
+ return NULL;
+ }
+ PyObject *bytes = PyUnicode_AsEncodedString(str, curses_screen_encoding,
+ NULL);
+ Py_DECREF(str);
+ if (bytes == NULL) {
+ return NULL;
+ }
+ if (PyBytes_GET_SIZE(bytes) != 1) {
+ Py_DECREF(bytes);
+ PyErr_SetString(PyExc_OverflowError,
+ "character does not fit in a single byte");
+ return NULL;
+ }
+ int b = (unsigned char)PyBytes_AS_STRING(bytes)[0];
+ Py_DECREF(bytes);
+ return curses_check_err(module, ungetch(b), "ungetch", "unget_wch");
#endif
+}
#ifdef HAVE_CURSES_USE_ENV
/*[clinic input]
return return_value;
}
-#if defined(HAVE_NCURSESW)
-
PyDoc_STRVAR(_curses_window_get_wch__doc__,
"get_wch([y, x])\n"
"Get a wide character from terminal keyboard.\n"
return return_value;
}
-#endif /* defined(HAVE_NCURSESW) */
-
PyDoc_STRVAR(_curses_window_hline__doc__,
"hline([y, x,] ch, n, [attr])\n"
"Display a horizontal line.\n"
return _curses_erasechar_impl(module);
}
-#if defined(HAVE_NCURSESW)
-
PyDoc_STRVAR(_curses_erasewchar__doc__,
"erasewchar($module, /)\n"
"--\n"
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"
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"
#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_UNGETCH_METHODDEF \
{"ungetch", (PyCFunction)_curses_ungetch, METH_O, _curses_ungetch__doc__},
-#if defined(HAVE_NCURSESW)
-
PyDoc_STRVAR(_curses_unget_wch__doc__,
"unget_wch($module, ch, /)\n"
"--\n"
#define _CURSES_UNGET_WCH_METHODDEF \
{"unget_wch", (PyCFunction)_curses_unget_wch, METH_O, _curses_unget_wch__doc__},
-#endif /* defined(HAVE_NCURSESW) */
-
#if defined(HAVE_CURSES_USE_ENV)
PyDoc_STRVAR(_curses_use_env__doc__,
#define _CURSES_WINDOW_ENCLOSE_METHODDEF
#endif /* !defined(_CURSES_WINDOW_ENCLOSE_METHODDEF) */
-#ifndef _CURSES_WINDOW_GET_WCH_METHODDEF
- #define _CURSES_WINDOW_GET_WCH_METHODDEF
-#endif /* !defined(_CURSES_WINDOW_GET_WCH_METHODDEF) */
-
#ifndef _CURSES_WINDOW_NOUTREFRESH_METHODDEF
#define _CURSES_WINDOW_NOUTREFRESH_METHODDEF
#endif /* !defined(_CURSES_WINDOW_NOUTREFRESH_METHODDEF) */
#define _CURSES_IS_RAW_METHODDEF
#endif /* !defined(_CURSES_IS_RAW_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_USE_ENV_METHODDEF
#define _CURSES_USE_ENV_METHODDEF
#endif /* !defined(_CURSES_USE_ENV_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=bbf6d77a5813b1e1 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=f48f8e3554b30b86 input=a9049054013a1b77]*/