diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst
index e15067e5743bc4..801fbec96c6ee6 100644
--- a/Doc/library/curses.rst
+++ b/Doc/library/curses.rst
@@ -1040,6 +1040,26 @@ Terminfo database
:func:`setupterm` (or :func:`initscr`) must be called first.
+.. function:: tgetent(term)
+.. function:: tgetflag(id)
+.. function:: tgetnum(id)
+.. function:: tgetstr(id)
+.. function:: tgoto(capability, col, row)
+
+ Access the terminal's legacy termcap entries. Call :func:`setupterm` first.
+ ``tgetent`` returns ``0`` or ``1``; the other lookup functions return their
+ termcap value, or ``None`` when no value is available. *capability* must be
+ a bytes object returned by :func:`tgetstr`.
+
+.. function:: mcprint(string, count)
+
+ Send *count* copies of *string* to the terminal printer.
+
+.. function:: vidattr(attrs)
+.. function:: vidputs(attrs)
+
+ Set the terminal video attributes represented by *attrs*.
+
Utilities
~~~~~~~~~
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index ec64ce52dd9e8c..180dfbf1134017 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -1886,6 +1886,35 @@ def test_misc_module_funcs(self):
curses.newpad(50, 50)
+ @requires_curses_func('mcprint')
+ def test_mcprint(self):
+ try:
+ result = curses.mcprint('x', 1)
+ except curses.error:
+ pass
+ else:
+ self.assertIsNone(result)
+
+ @requires_curses_func('tgetent')
+ def test_termcap_functions(self):
+ self.assertIn(curses.tgetent(os.environ.get('TERM', 'unknown')), (0, 1))
+ self.assertIsInstance(curses.tgetflag('am'), int)
+ self.assertIsInstance(curses.tgetnum('co'), int)
+ value = curses.tgetstr('cl')
+ if value is None:
+ self.assertIsNone(value)
+ else:
+ self.assertIsInstance(value, bytes)
+ self.assertIsInstance(curses.tgoto(value, 0, 0), bytes)
+
+ @requires_curses_func('vidattr')
+ def test_video_attributes(self):
+ for function in curses.vidattr, curses.vidputs:
+ try:
+ self.assertIsNone(function(curses.A_NORMAL))
+ except curses.error:
+ pass
+
def test_env_queries(self):
self.assertIsInstance(curses.termname(), bytes)
self.assertIsInstance(curses.longname(), bytes)
diff --git a/Modules/_curses_form.c b/Modules/_curses_form.c
new file mode 100644
index 00000000000000..cff46c65f4d54f
--- /dev/null
+++ b/Modules/_curses_form.c
@@ -0,0 +1,715 @@
+/*
+ * Interface to the ncurses panel library
+ *
+ * Original version by Thomas Gellekum
+ */
+
+/* Release Number */
+
+static const char PyCursesVersion[] = "0.1";
+
+/* Includes */
+
+#include "Python.h"
+
+#include "py_curses.h"
+
+#include
+
+typedef struct {
+ PyObject *PyCursesError;
+ PyTypeObject *PyCursesPanel_Type;
+} _curses_panel_state;
+
+static inline _curses_panel_state *
+get_curses_panel_state(PyObject *module)
+{
+ void *state = PyModule_GetState(module);
+ assert(state != NULL);
+ return (_curses_panel_state *)state;
+}
+
+static int
+_curses_panel_clear(PyObject *mod)
+{
+ _curses_panel_state *state = get_curses_panel_state(mod);
+ Py_CLEAR(state->PyCursesError);
+ Py_CLEAR(state->PyCursesPanel_Type);
+ return 0;
+}
+
+static int
+_curses_panel_traverse(PyObject *mod, visitproc visit, void *arg)
+{
+ Py_VISIT(Py_TYPE(mod));
+ _curses_panel_state *state = get_curses_panel_state(mod);
+ Py_VISIT(state->PyCursesError);
+ Py_VISIT(state->PyCursesPanel_Type);
+ return 0;
+}
+
+static void
+_curses_panel_free(void *mod)
+{
+ _curses_panel_clear((PyObject *) mod);
+}
+
+/* Utility Functions */
+
+/*
+ * Check the return code from a curses function and return None
+ * or raise an exception as appropriate.
+ */
+
+static PyObject *
+PyCursesCheckERR(_curses_panel_state *state, int code, const char *fname)
+{
+ if (code != ERR) {
+ Py_RETURN_NONE;
+ }
+ else {
+ if (fname == NULL) {
+ PyErr_SetString(state->PyCursesError, catchall_ERR);
+ }
+ else {
+ PyErr_Format(state->PyCursesError, "%s() returned ERR", fname);
+ }
+ return NULL;
+ }
+}
+
+/*****************************************************************************
+ The Panel Object
+******************************************************************************/
+
+/* Definition of the panel object and panel type */
+
+typedef struct {
+ PyObject_HEAD
+ PANEL *pan;
+ PyCursesWindowObject *wo; /* for reference counts */
+} PyCursesPanelObject;
+
+/* Some helper functions. The problem is that there's always a window
+ associated with a panel. To ensure that Python's GC doesn't pull
+ this window from under our feet we need to keep track of references
+ to the corresponding window object within Python. We can't use
+ dupwin(oldwin) to keep a copy of the curses WINDOW because the
+ contents of oldwin is copied only once; code like
+
+ win = newwin(...)
+ pan = win.panel()
+ win.addstr(some_string)
+ pan.window().addstr(other_string)
+
+ will fail. */
+
+/* We keep a linked list of PyCursesPanelObjects, lop. A list should
+ suffice, I don't expect more than a handful or at most a few
+ dozens of panel objects within a typical program. */
+typedef struct _list_of_panels {
+ PyCursesPanelObject *po;
+ struct _list_of_panels *next;
+} list_of_panels;
+
+/* list anchor */
+static list_of_panels *lop;
+
+/* Insert a new panel object into lop */
+static int
+insert_lop(PyCursesPanelObject *po)
+{
+ list_of_panels *new;
+
+ if ((new = (list_of_panels *)PyMem_Malloc(sizeof(list_of_panels))) == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ new->po = po;
+ new->next = lop;
+ lop = new;
+ return 0;
+}
+
+/* Remove the panel object from lop */
+static void
+remove_lop(PyCursesPanelObject *po)
+{
+ list_of_panels *temp, *n;
+
+ temp = lop;
+ if (temp->po == po) {
+ lop = temp->next;
+ PyMem_Free(temp);
+ return;
+ }
+ while (temp->next == NULL || temp->next->po != po) {
+ if (temp->next == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "remove_lop: can't find Panel Object");
+ return;
+ }
+ temp = temp->next;
+ }
+ n = temp->next->next;
+ PyMem_Free(temp->next);
+ temp->next = n;
+ return;
+}
+
+/* Return the panel object that corresponds to pan */
+static PyCursesPanelObject *
+find_po(PANEL *pan)
+{
+ list_of_panels *temp;
+ for (temp = lop; temp->po->pan != pan; temp = temp->next)
+ if (temp->next == NULL) return NULL; /* not found!? */
+ return temp->po;
+}
+
+/*[clinic input]
+module _curses_panel
+class _curses_panel.panel "PyCursesPanelObject *" "&PyCursesPanel_Type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=2f4ef263ca850a31]*/
+
+#include "clinic/_curses_panel.c.h"
+
+/* ------------- PANEL routines --------------- */
+
+/*[clinic input]
+_curses_panel.panel.bottom
+
+ cls: defining_class
+
+Push the panel to the bottom of the stack.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_bottom_impl(PyCursesPanelObject *self, PyTypeObject *cls)
+/*[clinic end generated code: output=8ec7fbbc08554021 input=6b7d2c0578b5a1c4]*/
+{
+ _curses_panel_state *state = PyType_GetModuleState(cls);
+ return PyCursesCheckERR(state, bottom_panel(self->pan), "bottom");
+}
+
+/*[clinic input]
+_curses_panel.panel.hide
+
+ cls: defining_class
+
+Hide the panel.
+
+This does not delete the object, it just makes the window on screen invisible.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_hide_impl(PyCursesPanelObject *self, PyTypeObject *cls)
+/*[clinic end generated code: output=cc6ab7203cdc1450 input=1bfc741f473e6055]*/
+{
+ _curses_panel_state *state = PyType_GetModuleState(cls);
+ return PyCursesCheckERR(state, hide_panel(self->pan), "hide");
+}
+
+/*[clinic input]
+_curses_panel.panel.show
+
+ cls: defining_class
+
+Display the panel (which might have been hidden).
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_show_impl(PyCursesPanelObject *self, PyTypeObject *cls)
+/*[clinic end generated code: output=dc3421de375f0409 input=8122e80151cb4379]*/
+{
+ _curses_panel_state *state = PyType_GetModuleState(cls);
+ return PyCursesCheckERR(state, show_panel(self->pan), "show");
+}
+
+/*[clinic input]
+_curses_panel.panel.top
+
+ cls: defining_class
+
+Push panel to the top of the stack.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_top_impl(PyCursesPanelObject *self, PyTypeObject *cls)
+/*[clinic end generated code: output=10a072e511e873f7 input=1f372d597dda3379]*/
+{
+ _curses_panel_state *state = PyType_GetModuleState(cls);
+ return PyCursesCheckERR(state, top_panel(self->pan), "top");
+}
+
+/* Allocation and deallocation of Panel Objects */
+
+static PyObject *
+PyCursesPanel_New(_curses_panel_state *state, PANEL *pan,
+ PyCursesWindowObject *wo)
+{
+ PyCursesPanelObject *po = PyObject_New(PyCursesPanelObject,
+ state->PyCursesPanel_Type);
+ if (po == NULL) {
+ return NULL;
+ }
+
+ po->pan = pan;
+ if (insert_lop(po) < 0) {
+ po->wo = NULL;
+ Py_DECREF(po);
+ return NULL;
+ }
+ po->wo = (PyCursesWindowObject*)Py_NewRef(wo);
+ return (PyObject *)po;
+}
+
+static void
+PyCursesPanel_Dealloc(PyCursesPanelObject *po)
+{
+ PyObject *tp, *obj;
+
+ tp = (PyObject *) Py_TYPE(po);
+ obj = (PyObject *) panel_userptr(po->pan);
+ if (obj) {
+ (void)set_panel_userptr(po->pan, NULL);
+ Py_DECREF(obj);
+ }
+ (void)del_panel(po->pan);
+ if (po->wo != NULL) {
+ Py_DECREF(po->wo);
+ remove_lop(po);
+ }
+ PyObject_Free(po);
+ Py_DECREF(tp);
+}
+
+/* panel_above(NULL) returns the bottom panel in the stack. To get
+ this behaviour we use curses.panel.bottom_panel(). */
+/*[clinic input]
+_curses_panel.panel.above
+
+Return the panel above the current panel.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_above_impl(PyCursesPanelObject *self)
+/*[clinic end generated code: output=70ac06d25fd3b4da input=c059994022976788]*/
+{
+ PANEL *pan;
+ PyCursesPanelObject *po;
+
+ pan = panel_above(self->pan);
+
+ if (pan == NULL) { /* valid output, it means the calling panel
+ is on top of the stack */
+ Py_RETURN_NONE;
+ }
+ po = find_po(pan);
+ if (po == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "panel_above: can't find Panel Object");
+ return NULL;
+ }
+ return Py_NewRef(po);
+}
+
+/* panel_below(NULL) returns the top panel in the stack. To get
+ this behaviour we use curses.panel.top_panel(). */
+/*[clinic input]
+_curses_panel.panel.below
+
+Return the panel below the current panel.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_below_impl(PyCursesPanelObject *self)
+/*[clinic end generated code: output=282861122e06e3de input=cc08f61936d297c6]*/
+{
+ PANEL *pan;
+ PyCursesPanelObject *po;
+
+ pan = panel_below(self->pan);
+
+ if (pan == NULL) { /* valid output, it means the calling panel
+ is on the bottom of the stack */
+ Py_RETURN_NONE;
+ }
+ po = find_po(pan);
+ if (po == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "panel_below: can't find Panel Object");
+ return NULL;
+ }
+ return Py_NewRef(po);
+}
+
+/*[clinic input]
+_curses_panel.panel.hidden
+
+Return True if the panel is hidden (not visible), False otherwise.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_hidden_impl(PyCursesPanelObject *self)
+/*[clinic end generated code: output=66eebd1ab4501a71 input=453d4b4fce25e21a]*/
+{
+ if (panel_hidden(self->pan))
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+}
+
+/*[clinic input]
+_curses_panel.panel.move
+
+ cls: defining_class
+ y: int
+ x: int
+ /
+
+Move the panel to the screen coordinates (y, x).
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_move_impl(PyCursesPanelObject *self, PyTypeObject *cls,
+ int y, int x)
+/*[clinic end generated code: output=ce546c93e56867da input=60a0e7912ff99849]*/
+{
+ _curses_panel_state *state = PyType_GetModuleState(cls);
+ return PyCursesCheckERR(state, move_panel(self->pan, y, x), "move_panel");
+}
+
+/*[clinic input]
+_curses_panel.panel.window
+
+Return the window object associated with the panel.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_window_impl(PyCursesPanelObject *self)
+/*[clinic end generated code: output=5f05940d4106b4cb input=6067353d2c307901]*/
+{
+ return Py_NewRef(self->wo);
+}
+
+/*[clinic input]
+_curses_panel.panel.replace
+
+ cls: defining_class
+ win: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
+ /
+
+Change the window associated with the panel to the window win.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_replace_impl(PyCursesPanelObject *self,
+ PyTypeObject *cls,
+ PyCursesWindowObject *win)
+/*[clinic end generated code: output=c71f95c212d58ae7 input=dbec7180ece41ff5]*/
+{
+ _curses_panel_state *state = PyType_GetModuleState(cls);
+
+ PyCursesPanelObject *po = find_po(self->pan);
+ if (po == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "replace_panel: can't find Panel Object");
+ return NULL;
+ }
+
+ int rtn = replace_panel(self->pan, win->win);
+ if (rtn == ERR) {
+ PyErr_SetString(state->PyCursesError, "replace_panel() returned ERR");
+ return NULL;
+ }
+ Py_SETREF(po->wo, (PyCursesWindowObject*)Py_NewRef(win));
+ Py_RETURN_NONE;
+}
+
+/*[clinic input]
+_curses_panel.panel.set_userptr
+
+ cls: defining_class
+ obj: object
+ /
+
+Set the panel's user pointer to obj.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_set_userptr_impl(PyCursesPanelObject *self,
+ PyTypeObject *cls, PyObject *obj)
+/*[clinic end generated code: output=db74f3db07b28080 input=e3fee2ff7b1b8e48]*/
+{
+ PyCursesInitialised;
+ Py_INCREF(obj);
+ PyObject *oldobj = (PyObject *) panel_userptr(self->pan);
+ int rc = set_panel_userptr(self->pan, (void*)obj);
+ if (rc == ERR) {
+ /* In case of an ncurses error, decref the new object again */
+ Py_DECREF(obj);
+ }
+ else {
+ Py_XDECREF(oldobj);
+ }
+
+ _curses_panel_state *state = PyType_GetModuleState(cls);
+ return PyCursesCheckERR(state, rc, "set_panel_userptr");
+}
+
+/*[clinic input]
+_curses_panel.panel.userptr
+
+ cls: defining_class
+
+Return the user pointer for the panel.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_panel_userptr_impl(PyCursesPanelObject *self,
+ PyTypeObject *cls)
+/*[clinic end generated code: output=eea6e6f39ffc0179 input=f22ca4f115e30a80]*/
+{
+ _curses_panel_state *state = PyType_GetModuleState(cls);
+
+ PyCursesInitialised;
+ PyObject *obj = (PyObject *) panel_userptr(self->pan);
+ if (obj == NULL) {
+ PyErr_SetString(state->PyCursesError, "no userptr set");
+ return NULL;
+ }
+
+ return Py_NewRef(obj);
+}
+
+
+/* Module interface */
+
+static PyMethodDef PyCursesPanel_Methods[] = {
+ _CURSES_PANEL_PANEL_ABOVE_METHODDEF
+ _CURSES_PANEL_PANEL_BELOW_METHODDEF
+ _CURSES_PANEL_PANEL_BOTTOM_METHODDEF
+ _CURSES_PANEL_PANEL_HIDDEN_METHODDEF
+ _CURSES_PANEL_PANEL_HIDE_METHODDEF
+ _CURSES_PANEL_PANEL_MOVE_METHODDEF
+ _CURSES_PANEL_PANEL_REPLACE_METHODDEF
+ _CURSES_PANEL_PANEL_SET_USERPTR_METHODDEF
+ _CURSES_PANEL_PANEL_SHOW_METHODDEF
+ _CURSES_PANEL_PANEL_TOP_METHODDEF
+ _CURSES_PANEL_PANEL_USERPTR_METHODDEF
+ _CURSES_PANEL_PANEL_WINDOW_METHODDEF
+ {NULL, NULL} /* sentinel */
+};
+
+/* -------------------------------------------------------*/
+
+static PyType_Slot PyCursesPanel_Type_slots[] = {
+ {Py_tp_dealloc, PyCursesPanel_Dealloc},
+ {Py_tp_methods, PyCursesPanel_Methods},
+ {0, 0},
+};
+
+static PyType_Spec PyCursesPanel_Type_spec = {
+ .name = "_curses_panel.panel",
+ .basicsize = sizeof(PyCursesPanelObject),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
+ .slots = PyCursesPanel_Type_slots
+};
+
+/* Wrapper for panel_above(NULL). This function returns the bottom
+ panel of the stack, so it's renamed to bottom_panel().
+ panel.above() *requires* a panel object in the first place which
+ may be undesirable. */
+/*[clinic input]
+_curses_panel.bottom_panel
+
+Return the bottom panel in the panel stack.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_bottom_panel_impl(PyObject *module)
+/*[clinic end generated code: output=3aba9f985f4c2bd0 input=634c2a8078b3d7e4]*/
+{
+ PANEL *pan;
+ PyCursesPanelObject *po;
+
+ PyCursesInitialised;
+
+ pan = panel_above(NULL);
+
+ if (pan == NULL) { /* valid output, it means
+ there's no panel at all */
+ Py_RETURN_NONE;
+ }
+ po = find_po(pan);
+ if (po == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "panel_above: can't find Panel Object");
+ return NULL;
+ }
+ return Py_NewRef(po);
+}
+
+/*[clinic input]
+_curses_panel.new_panel
+
+ win: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
+ /
+
+Return a panel object, associating it with the given window win.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_new_panel_impl(PyObject *module, PyCursesWindowObject *win)
+/*[clinic end generated code: output=45e948e0176a9bd2 input=74d4754e0ebe4800]*/
+{
+ _curses_panel_state *state = get_curses_panel_state(module);
+
+ PANEL *pan = new_panel(win->win);
+ if (pan == NULL) {
+ PyErr_SetString(state->PyCursesError, catchall_NULL);
+ return NULL;
+ }
+ return (PyObject *)PyCursesPanel_New(state, pan, win);
+}
+
+
+/* Wrapper for panel_below(NULL). This function returns the top panel
+ of the stack, so it's renamed to top_panel(). panel.below()
+ *requires* a panel object in the first place which may be
+ undesirable. */
+/*[clinic input]
+_curses_panel.top_panel
+
+Return the top panel in the panel stack.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_top_panel_impl(PyObject *module)
+/*[clinic end generated code: output=86704988bea8508e input=e62d6278dba39e79]*/
+{
+ PANEL *pan;
+ PyCursesPanelObject *po;
+
+ PyCursesInitialised;
+
+ pan = panel_below(NULL);
+
+ if (pan == NULL) { /* valid output, it means
+ there's no panel at all */
+ Py_RETURN_NONE;
+ }
+ po = find_po(pan);
+ if (po == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "panel_below: can't find Panel Object");
+ return NULL;
+ }
+ return Py_NewRef(po);
+}
+
+/*[clinic input]
+_curses_panel.update_panels
+
+Updates the virtual screen after changes in the panel stack.
+
+This does not call curses.doupdate(), so you'll have to do this yourself.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_panel_update_panels_impl(PyObject *module)
+/*[clinic end generated code: output=2f3b4c2e03d90ded input=5299624c9a708621]*/
+{
+ PyCursesInitialised;
+ update_panels();
+ Py_RETURN_NONE;
+}
+
+/* List of functions defined in the module */
+
+static PyMethodDef PyCurses_methods[] = {
+ _CURSES_PANEL_BOTTOM_PANEL_METHODDEF
+ _CURSES_PANEL_NEW_PANEL_METHODDEF
+ _CURSES_PANEL_TOP_PANEL_METHODDEF
+ _CURSES_PANEL_UPDATE_PANELS_METHODDEF
+ {NULL, NULL} /* sentinel */
+};
+
+/* Initialization function for the module */
+static int
+_curses_panel_exec(PyObject *mod)
+{
+ _curses_panel_state *state = get_curses_panel_state(mod);
+ /* Initialize object type */
+ state->PyCursesPanel_Type = (PyTypeObject *)PyType_FromModuleAndSpec(
+ mod, &PyCursesPanel_Type_spec, NULL);
+ if (state->PyCursesPanel_Type == NULL) {
+ return -1;
+ }
+
+ if (PyModule_AddType(mod, state->PyCursesPanel_Type) < 0) {
+ return -1;
+ }
+
+ import_curses();
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+
+ /* For exception _curses_panel.error */
+ state->PyCursesError = PyErr_NewException(
+ "_curses_form.error", NULL, NULL);
+
+ if (PyModule_AddObjectRef(mod, "error", state->PyCursesError) < 0) {
+ return -1;
+ }
+
+ /* Make the version available */
+ PyObject *v = PyUnicode_FromString(PyCursesVersion);
+ if (v == NULL) {
+ return -1;
+ }
+
+ PyObject *d = PyModule_GetDict(mod);
+ if (PyDict_SetItemString(d, "version", v) < 0) {
+ Py_DECREF(v);
+ return -1;
+ }
+ if (PyDict_SetItemString(d, "__version__", v) < 0) {
+ Py_DECREF(v);
+ return -1;
+ }
+
+ Py_DECREF(v);
+
+ return 0;
+}
+
+static PyModuleDef_Slot _curses_slots[] = {
+ {Py_mod_exec, _curses_panel_exec},
+ // XXX gh-103092: fix isolation.
+ {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
+ //{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
+ {0, NULL}
+};
+
+/* Module definition */
+static struct PyModuleDef _curses_formmodule = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "_curses_form",
+ .m_size = sizeof(_curses_form_state),
+ .m_methods = PyCurses_methods,
+ .m_slots = _curses_slots,
+ .m_traverse = _curses_form_traverse,
+ .m_clear = _curses_form_clear,
+ .m_free = _curses_form_free
+};
+
+/* Initiate module */
+PyMODINIT_FUNC
+PyInit__curses_form(void)
+{
+ return PyModuleDef_Init(&_curses_formmodule);
+}
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index b2b410cfe5f5cc..2bb9fb194d0311 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -40,8 +40,8 @@
Here's a list of currently unsupported functions:
- del_curterm mcprint mvcur restartterm
- ripoffline set_curterm setterm
+ del_curterm mvcur restartterm
+ set_curterm setterm
tgetent tgetflag tgetnum tgetstr tgoto tputs
vidattr vidputs
@@ -2063,6 +2063,109 @@ PyCursesWindow_New(cursesmodule_state *state,
return (PyObject *)wo;
}
+typedef struct {
+ cursesmodule_state *state;
+ PyObject *callbacks[5];
+ PyObject *exception;
+ int count;
+ int index;
+} curses_ripoffline_context;
+
+static curses_ripoffline_context ripoffline_context;
+
+static void
+PyCurses_RipofflineClear(void)
+{
+ for (int i = 0; i < ripoffline_context.count; i++) {
+ Py_CLEAR(ripoffline_context.callbacks[i]);
+ }
+ Py_CLEAR(ripoffline_context.exception);
+ ripoffline_context.state = NULL;
+ ripoffline_context.count = 0;
+ ripoffline_context.index = 0;
+}
+
+static void
+PyCurses_RipofflineSaveException(void)
+{
+ PyObject *type, *value, *traceback;
+ PyErr_Fetch(&type, &value, &traceback);
+ Py_XDECREF(type);
+ Py_XDECREF(traceback);
+ if (ripoffline_context.exception == NULL) {
+ ripoffline_context.exception = value;
+ }
+ else {
+ Py_XDECREF(value);
+ }
+}
+
+static int
+PyCurses_RipofflineCallback(WINDOW *window, int columns)
+{
+ if (ripoffline_context.index >= ripoffline_context.count) {
+ return 0;
+ }
+ PyObject *callback = ripoffline_context.callbacks[ripoffline_context.index++];
+ PyObject *window_object = PyCursesWindow_New(
+ ripoffline_context.state, window, NULL, NULL, NULL);
+ if (window_object == NULL) {
+ PyCurses_RipofflineSaveException();
+ return 0;
+ }
+ PyObject *result = PyObject_CallFunction(callback, "Oi", window_object,
+ columns);
+ Py_DECREF(window_object);
+ if (result == NULL) {
+ PyCurses_RipofflineSaveException();
+ return 0;
+ }
+ Py_DECREF(result);
+ return 0;
+}
+
+static int
+PyCurses_RipofflineFinish(void)
+{
+ if (ripoffline_context.exception != NULL) {
+ PyObject *exception = ripoffline_context.exception;
+ PyErr_SetObject((PyObject *)Py_TYPE(exception), exception);
+ PyCurses_RipofflineClear();
+ return -1;
+ }
+ PyCurses_RipofflineClear();
+ return 0;
+}
+
+static PyObject *
+PyCurses_Ripoffline(PyObject *module, PyObject *args)
+{
+ int line;
+ PyObject *callback;
+ if (!PyArg_ParseTuple(args, "iO:ripoffline", &line, &callback)) {
+ return NULL;
+ }
+ if (!PyCallable_Check(callback)) {
+ PyErr_SetString(PyExc_TypeError, "callback must be callable");
+ return NULL;
+ }
+ if (ripoffline_context.count >= 5) {
+ cursesmodule_state *state = get_cursesmodule_state(module);
+ PyErr_SetString(state->error,
+ "ripoffline() called more than five times");
+ return NULL;
+ }
+ if (ripoffline_context.count == 0) {
+ ripoffline_context.state = get_cursesmodule_state(module);
+ }
+ if (ripoffline(line, PyCurses_RipofflineCallback) == ERR) {
+ return curses_check_err(module, ERR, "ripoffline", NULL);
+ }
+ ripoffline_context.callbacks[ripoffline_context.count++] =
+ Py_NewRef(callback);
+ Py_RETURN_NONE;
+}
+
static void
PyCursesWindow_dealloc(PyObject *self)
{
@@ -7010,6 +7113,9 @@ _curses_initscr_impl(PyObject *module)
curses_set_null_error(module, "initscr", NULL);
return NULL;
}
+ if (PyCurses_RipofflineFinish() < 0) {
+ return NULL;
+ }
cursesmodule_state *state = get_cursesmodule_state(module);
if (state->prescreen != NULL) {
@@ -7192,6 +7298,12 @@ _curses_newterm_impl(PyObject *module, const char *type, PyObject *fd,
fclose(infp);
return NULL;
}
+ if (PyCurses_RipofflineFinish() < 0) {
+ delscreen(screen);
+ 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;
@@ -7901,6 +8013,111 @@ _curses_putp_impl(PyObject *module, const char *string)
return curses_check_err(module, putp(string), "putp", NULL);
}
+static PyObject *
+PyCurses_Mcprint(PyObject *module, PyObject *args)
+{
+ const char *text;
+ Py_ssize_t length;
+ int count;
+ if (!PyArg_ParseTuple(args, "s#i:mcprint", &text, &length, &count)) {
+ return NULL;
+ }
+ PyCursesStatefulInitialised(module);
+ return curses_check_err(module, mcprint((char *)text, count),
+ "mcprint", NULL);
+}
+
+#if defined(HAVE_TERM_H) && !defined(STRICT_SYSV_CURSES)
+static PyObject *
+PyCurses_Tgetent(PyObject *module, PyObject *args)
+{
+ char *term;
+ if (!PyArg_ParseTuple(args, "s:tgetent", &term)) {
+ return NULL;
+ }
+ PyCursesStatefulSetupTermCalled(module);
+ char buffer[2048];
+ return PyLong_FromLong(tgetent(buffer, term));
+}
+
+static PyObject *
+PyCurses_Tgetflag(PyObject *module, PyObject *args)
+{
+ char *id;
+ if (!PyArg_ParseTuple(args, "s:tgetflag", &id)) {
+ return NULL;
+ }
+ PyCursesStatefulSetupTermCalled(module);
+ return PyLong_FromLong(tgetflag(id));
+}
+
+static PyObject *
+PyCurses_Tgetnum(PyObject *module, PyObject *args)
+{
+ char *id;
+ if (!PyArg_ParseTuple(args, "s:tgetnum", &id)) {
+ return NULL;
+ }
+ PyCursesStatefulSetupTermCalled(module);
+ return PyLong_FromLong(tgetnum(id));
+}
+
+static PyObject *
+PyCurses_Tgetstr(PyObject *module, PyObject *args)
+{
+ char *id;
+ if (!PyArg_ParseTuple(args, "s:tgetstr", &id)) {
+ return NULL;
+ }
+ PyCursesStatefulSetupTermCalled(module);
+ char *area = NULL;
+ char *result = tgetstr(id, &area);
+ if (result == NULL || result == (char *)-1) {
+ Py_RETURN_NONE;
+ }
+ return PyBytes_FromString(result);
+}
+
+static PyObject *
+PyCurses_Tgoto(PyObject *module, PyObject *args)
+{
+ char *capability;
+ int col, row;
+ if (!PyArg_ParseTuple(args, "yii:tgoto", &capability, &col, &row)) {
+ return NULL;
+ }
+ PyCursesStatefulSetupTermCalled(module);
+ char *result = tgoto(capability, col, row);
+ if (result == NULL || result == (char *)-1) {
+ Py_RETURN_NONE;
+ }
+ return PyBytes_FromString(result);
+}
+#endif
+
+static PyObject *
+PyCurses_Vidattr(PyObject *module, PyObject *args)
+{
+ unsigned long attrs;
+ if (!PyArg_ParseTuple(args, "k:vidattr", &attrs)) {
+ return NULL;
+ }
+ PyCursesStatefulInitialised(module);
+ return curses_check_err(module, vidattr((chtype)attrs), "vidattr", NULL);
+}
+
+static PyObject *
+PyCurses_Vidputs(PyObject *module, PyObject *args)
+{
+ unsigned long attrs;
+ if (!PyArg_ParseTuple(args, "k:vidputs", &attrs)) {
+ return NULL;
+ }
+ PyCursesStatefulInitialised(module);
+ return curses_check_err(module, vidputs((chtype)attrs, putchar),
+ "vidputs", NULL);
+}
+
/*[clinic input]
_curses.qiflush
@@ -9171,6 +9388,7 @@ static PyMethodDef cursesmodule_methods[] = {
_CURSES_KILLCHAR_METHODDEF
_CURSES_KILLWCHAR_METHODDEF
_CURSES_LONGNAME_METHODDEF
+ {"mcprint", (PyCFunction)PyCurses_Mcprint, METH_VARARGS},
_CURSES_META_METHODDEF
_CURSES_MOUSEINTERVAL_METHODDEF
_CURSES_MOUSEMASK_METHODDEF
@@ -9190,6 +9408,15 @@ static PyMethodDef cursesmodule_methods[] = {
_CURSES_PAIR_CONTENT_METHODDEF
_CURSES_PAIR_NUMBER_METHODDEF
_CURSES_PUTP_METHODDEF
+ {"vidattr", (PyCFunction)PyCurses_Vidattr, METH_VARARGS},
+ {"vidputs", (PyCFunction)PyCurses_Vidputs, METH_VARARGS},
+#if defined(HAVE_TERM_H) && !defined(STRICT_SYSV_CURSES)
+ {"tgetent", (PyCFunction)PyCurses_Tgetent, METH_VARARGS},
+ {"tgetflag", (PyCFunction)PyCurses_Tgetflag, METH_VARARGS},
+ {"tgetnum", (PyCFunction)PyCurses_Tgetnum, METH_VARARGS},
+ {"tgetstr", (PyCFunction)PyCurses_Tgetstr, METH_VARARGS},
+ {"tgoto", (PyCFunction)PyCurses_Tgoto, METH_VARARGS},
+#endif
_CURSES_QIFLUSH_METHODDEF
_CURSES_RAW_METHODDEF
_CURSES_RESET_COLOR_PAIRS_METHODDEF
@@ -9198,6 +9425,7 @@ static PyMethodDef cursesmodule_methods[] = {
_CURSES_RESETTY_METHODDEF
_CURSES_RESIZETERM_METHODDEF
_CURSES_RESIZE_TERM_METHODDEF
+ {"ripoffline", (PyCFunction)PyCurses_Ripoffline, METH_VARARGS},
_CURSES_SAVETTY_METHODDEF
_CURSES_SCR_DUMP_METHODDEF
_CURSES_SCR_INIT_METHODDEF
diff --git a/Modules/clinic/_curses_form.c.h b/Modules/clinic/_curses_form.c.h
new file mode 100644
index 00000000000000..3baaecf3f836e6
--- /dev/null
+++ b/Modules/clinic/_curses_form.c.h
@@ -0,0 +1,385 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
+
+PyDoc_STRVAR(_curses_panel_panel_bottom__doc__,
+"bottom($self, /)\n"
+"--\n"
+"\n"
+"Push the panel to the bottom of the stack.");
+
+#define _CURSES_PANEL_PANEL_BOTTOM_METHODDEF \
+ {"bottom", _PyCFunction_CAST(_curses_panel_panel_bottom), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _curses_panel_panel_bottom__doc__},
+
+static PyObject *
+_curses_panel_panel_bottom_impl(PyCursesPanelObject *self, PyTypeObject *cls);
+
+static PyObject *
+_curses_panel_panel_bottom(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
+ PyErr_SetString(PyExc_TypeError, "bottom() takes no arguments");
+ return NULL;
+ }
+ return _curses_panel_panel_bottom_impl((PyCursesPanelObject *)self, cls);
+}
+
+PyDoc_STRVAR(_curses_panel_panel_hide__doc__,
+"hide($self, /)\n"
+"--\n"
+"\n"
+"Hide the panel.\n"
+"\n"
+"This does not delete the object, it just makes the window on screen invisible.");
+
+#define _CURSES_PANEL_PANEL_HIDE_METHODDEF \
+ {"hide", _PyCFunction_CAST(_curses_panel_panel_hide), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _curses_panel_panel_hide__doc__},
+
+static PyObject *
+_curses_panel_panel_hide_impl(PyCursesPanelObject *self, PyTypeObject *cls);
+
+static PyObject *
+_curses_panel_panel_hide(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
+ PyErr_SetString(PyExc_TypeError, "hide() takes no arguments");
+ return NULL;
+ }
+ return _curses_panel_panel_hide_impl((PyCursesPanelObject *)self, cls);
+}
+
+PyDoc_STRVAR(_curses_panel_panel_show__doc__,
+"show($self, /)\n"
+"--\n"
+"\n"
+"Display the panel (which might have been hidden).");
+
+#define _CURSES_PANEL_PANEL_SHOW_METHODDEF \
+ {"show", _PyCFunction_CAST(_curses_panel_panel_show), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _curses_panel_panel_show__doc__},
+
+static PyObject *
+_curses_panel_panel_show_impl(PyCursesPanelObject *self, PyTypeObject *cls);
+
+static PyObject *
+_curses_panel_panel_show(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
+ PyErr_SetString(PyExc_TypeError, "show() takes no arguments");
+ return NULL;
+ }
+ return _curses_panel_panel_show_impl((PyCursesPanelObject *)self, cls);
+}
+
+PyDoc_STRVAR(_curses_panel_panel_top__doc__,
+"top($self, /)\n"
+"--\n"
+"\n"
+"Push panel to the top of the stack.");
+
+#define _CURSES_PANEL_PANEL_TOP_METHODDEF \
+ {"top", _PyCFunction_CAST(_curses_panel_panel_top), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _curses_panel_panel_top__doc__},
+
+static PyObject *
+_curses_panel_panel_top_impl(PyCursesPanelObject *self, PyTypeObject *cls);
+
+static PyObject *
+_curses_panel_panel_top(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
+ PyErr_SetString(PyExc_TypeError, "top() takes no arguments");
+ return NULL;
+ }
+ return _curses_panel_panel_top_impl((PyCursesPanelObject *)self, cls);
+}
+
+PyDoc_STRVAR(_curses_panel_panel_above__doc__,
+"above($self, /)\n"
+"--\n"
+"\n"
+"Return the panel above the current panel.");
+
+#define _CURSES_PANEL_PANEL_ABOVE_METHODDEF \
+ {"above", (PyCFunction)_curses_panel_panel_above, METH_NOARGS, _curses_panel_panel_above__doc__},
+
+static PyObject *
+_curses_panel_panel_above_impl(PyCursesPanelObject *self);
+
+static PyObject *
+_curses_panel_panel_above(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_panel_panel_above_impl((PyCursesPanelObject *)self);
+}
+
+PyDoc_STRVAR(_curses_panel_panel_below__doc__,
+"below($self, /)\n"
+"--\n"
+"\n"
+"Return the panel below the current panel.");
+
+#define _CURSES_PANEL_PANEL_BELOW_METHODDEF \
+ {"below", (PyCFunction)_curses_panel_panel_below, METH_NOARGS, _curses_panel_panel_below__doc__},
+
+static PyObject *
+_curses_panel_panel_below_impl(PyCursesPanelObject *self);
+
+static PyObject *
+_curses_panel_panel_below(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_panel_panel_below_impl((PyCursesPanelObject *)self);
+}
+
+PyDoc_STRVAR(_curses_panel_panel_hidden__doc__,
+"hidden($self, /)\n"
+"--\n"
+"\n"
+"Return True if the panel is hidden (not visible), False otherwise.");
+
+#define _CURSES_PANEL_PANEL_HIDDEN_METHODDEF \
+ {"hidden", (PyCFunction)_curses_panel_panel_hidden, METH_NOARGS, _curses_panel_panel_hidden__doc__},
+
+static PyObject *
+_curses_panel_panel_hidden_impl(PyCursesPanelObject *self);
+
+static PyObject *
+_curses_panel_panel_hidden(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_panel_panel_hidden_impl((PyCursesPanelObject *)self);
+}
+
+PyDoc_STRVAR(_curses_panel_panel_move__doc__,
+"move($self, y, x, /)\n"
+"--\n"
+"\n"
+"Move the panel to the screen coordinates (y, x).");
+
+#define _CURSES_PANEL_PANEL_MOVE_METHODDEF \
+ {"move", _PyCFunction_CAST(_curses_panel_panel_move), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _curses_panel_panel_move__doc__},
+
+static PyObject *
+_curses_panel_panel_move_impl(PyCursesPanelObject *self, PyTypeObject *cls,
+ int y, int x);
+
+static PyObject *
+_curses_panel_panel_move(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ int y;
+ int x;
+
+ if (!_PyArg_NoKwnames("move", kwnames)) {
+ goto exit;
+ }
+ if (!_PyArg_CheckPositional("move", nargs, 2, 2)) {
+ goto exit;
+ }
+ y = PyLong_AsInt(args[0]);
+ if (y == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ x = PyLong_AsInt(args[1]);
+ if (x == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _curses_panel_panel_move_impl((PyCursesPanelObject *)self, cls, y, x);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_curses_panel_panel_window__doc__,
+"window($self, /)\n"
+"--\n"
+"\n"
+"Return the window object associated with the panel.");
+
+#define _CURSES_PANEL_PANEL_WINDOW_METHODDEF \
+ {"window", (PyCFunction)_curses_panel_panel_window, METH_NOARGS, _curses_panel_panel_window__doc__},
+
+static PyObject *
+_curses_panel_panel_window_impl(PyCursesPanelObject *self);
+
+static PyObject *
+_curses_panel_panel_window(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_panel_panel_window_impl((PyCursesPanelObject *)self);
+}
+
+PyDoc_STRVAR(_curses_panel_panel_replace__doc__,
+"replace($self, win, /)\n"
+"--\n"
+"\n"
+"Change the window associated with the panel to the window win.");
+
+#define _CURSES_PANEL_PANEL_REPLACE_METHODDEF \
+ {"replace", _PyCFunction_CAST(_curses_panel_panel_replace), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _curses_panel_panel_replace__doc__},
+
+static PyObject *
+_curses_panel_panel_replace_impl(PyCursesPanelObject *self,
+ PyTypeObject *cls,
+ PyCursesWindowObject *win);
+
+static PyObject *
+_curses_panel_panel_replace(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ PyCursesWindowObject *win;
+
+ if (!_PyArg_NoKwnames("replace", kwnames)) {
+ goto exit;
+ }
+ if (!_PyArg_CheckPositional("replace", nargs, 1, 1)) {
+ goto exit;
+ }
+ if (!PyObject_TypeCheck(args[0], &PyCursesWindow_Type)) {
+ _PyArg_BadArgument("replace", "argument 1", (&PyCursesWindow_Type)->tp_name, args[0]);
+ goto exit;
+ }
+ win = (PyCursesWindowObject *)args[0];
+ return_value = _curses_panel_panel_replace_impl((PyCursesPanelObject *)self, cls, win);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_curses_panel_panel_set_userptr__doc__,
+"set_userptr($self, obj, /)\n"
+"--\n"
+"\n"
+"Set the panel\'s user pointer to obj.");
+
+#define _CURSES_PANEL_PANEL_SET_USERPTR_METHODDEF \
+ {"set_userptr", _PyCFunction_CAST(_curses_panel_panel_set_userptr), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _curses_panel_panel_set_userptr__doc__},
+
+static PyObject *
+_curses_panel_panel_set_userptr_impl(PyCursesPanelObject *self,
+ PyTypeObject *cls, PyObject *obj);
+
+static PyObject *
+_curses_panel_panel_set_userptr(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ PyObject *obj;
+
+ if (!_PyArg_NoKwnames("set_userptr", kwnames)) {
+ goto exit;
+ }
+ if (!_PyArg_CheckPositional("set_userptr", nargs, 1, 1)) {
+ goto exit;
+ }
+ obj = args[0];
+ return_value = _curses_panel_panel_set_userptr_impl((PyCursesPanelObject *)self, cls, obj);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_curses_panel_panel_userptr__doc__,
+"userptr($self, /)\n"
+"--\n"
+"\n"
+"Return the user pointer for the panel.");
+
+#define _CURSES_PANEL_PANEL_USERPTR_METHODDEF \
+ {"userptr", _PyCFunction_CAST(_curses_panel_panel_userptr), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _curses_panel_panel_userptr__doc__},
+
+static PyObject *
+_curses_panel_panel_userptr_impl(PyCursesPanelObject *self,
+ PyTypeObject *cls);
+
+static PyObject *
+_curses_panel_panel_userptr(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
+ PyErr_SetString(PyExc_TypeError, "userptr() takes no arguments");
+ return NULL;
+ }
+ return _curses_panel_panel_userptr_impl((PyCursesPanelObject *)self, cls);
+}
+
+PyDoc_STRVAR(_curses_panel_bottom_panel__doc__,
+"bottom_panel($module, /)\n"
+"--\n"
+"\n"
+"Return the bottom panel in the panel stack.");
+
+#define _CURSES_PANEL_BOTTOM_PANEL_METHODDEF \
+ {"bottom_panel", (PyCFunction)_curses_panel_bottom_panel, METH_NOARGS, _curses_panel_bottom_panel__doc__},
+
+static PyObject *
+_curses_panel_bottom_panel_impl(PyObject *module);
+
+static PyObject *
+_curses_panel_bottom_panel(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_panel_bottom_panel_impl(module);
+}
+
+PyDoc_STRVAR(_curses_panel_new_panel__doc__,
+"new_panel($module, win, /)\n"
+"--\n"
+"\n"
+"Return a panel object, associating it with the given window win.");
+
+#define _CURSES_PANEL_NEW_PANEL_METHODDEF \
+ {"new_panel", (PyCFunction)_curses_panel_new_panel, METH_O, _curses_panel_new_panel__doc__},
+
+static PyObject *
+_curses_panel_new_panel_impl(PyObject *module, PyCursesWindowObject *win);
+
+static PyObject *
+_curses_panel_new_panel(PyObject *module, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ PyCursesWindowObject *win;
+
+ if (!PyObject_TypeCheck(arg, &PyCursesWindow_Type)) {
+ _PyArg_BadArgument("new_panel", "argument", (&PyCursesWindow_Type)->tp_name, arg);
+ goto exit;
+ }
+ win = (PyCursesWindowObject *)arg;
+ return_value = _curses_panel_new_panel_impl(module, win);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_curses_panel_top_panel__doc__,
+"top_panel($module, /)\n"
+"--\n"
+"\n"
+"Return the top panel in the panel stack.");
+
+#define _CURSES_PANEL_TOP_PANEL_METHODDEF \
+ {"top_panel", (PyCFunction)_curses_panel_top_panel, METH_NOARGS, _curses_panel_top_panel__doc__},
+
+static PyObject *
+_curses_panel_top_panel_impl(PyObject *module);
+
+static PyObject *
+_curses_panel_top_panel(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_panel_top_panel_impl(module);
+}
+
+PyDoc_STRVAR(_curses_panel_update_panels__doc__,
+"update_panels($module, /)\n"
+"--\n"
+"\n"
+"Updates the virtual screen after changes in the panel stack.\n"
+"\n"
+"This does not call curses.doupdate(), so you\'ll have to do this yourself.");
+
+#define _CURSES_PANEL_UPDATE_PANELS_METHODDEF \
+ {"update_panels", (PyCFunction)_curses_panel_update_panels, METH_NOARGS, _curses_panel_update_panels__doc__},
+
+static PyObject *
+_curses_panel_update_panels_impl(PyObject *module);
+
+static PyObject *
+_curses_panel_update_panels(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_panel_update_panels_impl(module);
+}
+/*[clinic end generated code: output=a331de3267fc5252 input=a9049054013a1b77]*/