Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,11 @@ Porting to Python 3.14
implementation details.
(Contributed by Victor Stinner in :gh:`120600` and :gh:`124127`.)

* Convert the :ref:`curses.window <curses-window-objects>` static type exposed
by the :c:macro:`!PyCursesWindow_Type` macro in :file:`Include/py_curses.h`
to a heap type.
(Contributed by Bénédikt Tran in :gh:`123961`.)


Deprecated
----------
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Convert the :ref:`curses.window <curses-window-objects>` static type exposed
by the :c:macro:`!PyCursesWindow_Type` macro in :file:`Include/py_curses.h`
to a heap type. Patch by Bénédikt Tran.
177 changes: 108 additions & 69 deletions Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ static const char PyCursesVersion[] = "2.2";
#endif

#include "Python.h"
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_structseq.h" // _PyStructSequence_NewType()
#include "pycore_capsule.h" // _PyCapsule_SetTraverse()
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_structseq.h" // _PyStructSequence_NewType()

#ifdef __hpux
#define STRICT_SYSV_CURSES
Expand Down Expand Up @@ -173,6 +174,12 @@ get_cursesmodule_state(PyObject *Py_UNUSED(module))
return &curses_global_state;
}

static inline _cursesmodule_state *
get_cursesmodule_state_by_cls(PyTypeObject *Py_UNUSED(cls))
{
return &curses_global_state;
}

static inline _cursesmodule_state *
get_cursesmodule_state_by_win(PyCursesWindowObject *Py_UNUSED(win))
{
Expand All @@ -181,9 +188,9 @@ get_cursesmodule_state_by_win(PyCursesWindowObject *Py_UNUSED(win))

/*[clinic input]
module _curses
class _curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type"
class _curses.window "PyCursesWindowObject *" "clinic_state()->window_type"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=43265c372c2887d6]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ae6cb623018f2cbc]*/

/* Tells whether setupterm() has been called to initialise terminfo. */
static int curses_setupterm_called = FALSE;
Expand Down Expand Up @@ -630,10 +637,6 @@ class component_converter(CConverter):
The Window Object
******************************************************************************/

/* Definition of the window type */

PyTypeObject PyCursesWindow_Type;

/* Function prototype macros for Window object

X - function name
Expand Down Expand Up @@ -743,10 +746,9 @@ Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns")
/* Allocation and deallocation of Window Objects */

static PyObject *
PyCursesWindow_New(WINDOW *win, const char *encoding)
PyCursesWindow_New(_cursesmodule_state *state,
WINDOW *win, const char *encoding)
{
PyCursesWindowObject *wo;

if (encoding == NULL) {
#if defined(MS_WINDOWS)
char *buffer[100];
Expand All @@ -758,15 +760,19 @@ PyCursesWindow_New(WINDOW *win, const char *encoding)
}
#elif defined(CODESET)
const char *codeset = nl_langinfo(CODESET);
if (codeset != NULL && codeset[0] != 0)
if (codeset != NULL && codeset[0] != 0) {
encoding = codeset;
}
#endif
if (encoding == NULL)
if (encoding == NULL) {
encoding = "utf-8";
}
}

wo = PyObject_New(PyCursesWindowObject, &PyCursesWindow_Type);
if (wo == NULL) return NULL;
PyCursesWindowObject *wo = PyObject_New(PyCursesWindowObject, state->window_type);
if (wo == NULL) {
return NULL;
}
wo->win = win;
wo->encoding = _PyMem_Strdup(encoding);
if (wo->encoding == NULL) {
Expand All @@ -778,16 +784,26 @@ PyCursesWindow_New(WINDOW *win, const char *encoding)
}

static void
PyCursesWindow_Dealloc(PyCursesWindowObject *wo)
PyCursesWindow_dealloc(PyObject *self)
{
PyTypeObject *window_type = Py_TYPE(self);
PyCursesWindowObject *wo = (PyCursesWindowObject *)self;
if (wo->win != stdscr && wo->win != NULL) {
// silently ignore errors in delwin(3)
(void)delwin(wo->win);
}
if (wo->encoding != NULL) {
PyMem_Free(wo->encoding);
}
PyObject_Free(wo);
window_type->tp_free(self);
Py_DECREF(window_type);
}

static int
PyCursesWindow_traverse(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
return 0;
}

/* Addch, Addstr, Addnstr */
Expand Down Expand Up @@ -1391,7 +1407,8 @@ _curses_window_derwin_impl(PyCursesWindowObject *self, int group_left_1,
return NULL;
}

return (PyObject *)PyCursesWindow_New(win, NULL);
_cursesmodule_state *state = get_cursesmodule_state_by_win(self);
return PyCursesWindow_New(state, win, NULL);
}

