Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 110 additions & 2 deletions Doc/library/curses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,9 @@ The module :mod:`!curses` defines the following functions:
.. function:: nofilter()

Undo the effect of a previous :func:`.filter` call.
Like :func:`.filter`, it must be called before :func:`initscr` so that the
next initialization uses the full screen again.
Like :func:`.filter`, it must be called before :func:`initscr` (or
:func:`newterm`) so that the next initialization uses the full screen
again.

Availability: if the underlying curses library provides ``nofilter()``.

Expand Down Expand Up @@ -442,6 +443,36 @@ The module :mod:`!curses` defines the following functions:
right corner of the screen.


.. function:: newterm(type=None, fd=None, infd=None, /)

Initialize a new terminal in addition to the one initialized by
:func:`initscr`,
and return a :ref:`screen <curses-screen-objects>` for it.
This allows a program to drive more than one terminal.

*type* is the terminal name, as in :func:`setupterm`;
if ``None``, the value of the :envvar:`TERM` environment variable is used.
*fd* and *infd* are the output and input files for the terminal:
either a file object or a file descriptor.
They default to :data:`sys.stdout` and :data:`sys.stdin`.

The new screen becomes the current one.
Use :func:`set_term` to switch between screens.

.. versionadded:: next


.. function:: new_prescr()

Return a new :ref:`screen <curses-screen-objects>`
that can be used to call functions that affect global state
before :func:`initscr` or :func:`newterm` is called.

Availability: if the underlying curses library provides ``new_prescr()``.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we not be using the directive?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it only supports limited set of OS names.


.. versionadded:: next


.. function:: nl(flag=True)

Enter newline mode. This mode translates the return key into newline on input,
Expand Down Expand Up @@ -586,6 +617,17 @@ The module :mod:`!curses` defines the following functions:

.. versionadded:: 3.9


.. function:: set_term(screen, /)

Make *screen*, a :ref:`screen <curses-screen-objects>` returned by
:func:`newterm`, the current terminal,
and return the previously current screen.
Returns ``None`` if the previous screen was the one created by
:func:`initscr`.

.. versionadded:: next

.. function:: setsyx(y, x)

Set the virtual screen cursor to *y*, *x*. If *y* and *x* are both ``-1``, then
Expand Down Expand Up @@ -1380,13 +1422,79 @@ Window objects
:meth:`refresh`.


.. method:: window.use(func, /, *args, **kwargs)

Call ``func(window, *args, **kwargs)`` with the lock of the window held,
and return its result.
This provides automatic protection for the window
against concurrent access from another thread.

Availability: if the underlying curses library provides ``use_window()``.

.. versionadded:: next


.. method:: window.vline(ch, n[, attr])
window.vline(y, x, ch, n[, attr])

Display a vertical line starting at ``(y, x)`` with length *n* consisting of the
character *ch* with attributes *attr*.


.. _curses-screen-objects:

Screen objects
--------------

.. class:: screen

A *screen* object represents a terminal initialized by :func:`newterm`
(or :func:`new_prescr`),
in addition to the default screen created by :func:`initscr`.
Screen objects are returned by those functions;
they cannot be instantiated directly.

A screen is freed automatically once it is no longer referenced,
either directly or through one of its windows.
Each window keeps its screen alive,
so a screen remains valid as long as any of its windows does.

.. versionadded:: next


.. method:: screen.close()

Detach the screen's standard window,
breaking the reference cycle between them
so the screen can be reclaimed promptly instead of waiting for a
garbage collection.
Afterwards :attr:`~screen.stdscr` is ``None``
and the window it returned earlier can no longer be used.
The screen's resources are released
once it and all its windows are no longer referenced.

.. versionadded:: next


.. attribute:: screen.stdscr

The standard :ref:`window <curses-window-objects>` of the screen,
covering the whole terminal,
or ``None`` for a screen created by :func:`new_prescr`.


.. method:: screen.use(func, /, *args, **kwargs)

Call ``func(screen, *args, **kwargs)`` with the lock of the screen held,
and return its result.
This provides automatic protection for the screen
against concurrent access from another thread.

Availability: if the underlying curses library provides ``use_screen()``.

.. versionadded:: next


Constants
---------

Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@
curses
------

* Add support for multiple terminals to the :mod:`curses` module:

Check warning on line 92 in Doc/whatsnew/3.16.rst

View workflow job for this annotation

GitHub Actions / Docs / Docs

py:meth reference target not found: window.use [ref.meth]
the new functions :func:`curses.newterm`, :func:`curses.set_term`
and :func:`curses.new_prescr`,
the corresponding :ref:`screen <curses-screen-objects>` object,
and the :meth:`window.use` method.
(Contributed by Serhiy Storchaka in :gh:`90092`.)

* Add :func:`curses.nofilter`, which undoes the effect of :func:`curses.filter`.
(Contributed by Serhiy Storchaka in :gh:`151744`.)

Expand Down
10 changes: 10 additions & 0 deletions Include/py_curses.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,18 @@ typedef struct PyCursesWindowObject {
WINDOW *win;
char *encoding;
struct PyCursesWindowObject *orig;
PyObject *screen; /* the screen the window belongs to, or NULL,
kept alive for the lifetime of the window */
} PyCursesWindowObject;

typedef struct {
PyObject_HEAD
SCREEN *screen; /* NULL after the screen has been deleted */
FILE *outfp; /* owned output stream, or NULL */
FILE *infp; /* owned input stream, or NULL */
PyObject *stdscr; /* the screen's standard window, or NULL */
} PyCursesScreenObject;

#define PyCurses_CAPSULE_NAME "_curses._C_API"


Expand Down
17 changes: 17 additions & 0 deletions Lib/curses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,23 @@ def initscr():
setattr(curses, key, value)
return stdscr

# newterm() is wrapped for the same reason as initscr(): the ACS_* constants
# and LINES/COLS only become available once a terminal is initialized, and are
# then copied to the curses package's dictionary.

try:
newterm
except NameError:
pass
else:
def newterm(type=None, fd=None, infd=None, /):
import _curses, curses
screen = _curses.newterm(type, fd, infd)
for key, value in _curses.__dict__.items():
if key.startswith('ACS_') or key in ('LINES', 'COLS'):
setattr(curses, key, value)
return screen

# This is a similar wrapper for start_color(), which adds the COLORS and
# COLOR_PAIRS variables which are only available after start_color() is
# called.
Expand Down
Loading
Loading