set_term() and screen.use() now update curses.LINES, curses.COLS,
curses.COLORS and curses.COLOR_PAIRS to the values of the current
screen. LINES and COLS are taken from the size of its standard window,
since ncurses sets its own LINES and COLS only when a screen is created.
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
initscr() and newterm() adopt the screen created by new_prescr(), so the
screen object returned by new_prescr() no longer owns it after that, and
new_prescr() returns the same object while the screen is pending.
slk_color() cast its color pair to a short, so pairs of 32768 and above
were rejected or silently applied as a different pair. Use
extended_slk_color() when it is available, like slk_attr_set() does.
complexstr_concat() is the sq_concat slot and returned NotImplemented
for a non-complexstr operand. PyNumber_Add() returns an sq_concat
result verbatim, so the singleton reached Python code instead of a
TypeError, and an augmented assignment rebound the name to it.
Setting the module constants with an unsigned conversion, needed for the
chtype constants that can set bits beyond a 32-bit long, turned ERR from -1
into 18446744073709551615. Set ERR and OK with a signed conversion.
Instead of one PyArg_ParseTuple() call per number of arguments, generate
the inlined code of the converters, as for all other parsing methods.
Every argument is parsed once.
Such functions now use the fastcall convention, except for constructors
and other functions which need a tuple.
instr(), in_wstr() and in_wchstr() clamped the count to 2047 and silently
truncated a longer line, which a pad can have. A window read cannot return
more than the columns left on the line, in the unit each method counts: cells,
characters, or bytes at CCHARW_MAX characters of MB_CUR_MAX bytes per cell.
Cap the count by that, and read the rest of the line when the count is
omitted, which it now can be.
getstr() and get_wstr() read the keyboard rather than the window, so their
limit stays.
Passing n to the library is ncurses' reading of n: it stores n characters
and adds a terminator. NetBSD curses counts the terminator in n.
Ask a library that is neither ncurses nor PDCurses for n + 1, and read
again if it stored more than asked; truncating could split a multibyte
character. This is not possible for input, so getstr() and get_wstr() are
left as they are.
instr() now takes the length from the value returned by winnstr(), as
X/Open specifies, instead of searching for a terminator which it does not.
Fix wrong types: instr() and getstr() return a bytes object, not a str, and
their n limits the number of bytes; getkey() returns a str; unctrl() returns
a bytes object. Make clear whether an integer standing for a character is an
encoded byte or a character code.
Rewrite the documentation of getch(), get_wch(), getkey(), getstr(),
get_wstr(), instr(), in_wstr() and in_wchstr(), following X/Open Curses.
setcchar() takes a NUL-terminated string, so a NUL sharing a cell dropped
the rest of the cell, and an empty cell read back as complexchar('').
Reject a NUL that shares a cell or that joins a complexstr, and read an
empty cell as complexchar('\0'), as a narrow build already does.
The integer 0 asks for the default character, but it was rejected when
passed together with a string character. Choose the drawing function by
what the arguments need: only a complexchar cannot be drawn as a byte
character, and a string character is narrowed when one is needed.
Document the defaults as 0 rather than as the ACS_* codes, which are not
defined before initscr().
* Remove the NEWS entry
The regression is not in any release: wide characters in border() and box()
are new in 3.16.
They are the counterparts of the ACS_* line-drawing codes as complexchar
cells, added to the module by initscr() and newterm() like the ACS_* ones.
Besides the codes that mirror the ACS_* ones, ncurses provides the
double-line and thick-line families, which have no ACS_* counterpart.
addstr(), addnstr(), insstr() and insnstr() saved the window rendition with
getattrs() and put it back with wattrset(), whose A_COLOR field holds only
pairs 0 to 255, so a window using a larger pair lost it. Save and restore
the pair with wattr_get() and wattr_set() where they exist.
addstr(), addnstr(), insstr() and insnstr() set the window rendition to
the caller's attr, write, then restore the previous rendition. Since
30dde1eeb3 the restore sits below an early return taken when the write
fails, so a failed write leaves the caller's attr on the window and
drops whatever the application had set with attrset().
Restore the rendition first and report the write error afterwards. A
wattrset() failure is still reported when the write itself succeeded.
screen.use() makes its screen current for the callback, but the module
kept recording the previously current screen, so newwin(), newpad() and
getwin() tagged the new window with the wrong owner. The window then
failed to keep its own screen alive: the screen could be freed while the
window was still in use, and the next call on it read freed memory.
initscr() inside use() returned the other screen's standard window for
the same reason.
On a wide build getcchar() also reports the color pair in the A_COLOR
bits of the attributes, where it saturates at 255, so complexchar.attr
carried it. Mask those bits out in curses_getcchar(), as the narrow
build already does when packing a cell.
chgat(), getstr(), instr(), get_wstr(), in_wstr() and in_wchstr() were
parsing their arguments by hand, because Argument Clinic did not support
their signatures. Their helper for parsing the optional y, x and n
arguments is no longer needed.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
termattrs() returns a chtype mask, but it was routed through an int, so a
terminal that advertises A_ITALIC came back negative and the result could
no longer be passed to the attribute functions.
Parse the integer forms of addch() and friends as unsigned long long: the
width of a chtype is up to the library.
Expose _curses._wide_character_support so that the tests can ask what a
cell holds instead of probing for a function.
Mask an integer keystroke in textpad as the byte it is, and keep the key
codes out of the printable range.
in_wstr() searched the result for a terminating null, but winnwstr()
writes one only if it stored at least one character. Use the number of
characters it returns as the length.
getparent() calls no curses function, it returns the window's stored parent,
but it was compiled only when the ncurses extension functions were present.
Close the guard after getscrreg(), which does need it.
The test moves out of test_state_getters, which is gated on is_scrollok(), so
that getparent() is covered on backends without the extensions.
screen.use() makes its screen current for the callback, so a new_prescr()
screen, which has no terminal, can be current without set_term().
Operations that need a terminal then crashed inside curses.
Raise curses.error instead, checking that stdscr exists.
initscr() called while a newterm() screen is current returned a second
window object over that screen's standard window, with no reference to
the screen. Either wrapper could then free the window used by the other.
Return the screen's own standard window instead.
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.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
getcchar() rejected a non-NULL opts argument before ncurses 6.3, so read
the color pair through the short slot instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Build hygiene so the curses modules build against a limited curses (e.g. the
native SVr4 curses of illumos/Solaris), matching how other optional functions
are already gated:
* Probe and #ifdef-gate the X/Open attr_t functions (window.attr_get/attr_set/
attr_on/attr_off/color_set), the soft-label attribute functions
(slk_attr_on/off/set, slk_color) and scr_set(); scr_set is probed separately
from the scr_dump family, which SVr4 has without it.
* Stop gating update_lines_cols() on resizeterm(): it only reads LINES/COLS and
is used unconditionally (e.g. by set_term()), so a build without resizeterm()
failed to link.
* On Solaris/illumos define _BOOL (and include <stdbool.h>) so the SVr4
<curses.h> "typedef char bool" does not clash with C's bool.
test.test_curses: skip or guard the tests that use the now-optional functions,
split test_attributes so its chtype-based part still runs, and treat the native
curses of NetBSD and illumos/Solaris as having broken newterm() (they crash on
repeated newterm()/delscreen(), like ncurses before 6.5).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
On a wide build, winsch() does not locale-decode a byte above 127, unlike
waddch(), so insch() inserted '¤' (U+00A4) instead of '€' for byte 0xA4
under ISO-8859-15. Decode the byte with btowc() and insert it as a wide
character, like addch().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On a wide build, winch() returns the low 8 bits of the character's code
point with no locale conversion, so inch()/mvinch() disagreed with instr()
for a non-ASCII character of an 8-bit locale ('€' under ISO-8859-15 gave
0xAC instead of 0xA4). Re-encode the cell to its locale byte via wctob(),
as ncurses does for getbkgd().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The mouse interface (getmouse(), has_mouse(), the BUTTON* constants,
window.mouse_trafo(), ...) and the window is_*() state-query methods were gated
on ncurses-specific macros, so they were dropped on other curses implementations
that provide them, such as NetBSD curses and PDCurses.
Gate them instead on configure capability probes (for functions NetBSD curses
provides, since it defines no identifying macro) or on NCURSES_EXT_FUNCS or the
PDCURSES macro (for functions only ncurses and PDCurses provide). Probe for
getmouse() with its X/Open getmouse(MEVENT *) signature, since PDCurses declares
an incompatible getmouse(void) unless built for the ncurses mouse API, which
PDC_NCMOUSE now always selects.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
has_mouse() was added to ncurses after the 5.7 release, but the binding
guarded it only with NCURSES_MOUSE_VERSION, which 5.7 already defines, so
the module failed to compile against ncurses 5.7 (such as the system
curses on macOS). Gate it additionally on NCURSES_EXT_FUNCS, which holds
the library's patch date.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Wrap the X/Open Curses soft-label-key functions: slk_init, slk_set,
slk_label, slk_refresh, slk_noutrefresh, slk_clear, slk_restore,
slk_touch, the chtype attribute functions slk_attron, slk_attroff,
slk_attrset and slk_attr, and the attr_t functions slk_attr_on,
slk_attr_off, slk_attr_set and slk_color.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
window.in_wch(), window.in_wchstr() and window.getbkgrnd() read a cell
into an uninitialized cchar_t, relying on the curses library to leave the
text NUL-terminated -- which ncurses does but X/Open does not require, so
some libraries (such as NetBSD curses) returned uninitialized bytes as
wide characters. Zero-initialize the cell buffers before the read and
default the getcchar() output to an empty string.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Some curses functions were called unconditionally or gated only by the
ncurses-specific NCURSES_EXT_FUNCS macro, which broke building the module
against other curses implementations (narrow ncurses, NetBSD curses) even
when they provided the function. Detect each with a configure capability
probe and gate on HAVE_CURSES_*.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
has_mouse() reports whether the mouse driver was successfully initialized.
window.mouse_trafo(y, x, to_screen) converts a coordinate pair between
window-relative and screen-relative coordinates, returning the (y, x) pair or
None if it lies outside the window. Together these complete the curses mouse
interface.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
curses.window.get_wch, curses.window.get_wstr, curses.unget_wch,
curses.erasewchar, curses.killwchar and curses.wunctrl now also work when
Python is built against a non-wide curses library, on an 8-bit locale, where
each character is a single byte in the relevant encoding. curses.ungetch now
also accepts a one-character string, like curses.unget_wch.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Back complexchar, complexstr and the cell read methods (in_wch,
in_wchstr, in_wstr, getbkgrnd) with a chtype instead of a cchar_t when
ncursesw is absent, so the same code works on both builds. A narrow
build is limited to one character per cell, encodable as a single byte in
the window's encoding (8-bit locales), with the color pair limited to the
color_pair() range.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
term_attrs() returns the video attributes supported by the terminal as
WA_* values, the counterpart of termattrs() for the A_* values.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
curses.color_pair() now raises OverflowError for a pair number too large
to be packed, instead of silently masking it to a different pair.
The attr argument of the character-cell and attribute methods (addch,
addstr, attron, attrset and others) now goes through the checked attr
converter, so an out-of-range or non-integer attribute is rejected rather
than silently truncated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The curses C types and exceptions now set their tp_name to the public
module, so __module__, repr() and help() report curses.window,
curses.complexchar, curses.complexstr, curses.screen, curses.error,
curses.panel.panel and curses.panel.error instead of the underscore
extension modules.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add define_key(), key_defined() and keyok(), the ncurses extensions for
managing how control strings are recognized as key codes, beyond the
predefined terminfo keys and the all-or-nothing window.keypad().
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
These module-level functions write the whole virtual screen to a file and
load it back -- the screen-wide counterpart of window.putwin()/getwin().
The filename argument accepts a string or a path-like object.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dupwin() returns a new window that is an independent duplicate of an existing
one -- same size, position, contents and attributes, but with its own cell
buffer, so changes to one do not affect the other.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>