.. 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()``.
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()``.
+
+ .. versionadded:: next
+
+
.. function:: nl(flag=True)
Enter newline mode. This mode translates the return key into newline on input,
.. 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
: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])
Wide and combining characters are now accepted.
+.. _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
---------
curses
------
+* Add support for multiple terminals to the :mod:`curses` module:
+ 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() <curses.window.use>` method.
+ (Contributed by Serhiy Storchaka in :gh:`90092`.)
+
* The :mod:`curses` character-cell window methods now accept a full character
cell --- a spacing character optionally followed by combining characters ---
in addition to a single integer or byte character. This affects
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"
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.
import functools
import inspect
import os
+import select
import string
import sys
import tempfile
+import threading
import unittest
from unittest.mock import MagicMock
from test.support import (requires, verbose, SaveSignals, cpython_only,
check_disallow_instantiation, MISSING_C_DOCSTRINGS,
- gc_collect)
+ gc_collect, SHORT_TIMEOUT)
from test.support.import_helper import import_module
# Optionally test curses module. This currently requires that the
term = os.environ.get('TERM')
SHORT_MAX = 0x7fff
-# If newterm was supported we could use it instead of initscr and not exit
+# newterm() is used when available (it reports errors instead of exiting), but
+# initscr() is still the fallback, and an unusable $TERM has no terminal to
+# drive either way.
@unittest.skipIf(not term or term == 'unknown',
- "$TERM=%r, calling initscr() may cause exit" % term)
+ "$TERM=%r, no usable terminal" % term)
@unittest.skipIf(sys.platform == "cygwin",
"cygwin's curses mostly just hangs")
class TestCurses(unittest.TestCase):
sys.stderr.flush()
sys.stdout.flush()
print(file=self.output, flush=True)
- self.stdscr = curses.initscr()
+ if hasattr(curses, 'newterm'):
+ # Use newterm() rather than initscr(): it reports errors instead of
+ # exiting, and gives each test a fresh screen, which also lets
+ # ScreenTests run newterm()/set_term() in the same process.
+ try:
+ infd = sys.__stdin__.fileno()
+ except (AttributeError, ValueError, OSError):
+ infd = stdout_fd
+ self.screen = curses.newterm(term, stdout_fd, infd)
+ self.stdscr = self.screen.stdscr
+ # Close the screen after the test to break its window<->screen
+ # reference cycle deterministically, rather than leaving it for the
+ # cyclic GC to collect during a much later test (where a window's
+ # delwin() can fail -- an unraisable error on macOS).
+ self.addCleanup(self.screen.close)
+ self.addCleanup(setattr, self, 'screen', None)
+ self.addCleanup(setattr, self, 'stdscr', None)
+ else:
+ self.stdscr = curses.initscr()
if self.isatty:
curses.savetty()
self.addCleanup(curses.endwin)
@requires_curses_func('filter')
def test_filter(self):
- # TODO: Should be called before initscr() or newterm() are called.
+ # filter() must be called before initscr()/newterm(); it confines
+ # curses to a single line. Undo it with nofilter() afterwards so that
+ # it does not shrink the screens created by later tests.
curses.filter()
if hasattr(curses, 'nofilter'):
- curses.nofilter()
+ self.addCleanup(curses.nofilter)
@requires_curses_func('use_env')
def test_use_env(self):
self.skipTest('cannot change color (use_default_colors() failed)')
self.assertEqual(curses.pair_content(0), (-1, -1))
+ @requires_curses_window_meth('use')
+ def test_use_window(self):
+ win = self.stdscr
+ self.assertEqual(win.use(lambda w, a, b: (w is win, a, b), 5, b=6),
+ (True, 5, 6))
+ with self.assertRaises(ZeroDivisionError):
+ win.use(lambda w: 1 / 0)
+
+ @unittest.skipUnless(hasattr(curses.screen, 'use'),
+ 'requires screen.use()')
+ def test_use_screen(self):
+ screen = self.screen
+ self.assertEqual(
+ screen.use(lambda sc, flag: (sc is screen, flag), flag=True),
+ (True, True))
+
@requires_curses_func('assume_default_colors')
@requires_colors
def test_assume_default_colors(self):
def test_userptr_segfault(self):
w = curses.newwin(10, 10)
panel = curses.panel.new_panel(w)
+ # set_userptr(A()) makes a panel<->userptr reference cycle (A.__del__
+ # closes over panel); clean it up so the panel and its window do not
+ # linger until a later test collects them.
+ self.addCleanup(self._delete_panels, panel)
class A:
def __del__(self):
panel.set_userptr(None)
curses.resize_term(35000, 1)
with self.assertRaises(OverflowError):
curses.resize_term(1, 35000)
- # GH-120378: Overflow failure in resize_term() causes refresh to fail
- tmp = curses.initscr()
- tmp.erase()
+ # GH-120378: a failed resize can leave refresh broken; restore the
+ # original size to recover. Avoid initscr(), which would switch away
+ # from the shared newterm() screen and corrupt later tests.
+ curses.resize_term(lines, cols)
+ self.stdscr.erase()
@requires_curses_func('resizeterm')
def test_resizeterm(self):
curses.resizeterm(35000, 1)
with self.assertRaises(OverflowError):
curses.resizeterm(1, 35000)
- # GH-120378: Overflow failure in resizeterm() causes refresh to fail
- tmp = curses.initscr()
- tmp.erase()
+ # GH-120378: a failed resize can leave refresh broken; restore the
+ # original size to recover. Avoid initscr(), which would switch away
+ # from the shared newterm() screen and corrupt later tests.
+ curses.resizeterm(lines, cols)
+ self.stdscr.erase()
def test_ungetch(self):
curses.ungetch(b'A')
self.mock_win.reset_mock()
+@unittest.skipUnless(hasattr(curses, 'newterm'), 'requires curses.newterm()')
+@unittest.skipIf(not term or term == 'unknown',
+ "$TERM=%r, newterm() may not work" % term)
+@unittest.skipIf(sys.platform == "cygwin",
+ "cygwin's curses mostly just hangs")
+class ScreenTests(unittest.TestCase):
+ # newterm()/set_term() mutate global curses state, but each test drives its
+ # own pseudo-terminal(s) and never touches the screen shared by TestCurses,
+ # whose setUp() makes that screen current again. So these can run in this
+ # process, without a real terminal and without a subprocess.
+
+ def setUp(self):
+ # newterm() may install signal handlers; restore them afterwards.
+ self.save_signals = SaveSignals()
+ self.save_signals.save()
+ self.addCleanup(self.save_signals.restore)
+
+ def tearDown(self):
+ # Leave visual mode and reclaim the test's screens while their
+ # pseudo-terminals are still open (make_pty() closes them later).
+ try:
+ curses.endwin()
+ except curses.error:
+ pass
+ gc_collect()
+
+ @staticmethod
+ def _drain_pty(master, stop):
+ # Read and discard whatever curses writes to the screen, until asked to
+ # stop and nothing more is pending. poll() rather than a blocking
+ # read() so we can stop without closing the fd (closing it while this
+ # thread is blocked in read() hangs on macOS).
+ poller = select.poll()
+ poller.register(master, select.POLLIN)
+ while True:
+ if poller.poll(100):
+ try:
+ if not os.read(master, 1024):
+ break # EOF
+ except OSError:
+ break
+ elif stop.is_set():
+ break
+
+ def make_pty(self):
+ master, slave = os.openpty()
+ # Nothing reads the master end, so writing to the slave and the
+ # tcdrain() in endwin() can block on macOS once the pty buffer fills;
+ # drain it from a background thread (endwin() releases the GIL).
+ stop = threading.Event()
+ reader = threading.Thread(target=self._drain_pty, args=(master, stop),
+ daemon=True)
+ reader.start()
+ # Stop and join the reader before closing the fds: on macOS, closing
+ # either end while the reader is blocked in read() hangs.
+ def stop_reader():
+ stop.set()
+ reader.join(SHORT_TIMEOUT)
+ self.addCleanup(os.close, master)
+ self.addCleanup(os.close, slave)
+ self.addCleanup(stop_reader)
+ return slave
+
+ def test_newterm(self):
+ s = self.make_pty()
+ screen = curses.newterm('xterm', s, s)
+ self.assertIsInstance(screen, curses.screen)
+ win = screen.stdscr
+ self.assertIsInstance(win, curses.window)
+ self.assertEqual(win.getmaxyx(), (24, 80))
+ win.addstr(0, 0, 'hello')
+ win.refresh()
+
+ def test_newterm_file_object(self):
+ # type=None uses $TERM; the file arguments accept file objects too.
+ s = self.make_pty()
+ out = os.fdopen(os.dup(s), 'wb', buffering=0)
+ self.addCleanup(out.close)
+ screen = curses.newterm(None, out, s)
+ self.assertIsInstance(screen, curses.screen)
+
+ def test_set_term(self):
+ s = self.make_pty()
+ s2 = self.make_pty()
+ a = curses.newterm('xterm', s, s) # current screen is a
+ b = curses.newterm('xterm', s2, s2) # current screen is b
+ self.assertIs(curses.set_term(a), b) # returns the previous one
+ self.assertIs(curses.set_term(b), a)
+
+ def test_window_keeps_screen_alive(self):
+ # The standard window keeps its screen alive; dropping every other
+ # reference and collecting must not invalidate the window.
+ s = self.make_pty()
+ win = curses.newterm('xterm', s, s).stdscr
+ gc_collect()
+ win.addstr(0, 0, 'still alive')
+ win.refresh()
+
+ def test_screen_freed(self):
+ # Dropping all references to a (non-current) screen and its windows
+ # frees it without error.
+ s = self.make_pty()
+ s2 = self.make_pty()
+ a = curses.newterm('xterm', s, s)
+ b = curses.newterm('xterm', s2, s2) # a is no longer current
+ del a
+ gc_collect()
+
+ def test_close(self):
+ s = self.make_pty()
+ screen = curses.newterm('xterm', s, s)
+ win = screen.stdscr
+ self.assertIsInstance(win, curses.window)
+ screen.close()
+ # After close() the standard window is detached and unusable, and
+ # stdscr is None. No reference cycle remains.
+ self.assertIsNone(screen.stdscr)
+ self.assertRaises(curses.error, win.addstr, 0, 0, 'x')
+ # close() is idempotent.
+ screen.close()
+
+ @unittest.skipUnless(hasattr(curses, 'new_prescr'),
+ 'requires curses.new_prescr()')
+ def test_new_prescr(self):
+ screen = curses.new_prescr()
+ self.assertIsInstance(screen, curses.screen)
+ self.assertIsNone(screen.stdscr)
+ del screen
+ gc_collect()
+
+ @cpython_only
+ def test_disallow_instantiation(self):
+ # The screen type cannot be instantiated directly (bpo-43916).
+ check_disallow_instantiation(self, curses.screen)
+
+
if __name__ == '__main__':
unittest.main()
--- /dev/null
+Add support for multiple terminals to the :mod:`curses` module: 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:`curses.window.use` method.
Here's a list of currently unsupported functions:
addchnstr addchstr color_set define_key
- del_curterm delscreen dupwin inchnstr inchstr innstr keyok
+ del_curterm dupwin inchnstr inchstr innstr keyok
mcprint mvaddchnstr mvaddchstr mvcur mvinchnstr
mvinchstr mvinnstr mmvwaddchnstr mvwaddchstr
- mvwinchnstr mvwinchstr mvwinnstr newterm
+ mvwinchnstr mvwinchstr mvwinnstr
restartterm ripoffline scr_dump
- scr_init scr_restore scr_set scrl set_curterm set_term setterm
+ scr_init scr_restore scr_set scrl set_curterm setterm
tgetent tgetflag tgetnum tgetstr tgoto timeout tputs
vidattr vidputs waddchnstr waddchstr
wcolor_set winchnstr winchstr winnstr wmouse_trafo wscrl
#include "pycore_capsule.h" // _PyCapsule_SetTraverse()
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_structseq.h" // _PyStructSequence_NewType()
-#include "pycore_fileutils.h" // _Py_set_inheritable
+#include "pycore_fileutils.h" // _Py_dup(), _Py_set_inheritable()
#ifdef __hpux
#define STRICT_SYSV_CURSES
typedef struct {
PyObject *error; // curses exception type
PyTypeObject *window_type; // exposed by PyCursesWindow_Type
+ PyTypeObject *screen_type; // _curses.screen
+ PyObject *topscreen; // owned ref to the current screen object,
+ // or NULL for the initscr() screen
} cursesmodule_state;
static inline cursesmodule_state *
}
#define _PyCursesWindowObject_CAST(op) ((PyCursesWindowObject *)(op))
+#define _PyCursesScreenObject_CAST(op) ((PyCursesScreenObject *)(op))
/*[clinic input]
module _curses
class _curses.window "PyCursesWindowObject *" "clinic_state()->window_type"
+class _curses.screen "PyCursesScreenObject *" "clinic_state()->screen_type"
[clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ae6cb623018f2cbc]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=4b027ab105ab94e1]*/
/* Indicate whether the module has already been loaded or not. */
static int curses_module_loaded = 0;
static PyObject *
PyCursesWindow_New(cursesmodule_state *state,
WINDOW *win, const char *encoding,
- PyCursesWindowObject *orig)
+ PyCursesWindowObject *orig, PyObject *screen)
{
if (encoding == NULL) {
#if defined(MS_WINDOWS)
return NULL;
}
wo->win = win;
+ wo->orig = (PyCursesWindowObject *)Py_XNewRef((PyObject *)orig);
+ wo->screen = Py_XNewRef(screen);
wo->encoding = _PyMem_Strdup(encoding);
if (wo->encoding == NULL) {
Py_DECREF(wo);
PyErr_NoMemory();
return NULL;
}
- wo->orig = orig;
- Py_XINCREF(orig);
PyObject_GC_Track((PyObject *)wo);
return (PyObject *)wo;
}
PyMem_Free(wo->encoding);
}
Py_XDECREF(wo->orig);
+ Py_XDECREF(wo->screen);
window_type->tp_free(self);
Py_DECREF(window_type);
}
Py_VISIT(Py_TYPE(self));
PyCursesWindowObject *wo = (PyCursesWindowObject *)self;
Py_VISIT(wo->orig);
+ Py_VISIT(wo->screen);
return 0;
}
}
cursesmodule_state *state = get_cursesmodule_state_by_win(self);
- return PyCursesWindow_New(state, win, NULL, self);
+ return PyCursesWindow_New(state, win, NULL, self, self->screen);
}
/*[clinic input]
}
cursesmodule_state *state = get_cursesmodule_state_by_win(self);
- return PyCursesWindow_New(state, win, self->encoding, self);
+ return PyCursesWindow_New(state, win, self->encoding, self, self->screen);
}
/*[clinic input]
#include "clinic/_cursesmodule.c.h"
#undef clinic_state
+#if defined(HAVE_CURSES_USE_SCREEN) || defined(HAVE_CURSES_USE_WINDOW)
+/* Shared trampoline for window.use()/screen.use(): call
+ func(obj, *extra, **kwargs) and store the result (NULL on exception) in
+ data->result. */
+typedef struct {
+ PyObject *obj; /* the window or screen object */
+ PyObject *func; /* the callable */
+ PyObject *extra; /* extra positional arguments (a tuple) */
+ PyObject *kwargs; /* keyword arguments (a dict), or NULL */
+ PyObject *result; /* output: the call result, or NULL */
+} curses_use_data;
+
+static void
+curses_use_call(curses_use_data *data)
+{
+ Py_ssize_t n = PyTuple_GET_SIZE(data->extra);
+ PyObject *callargs = PyTuple_New(n + 1);
+ if (callargs == NULL) {
+ data->result = NULL;
+ return;
+ }
+ PyTuple_SET_ITEM(callargs, 0, Py_NewRef(data->obj));
+ for (Py_ssize_t i = 0; i < n; i++) {
+ PyTuple_SET_ITEM(callargs, i + 1,
+ Py_NewRef(PyTuple_GET_ITEM(data->extra, i)));
+ }
+ data->result = PyObject_Call(data->func, callargs, data->kwargs);
+ Py_DECREF(callargs);
+}
+
+/* Parse (func, *extra) from a use() method's argument tuple. */
+static int
+curses_use_parse(PyObject *args, PyObject **func, PyObject **extra)
+{
+ Py_ssize_t nargs = PyTuple_GET_SIZE(args);
+ if (nargs < 1) {
+ PyErr_SetString(PyExc_TypeError,
+ "use() missing required argument 'func'");
+ return -1;
+ }
+ *func = PyTuple_GET_ITEM(args, 0);
+ if (!PyCallable_Check(*func)) {
+ PyErr_SetString(PyExc_TypeError, "use(): func must be callable");
+ return -1;
+ }
+ *extra = PyTuple_GetSlice(args, 1, nargs);
+ return *extra == NULL ? -1 : 0;
+}
+#endif
+
+#ifdef HAVE_CURSES_USE_WINDOW
+static int
+curses_use_window_cb(WINDOW *Py_UNUSED(win), void *data)
+{
+ curses_use_call((curses_use_data *)data);
+ return 0;
+}
+
+PyDoc_STRVAR(PyCursesWindow_use__doc__,
+"use($self, func, /, *args, **kwargs)\n--\n\n"
+"Call func(win, *args, **kwargs) with the window locked,\n"
+"and return its result.");
+
+static PyObject *
+PyCursesWindow_use(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyCursesWindowObject *wo = _PyCursesWindowObject_CAST(self);
+ PyObject *func, *extra;
+ if (curses_use_parse(args, &func, &extra) < 0) {
+ return NULL;
+ }
+ curses_use_data data = {self, func, extra, kwargs, NULL};
+ use_window(wo->win, curses_use_window_cb, &data);
+ Py_DECREF(extra);
+ return data.result;
+}
+#endif /* HAVE_CURSES_USE_WINDOW */
+
static PyMethodDef PyCursesWindow_methods[] = {
_CURSES_WINDOW_ADDCH_METHODDEF
_CURSES_WINDOW_ADDNSTR_METHODDEF
"untouchwin($self, /)\n--\n\n"
"Mark all lines in the window as unchanged since last refresh()."},
_CURSES_WINDOW_VLINE_METHODDEF
+#ifdef HAVE_CURSES_USE_WINDOW
+ {"use", _PyCFunction_CAST(PyCursesWindow_use),
+ METH_VARARGS | METH_KEYWORDS, PyCursesWindow_use__doc__},
+#endif
{NULL, NULL} /* sentinel */
};
.slots = PyCursesWindow_Type_slots
};
+/* -------------------------------------------------------*/
+/* Screen objects (multiple terminals) */
+/* -------------------------------------------------------*/
+
+static PyObject *
+PyCursesScreen_New(cursesmodule_state *state, SCREEN *screen,
+ FILE *outfp, FILE *infp, PyObject *stdscr)
+{
+ PyCursesScreenObject *so = PyObject_GC_New(PyCursesScreenObject,
+ state->screen_type);
+ if (so == NULL) {
+ return NULL;
+ }
+ so->screen = screen;
+ so->outfp = outfp;
+ so->infp = infp;
+ so->stdscr = Py_XNewRef(stdscr);
+ PyObject_GC_Track((PyObject *)so);
+ return (PyObject *)so;
+}
+
+/* Free the C SCREEN and the FILE* streams owned by a screen object.
+ Safe to call more than once.
+
+ This must run by reference counting (from the dealloc), not from tp_clear:
+ it has to happen only once every window on the screen is gone, and thus
+ after del_panel() for any panel built on one of those windows. delscreen()
+ tears down the screen that del_panel() needs, so a panel outliving its
+ screen would crash. */
+static void
+curses_screen_close(PyCursesScreenObject *so)
+{
+ if (so->screen != NULL) {
+ delscreen(so->screen);
+ so->screen = NULL;
+ }
+ if (so->outfp != NULL) {
+ fclose(so->outfp);
+ so->outfp = NULL;
+ }
+ if (so->infp != NULL) {
+ fclose(so->infp);
+ so->infp = NULL;
+ }
+}
+
+static PyObject *
+PyCursesScreen_get_stdscr(PyObject *self, void *Py_UNUSED(closure))
+{
+ PyCursesScreenObject *so = _PyCursesScreenObject_CAST(self);
+ if (so->stdscr == NULL) {
+ Py_RETURN_NONE;
+ }
+ return Py_NewRef(so->stdscr);
+}
+
+static int
+PyCursesScreen_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ Py_VISIT(Py_TYPE(self));
+ Py_VISIT(_PyCursesScreenObject_CAST(self)->stdscr);
+ return 0;
+}
+
+static int
+PyCursesScreen_clear(PyObject *self)
+{
+ PyCursesScreenObject *so = _PyCursesScreenObject_CAST(self);
+ /* Break the reference cycle between a screen and its standard window by
+ dropping the reference to that window. Do NOT delscreen() here: that is
+ deferred to the dealloc so it runs after every window (see
+ curses_screen_close()). delscreen() will free the standard window, so
+ detach it from its wrapper first: the wrapper must not delwin() a window
+ that delscreen() frees. Any further use of the wrapper operates on a
+ NULL window and fails cleanly. */
+ if (so->stdscr != NULL) {
+ ((PyCursesWindowObject *)so->stdscr)->win = NULL;
+ }
+ Py_CLEAR(so->stdscr);
+ return 0;
+}
+
+static void
+PyCursesScreen_dealloc(PyObject *self)
+{
+ PyTypeObject *tp = Py_TYPE(self);
+ PyObject_GC_UnTrack(self);
+ (void)PyCursesScreen_clear(self);
+ curses_screen_close(_PyCursesScreenObject_CAST(self));
+ tp->tp_free(self);
+ Py_DECREF(tp);
+}
+
+static PyGetSetDef PyCursesScreen_getsets[] = {
+ {"stdscr", PyCursesScreen_get_stdscr, NULL,
+ "the screen's standard window (stdscr)", NULL},
+ {NULL, NULL, NULL, NULL, NULL} /* sentinel */
+};
+
+#ifdef HAVE_CURSES_USE_SCREEN
+static int
+curses_use_screen_cb(SCREEN *Py_UNUSED(sp), void *data)
+{
+ curses_use_call((curses_use_data *)data);
+ return 0;
+}
+
+PyDoc_STRVAR(PyCursesScreen_use__doc__,
+"use($self, func, /, *args, **kwargs)\n--\n\n"
+"Call func(screen, *args, **kwargs) with the screen locked,\n"
+"and return its result.");
+
+static PyObject *
+PyCursesScreen_use(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyCursesScreenObject *so = _PyCursesScreenObject_CAST(self);
+ if (so->screen == NULL) {
+ cursesmodule_state *state = get_cursesmodule_state_by_cls(Py_TYPE(self));
+ PyErr_SetString(state->error, "the screen has been deleted");
+ return NULL;
+ }
+ PyObject *func, *extra;
+ if (curses_use_parse(args, &func, &extra) < 0) {
+ return NULL;
+ }
+ curses_use_data data = {self, func, extra, kwargs, NULL};
+ use_screen(so->screen, curses_use_screen_cb, &data);
+ Py_DECREF(extra);
+ return data.result;
+}
+#endif /* HAVE_CURSES_USE_SCREEN */
+
+PyDoc_STRVAR(PyCursesScreen_close__doc__,
+"close($self, /)\n--\n\n"
+"Detach the screen's standard window, breaking their reference cycle.\n\n"
+"Afterwards the stdscr attribute is None and the window it returned earlier\n"
+"can no longer be used. The screen is released once it and its windows are\n"
+"no longer referenced.");
+
+static PyObject *
+PyCursesScreen_close(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ (void)PyCursesScreen_clear(self);
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef PyCursesScreen_methods[] = {
+ {"close", PyCursesScreen_close, METH_NOARGS,
+ PyCursesScreen_close__doc__},
+#ifdef HAVE_CURSES_USE_SCREEN
+ {"use", _PyCFunction_CAST(PyCursesScreen_use),
+ METH_VARARGS | METH_KEYWORDS, PyCursesScreen_use__doc__},
+#endif
+ {NULL, NULL} /* sentinel */
+};
+
+static PyType_Slot PyCursesScreen_Type_slots[] = {
+ {Py_tp_methods, PyCursesScreen_methods},
+ {Py_tp_getset, PyCursesScreen_getsets},
+ {Py_tp_dealloc, PyCursesScreen_dealloc},
+ {Py_tp_traverse, PyCursesScreen_traverse},
+ {Py_tp_clear, PyCursesScreen_clear},
+ {0, NULL}
+};
+
+static PyType_Spec PyCursesScreen_Type_spec = {
+ .name = "_curses.screen",
+ .basicsize = sizeof(PyCursesScreenObject),
+ .flags = Py_TPFLAGS_DEFAULT
+ | Py_TPFLAGS_DISALLOW_INSTANTIATION
+ | Py_TPFLAGS_IMMUTABLETYPE
+ | Py_TPFLAGS_HEAPTYPE
+ | Py_TPFLAGS_HAVE_GC,
+ .slots = PyCursesScreen_Type_slots
+};
+
/* -------------------------------------------------------*/
/*
Undo the effect of a preceding filter() call.
-Must be called before initscr(). It restores the normal behaviour
-disabled by filter(), so that the next initscr() uses the full screen
-rather than a single line.
+Must be called before initscr(). It restores the normal behaviour that
+filter() disables, so that the next initscr() or newterm() uses the full
+screen rather than a single line.
[clinic start generated code]*/
static PyObject *
_curses_nofilter_impl(PyObject *module)
-/*[clinic end generated code: output=d95ca4d48a6bdbdf input=58aea83b1a5c969f]*/
+/*[clinic end generated code: output=d95ca4d48a6bdbdf input=53183055c0901ab7]*/
{
/* not checking for PyCursesInitialised here since nofilter() must
be called before initscr() */
static PyObject *
_curses_endwin_impl(PyObject *module)
/*[clinic end generated code: output=c0150cd96d2f4128 input=e172cfa43062f3fa]*/
-NoArgNoReturnFunctionBody(endwin)
+{
+ PyCursesStatefulInitialised(module);
+
+ /* endwin() writes to the terminal and may call tcdrain(), which can block
+ (e.g. on a pty whose output is not being read); release the GIL so other
+ threads -- including one draining that terminal -- can run meanwhile. */
+ int code;
+ Py_BEGIN_ALLOW_THREADS
+ code = endwin();
+ Py_END_ALLOW_THREADS
+ return curses_check_err(module, code, "endwin", NULL);
+}
/*[clinic input]
_curses.erasechar
goto error;
}
cursesmodule_state *state = get_cursesmodule_state(module);
- res = PyCursesWindow_New(state, win, NULL, NULL);
+ res = PyCursesWindow_New(state, win, NULL, NULL, state->topscreen);
error:
fclose(fp);
return 0;
}
-/*[clinic input]
-_curses.initscr
-
-Initialize the library.
-
-Return a WindowObject which represents the whole screen.
-[clinic start generated code]*/
-
-static PyObject *
-_curses_initscr_impl(PyObject *module)
-/*[clinic end generated code: output=619fb68443810b7b input=514f4bce1821f6b5]*/
+/* Populate the module dictionary with the ACS_* line-drawing constants and
+ LINES/COLS. These are only meaningful once a screen exists (after
+ initscr() or newterm()), which is why this is not done at module
+ initialisation. Returns 0 on success, -1 with an exception set. */
+static int
+curses_init_dict(PyObject *module)
{
- WINDOW *win;
-
- if (curses_initscr_called) {
- cursesmodule_state *state = get_cursesmodule_state(module);
- int code = wrefresh(stdscr);
- if (code == ERR) {
- _curses_set_null_error(state, "wrefresh", "initscr");
- return NULL;
- }
- PyObject *winobj = PyCursesWindow_New(state, stdscr, NULL, NULL);
- if (winobj == NULL) {
- return NULL;
- }
- if (curses_update_screen_encoding(winobj) < 0) {
- Py_DECREF(winobj);
- return NULL;
- }
- return winobj;
- }
-
- win = initscr();
-
- if (win == NULL) {
- curses_set_null_error(module, "initscr", NULL);
- return NULL;
- }
-
- curses_initscr_called = curses_setupterm_called = TRUE;
-
PyObject *module_dict = PyModule_GetDict(module); // borrowed
if (module_dict == NULL) {
- return NULL;
+ return -1;
}
/* This was moved from initcurses() because it core dumped on SGI,
where they're not defined until you've called initscr() */
do { \
PyObject *value = PyLong_FromLong((long)(VALUE)); \
if (value == NULL) { \
- return NULL; \
+ return -1; \
} \
int rc = PyDict_SetItemString(module_dict, (NAME), value); \
Py_DECREF(value); \
if (rc < 0) { \
- return NULL; \
+ return -1; \
} \
} while (0)
SetDictInt("LINES", LINES);
SetDictInt("COLS", COLS);
#undef SetDictInt
+ return 0;
+}
+
+/*[clinic input]
+_curses.initscr
+
+Initialize the library.
+
+Return a WindowObject which represents the whole screen.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_initscr_impl(PyObject *module)
+/*[clinic end generated code: output=619fb68443810b7b input=514f4bce1821f6b5]*/
+{
+ WINDOW *win;
+
+ if (curses_initscr_called) {
+ cursesmodule_state *state = get_cursesmodule_state(module);
+ int code = wrefresh(stdscr);
+ if (code == ERR) {
+ _curses_set_null_error(state, "wrefresh", "initscr");
+ return NULL;
+ }
+ PyObject *winobj = PyCursesWindow_New(state, stdscr, NULL, NULL, NULL);
+ if (winobj == NULL) {
+ return NULL;
+ }
+ if (curses_update_screen_encoding(winobj) < 0) {
+ Py_DECREF(winobj);
+ return NULL;
+ }
+ return winobj;
+ }
+
+ win = initscr();
+
+ if (win == NULL) {
+ curses_set_null_error(module, "initscr", NULL);
+ return NULL;
+ }
+
+ curses_initscr_called = curses_setupterm_called = TRUE;
+
+ if (curses_init_dict(module) < 0) {
+ return NULL;
+ }
cursesmodule_state *state = get_cursesmodule_state(module);
- PyObject *winobj = PyCursesWindow_New(state, win, NULL, NULL);
+ PyObject *winobj = PyCursesWindow_New(state, win, NULL, NULL, NULL);
if (winobj == NULL) {
return NULL;
}
Py_RETURN_NONE;
}
+static int update_lines_cols(PyObject *private_module); /* defined below */
+
+/* Return a file descriptor for obj, or, if obj is NULL or None, for the
+ sys.<stdname> stream. Returns -1 with an exception set on error. */
+static int
+curses_fileno(PyObject *module, PyObject *obj, const char *stdname)
+{
+ if (obj != NULL && obj != Py_None) {
+ return PyObject_AsFileDescriptor(obj);
+ }
+ PyObject *stream;
+ if (PySys_GetOptionalAttrString(stdname, &stream) < 0) {
+ return -1;
+ }
+ if (stream == NULL || stream == Py_None) {
+ cursesmodule_state *state = get_cursesmodule_state(module);
+ PyErr_Format(state->error, "lost sys.%s", stdname);
+ Py_XDECREF(stream);
+ return -1;
+ }
+ int fd = PyObject_AsFileDescriptor(stream);
+ Py_DECREF(stream);
+ return fd;
+}
+
+/* Duplicate fd and wrap it in a new (non-inheritable) stdio stream that the
+ screen object will own. Duplicating means closing the stream later does
+ not close the caller's fd. Returns NULL with an exception set on error. */
+static FILE *
+curses_fdopen_dup(int fd, const char *mode)
+{
+ /* _Py_dup() duplicates the descriptor and makes the copy non-inheritable
+ atomically (and sets the error on failure). */
+ int dfd = _Py_dup(fd);
+ if (dfd < 0) {
+ return NULL;
+ }
+ FILE *stream = fdopen(dfd, mode);
+ if (stream == NULL) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ close(dfd);
+ return NULL;
+ }
+ return stream;
+}
+
+/*[clinic input]
+_curses.newterm
+
+ type: str(accept={str, NoneType}) = None
+ Terminal name; if None, the TERM environment variable is used.
+ fd: object = None
+ Output file object or descriptor (default: sys.stdout).
+ infd: object = None
+ Input file object or descriptor (default: sys.stdin).
+ /
+
+Return a new screen for the terminal, in addition to the initial screen.
+
+This is an alternative to initscr() for programs running on more than
+one terminal. Use set_term() to switch between the screens.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_newterm_impl(PyObject *module, const char *type, PyObject *fd,
+ PyObject *infd)
+/*[clinic end generated code: output=62663c31909d796c input=98507fe48c2e93cb]*/
+{
+ /* Duplicate each descriptor right after resolving it: resolving the other
+ one runs arbitrary Python code (e.g. a fileno() method) that could close
+ this one before it is duplicated. */
+ int out_fd = curses_fileno(module, fd, "stdout");
+ if (out_fd < 0) {
+ return NULL;
+ }
+ FILE *outfp = curses_fdopen_dup(out_fd, "wb");
+ if (outfp == NULL) {
+ return NULL;
+ }
+
+ int in_fd = curses_fileno(module, infd, "stdin");
+ if (in_fd < 0) {
+ fclose(outfp);
+ return NULL;
+ }
+ FILE *infp = curses_fdopen_dup(in_fd, "rb");
+ if (infp == NULL) {
+ fclose(outfp);
+ return NULL;
+ }
+
+ SCREEN *screen = newterm((char *)type, outfp, infp);
+ if (screen == NULL) {
+ curses_set_null_error(module, "newterm", NULL);
+ fclose(outfp);
+ fclose(infp);
+ return NULL;
+ }
+ /* newterm() makes the new screen the current one, so stdscr now refers
+ to its standard window. */
+ curses_initscr_called = curses_setupterm_called = TRUE;
+
+ cursesmodule_state *state = get_cursesmodule_state(module);
+ /* The screen object owns the SCREEN and the streams; deleting it (when it
+ is no longer referenced) calls delscreen() and closes the streams. */
+ PyObject *screenobj = PyCursesScreen_New(state, screen, outfp, infp, NULL);
+ if (screenobj == NULL) {
+ delscreen(screen);
+ fclose(outfp);
+ fclose(infp);
+ return NULL;
+ }
+ /* The standard window keeps the screen alive for its own lifetime. */
+ PyObject *win = PyCursesWindow_New(state, stdscr, NULL, NULL, screenobj);
+ if (win == NULL ||
+ curses_update_screen_encoding(win) < 0 ||
+ curses_init_dict(module) < 0)
+ {
+ Py_XDECREF(win);
+ Py_DECREF(screenobj);
+ return NULL;
+ }
+ ((PyCursesScreenObject *)screenobj)->stdscr = Py_NewRef(win);
+ Py_DECREF(win);
+ Py_XSETREF(state->topscreen, Py_NewRef(screenobj));
+ return screenobj;
+}
+
+/* Check that obj is an open screen object; returns it cast, or NULL with
+ TypeError/curses.error set. */
+static PyCursesScreenObject *
+curses_check_screen(PyObject *module, PyObject *obj)
+{
+ cursesmodule_state *state = get_cursesmodule_state(module);
+ if (!PyObject_TypeCheck(obj, state->screen_type)) {
+ PyErr_Format(PyExc_TypeError,
+ "expected a curses screen, got %T", obj);
+ return NULL;
+ }
+ PyCursesScreenObject *so = _PyCursesScreenObject_CAST(obj);
+ if (so->screen == NULL) {
+ PyErr_SetString(state->error, "the screen has been deleted");
+ return NULL;
+ }
+ return so;
+}
+
+/*[clinic input]
+_curses.set_term
+
+ screen: object
+ /
+
+Switch to the given screen and return the previously current screen.
+
+Returns None if the previous screen was the one created by initscr().
+[clinic start generated code]*/
+
+static PyObject *
+_curses_set_term(PyObject *module, PyObject *screen)
+/*[clinic end generated code: output=204cf9c40523bdef input=ed4dba18dd9adf6a]*/
+{
+ PyCursesScreenObject *so = curses_check_screen(module, screen);
+ if (so == NULL) {
+ return NULL;
+ }
+ set_term(so->screen);
+ if (!update_lines_cols(module)) {
+ return NULL;
+ }
+ cursesmodule_state *state = get_cursesmodule_state(module);
+ PyObject *prev = state->topscreen; /* steal the owned reference */
+ state->topscreen = Py_NewRef(screen);
+ return prev != NULL ? prev : Py_NewRef(Py_None);
+}
+
+#ifdef HAVE_CURSES_NEW_PRESCR
+/*[clinic input]
+_curses.new_prescr
+
+Create a screen and return it, without initializing a terminal.
+
+The screen can be used to call functions that affect the screen before
+calling newterm() or initscr().
+[clinic start generated code]*/
+
+static PyObject *
+_curses_new_prescr_impl(PyObject *module)
+/*[clinic end generated code: output=e7de5031da7511e2 input=1a3a89d630b641c3]*/
+{
+ SCREEN *screen = new_prescr();
+ if (screen == NULL) {
+ curses_set_null_error(module, "new_prescr", NULL);
+ return NULL;
+ }
+ cursesmodule_state *state = get_cursesmodule_state(module);
+ return PyCursesScreen_New(state, screen, NULL, NULL, NULL);
+}
+#endif /* HAVE_CURSES_NEW_PRESCR */
+
#if defined(NCURSES_EXT_FUNCS) && NCURSES_EXT_FUNCS >= 20081102
// https://invisible-island.net/ncurses/NEWS.html#index-t20080119
}
cursesmodule_state *state = get_cursesmodule_state(module);
- return PyCursesWindow_New(state, win, NULL, NULL);
+ return PyCursesWindow_New(state, win, NULL, NULL, state->topscreen);
}
/*[clinic input]
}
cursesmodule_state *state = get_cursesmodule_state(module);
- return PyCursesWindow_New(state, win, NULL, NULL);
+ return PyCursesWindow_New(state, win, NULL, NULL, state->topscreen);
}
/*[clinic input]
_CURSES_MOUSEMASK_METHODDEF
_CURSES_NAPMS_METHODDEF
_CURSES_NEWPAD_METHODDEF
+ _CURSES_NEWTERM_METHODDEF
_CURSES_NEWWIN_METHODDEF
+#ifdef HAVE_CURSES_NEW_PRESCR
+ _CURSES_NEW_PRESCR_METHODDEF
+#endif
_CURSES_NL_METHODDEF
_CURSES_NOCBREAK_METHODDEF
_CURSES_NOECHO_METHODDEF
#endif
_CURSES_GET_TABSIZE_METHODDEF
_CURSES_SET_TABSIZE_METHODDEF
+ _CURSES_SET_TERM_METHODDEF
_CURSES_SETSYX_METHODDEF
_CURSES_SETUPTERM_METHODDEF
_CURSES_START_COLOR_METHODDEF
cursesmodule_state *state = get_cursesmodule_state(mod);
Py_VISIT(state->error);
Py_VISIT(state->window_type);
+ Py_VISIT(state->screen_type);
+ Py_VISIT(state->topscreen);
return 0;
}
cursesmodule_state *state = get_cursesmodule_state(mod);
Py_CLEAR(state->error);
Py_CLEAR(state->window_type);
+ Py_CLEAR(state->screen_type);
+ Py_CLEAR(state->topscreen);
return 0;
}
if (PyModule_AddType(module, state->window_type) < 0) {
return -1;
}
+ state->screen_type = (PyTypeObject *)PyType_FromModuleAndSpec(
+ module, &PyCursesScreen_Type_spec, NULL);
+ if (state->screen_type == NULL) {
+ return -1;
+ }
+ if (PyModule_AddType(module, state->screen_type) < 0) {
+ return -1;
+ }
/* Add some symbolic constants to the module */
PyObject *module_dict = PyModule_GetDict(module);
"\n"
"Undo the effect of a preceding filter() call.\n"
"\n"
-"Must be called before initscr(). It restores the normal behaviour\n"
-"disabled by filter(), so that the next initscr() uses the full screen\n"
-"rather than a single line.");
+"Must be called before initscr(). It restores the normal behaviour that\n"
+"filter() disables, so that the next initscr() or newterm() uses the full\n"
+"screen rather than a single line.");
#define _CURSES_NOFILTER_METHODDEF \
{"nofilter", (PyCFunction)_curses_nofilter, METH_NOARGS, _curses_nofilter__doc__},
return return_value;
}
+PyDoc_STRVAR(_curses_newterm__doc__,
+"newterm($module, type=None, fd=None, infd=None, /)\n"
+"--\n"
+"\n"
+"Return a new screen for the terminal, in addition to the initial screen.\n"
+"\n"
+" type\n"
+" Terminal name; if None, the TERM environment variable is used.\n"
+" fd\n"
+" Output file object or descriptor (default: sys.stdout).\n"
+" infd\n"
+" Input file object or descriptor (default: sys.stdin).\n"
+"\n"
+"This is an alternative to initscr() for programs running on more than\n"
+"one terminal. Use set_term() to switch between the screens.");
+
+#define _CURSES_NEWTERM_METHODDEF \
+ {"newterm", _PyCFunction_CAST(_curses_newterm), METH_FASTCALL, _curses_newterm__doc__},
+
+static PyObject *
+_curses_newterm_impl(PyObject *module, const char *type, PyObject *fd,
+ PyObject *infd);
+
+static PyObject *
+_curses_newterm(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ const char *type = NULL;
+ PyObject *fd = Py_None;
+ PyObject *infd = Py_None;
+
+ if (!_PyArg_CheckPositional("newterm", nargs, 0, 3)) {
+ goto exit;
+ }
+ if (nargs < 1) {
+ goto skip_optional;
+ }
+ if (args[0] == Py_None) {
+ type = NULL;
+ }
+ else if (PyUnicode_Check(args[0])) {
+ Py_ssize_t type_length;
+ type = PyUnicode_AsUTF8AndSize(args[0], &type_length);
+ if (type == NULL) {
+ goto exit;
+ }
+ if (strlen(type) != (size_t)type_length) {
+ PyErr_SetString(PyExc_ValueError, "embedded null character");
+ goto exit;
+ }
+ }
+ else {
+ _PyArg_BadArgument("newterm", "argument 1", "str or None", args[0]);
+ goto exit;
+ }
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ fd = args[1];
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ infd = args[2];
+skip_optional:
+ return_value = _curses_newterm_impl(module, type, fd, infd);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_curses_set_term__doc__,
+"set_term($module, screen, /)\n"
+"--\n"
+"\n"
+"Switch to the given screen and return the previously current screen.\n"
+"\n"
+"Returns None if the previous screen was the one created by initscr().");
+
+#define _CURSES_SET_TERM_METHODDEF \
+ {"set_term", (PyCFunction)_curses_set_term, METH_O, _curses_set_term__doc__},
+
+#if defined(HAVE_CURSES_NEW_PRESCR)
+
+PyDoc_STRVAR(_curses_new_prescr__doc__,
+"new_prescr($module, /)\n"
+"--\n"
+"\n"
+"Create a screen and return it, without initializing a terminal.\n"
+"\n"
+"The screen can be used to call functions that affect the screen before\n"
+"calling newterm() or initscr().");
+
+#define _CURSES_NEW_PRESCR_METHODDEF \
+ {"new_prescr", (PyCFunction)_curses_new_prescr, METH_NOARGS, _curses_new_prescr__doc__},
+
+static PyObject *
+_curses_new_prescr_impl(PyObject *module);
+
+static PyObject *
+_curses_new_prescr(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_new_prescr_impl(module);
+}
+
+#endif /* defined(HAVE_CURSES_NEW_PRESCR) */
+
#if (defined(NCURSES_EXT_FUNCS) && NCURSES_EXT_FUNCS >= 20081102)
PyDoc_STRVAR(_curses_get_escdelay__doc__,
#define _CURSES_HAS_KEY_METHODDEF
#endif /* !defined(_CURSES_HAS_KEY_METHODDEF) */
+#ifndef _CURSES_NEW_PRESCR_METHODDEF
+ #define _CURSES_NEW_PRESCR_METHODDEF
+#endif /* !defined(_CURSES_NEW_PRESCR_METHODDEF) */
+
#ifndef _CURSES_GET_ESCDELAY_METHODDEF
#define _CURSES_GET_ESCDELAY_METHODDEF
#endif /* !defined(_CURSES_GET_ESCDELAY_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=0bce70b538541c9e input=a9049054013a1b77]*/
+/*[clinic end generated code: output=8188ebf7404d028a input=a9049054013a1b77]*/
+
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for curses function new_prescr" >&5
+printf %s "checking for curses function new_prescr... " >&6; }
+if test ${ac_cv_lib_curses_new_prescr+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#define NCURSES_OPAQUE 0
+#if defined(HAVE_NCURSESW_NCURSES_H)
+# include <ncursesw/ncurses.h>
+#elif defined(HAVE_NCURSESW_CURSES_H)
+# include <ncursesw/curses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+# include <ncurses/ncurses.h>
+#elif defined(HAVE_NCURSES_CURSES_H)
+# include <ncurses/curses.h>
+#elif defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+#elif defined(HAVE_CURSES_H)
+# include <curses.h>
+#endif
+
+int
+main (void)
+{
+
+ #ifndef new_prescr
+ void *x=new_prescr
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_lib_curses_new_prescr=yes
+else case e in #(
+ e) ac_cv_lib_curses_new_prescr=no ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_new_prescr" >&5
+printf "%s\n" "$ac_cv_lib_curses_new_prescr" >&6; }
+ if test "x$ac_cv_lib_curses_new_prescr" = xyes
+then :
+
+printf "%s\n" "#define HAVE_CURSES_NEW_PRESCR 1" >>confdefs.h
+
+fi
+
+
+
+
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for curses function use_screen" >&5
+printf %s "checking for curses function use_screen... " >&6; }
+if test ${ac_cv_lib_curses_use_screen+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#define NCURSES_OPAQUE 0
+#if defined(HAVE_NCURSESW_NCURSES_H)
+# include <ncursesw/ncurses.h>
+#elif defined(HAVE_NCURSESW_CURSES_H)
+# include <ncursesw/curses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+# include <ncurses/ncurses.h>
+#elif defined(HAVE_NCURSES_CURSES_H)
+# include <ncurses/curses.h>
+#elif defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+#elif defined(HAVE_CURSES_H)
+# include <curses.h>
+#endif
+
+int
+main (void)
+{
+
+ #ifndef use_screen
+ void *x=use_screen
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_lib_curses_use_screen=yes
+else case e in #(
+ e) ac_cv_lib_curses_use_screen=no ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_use_screen" >&5
+printf "%s\n" "$ac_cv_lib_curses_use_screen" >&6; }
+ if test "x$ac_cv_lib_curses_use_screen" = xyes
+then :
+
+printf "%s\n" "#define HAVE_CURSES_USE_SCREEN 1" >>confdefs.h
+
+fi
+
+
+
+
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for curses function use_window" >&5
+printf %s "checking for curses function use_window... " >&6; }
+if test ${ac_cv_lib_curses_use_window+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#define NCURSES_OPAQUE 0
+#if defined(HAVE_NCURSESW_NCURSES_H)
+# include <ncursesw/ncurses.h>
+#elif defined(HAVE_NCURSESW_CURSES_H)
+# include <ncursesw/curses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+# include <ncurses/ncurses.h>
+#elif defined(HAVE_NCURSES_CURSES_H)
+# include <ncurses/curses.h>
+#elif defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+#elif defined(HAVE_CURSES_H)
+# include <curses.h>
+#endif
+
+int
+main (void)
+{
+
+ #ifndef use_window
+ void *x=use_window
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_lib_curses_use_window=yes
+else case e in #(
+ e) ac_cv_lib_curses_use_window=no ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_use_window" >&5
+printf "%s\n" "$ac_cv_lib_curses_use_window" >&6; }
+ if test "x$ac_cv_lib_curses_use_window" = xyes
+then :
+
+printf "%s\n" "#define HAVE_CURSES_USE_WINDOW 1" >>confdefs.h
+
+fi
+
+
+
CPPFLAGS=$ac_save_cppflags
fi
PY_CHECK_CURSES_FUNC([has_key])
PY_CHECK_CURSES_FUNC([typeahead])
PY_CHECK_CURSES_FUNC([use_env])
+PY_CHECK_CURSES_FUNC([new_prescr])
+PY_CHECK_CURSES_FUNC([use_screen])
+PY_CHECK_CURSES_FUNC([use_window])
CPPFLAGS=$ac_save_cppflags
])dnl have_curses != no
])dnl save env
/* Define if you have the 'is_term_resized' function. */
#undef HAVE_CURSES_IS_TERM_RESIZED
+/* Define if you have the 'new_prescr' function. */
+#undef HAVE_CURSES_NEW_PRESCR
+
/* Define if you have the 'nofilter' function. */
#undef HAVE_CURSES_NOFILTER
/* Define if you have the 'use_env' function. */
#undef HAVE_CURSES_USE_ENV
+/* Define if you have the 'use_screen' function. */
+#undef HAVE_CURSES_USE_SCREEN
+
+/* Define if you have the 'use_window' function. */
+#undef HAVE_CURSES_USE_WINDOW
+
/* Define if you have the 'wchgat' function. */
#undef HAVE_CURSES_WCHGAT