@@ -32,6 +32,8 @@ typedef struct {
3232 PyTypeObject * PyCursesPanel_Type ;
3333} _curses_panel_state ;
3434
35+ typedef struct PyCursesPanelObject PyCursesPanelObject ;
36+
3537static inline _curses_panel_state *
3638get_curses_panel_state (PyObject * module )
3739{
@@ -40,6 +42,25 @@ get_curses_panel_state(PyObject *module)
4042 return (_curses_panel_state * )state ;
4143}
4244
45+ static inline _curses_panel_state *
46+ get_curses_panel_state_by_panel (PyCursesPanelObject * panel )
47+ {
48+ /*
49+ * Note: 'state' may be NULL if Py_TYPE(panel) is not a heap
50+ * type associated with this module, but the compiler would
51+ * have likely already complained with an "invalid pointer
52+ * type" at compile-time.
53+ *
54+ * To make it more robust, all functions recovering a module's
55+ * state from an object should expect to return NULL with an
56+ * exception set (in contrast to functions recovering a module's
57+ * state from a module itself).
58+ */
59+ void * state = PyType_GetModuleState (Py_TYPE (panel ));
60+ assert (state != NULL );
61+ return (_curses_panel_state * )state ;
62+ }
63+
4364static int
4465_curses_panel_clear (PyObject * mod )
4566{
@@ -95,12 +116,14 @@ PyCursesCheckERR(_curses_panel_state *state, int code, const char *fname)
95116
96117/* Definition of the panel object and panel type */
97118
98- typedef struct {
119+ typedef struct PyCursesPanelObject {
99120 PyObject_HEAD
100121 PANEL * pan ;
101122 PyCursesWindowObject * wo ; /* for reference counts */
102123} PyCursesPanelObject ;
103124
125+ #define _PyCursesPanelObject_CAST (op ) ((PyCursesPanelObject *)(op))
126+
104127/* Some helper functions. The problem is that there's always a window
105128 associated with a panel. To ensure that Python's GC doesn't pull
106129 this window from under our feet we need to keep track of references
@@ -260,8 +283,11 @@ static PyObject *
260283PyCursesPanel_New (_curses_panel_state * state , PANEL * pan ,
261284 PyCursesWindowObject * wo )
262285{
263- PyCursesPanelObject * po = PyObject_New (PyCursesPanelObject ,
264- state -> PyCursesPanel_Type );
286+ assert (state != NULL );
287+ PyTypeObject * type = state -> PyCursesPanel_Type ;
288+ assert (type != NULL );
289+ assert (type -> tp_alloc != NULL );
290+ PyCursesPanelObject * po = (PyCursesPanelObject * )type -> tp_alloc (type , 0 );
265291 if (po == NULL ) {
266292 return NULL ;
267293 }
@@ -276,26 +302,57 @@ PyCursesPanel_New(_curses_panel_state *state, PANEL *pan,
276302 return (PyObject * )po ;
277303}
278304
305+ static int
306+ PyCursesPanel_Clear (PyObject * op )
307+ {
308+ PyCursesPanelObject * self = _PyCursesPanelObject_CAST (op );
309+ PyObject * extra = (PyObject * )panel_userptr (self -> pan );
310+ if (extra != NULL ) {
311+ Py_DECREF (extra );
312+ if (set_panel_userptr (self -> pan , NULL ) == ERR ) {
313+ _curses_panel_state * state = get_curses_panel_state_by_panel (self );
314+ PyErr_SetString (state -> PyCursesError ,
315+ "set_panel_userptr() returned ERR" );
316+ return -1 ;
317+ }
318+ }
319+ // self->wo should not be cleared because an associated WINDOW may exist
320+ return 0 ;
321+ }
322+
279323static void
280- PyCursesPanel_Dealloc (PyCursesPanelObject * po )
324+ PyCursesPanel_Dealloc (PyObject * self )
281325{
282- PyObject * tp , * obj ;
326+ PyTypeObject * tp = Py_TYPE (self );
327+ PyObject_GC_UnTrack (self );
283328
284- tp = (PyObject * ) Py_TYPE (po );
285- obj = (PyObject * ) panel_userptr (po -> pan );
286- if (obj ) {
287- (void )set_panel_userptr (po -> pan , NULL );
288- Py_DECREF (obj );
329+ PyCursesPanelObject * po = _PyCursesPanelObject_CAST (self );
330+ if (PyCursesPanel_Clear (self ) < 0 ) {
331+ PyErr_FormatUnraisable ("Exception ignored in PyCursesPanel_Dealloc()" );
332+ }
333+ if (del_panel (po -> pan ) == ERR && !PyErr_Occurred ()) {
334+ _curses_panel_state * state = get_curses_panel_state_by_panel (po );
335+ PyErr_SetString (state -> PyCursesError , "del_panel() returned ERR" );
336+ PyErr_FormatUnraisable ("Exception ignored in PyCursesPanel_Dealloc()" );
289337 }
290- (void )del_panel (po -> pan );
291338 if (po -> wo != NULL ) {
292339 Py_DECREF (po -> wo );
293340 remove_lop (po );
294341 }
295- PyObject_Free (po );
342+ tp -> tp_free (po );
296343 Py_DECREF (tp );
297344}
298345
346+ static int
347+ PyCursesPanel_Traverse (PyObject * op , visitproc visit , void * arg )
348+ {
349+ PyCursesPanelObject * self = _PyCursesPanelObject_CAST (op );
350+ Py_VISIT (Py_TYPE (op ));
351+ Py_VISIT (panel_userptr (self -> pan ));
352+ Py_VISIT (self -> wo );
353+ return 0 ;
354+ }
355+
299356/* panel_above(NULL) returns the bottom panel in the stack. To get
300357 this behaviour we use curses.panel.bottom_panel(). */
301358/*[clinic input]
@@ -517,15 +574,21 @@ static PyMethodDef PyCursesPanel_Methods[] = {
517574/* -------------------------------------------------------*/
518575
519576static PyType_Slot PyCursesPanel_Type_slots [] = {
577+ {Py_tp_clear , PyCursesPanel_Clear },
520578 {Py_tp_dealloc , PyCursesPanel_Dealloc },
579+ {Py_tp_traverse , PyCursesPanel_Traverse },
521580 {Py_tp_methods , PyCursesPanel_Methods },
522581 {0 , 0 },
523582};
524583
525584static PyType_Spec PyCursesPanel_Type_spec = {
526585 .name = "_curses_panel.panel" ,
527586 .basicsize = sizeof (PyCursesPanelObject ),
528- .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION ,
587+ .flags = (
588+ Py_TPFLAGS_DEFAULT
589+ | Py_TPFLAGS_DISALLOW_INSTANTIATION
590+ | Py_TPFLAGS_HAVE_GC
591+ ),
529592 .slots = PyCursesPanel_Type_slots
530593};
531594
0 commit comments