Skip to content

gh-85796: Build the curses module on Windows against PDCurses - #154956

Open
serhiy-storchaka wants to merge 3 commits into
python:mainfrom
serhiy-storchaka:curses-windows-pdcurses
Open

serhiy-storchaka wants to merge 3 commits into
python:mainfrom
serhiy-storchaka:curses-windows-pdcurses

Conversation

@serhiy-storchaka

@serhiy-storchaka serhiy-storchaka commented Jul 30, 2026

Copy link
Copy Markdown
Member

Point PDCURSES_DIR at a PDCursesMod checkout and build.bat compiles pdcurses.dll, _curses.pyd and _curses_panel.pyd; without it nothing changes, and the installer does not ship them yet. The library is built wide, with a 64-bit chtype, and both modules link the one DLL, so they share a screen and a panel stack.

PDCurses has no terminfo database, so PC/pdcurses supplies <term.h> and the entry points the module references unconditionally: setupterm() reports success, the capability queries report "not found". What PDCurses does provide is declared in one block, in the same HAVE_CURSES_* terms that configure defines on Unix, since configure does not run here.

curses.pdcurses_version reports the library version, as ncurses_version does, and window.encoding follows the program locale, or is UTF-8 where PDCurses forces it. curses.termattrs() now returns its mask through an unsigned long long, as term_attrs() does, since a 64-bit chtype reaches past the 32 bits of an unsigned long on Windows.

test_curses covers the differences rather than skipping them: PDCurses has its own color model, keeps one screen per process, and reports some errors differently. $TERM no longer gates it there, since PDCurses does not consult it.

Tested on Windows 11, and against PDCursesMod 4.5.4 on Linux and FreeBSD in five configurations — wide and narrow, 32- and 64-bit chtype, forced UTF-8 — in three locales each. test_curses passes in all of them but the wide builds on FreeBSD in an 8-bit locale, where wchar_t holds the raw locale value rather than a code point and the round-trip through wcstombs() fails; the system ncurses fails the same tests there.

The curses and _curses_panel modules are built when PDCURSES_DIR points
to a PDCursesMod source tree, linking it as a shared library so both
modules share one screen and panel stack.  PDCurses has no terminfo
database, so PC/pdcurses supplies the entry points the module needs.

curses.pdcurses_version reports the library version, as ncurses_version
already does.  What PDCurses provides is declared in one block, in the
same HAVE_CURSES_* terms that configure defines on Unix.

window.encoding follows the program locale, or is UTF-8 where PDCurses
is built to force it.  A cell holds a character on a wide build and a
locale byte on a narrow one, whichever library provides it.

test_curses covers the differences: PDCurses has its own color model,
supports one screen per process, and reports some errors differently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@read-the-docs-community

read-the-docs-community Bot commented Jul 30, 2026

Copy link
Copy Markdown

@serhiy-storchaka
serhiy-storchaka requested a review from zooba July 31, 2026 07:34
…rses

# Conflicts:
#	Lib/test/test_curses.py
#	Modules/clinic/_cursesmodule.c.h
PDCursesMod's setcchar() reads uninitialized memory for an empty string
(PDCursesMod issue python#388), so build a cell without text directly.  Its
winnstr() reads at most 512 bytes, and a NUL is written in its ^@ form.
@vstinner

Copy link
Copy Markdown
Member

Hum. Adding a new dependency to Python on Windows sounds like a non-trivial change. It would be worth it to open a discussion at discuss.python.org in the Core dev category to discuss it. I don't expect that many people care about suporting curses on Windows, but it would be nice to get some feedback from users, and discuss the choice of PDCursesMod.

@zooba

zooba commented Sep 16, 2026

Copy link
Copy Markdown
Member

There are some changes required for the Windows build, but I think those can wait until after we're sure we want to be doing this (e.g. conditions in pcbuild.proj will likely break building inside of Visual Studio, since that file isn't used there, so optional components are actually far more complex to set up - this is usually why we make it non-optional and mirror the sources in cpython-source-deps or a build in cpython-bin-deps).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants