342 Commits
Author SHA1 Message Date
Inada NaokiandEmma Smith b26fc41b5c Remove obsolete and unused configure macros (#159513)
Co-authored-by: Emma Smith <emma@emmatyping.dev>
2026-10-25 05:18:16 +00:00
b953155e0d gh-155976: Refresh curses LINES, COLS, COLORS and COLOR_PAIRS on screen switch (GH-155994)
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>
2026-10-18 09:09:25 +00:00
Serhiy Storchaka b1e2766ad6 gh-157546: Add missing docstrings in curses (GH-157547)
The Python wrappers initscr(), newterm() and start_color() now use the
docstrings of the wrapped C functions, and the screen type has a
docstring.
2026-09-21 14:49:28 +03:00
Bhuvansh 0fc5ebc7a1 gh-155875: Fix use-after-free in curses after new_prescr() (GH-155914)
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.
2026-09-15 11:41:44 +00:00
Vyron Vasileiadis 23180c5008 gh-156953: Fix a reference leak in curses window.insnstr() (GH-156954)
insnstr() did not release the bytes object it converted its argument to
when setting the attributes failed, unlike addstr(), addnstr() and
insstr().
2026-09-07 22:44:20 +03:00
Vyron Vasileiadis dcc4e5247a gh-156951: Do not truncate the color pair in curses.slk_color() (GH-156952)
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.
2026-09-07 22:21:47 +03:00
Vyron Vasileiadis 326f7f74cd gh-156963: Raise TypeError when concatenating complexstr with another type (GH-156964)
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.
2026-09-07 19:02:35 +00:00
Serhiy Storchaka a728080492 gh-156348: Fix the value of curses.ERR (GH-156349)
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.
2026-08-25 09:22:46 +00:00
Serhiy Storchaka c1fc445061 gh-155373: Inline the parsing code of optional groups in Argument Clinic (GH-155374)
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.
2026-08-23 22:15:58 +03:00
Serhiy Storchaka ca79981cc0 gh-156230: Bound a curses window read by the window, not by 2047 (GH-156282)
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.
2026-08-23 19:53:59 +03:00
Serhiy Storchaka 43a1869f7e gh-154855: Ask non-ncurses curses for one more character (GH-154870)
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.
2026-08-23 12:31:51 +00:00
Serhiy Storchaka be87bfa8a1 gh-156234: Fix and rewrite the curses documentation on reading (GH-156235)
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.
2026-08-23 14:42:46 +03:00
Serhiy Storchaka 9c48dfe992 gh-155877: Fix a NUL in a curses character cell (GH-156158)
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.
2026-08-21 18:53:59 +03:00
Serhiy Storchaka b9d9c3f99c gh-155499: Fix curses window.border() and window.box() with 0 (GH-155871)
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.
2026-08-21 07:20:21 +00:00
Serhiy Storchaka 8e01c90829 gh-155863: Add the WACS_* constants to the curses module (GH-155870)
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.
2026-08-21 09:41:01 +03:00
Serhiy Storchaka 0f9fb00af8 gh-156138: Keep the color pair when a curses write restores the rendition (GH-156139)
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.
2026-08-21 09:39:12 +03:00
Vyron Vasileiadis 83531fd39f gh-155974: Restore the window attributes when a curses write fails (GH-155975)
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.
2026-08-20 22:08:50 +03:00
Vyron Vasileiadis f10166035d gh-155864: Keep the module's current screen in step with use_screen() (GH-155865)
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.
2026-08-16 10:45:05 +03:00
Vyron Vasileiadis a7541c3843 gh-154840: Keep the color pair out of curses complexchar.attr (GH-154841)
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.
2026-08-11 11:50:14 +03:00
Serhiy StorchakaandClaude Opus 5 ea85e8faec gh-64502: Convert curses methods with optional groups to Argument Clinic (GH-155229)
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>
2026-08-05 14:34:58 +03:00
Vyron Vasileiadis 0b083c43a4 gh-154874: Fix the sign of curses.termattrs() (GH-154875)
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.
2026-07-30 19:50:03 +00:00
Serhiy Storchaka 9ef95d1b0d gh-154934: Remove ncurses assumptions from curses and its tests (GH-154940)
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.
2026-07-30 21:12:29 +03:00
Bhuvansh 0e54a406b4 gh-154587: Fix Windows buffer declaration in _cursesmodule (GH-154609) 2026-07-30 17:15:05 +03:00
Vyron Vasileiadis 9c91fd958f gh-154781: Fix garbage from curses window.in_wstr() (GH-154782)
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.
2026-07-29 09:30:37 +03:00
Vyron Vasileiadis 6af6165c21 gh-154788: Make curses window.getparent() unconditional (GH-154789)
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.
2026-07-29 09:07:15 +03:00
Serhiy Storchaka c8eb79d5e8 gh-154786: Fix crashes on a screen without a terminal (GH-154787)
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.
2026-07-28 08:00:39 +03:00
Vyron Vasileiadis b099df5f17 gh-154749: Reject a terminal-less screen in curses.set_term() (GH-154750)
set_term() accepted a screen returned by new_prescr(), which has no
terminal, and the next refresh crashed inside curses.  Raise
curses.error instead.
2026-07-27 18:59:46 +00:00
Vyron Vasileiadis 92efaff49b gh-154751: Fix use-after-free in curses.initscr() after newterm() (GH-154752)
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.
2026-07-27 21:24:10 +03:00
Serhiy StorchakaandClaude Opus 4.8 b6188749d6 gh-153862: Fix spurious color pair in curses window.inch() on a wide build (GH-154703)
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>
2026-07-26 11:02:50 +03:00
Serhiy StorchakaandClaude Opus 4.8 aad438b0b6 gh-154525: Fix curses getcchar() failure on ncurses older than 6.3 (GH-154526)
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>
2026-07-23 17:48:17 +03:00
Serhiy StorchakaandClaude Opus 4.8 104c397d8f gh-154055: Gate optional curses functions absent on old SVr4 curses (GH-154057)
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>
2026-07-19 15:26:47 +03:00
Serhiy StorchakaandClaude Opus 4.8 223cbffca6 gh-153864: Fix curses window.insch() for non-ASCII characters on a wide build (GH-153865)
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>
2026-07-19 08:45:06 +00:00
Serhiy StorchakaandClaude Opus 4.8 a83aaa9149 gh-153862: Fix curses window.inch() for non-ASCII characters on a wide build (GH-153863)
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>
2026-07-19 10:43:22 +03:00
Bénédikt Tran cfe3e07f54 gh-153009: Fix compilation of curses on platforms that define the stdscr macro (#153010) 2026-07-04 12:21:16 +02:00
Andrii Hrimov 369ce438bc gh-152813: Fix curses build warning on macOS (GH-152814) 2026-07-02 18:38:41 +03:00
Serhiy StorchakaandClaude Opus 4.8 7bbea4f486 gh-152502: Detect the curses mouse interface and is_* methods portably (GH-152705)
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>
2026-06-30 19:24:07 +00:00
Serhiy StorchakaandClaude Opus 4.8 11d42bd269 gh-152325: Gate curses.has_mouse() on the ncurses patch level (GH-152652)
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>
2026-06-30 09:09:09 +00:00
Serhiy StorchakaandClaude Opus 4.8 089e6f622b gh-152263: Add curses soft-label-key functions (GH-152264)
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>
2026-06-29 20:57:56 +03:00
Serhiy StorchakaandClaude Opus 4.8 e7b21b66a4 gh-152503: Fix garbage text from curses wide-character cell reads (GH-152505)
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>
2026-06-29 12:06:47 +00:00
Serhiy StorchakaandClaude Opus 4.8 0635e55b47 gh-152502: Detect optional curses functions with configure probes (GH-152504)
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>
2026-06-29 12:02:12 +00:00
Serhiy StorchakaandClaude Opus 4.8 306d7a7649 gh-152325: Add curses.has_mouse() and curses.window.mouse_trafo() (GH-152484)
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>
2026-06-28 14:09:03 +00:00
Serhiy StorchakaandClaude Opus 4.8 65585caa72 Remove now-wrapped functions from the curses unsupported list (GH-152480)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 10:53:51 +00:00
Serhiy StorchakaandClaude Opus 4.8 286b51257a gh-152470: Make wide-character curses functions work on a narrow build (GH-152476)
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>
2026-06-28 12:35:37 +03:00
Serhiy StorchakaandClaude Opus 4.8 95bfaffd52 gh-152233: Make the curses cell API work without ncursesw (ПР-152466)
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>
2026-06-28 05:22:43 +00:00
Serhiy StorchakaandClaude Opus 4.8 0ed6711465 gh-152332: Add the curses.term_attrs() function (GH-152333)
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>
2026-06-27 23:23:55 +03:00
Serhiy StorchakaandClaude Opus 4.8 7bf63facfd gh-152275: Add integer overflow guards to the curses chtype and color-pair packing path (GH-152303)
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>
2026-06-27 15:42:41 +03:00
Serhiy StorchakaandClaude Opus 4.8 fd63e21c0b gh-87904: Report the public module name in curses types and exceptions (GH-152341)
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>
2026-06-27 10:10:11 +03:00
Serhiy StorchakaandClaude Opus 4.8 8f4abfc392 gh-152334: Add curses key-management functions (GH-152338)
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>
2026-06-27 01:59:24 +03:00
Serhiy StorchakaandClaude Opus 4.8 a85e73b322 gh-152260: Add curses.scr_dump(), scr_restore(), scr_init() and scr_set() (GH-152261)
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>
2026-06-26 16:36:31 +00:00
Serhiy StorchakaandClaude Opus 4.8 49d484e4b1 gh-152258: Add curses.window.dupwin() (GH-152259)
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>
2026-06-26 14:28:53 +00:00