/*[clinic input]
Expand Down Expand Up @@ -2140,7 +2157,7 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self)
/*[clinic input]
_curses.window.overlay

destwin: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
destwin: object(type="PyCursesWindowObject *", subclass_of="clinic_state()->window_type")

[
sminrow: int
Expand Down Expand Up @@ -2169,7 +2186,7 @@ _curses_window_overlay_impl(PyCursesWindowObject *self,
PyCursesWindowObject *destwin, int group_right_1,
int sminrow, int smincol, int dminrow,
int dmincol, int dmaxrow, int dmaxcol)
/*[clinic end generated code: output=82bb2c4cb443ca58 input=7edd23ad22cc1984]*/
/*[clinic end generated code: output=82bb2c4cb443ca58 input=6e4b32a7c627a356]*/
{
int rtn;

Expand All @@ -2187,7 +2204,7 @@ _curses_window_overlay_impl(PyCursesWindowObject *self,
/*[clinic input]
_curses.window.overwrite

destwin: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
destwin: object(type="PyCursesWindowObject *", subclass_of="clinic_state()->window_type")

[
sminrow: int
Expand Down Expand Up @@ -2217,7 +2234,7 @@ _curses_window_overwrite_impl(PyCursesWindowObject *self,
int group_right_1, int sminrow, int smincol,
int dminrow, int dmincol, int dmaxrow,
int dmaxcol)
/*[clinic end generated code: output=12ae007d1681be28 input=ea5de1b35cd948e0]*/
/*[clinic end generated code: output=12ae007d1681be28 input=d83dd8b24ff2bcc9]*/
{
int rtn;

Expand Down Expand Up @@ -2426,7 +2443,8 @@ _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1,
return NULL;
}

return (PyObject *)PyCursesWindow_New(win, self->encoding);
_cursesmodule_state *state = get_cursesmodule_state_by_win(self);
return PyCursesWindow_New(state, win, self->encoding);
}

/*[clinic input]
Expand Down Expand Up @@ -2564,9 +2582,11 @@ PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value, void *P
return 0;
}

#define clinic_state() (get_cursesmodule_state_by_cls(Py_TYPE(self)))
#include "clinic/_cursesmodule.c.h"
#undef clinic_state

static PyMethodDef PyCursesWindow_Methods[] = {
static PyMethodDef PyCursesWindow_methods[] = {
_CURSES_WINDOW_ADDCH_METHODDEF
_CURSES_WINDOW_ADDNSTR_METHODDEF
_CURSES_WINDOW_ADDSTR_METHODDEF
Expand Down Expand Up @@ -2660,42 +2680,26 @@ static PyGetSetDef PyCursesWindow_getsets[] = {
{NULL, NULL, NULL, NULL } /* sentinel */
};

/* -------------------------------------------------------*/
static PyType_Slot PyCursesWindow_Type_slots[] = {
{Py_tp_methods, PyCursesWindow_methods},
{Py_tp_getset, PyCursesWindow_getsets},
{Py_tp_dealloc, PyCursesWindow_dealloc},
{Py_tp_traverse, PyCursesWindow_traverse},
{0, NULL}
};

PyTypeObject PyCursesWindow_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_curses.window", /*tp_name*/
sizeof(PyCursesWindowObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)PyCursesWindow_Dealloc, /*tp_dealloc*/
0, /*tp_vectorcall_offset*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
0, /*tp_as_async*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
PyCursesWindow_Methods, /*tp_methods*/
0, /* tp_members */
PyCursesWindow_getsets, /* tp_getset */
static PyType_Spec PyCursesWindow_Type_spec = {
.name = "_curses.window",
.basicsize = sizeof(PyCursesWindowObject),
.flags = Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_DISALLOW_INSTANTIATION
| Py_TPFLAGS_IMMUTABLETYPE
| Py_TPFLAGS_HEAPTYPE,
.slots = PyCursesWindow_Type_slots
};

