@@ -32,6 +32,8 @@ typedef struct {
32
32
PyTypeObject * PyCursesPanel_Type ;
33
33
} _curses_panel_state ;
34
34
35
+ typedef struct PyCursesPanelObject PyCursesPanelObject ;
36
+
35
37
static inline _curses_panel_state *
36
38
get_curses_panel_state (PyObject * module )
37
39
{
@@ -40,6 +42,25 @@ get_curses_panel_state(PyObject *module)
40
42
return (_curses_panel_state * )state ;
41
43
}
42
44
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
+
43
64
static int
44
65
_curses_panel_clear (PyObject * mod )
45
66
{
@@ -95,12 +116,14 @@ PyCursesCheckERR(_curses_panel_state *state, int code, const char *fname)
95
116
96
117
/* Definition of the panel object and panel type */
97
118
98
- typedef struct {
119
+ typedef struct PyCursesPanelObject {
99
120
PyObject_HEAD
100
121
PANEL * pan ;
101
122
PyCursesWindowObject * wo ; /* for reference counts */
102
123
} PyCursesPanelObject ;
103
124
125
+ #define _PyCursesPanelObject_CAST (op ) ((PyCursesPanelObject *)(op))
126
+
104
127
/* Some helper functions. The problem is that there's always a window
105
128
associated with a panel. To ensure that Python's GC doesn't pull
106
129
this window from under our feet we need to keep track of references
@@ -260,8 +283,11 @@ static PyObject *
260
283
PyCursesPanel_New (_curses_panel_state * state , PANEL * pan ,
261
284
PyCursesWindowObject * wo )
262
285
{
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 );
265
291
if (po == NULL ) {
266
292
return NULL ;
267
293
}
@@ -276,26 +302,57 @@ PyCursesPanel_New(_curses_panel_state *state, PANEL *pan,
276
302
return (PyObject * )po ;
277
303
}
278
304
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
+
279
323
static void
280
- PyCursesPanel_Dealloc (PyCursesPanelObject * po )
324
+ PyCursesPanel_Dealloc (PyObject * self )
281
325
{
282
- PyObject * tp , * obj ;
326
+ PyTypeObject * tp = Py_TYPE (self );
327
+ PyObject_GC_UnTrack (self );
283
328
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()" );
289
337
}
290
- (void )del_panel (po -> pan );
291
338
if (po -> wo != NULL ) {
292
339
Py_DECREF (po -> wo );
293
340
remove_lop (po );
294
341
}
295
- PyObject_Free (po );
342
+ tp -> tp_free (po );
296
343
Py_DECREF (tp );
297
344
}
298
345
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
+
299
356
/* panel_above(NULL) returns the bottom panel in the stack. To get
300
357
this behaviour we use curses.panel.bottom_panel(). */
301
358
/*[clinic input]
@@ -517,15 +574,21 @@ static PyMethodDef PyCursesPanel_Methods[] = {
517
574
/* -------------------------------------------------------*/
518
575
519
576
static PyType_Slot PyCursesPanel_Type_slots [] = {
577
+ {Py_tp_clear , PyCursesPanel_Clear },
520
578
{Py_tp_dealloc , PyCursesPanel_Dealloc },
579
+ {Py_tp_traverse , PyCursesPanel_Traverse },
521
580
{Py_tp_methods , PyCursesPanel_Methods },
522
581
{0 , 0 },
523
582
};
524
583
525
584
static PyType_Spec PyCursesPanel_Type_spec = {
526
585
.name = "_curses_panel.panel" ,
527
586
.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
+ ),
529
592
.slots = PyCursesPanel_Type_slots
530
593
};
531
594
0 commit comments