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 * |
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 |
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 |
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 |
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 * |
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 |
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 |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 |
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 |
717 | PyInit__curses_panel(void) |
718 | { |
719 | return PyModuleDef_Init(&_curses_panelmodule); |
720 | } |
721 | |