/* -------------------------------------------------------*/

/* Function Body Macros - They are ugly but very, very useful. ;-)

X - function name
Expand Down Expand Up @@ -3177,7 +3181,8 @@ _curses_getwin(PyObject *module, PyObject *file)
PyErr_SetString(state->error, catchall_NULL);
goto error;
}
res = PyCursesWindow_New(win, NULL);
_cursesmodule_state *state = get_cursesmodule_state(module);
res = PyCursesWindow_New(state, win, NULL);

error:
fclose(fp);
Expand Down Expand Up @@ -3349,7 +3354,8 @@ _curses_initscr_impl(PyObject *module)

if (curses_initscr_called) {
wrefresh(stdscr);
return (PyObject *)PyCursesWindow_New(stdscr, NULL);
_cursesmodule_state *state = get_cursesmodule_state(module);
return PyCursesWindow_New(state, stdscr, NULL);
}

win = initscr();
Expand Down Expand Up @@ -3452,12 +3458,13 @@ _curses_initscr_impl(PyObject *module)
SetDictInt("COLS", COLS);
#undef SetDictInt

PyCursesWindowObject *winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL);
_cursesmodule_state *state = get_cursesmodule_state(module);
PyObject *winobj = PyCursesWindow_New(state, win, NULL);
if (winobj == NULL) {
return NULL;
}
curses_screen_encoding = winobj->encoding;
return (PyObject *)winobj;
curses_screen_encoding = ((PyCursesWindowObject *)winobj)->encoding;
return winobj;
}

/*[clinic input]
Expand Down Expand Up @@ -3829,7 +3836,8 @@ _curses_newpad_impl(PyObject *module, int nlines, int ncols)
return NULL;
}

return (PyObject *)PyCursesWindow_New(win, NULL);
_cursesmodule_state *state = get_cursesmodule_state(module);
return PyCursesWindow_New(state, win, NULL);
}

/*[clinic input]
Expand Down Expand Up @@ -3869,7 +3877,8 @@ _curses_newwin_impl(PyObject *module, int nlines, int ncols,
return NULL;
}

return (PyObject *)PyCursesWindow_New(win, NULL);
_cursesmodule_state *state = get_cursesmodule_state(module);
return PyCursesWindow_New(state, win, NULL);
}

/*[clinic input]
Expand Down Expand Up @@ -4893,11 +4902,40 @@ curses_capi_capsule_destructor(PyObject *op)
curses_capi_free(capi);
}

static int
curses_capi_capsule_traverse(PyObject *op, visitproc visit, void *arg)
{
void **capi_ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME);
assert(capi_ptr != NULL);
Py_VISIT(capi_ptr[0]); // visit curses window type
return 0;
}

static int
curses_capi_capsule_clear(PyObject *op)
{
void **capi_ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME);
assert(capi_ptr != NULL);
Py_CLEAR(capi_ptr[0]); // clear curses window type
return 0;
}

static PyObject *
curses_capi_capsule_new(void *capi)
{
return PyCapsule_New(capi, PyCurses_CAPSULE_NAME,
curses_capi_capsule_destructor);
PyObject *capsule = PyCapsule_New(capi, PyCurses_CAPSULE_NAME,
curses_capi_capsule_destructor);
if (capsule == NULL) {
return NULL;
}
if (_PyCapsule_SetTraverse(capsule,
curses_capi_capsule_traverse,
curses_capi_capsule_clear) < 0)
{
Py_DECREF(capsule);
return NULL;
}
return capsule;
}

/* Module initialization */
Expand All @@ -4907,13 +4945,14 @@ cursesmodule_exec(PyObject *module)
{
_cursesmodule_state *state = get_cursesmodule_state(module);
/* Initialize object type */
if (PyType_Ready(&PyCursesWindow_Type) < 0) {
state->window_type = (PyTypeObject *)PyType_FromModuleAndSpec(
module, &PyCursesWindow_Type_spec, NULL);
if (state->window_type == NULL) {
return -1;
}
if (PyModule_AddType(module, &PyCursesWindow_Type) < 0) {
if (PyModule_AddType(module, state->window_type) < 0) {
return -1;
}
state->window_type = &PyCursesWindow_Type;

/* Add some symbolic constants to the module */
PyObject *module_dict = PyModule_GetDict(module);
Expand Down
Loading
Loading