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