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