No issues found
1 /*
2 * Interface to the ncurses panel library
3 *
4 * Original version by Thomas Gellekum
5 */
6
7 /* Release Number */
8
9 static 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 static PyObject *PyCursesError;
20
21
22 /* Utility Functions */
23
24 /*
25 * Check the return code from a curses function and return None
26 * or raise an exception as appropriate.
27 */
28
29 static PyObject *
30 PyCursesCheckERR(int code, char *fname)
31 {
32 if (code != ERR) {
33 Py_INCREF(Py_None);
34 return Py_None;
35 } else {
36 if (fname == NULL) {
37 PyErr_SetString(PyCursesError, catchall_ERR);
38 } else {
39 PyErr_Format(PyCursesError, "%s() returned ERR", fname);
40 }
41 return NULL;
42 }
43 }
44
45 /*****************************************************************************
46 The Panel Object
47 ******************************************************************************/
48
49 /* Definition of the panel object and panel type */
50
51 typedef struct {
52 PyObject_HEAD
53 PANEL *pan;
54 PyCursesWindowObject *wo; /* for reference counts */
55 } PyCursesPanelObject;
56
57 PyTypeObject PyCursesPanel_Type;
58
59 #define PyCursesPanel_Check(v) (Py_TYPE(v) == &PyCursesPanel_Type)
60
61 /* Some helper functions. The problem is that there's always a window
62 associated with a panel. To ensure that Python's GC doesn't pull
63 this window from under our feet we need to keep track of references
64 to the corresponding window object within Python. We can't use
65 dupwin(oldwin) to keep a copy of the curses WINDOW because the
66 contents of oldwin is copied only once; code like
67
68 win = newwin(...)
69 pan = win.panel()
70 win.addstr(some_string)
71 pan.window().addstr(other_string)
72
73 will fail. */
74
75 /* We keep a linked list of PyCursesPanelObjects, lop. A list should
76 suffice, I don't expect more than a handful or at most a few
77 dozens of panel objects within a typical program. */
78 typedef struct _list_of_panels {
79 PyCursesPanelObject *po;
80 struct _list_of_panels *next;
81 } list_of_panels;
82
83 /* list anchor */
84 static list_of_panels *lop;
85
86 /* Insert a new panel object into lop */
87 static int
88 insert_lop(PyCursesPanelObject *po)
89 {
90 list_of_panels *new;
91
92 if ((new = (list_of_panels *)malloc(sizeof(list_of_panels))) == NULL) {
93 PyErr_NoMemory();
94 return -1;
95 }
96 new->po = po;
97 new->next = lop;
98 lop = new;
99 return 0;
100 }
101
102 /* Remove the panel object from lop */
103 static void
104 remove_lop(PyCursesPanelObject *po)
105 {
106 list_of_panels *temp, *n;
107
108 temp = lop;
109 if (temp->po == po) {
110 lop = temp->next;
111 free(temp);
112 return;
113 }
114 while (temp->next == NULL || temp->next->po != po) {
115 if (temp->next == NULL) {
116 PyErr_SetString(PyExc_RuntimeError,
117 "remove_lop: can't find Panel Object");
118 return;
119 }
120 temp = temp->next;
121 }
122 n = temp->next->next;
123 free(temp->next);
124 temp->next = n;
125 return;
126 }
127
128 /* Return the panel object that corresponds to pan */
129 static PyCursesPanelObject *
130 find_po(PANEL *pan)
131 {
132 list_of_panels *temp;
133 for (temp = lop; temp->po->pan != pan; temp = temp->next)
134 if (temp->next == NULL) return NULL; /* not found!? */
135 return temp->po;
136 }
137
138 /* Function Prototype Macros - They are ugly but very, very useful. ;-)
139
140 X - function name
141 TYPE - parameter Type
142 ERGSTR - format string for construction of the return value
143 PARSESTR - format string for argument parsing */
144
145 #define Panel_NoArgNoReturnFunction(X) \
146 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
147 { return PyCursesCheckERR(X(self->pan), # X); }
148
149 #define Panel_NoArgTrueFalseFunction(X) \
150 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
151 { \
152 if (X (self->pan) == FALSE) { Py_INCREF(Py_False); return Py_False; } \
153 else { Py_INCREF(Py_True); return Py_True; } }
154
155 #define Panel_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \
156 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
157 { \
158 TYPE arg1, arg2; \
159 if (!PyArg_ParseTuple(args, PARSESTR, &arg1, &arg2)) return NULL; \
160 return PyCursesCheckERR(X(self->pan, arg1, arg2), # X); }
161
162 /* ------------- PANEL routines --------------- */
163
164 Panel_NoArgNoReturnFunction(bottom_panel)
165 Panel_NoArgNoReturnFunction(hide_panel)
166 Panel_NoArgNoReturnFunction(show_panel)
167 Panel_NoArgNoReturnFunction(top_panel)
168 Panel_NoArgTrueFalseFunction(panel_hidden)
169 Panel_TwoArgNoReturnFunction(move_panel, int, "ii;y,x")
170
171 /* Allocation and deallocation of Panel Objects */
172
173 static PyObject *
174 PyCursesPanel_New(PANEL *pan, PyCursesWindowObject *wo)
175 {
176 PyCursesPanelObject *po;
177
178 po = PyObject_NEW(PyCursesPanelObject, &PyCursesPanel_Type);
179 if (po == NULL) return NULL;
180 po->pan = pan;
181 if (insert_lop(po) < 0) {
182 po->wo = NULL;
183 Py_DECREF(po);
184 return NULL;
185 }
186 po->wo = wo;
187 Py_INCREF(wo);
188 return (PyObject *)po;
189 }
190
191 static void
192 PyCursesPanel_Dealloc(PyCursesPanelObject *po)
193 {
194 (void)del_panel(po->pan);
195 if (po->wo != NULL) {
196 Py_DECREF(po->wo);
197 remove_lop(po);
198 }
199 PyObject_DEL(po);
200 }
201
202 /* panel_above(NULL) returns the bottom panel in the stack. To get
203 this behaviour we use curses.panel.bottom_panel(). */
204 static PyObject *
205 PyCursesPanel_above(PyCursesPanelObject *self)
206 {
207 PANEL *pan;
208 PyCursesPanelObject *po;
209
210 pan = panel_above(self->pan);
211
212 if (pan == NULL) { /* valid output, it means the calling panel
213 is on top of the stack */
214 Py_INCREF(Py_None);
215 return Py_None;
216 }
217 po = find_po(pan);
218 if (po == NULL) {
219 PyErr_SetString(PyExc_RuntimeError,
220 "panel_above: can't find Panel Object");
221 return NULL;
222 }
223 Py_INCREF(po);
224 return (PyObject *)po;
225 }
226
227 /* panel_below(NULL) returns the top panel in the stack. To get
228 this behaviour we use curses.panel.top_panel(). */
229 static PyObject *
230 PyCursesPanel_below(PyCursesPanelObject *self)
231 {
232 PANEL *pan;
233 PyCursesPanelObject *po;
234
235 pan = panel_below(self->pan);
236
237 if (pan == NULL) { /* valid output, it means the calling panel
238 is on the bottom of the stack */
239 Py_INCREF(Py_None);
240 return Py_None;
241 }
242 po = find_po(pan);
243 if (po == NULL) {
244 PyErr_SetString(PyExc_RuntimeError,
245 "panel_below: can't find Panel Object");
246 return NULL;
247 }
248 Py_INCREF(po);
249 return (PyObject *)po;
250 }
251
252 static PyObject *
253 PyCursesPanel_window(PyCursesPanelObject *self)
254 {
255 Py_INCREF(self->wo);
256 return (PyObject *)self->wo;
257 }
258
259 static PyObject *
260 PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args)
261 {
262 PyCursesPanelObject *po;
263 PyCursesWindowObject *temp;
264 int rtn;
265
266 if (PyTuple_Size(args) != 1) {
267 PyErr_SetString(PyExc_TypeError, "replace requires one argument");
268 return NULL;
269 }
270 if (!PyArg_ParseTuple(args, "O!;window object",
271 &PyCursesWindow_Type, &temp))
272 return NULL;
273
274 po = find_po(self->pan);
275 if (po == NULL) {
276 PyErr_SetString(PyExc_RuntimeError,
277 "replace_panel: can't find Panel Object");
278 return NULL;
279 }
280
281 rtn = replace_panel(self->pan, temp->win);
282 if (rtn == ERR) {
283 PyErr_SetString(PyCursesError, "replace_panel() returned ERR");
284 return NULL;
285 }
286 Py_DECREF(po->wo);
287 po->wo = temp;
288 Py_INCREF(po->wo);
289 Py_INCREF(Py_None);
290 return Py_None;
291 }
292
293 static PyObject *
294 PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj)
295 {
296 Py_INCREF(obj);
297 return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj),
298 "set_panel_userptr");
299 }
300
301 static PyObject *
302 PyCursesPanel_userptr(PyCursesPanelObject *self)
303 {
304 PyObject *obj;
305 PyCursesInitialised;
306 obj = (PyObject *) panel_userptr(self->pan);
307 if (obj == NULL) {
308 PyErr_SetString(PyCursesError, "no userptr set");
309 return NULL;
310 }
311
312 Py_INCREF(obj);
313 return obj;
314 }
315
316
317 /* Module interface */
318
319 static PyMethodDef PyCursesPanel_Methods[] = {
320 {"above", (PyCFunction)PyCursesPanel_above, METH_NOARGS},
321 {"below", (PyCFunction)PyCursesPanel_below, METH_NOARGS},
322 {"bottom", (PyCFunction)PyCursesPanel_bottom_panel, METH_NOARGS},
323 {"hidden", (PyCFunction)PyCursesPanel_panel_hidden, METH_NOARGS},
324 {"hide", (PyCFunction)PyCursesPanel_hide_panel, METH_NOARGS},
325 {"move", (PyCFunction)PyCursesPanel_move_panel, METH_VARARGS},
326 {"replace", (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS},
327 {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr, METH_O},
328 {"show", (PyCFunction)PyCursesPanel_show_panel, METH_NOARGS},
329 {"top", (PyCFunction)PyCursesPanel_top_panel, METH_NOARGS},
330 {"userptr", (PyCFunction)PyCursesPanel_userptr, METH_NOARGS},
331 {"window", (PyCFunction)PyCursesPanel_window, METH_NOARGS},
332 {NULL, NULL} /* sentinel */
333 };
334
335 static PyObject *
336 PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name)
337 {
338 return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name);
339 }
340
341 /* -------------------------------------------------------*/
342
343 PyTypeObject PyCursesPanel_Type = {
344 PyVarObject_HEAD_INIT(NULL, 0)
345 "_curses_panel.curses panel", /*tp_name*/
346 sizeof(PyCursesPanelObject), /*tp_basicsize*/
347 0, /*tp_itemsize*/
348 /* methods */
349 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
350 0, /*tp_print*/
351 (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/
352 (setattrfunc)0, /*tp_setattr*/
353 0, /*tp_compare*/
354 0, /*tp_repr*/
355 0, /*tp_as_number*/
356 0, /*tp_as_sequence*/
357 0, /*tp_as_mapping*/
358 0, /*tp_hash*/
359 };
360
361 /* Wrapper for panel_above(NULL). This function returns the bottom
362 panel of the stack, so it's renamed to bottom_panel().
363 panel.above() *requires* a panel object in the first place which
364 may be undesirable. */
365 static PyObject *
366 PyCurses_bottom_panel(PyObject *self)
367 {
368 PANEL *pan;
369 PyCursesPanelObject *po;
370
371 PyCursesInitialised;
372
373 pan = panel_above(NULL);
374
375 if (pan == NULL) { /* valid output, it means
376 there's no panel at all */
377 Py_INCREF(Py_None);
378 return Py_None;
379 }
380 po = find_po(pan);
381 if (po == NULL) {
382 PyErr_SetString(PyExc_RuntimeError,
383 "panel_above: can't find Panel Object");
384 return NULL;
385 }
386 Py_INCREF(po);
387 return (PyObject *)po;
388 }
389
390 static PyObject *
391 PyCurses_new_panel(PyObject *self, PyObject *args)
392 {
393 PyCursesWindowObject *win;
394 PANEL *pan;
395
396 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
397 return NULL;
398 pan = new_panel(win->win);
399 if (pan == NULL) {
400 PyErr_SetString(PyCursesError, catchall_NULL);
401 return NULL;
402 }
403 return (PyObject *)PyCursesPanel_New(pan, win);
404 }
405
406
407 /* Wrapper for panel_below(NULL). This function returns the top panel
408 of the stack, so it's renamed to top_panel(). panel.below()
409 *requires* a panel object in the first place which may be
410 undesirable. */
411 static PyObject *
412 PyCurses_top_panel(PyObject *self)
413 {
414 PANEL *pan;
415 PyCursesPanelObject *po;
416
417 PyCursesInitialised;
418
419 pan = panel_below(NULL);
420
421 if (pan == NULL) { /* valid output, it means
422 there's no panel at all */
423 Py_INCREF(Py_None);
424 return Py_None;
425 }
426 po = find_po(pan);
427 if (po == NULL) {
428 PyErr_SetString(PyExc_RuntimeError,
429 "panel_below: can't find Panel Object");
430 return NULL;
431 }
432 Py_INCREF(po);
433 return (PyObject *)po;
434 }
435
436 static PyObject *PyCurses_update_panels(PyObject *self)
437 {
438 PyCursesInitialised;
439 update_panels();
440 Py_INCREF(Py_None);
441 return Py_None;
442 }
443
444
445 /* List of functions defined in the module */
446
447 static PyMethodDef PyCurses_methods[] = {
448 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS},
449 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
450 {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS},
451 {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS},
452 {NULL, NULL} /* sentinel */
453 };
454
455 /* Initialization function for the module */
456
457 PyMODINIT_FUNC
458 init_curses_panel(void)
459 {
460 PyObject *m, *d, *v;
461
462 /* Initialize object type */
463 Py_TYPE(&PyCursesPanel_Type) = &PyType_Type;
464
465 import_curses();
466
467 /* Create the module and add the functions */
468 m = Py_InitModule("_curses_panel", PyCurses_methods);
469 if (m == NULL)
470 return;
471 d = PyModule_GetDict(m);
472
473 /* For exception _curses_panel.error */
474 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
475 PyDict_SetItemString(d, "error", PyCursesError);
476
477 /* Make the version available */
478 v = PyString_FromString(PyCursesVersion);
479 PyDict_SetItemString(d, "version", v);
480 PyDict_SetItemString(d, "__version__", v);
481 Py_DECREF(v);
482 }