• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *   Interface to the ncurses panel library
3  *
4  * Original version by Thomas Gellekum
5  */
6 
7 /* Release Number */
8 
9 static const char PyCursesVersion[] = "2.1";
10 
11 /* Includes */
12 
13 // clinic/_curses_panel.c.h uses internal pycore_modsupport.h API
14 #ifndef Py_BUILD_CORE_BUILTIN
15 #  define Py_BUILD_CORE_MODULE 1
16 #endif
17 
18 #include "Python.h"
19 
20 #include "py_curses.h"
21 
22 #if defined(HAVE_NCURSESW_PANEL_H)
23 #  include <ncursesw/panel.h>
24 #elif defined(HAVE_NCURSES_PANEL_H)
25 #  include <ncurses/panel.h>
26 #elif defined(HAVE_PANEL_H)
27 #  include <panel.h>
28 #endif
29 
30 typedef struct {
31     PyObject *PyCursesError;
32     PyTypeObject *PyCursesPanel_Type;
33 } _curses_panel_state;
34 
35 static inline _curses_panel_state *
get_curses_panel_state(PyObject * module)36 get_curses_panel_state(PyObject *module)
37 {
38     void *state = PyModule_GetState(module);
39     assert(state != NULL);
40     return (_curses_panel_state *)state;
41 }
42 
43 static int
_curses_panel_clear(PyObject * mod)44 _curses_panel_clear(PyObject *mod)
45 {
46     _curses_panel_state *state = get_curses_panel_state(mod);
47     Py_CLEAR(state->PyCursesError);
48     Py_CLEAR(state->PyCursesPanel_Type);
49     return 0;
50 }
51 
52 static int
_curses_panel_traverse(PyObject * mod,visitproc visit,void * arg)53 _curses_panel_traverse(PyObject *mod, visitproc visit, void *arg)
54 {
55     Py_VISIT(Py_TYPE(mod));
56     _curses_panel_state *state = get_curses_panel_state(mod);
57     Py_VISIT(state->PyCursesError);
58     Py_VISIT(state->PyCursesPanel_Type);
59     return 0;
60 }
61 
62 static void
_curses_panel_free(void * mod)63 _curses_panel_free(void *mod)
64 {
65     _curses_panel_clear((PyObject *) mod);
66 }
67 
68 /* Utility Functions */
69 
70 /*
71  * Check the return code from a curses function and return None
72  * or raise an exception as appropriate.
73  */
74 
75 static PyObject *
PyCursesCheckERR(_curses_panel_state * state,int code,const char * fname)76 PyCursesCheckERR(_curses_panel_state *state, int code, const char *fname)
77 {
78     if (code != ERR) {
79         Py_RETURN_NONE;
80     }
81     else {
82         if (fname == NULL) {
83             PyErr_SetString(state->PyCursesError, catchall_ERR);
84         }
85         else {
86             PyErr_Format(state->PyCursesError, "%s() returned ERR", fname);
87         }
88         return NULL;
89     }
90 }
91 
92 /*****************************************************************************
93  The Panel Object
94 ******************************************************************************/
95 
96 /* Definition of the panel object and panel type */
97 
98 typedef struct {
99     PyObject_HEAD
100     PANEL *pan;
101     PyCursesWindowObject *wo;   /* for reference counts */
102 } PyCursesPanelObject;
103 
104 /* Some helper functions. The problem is that there's always a window
105    associated with a panel. To ensure that Python's GC doesn't pull
106    this window from under our feet we need to keep track of references
107    to the corresponding window object within Python. We can't use
108    dupwin(oldwin) to keep a copy of the curses WINDOW because the
109    contents of oldwin is copied only once; code like
110 
111    win = newwin(...)
112    pan = win.panel()
113    win.addstr(some_string)
114    pan.window().addstr(other_string)
115 
116    will fail. */
117 
118 /* We keep a linked list of PyCursesPanelObjects, lop. A list should
119    suffice, I don't expect more than a handful or at most a few
120    dozens of panel objects within a typical program. */
121 typedef struct _list_of_panels {
122     PyCursesPanelObject *po;
123     struct _list_of_panels *next;
124 } list_of_panels;
125 
126 /* list anchor */
127 static list_of_panels *lop;
128 
129 /* Insert a new panel object into lop */
130 static int
insert_lop(PyCursesPanelObject * po)131 insert_lop(PyCursesPanelObject *po)
132 {
133     list_of_panels *new;
134 
135     if ((new = (list_of_panels *)PyMem_Malloc(sizeof(list_of_panels))) == NULL) {
136         PyErr_NoMemory();
137         return -1;
138     }
139     new->po = po;
140     new->next = lop;
141     lop = new;
142     return 0;
143 }
144 
145 /* Remove the panel object from lop */
146 static void
remove_lop(PyCursesPanelObject * po)147 remove_lop(PyCursesPanelObject *po)
148 {
149     list_of_panels *temp, *n;
150 
151     temp = lop;
152     if (temp->po == po) {
153         lop = temp->next;
154         PyMem_Free(temp);
155         return;
156     }
157     while (temp->next == NULL || temp->next->po != po) {
158         if (temp->next == NULL) {
159             PyErr_SetString(PyExc_RuntimeError,
160                             "remove_lop: can't find Panel Object");
161             return;
162         }
163         temp = temp->next;
164     }
165     n = temp->next->next;
166     PyMem_Free(temp->next);
167     temp->next = n;
168     return;
169 }
170 
171 /* Return the panel object that corresponds to pan */
172 static PyCursesPanelObject *
find_po(PANEL * pan)173 find_po(PANEL *pan)
174 {
175     list_of_panels *temp;
176     for (temp = lop; temp->po->pan != pan; temp = temp->next)
177         if (temp->next == NULL) return NULL;    /* not found!? */
178     return temp->po;
179 }
180 
181 /*[clinic input]
182 module _curses_panel
183 class _curses_panel.panel "PyCursesPanelObject *" "&PyCursesPanel_Type"
184 [clinic start generated code]*/
185 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=2f4ef263ca850a31]*/
186 
187 #include "clinic/_curses_panel.c.h"
188 
189 /* ------------- PANEL routines --------------- */
190 
191 /*[clinic input]
192 _curses_panel.panel.bottom
193 
194     cls: defining_class
195 
196 Push the panel to the bottom of the stack.
197 [clinic start generated code]*/
198 
199 static PyObject *
_curses_panel_panel_bottom_impl(PyCursesPanelObject * self,PyTypeObject * cls)200 _curses_panel_panel_bottom_impl(PyCursesPanelObject *self, PyTypeObject *cls)
201 /*[clinic end generated code: output=8ec7fbbc08554021 input=6b7d2c0578b5a1c4]*/
202 {
203     _curses_panel_state *state = PyType_GetModuleState(cls);
204     return PyCursesCheckERR(state, bottom_panel(self->pan), "bottom");
205 }
206 
207 /*[clinic input]
208 _curses_panel.panel.hide
209 
210     cls: defining_class
211 
212 Hide the panel.
213 
214 This does not delete the object, it just makes the window on screen invisible.
215 [clinic start generated code]*/
216 
217 static PyObject *
_curses_panel_panel_hide_impl(PyCursesPanelObject * self,PyTypeObject * cls)218 _curses_panel_panel_hide_impl(PyCursesPanelObject *self, PyTypeObject *cls)
219 /*[clinic end generated code: output=cc6ab7203cdc1450 input=1bfc741f473e6055]*/
220 {
221     _curses_panel_state *state = PyType_GetModuleState(cls);
222     return PyCursesCheckERR(state, hide_panel(self->pan), "hide");
223 }
224 
225 /*[clinic input]
226 _curses_panel.panel.show
227 
228     cls: defining_class
229 
230 Display the panel (which might have been hidden).
231 [clinic start generated code]*/
232 
233 static PyObject *
_curses_panel_panel_show_impl(PyCursesPanelObject * self,PyTypeObject * cls)234 _curses_panel_panel_show_impl(PyCursesPanelObject *self, PyTypeObject *cls)
235 /*[clinic end generated code: output=dc3421de375f0409 input=8122e80151cb4379]*/
236 {
237     _curses_panel_state *state = PyType_GetModuleState(cls);
238     return PyCursesCheckERR(state, show_panel(self->pan), "show");
239 }
240 
241 /*[clinic input]
242 _curses_panel.panel.top
243 
244     cls: defining_class
245 
246 Push panel to the top of the stack.
247 [clinic start generated code]*/
248 
249 static PyObject *
_curses_panel_panel_top_impl(PyCursesPanelObject * self,PyTypeObject * cls)250 _curses_panel_panel_top_impl(PyCursesPanelObject *self, PyTypeObject *cls)
251 /*[clinic end generated code: output=10a072e511e873f7 input=1f372d597dda3379]*/
252 {
253     _curses_panel_state *state = PyType_GetModuleState(cls);
254     return PyCursesCheckERR(state, top_panel(self->pan), "top");
255 }
256 
257 /* Allocation and deallocation of Panel Objects */
258 
259 static PyObject *
PyCursesPanel_New(_curses_panel_state * state,PANEL * pan,PyCursesWindowObject * wo)260 PyCursesPanel_New(_curses_panel_state *state, PANEL *pan,
261                   PyCursesWindowObject *wo)
262 {
263     PyCursesPanelObject *po = PyObject_New(PyCursesPanelObject,
264                                            state->PyCursesPanel_Type);
265     if (po == NULL) {
266         return NULL;
267     }
268 
269     po->pan = pan;
270     if (insert_lop(po) < 0) {
271         po->wo = NULL;
272         Py_DECREF(po);
273         return NULL;
274     }
275     po->wo = (PyCursesWindowObject*)Py_NewRef(wo);
276     return (PyObject *)po;
277 }
278 
279 static void
PyCursesPanel_Dealloc(PyCursesPanelObject * po)280 PyCursesPanel_Dealloc(PyCursesPanelObject *po)
281 {
282     PyObject *tp, *obj;
283 
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);
289     }
290     (void)del_panel(po->pan);
291     if (po->wo != NULL) {
292         Py_DECREF(po->wo);
293         remove_lop(po);
294     }
295     PyObject_Free(po);
296     Py_DECREF(tp);
297 }
298 
299 /* panel_above(NULL) returns the bottom panel in the stack. To get
300    this behaviour we use curses.panel.bottom_panel(). */
301 /*[clinic input]
302 _curses_panel.panel.above
303 
304 Return the panel above the current panel.
305 [clinic start generated code]*/
306 
307 static PyObject *
_curses_panel_panel_above_impl(PyCursesPanelObject * self)308 _curses_panel_panel_above_impl(PyCursesPanelObject *self)
309 /*[clinic end generated code: output=70ac06d25fd3b4da input=c059994022976788]*/
310 {
311     PANEL *pan;
312     PyCursesPanelObject *po;
313 
314     pan = panel_above(self->pan);
315 
316     if (pan == NULL) {          /* valid output, it means the calling panel
317                                    is on top of the stack */
318         Py_RETURN_NONE;
319     }
320     po = find_po(pan);
321     if (po == NULL) {
322         PyErr_SetString(PyExc_RuntimeError,
323                         "panel_above: can't find Panel Object");
324         return NULL;
325     }
326     return Py_NewRef(po);
327 }
328 
329 /* panel_below(NULL) returns the top panel in the stack. To get
330    this behaviour we use curses.panel.top_panel(). */
331 /*[clinic input]
332 _curses_panel.panel.below
333 
334 Return the panel below the current panel.
335 [clinic start generated code]*/
336 
337 static PyObject *
_curses_panel_panel_below_impl(PyCursesPanelObject * self)338 _curses_panel_panel_below_impl(PyCursesPanelObject *self)
339 /*[clinic end generated code: output=282861122e06e3de input=cc08f61936d297c6]*/
340 {
341     PANEL *pan;
342     PyCursesPanelObject *po;
343 
344     pan = panel_below(self->pan);
345 
346     if (pan == NULL) {          /* valid output, it means the calling panel
347                                    is on the bottom of the stack */
348         Py_RETURN_NONE;
349     }
350     po = find_po(pan);
351     if (po == NULL) {
352         PyErr_SetString(PyExc_RuntimeError,
353                         "panel_below: can't find Panel Object");
354         return NULL;
355     }
356     return Py_NewRef(po);
357 }
358 
359 /*[clinic input]
360 _curses_panel.panel.hidden
361 
362 Return True if the panel is hidden (not visible), False otherwise.
363 [clinic start generated code]*/
364 
365 static PyObject *
_curses_panel_panel_hidden_impl(PyCursesPanelObject * self)366 _curses_panel_panel_hidden_impl(PyCursesPanelObject *self)
367 /*[clinic end generated code: output=66eebd1ab4501a71 input=453d4b4fce25e21a]*/
368 {
369     if (panel_hidden(self->pan))
370         Py_RETURN_TRUE;
371     else
372         Py_RETURN_FALSE;
373 }
374 
375 /*[clinic input]
376 _curses_panel.panel.move
377 
378     cls: defining_class
379     y: int
380     x: int
381     /
382 
383 Move the panel to the screen coordinates (y, x).
384 [clinic start generated code]*/
385 
386 static PyObject *
_curses_panel_panel_move_impl(PyCursesPanelObject * self,PyTypeObject * cls,int y,int x)387 _curses_panel_panel_move_impl(PyCursesPanelObject *self, PyTypeObject *cls,
388                               int y, int x)
389 /*[clinic end generated code: output=ce546c93e56867da input=60a0e7912ff99849]*/
390 {
391     _curses_panel_state *state = PyType_GetModuleState(cls);
392     return PyCursesCheckERR(state, move_panel(self->pan, y, x), "move_panel");
393 }
394 
395 /*[clinic input]
396 _curses_panel.panel.window
397 
398 Return the window object associated with the panel.
399 [clinic start generated code]*/
400 
401 static PyObject *
_curses_panel_panel_window_impl(PyCursesPanelObject * self)402 _curses_panel_panel_window_impl(PyCursesPanelObject *self)
403 /*[clinic end generated code: output=5f05940d4106b4cb input=6067353d2c307901]*/
404 {
405     return Py_NewRef(self->wo);
406 }
407 
408 /*[clinic input]
409 _curses_panel.panel.replace
410 
411     cls: defining_class
412     win: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
413     /
414 
415 Change the window associated with the panel to the window win.
416 [clinic start generated code]*/
417 
418 static PyObject *
_curses_panel_panel_replace_impl(PyCursesPanelObject * self,PyTypeObject * cls,PyCursesWindowObject * win)419 _curses_panel_panel_replace_impl(PyCursesPanelObject *self,
420                                  PyTypeObject *cls,
421                                  PyCursesWindowObject *win)
422 /*[clinic end generated code: output=c71f95c212d58ae7 input=dbec7180ece41ff5]*/
423 {
424     _curses_panel_state *state = PyType_GetModuleState(cls);
425 
426     PyCursesPanelObject *po = find_po(self->pan);
427     if (po == NULL) {
428         PyErr_SetString(PyExc_RuntimeError,
429                         "replace_panel: can't find Panel Object");
430         return NULL;
431     }
432 
433     int rtn = replace_panel(self->pan, win->win);
434     if (rtn == ERR) {
435         PyErr_SetString(state->PyCursesError, "replace_panel() returned ERR");
436         return NULL;
437     }
438     Py_SETREF(po->wo, (PyCursesWindowObject*)Py_NewRef(win));
439     Py_RETURN_NONE;
440 }
441 
442 /*[clinic input]
443 _curses_panel.panel.set_userptr
444 
445     cls: defining_class
446     obj: object
447     /
448 
449 Set the panel's user pointer to obj.
450 [clinic start generated code]*/
451 
452 static PyObject *
_curses_panel_panel_set_userptr_impl(PyCursesPanelObject * self,PyTypeObject * cls,PyObject * obj)453 _curses_panel_panel_set_userptr_impl(PyCursesPanelObject *self,
454                                      PyTypeObject *cls, PyObject *obj)
455 /*[clinic end generated code: output=db74f3db07b28080 input=e3fee2ff7b1b8e48]*/
456 {
457     PyCursesInitialised;
458     Py_INCREF(obj);
459     PyObject *oldobj = (PyObject *) panel_userptr(self->pan);
460     int rc = set_panel_userptr(self->pan, (void*)obj);
461     if (rc == ERR) {
462         /* In case of an ncurses error, decref the new object again */
463         Py_DECREF(obj);
464     }
465     else {
466         Py_XDECREF(oldobj);
467     }
468 
469     _curses_panel_state *state = PyType_GetModuleState(cls);
470     return PyCursesCheckERR(state, rc, "set_panel_userptr");
471 }
472 
473 /*[clinic input]
474 _curses_panel.panel.userptr
475 
476     cls: defining_class
477 
478 Return the user pointer for the panel.
479 [clinic start generated code]*/
480 
481 static PyObject *
_curses_panel_panel_userptr_impl(PyCursesPanelObject * self,PyTypeObject * cls)482 _curses_panel_panel_userptr_impl(PyCursesPanelObject *self,
483                                  PyTypeObject *cls)
484 /*[clinic end generated code: output=eea6e6f39ffc0179 input=f22ca4f115e30a80]*/
485 {
486     _curses_panel_state *state = PyType_GetModuleState(cls);
487 
488     PyCursesInitialised;
489     PyObject *obj = (PyObject *) panel_userptr(self->pan);
490     if (obj == NULL) {
491         PyErr_SetString(state->PyCursesError, "no userptr set");
492         return NULL;
493     }
494 
495     return Py_NewRef(obj);
496 }
497 
498 
499 /* Module interface */
500 
501 static PyMethodDef PyCursesPanel_Methods[] = {
502     _CURSES_PANEL_PANEL_ABOVE_METHODDEF
503     _CURSES_PANEL_PANEL_BELOW_METHODDEF
504     _CURSES_PANEL_PANEL_BOTTOM_METHODDEF
505     _CURSES_PANEL_PANEL_HIDDEN_METHODDEF
506     _CURSES_PANEL_PANEL_HIDE_METHODDEF
507     _CURSES_PANEL_PANEL_MOVE_METHODDEF
508     _CURSES_PANEL_PANEL_REPLACE_METHODDEF
509     _CURSES_PANEL_PANEL_SET_USERPTR_METHODDEF
510     _CURSES_PANEL_PANEL_SHOW_METHODDEF
511     _CURSES_PANEL_PANEL_TOP_METHODDEF
512     _CURSES_PANEL_PANEL_USERPTR_METHODDEF
513     _CURSES_PANEL_PANEL_WINDOW_METHODDEF
514     {NULL,              NULL}   /* sentinel */
515 };
516 
517 /* -------------------------------------------------------*/
518 
519 static PyType_Slot PyCursesPanel_Type_slots[] = {
520     {Py_tp_dealloc, PyCursesPanel_Dealloc},
521     {Py_tp_methods, PyCursesPanel_Methods},
522     {0, 0},
523 };
524 
525 static PyType_Spec PyCursesPanel_Type_spec = {
526     .name = "_curses_panel.panel",
527     .basicsize = sizeof(PyCursesPanelObject),
528     .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
529     .slots = PyCursesPanel_Type_slots
530 };
531 
532 /* Wrapper for panel_above(NULL). This function returns the bottom
533    panel of the stack, so it's renamed to bottom_panel().
534    panel.above() *requires* a panel object in the first place which
535    may be undesirable. */
536 /*[clinic input]
537 _curses_panel.bottom_panel
538 
539 Return the bottom panel in the panel stack.
540 [clinic start generated code]*/
541 
542 static PyObject *
_curses_panel_bottom_panel_impl(PyObject * module)543 _curses_panel_bottom_panel_impl(PyObject *module)
544 /*[clinic end generated code: output=3aba9f985f4c2bd0 input=634c2a8078b3d7e4]*/
545 {
546     PANEL *pan;
547     PyCursesPanelObject *po;
548 
549     PyCursesInitialised;
550 
551     pan = panel_above(NULL);
552 
553     if (pan == NULL) {          /* valid output, it means
554                                    there's no panel at all */
555         Py_RETURN_NONE;
556     }
557     po = find_po(pan);
558     if (po == NULL) {
559         PyErr_SetString(PyExc_RuntimeError,
560                         "panel_above: can't find Panel Object");
561         return NULL;
562     }
563     return Py_NewRef(po);
564 }
565 
566 /*[clinic input]
567 _curses_panel.new_panel
568 
569     win: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
570     /
571 
572 Return a panel object, associating it with the given window win.
573 [clinic start generated code]*/
574 
575 static PyObject *
_curses_panel_new_panel_impl(PyObject * module,PyCursesWindowObject * win)576 _curses_panel_new_panel_impl(PyObject *module, PyCursesWindowObject *win)
577 /*[clinic end generated code: output=45e948e0176a9bd2 input=74d4754e0ebe4800]*/
578 {
579     _curses_panel_state *state = get_curses_panel_state(module);
580 
581     PANEL *pan = new_panel(win->win);
582     if (pan == NULL) {
583         PyErr_SetString(state->PyCursesError, catchall_NULL);
584         return NULL;
585     }
586     return (PyObject *)PyCursesPanel_New(state, pan, win);
587 }
588 
589 
590 /* Wrapper for panel_below(NULL). This function returns the top panel
591    of the stack, so it's renamed to top_panel(). panel.below()
592    *requires* a panel object in the first place which may be
593    undesirable. */
594 /*[clinic input]
595 _curses_panel.top_panel
596 
597 Return the top panel in the panel stack.
598 [clinic start generated code]*/
599 
600 static PyObject *
_curses_panel_top_panel_impl(PyObject * module)601 _curses_panel_top_panel_impl(PyObject *module)
602 /*[clinic end generated code: output=86704988bea8508e input=e62d6278dba39e79]*/
603 {
604     PANEL *pan;
605     PyCursesPanelObject *po;
606 
607     PyCursesInitialised;
608 
609     pan = panel_below(NULL);
610 
611     if (pan == NULL) {          /* valid output, it means
612                                    there's no panel at all */
613         Py_RETURN_NONE;
614     }
615     po = find_po(pan);
616     if (po == NULL) {
617         PyErr_SetString(PyExc_RuntimeError,
618                         "panel_below: can't find Panel Object");
619         return NULL;
620     }
621     return Py_NewRef(po);
622 }
623 
624 /*[clinic input]
625 _curses_panel.update_panels
626 
627 Updates the virtual screen after changes in the panel stack.
628 
629 This does not call curses.doupdate(), so you'll have to do this yourself.
630 [clinic start generated code]*/
631 
632 static PyObject *
_curses_panel_update_panels_impl(PyObject * module)633 _curses_panel_update_panels_impl(PyObject *module)
634 /*[clinic end generated code: output=2f3b4c2e03d90ded input=5299624c9a708621]*/
635 {
636     PyCursesInitialised;
637     update_panels();
638     Py_RETURN_NONE;
639 }
640 
641 /* List of functions defined in the module */
642 
643 static PyMethodDef PyCurses_methods[] = {
644     _CURSES_PANEL_BOTTOM_PANEL_METHODDEF
645     _CURSES_PANEL_NEW_PANEL_METHODDEF
646     _CURSES_PANEL_TOP_PANEL_METHODDEF
647     _CURSES_PANEL_UPDATE_PANELS_METHODDEF
648     {NULL,              NULL}           /* sentinel */
649 };
650 
651 /* Initialization function for the module */
652 static int
_curses_panel_exec(PyObject * mod)653 _curses_panel_exec(PyObject *mod)
654 {
655     _curses_panel_state *state = get_curses_panel_state(mod);
656     /* Initialize object type */
657     state->PyCursesPanel_Type = (PyTypeObject *)PyType_FromModuleAndSpec(
658         mod, &PyCursesPanel_Type_spec, NULL);
659     if (state->PyCursesPanel_Type == NULL) {
660         return -1;
661     }
662 
663     if (PyModule_AddType(mod, state->PyCursesPanel_Type) < 0) {
664         return -1;
665     }
666 
667     import_curses();
668     if (PyErr_Occurred()) {
669         return -1;
670     }
671 
672     /* For exception _curses_panel.error */
673     state->PyCursesError = PyErr_NewException(
674         "_curses_panel.error", NULL, NULL);
675 
676     if (PyModule_AddObjectRef(mod, "error", state->PyCursesError) < 0) {
677         return -1;
678     }
679 
680     /* Make the version available */
681     PyObject *v = PyUnicode_FromString(PyCursesVersion);
682     if (v == NULL) {
683         return -1;
684     }
685 
686     PyObject *d = PyModule_GetDict(mod);
687     if (PyDict_SetItemString(d, "version", v) < 0) {
688         Py_DECREF(v);
689         return -1;
690     }
691     if (PyDict_SetItemString(d, "__version__", v) < 0) {
692         Py_DECREF(v);
693         return -1;
694     }
695 
696     Py_DECREF(v);
697 
698     return 0;
699 }
700 
701 static PyModuleDef_Slot _curses_slots[] = {
702     {Py_mod_exec, _curses_panel_exec},
703     // XXX gh-103092: fix isolation.
704     {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
705     //{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
706     {Py_mod_gil, Py_MOD_GIL_NOT_USED},
707     {0, NULL}
708 };
709 
710 static struct PyModuleDef _curses_panelmodule = {
711     PyModuleDef_HEAD_INIT,
712     .m_name = "_curses_panel",
713     .m_size = sizeof(_curses_panel_state),
714     .m_methods = PyCurses_methods,
715     .m_slots = _curses_slots,
716     .m_traverse = _curses_panel_traverse,
717     .m_clear = _curses_panel_clear,
718     .m_free = _curses_panel_free
719 };
720 
721 PyMODINIT_FUNC
PyInit__curses_panel(void)722 PyInit__curses_panel(void)
723 {
724     return PyModuleDef_Init(&_curses_panelmodule);
725 }
726