|
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 po->wo = wo; |
|
182 Py_INCREF(wo); |
|
183 if (insert_lop(po) < 0) { |
|
184 PyObject_DEL(po); |
|
185 return NULL; |
|
186 } |
|
187 return (PyObject *)po; |
|
188 } |
|
189 |
|
190 static void |
|
191 PyCursesPanel_Dealloc(PyCursesPanelObject *po) |
|
192 { |
|
193 (void)del_panel(po->pan); |
|
194 Py_DECREF(po->wo); |
|
195 remove_lop(po); |
|
196 PyObject_DEL(po); |
|
197 } |
|
198 |
|
199 /* panel_above(NULL) returns the bottom panel in the stack. To get |
|
200 this behaviour we use curses.panel.bottom_panel(). */ |
|
201 static PyObject * |
|
202 PyCursesPanel_above(PyCursesPanelObject *self) |
|
203 { |
|
204 PANEL *pan; |
|
205 PyCursesPanelObject *po; |
|
206 |
|
207 pan = panel_above(self->pan); |
|
208 |
|
209 if (pan == NULL) { /* valid output, it means the calling panel |
|
210 is on top of the stack */ |
|
211 Py_INCREF(Py_None); |
|
212 return Py_None; |
|
213 } |
|
214 po = find_po(pan); |
|
215 if (po == NULL) { |
|
216 PyErr_SetString(PyExc_RuntimeError, |
|
217 "panel_above: can't find Panel Object"); |
|
218 return NULL; |
|
219 } |
|
220 Py_INCREF(po); |
|
221 return (PyObject *)po; |
|
222 } |
|
223 |
|
224 /* panel_below(NULL) returns the top panel in the stack. To get |
|
225 this behaviour we use curses.panel.top_panel(). */ |
|
226 static PyObject * |
|
227 PyCursesPanel_below(PyCursesPanelObject *self) |
|
228 { |
|
229 PANEL *pan; |
|
230 PyCursesPanelObject *po; |
|
231 |
|
232 pan = panel_below(self->pan); |
|
233 |
|
234 if (pan == NULL) { /* valid output, it means the calling panel |
|
235 is on the bottom of the stack */ |
|
236 Py_INCREF(Py_None); |
|
237 return Py_None; |
|
238 } |
|
239 po = find_po(pan); |
|
240 if (po == NULL) { |
|
241 PyErr_SetString(PyExc_RuntimeError, |
|
242 "panel_below: can't find Panel Object"); |
|
243 return NULL; |
|
244 } |
|
245 Py_INCREF(po); |
|
246 return (PyObject *)po; |
|
247 } |
|
248 |
|
249 static PyObject * |
|
250 PyCursesPanel_window(PyCursesPanelObject *self) |
|
251 { |
|
252 Py_INCREF(self->wo); |
|
253 return (PyObject *)self->wo; |
|
254 } |
|
255 |
|
256 static PyObject * |
|
257 PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args) |
|
258 { |
|
259 PyCursesPanelObject *po; |
|
260 PyCursesWindowObject *temp; |
|
261 int rtn; |
|
262 |
|
263 if (PyTuple_Size(args) != 1) { |
|
264 PyErr_SetString(PyExc_TypeError, "replace requires one argument"); |
|
265 return NULL; |
|
266 } |
|
267 if (!PyArg_ParseTuple(args, "O!;window object", |
|
268 &PyCursesWindow_Type, &temp)) |
|
269 return NULL; |
|
270 |
|
271 po = find_po(self->pan); |
|
272 if (po == NULL) { |
|
273 PyErr_SetString(PyExc_RuntimeError, |
|
274 "replace_panel: can't find Panel Object"); |
|
275 return NULL; |
|
276 } |
|
277 |
|
278 rtn = replace_panel(self->pan, temp->win); |
|
279 if (rtn == ERR) { |
|
280 PyErr_SetString(PyCursesError, "replace_panel() returned ERR"); |
|
281 return NULL; |
|
282 } |
|
283 Py_DECREF(po->wo); |
|
284 po->wo = temp; |
|
285 Py_INCREF(po->wo); |
|
286 Py_INCREF(Py_None); |
|
287 return Py_None; |
|
288 } |
|
289 |
|
290 static PyObject * |
|
291 PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj) |
|
292 { |
|
293 Py_INCREF(obj); |
|
294 return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj), |
|
295 "set_panel_userptr"); |
|
296 } |
|
297 |
|
298 static PyObject * |
|
299 PyCursesPanel_userptr(PyCursesPanelObject *self) |
|
300 { |
|
301 PyObject *obj; |
|
302 PyCursesInitialised; |
|
303 obj = (PyObject *) panel_userptr(self->pan); |
|
304 if (obj == NULL) { |
|
305 PyErr_SetString(PyCursesError, "no userptr set"); |
|
306 return NULL; |
|
307 } |
|
308 |
|
309 Py_INCREF(obj); |
|
310 return obj; |
|
311 } |
|
312 |
|
313 |
|
314 /* Module interface */ |
|
315 |
|
316 static PyMethodDef PyCursesPanel_Methods[] = { |
|
317 {"above", (PyCFunction)PyCursesPanel_above, METH_NOARGS}, |
|
318 {"below", (PyCFunction)PyCursesPanel_below, METH_NOARGS}, |
|
319 {"bottom", (PyCFunction)PyCursesPanel_bottom_panel, METH_NOARGS}, |
|
320 {"hidden", (PyCFunction)PyCursesPanel_panel_hidden, METH_NOARGS}, |
|
321 {"hide", (PyCFunction)PyCursesPanel_hide_panel, METH_NOARGS}, |
|
322 {"move", (PyCFunction)PyCursesPanel_move_panel, METH_VARARGS}, |
|
323 {"replace", (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS}, |
|
324 {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr, METH_O}, |
|
325 {"show", (PyCFunction)PyCursesPanel_show_panel, METH_NOARGS}, |
|
326 {"top", (PyCFunction)PyCursesPanel_top_panel, METH_NOARGS}, |
|
327 {"userptr", (PyCFunction)PyCursesPanel_userptr, METH_NOARGS}, |
|
328 {"window", (PyCFunction)PyCursesPanel_window, METH_NOARGS}, |
|
329 {NULL, NULL} /* sentinel */ |
|
330 }; |
|
331 |
|
332 static PyObject * |
|
333 PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name) |
|
334 { |
|
335 return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name); |
|
336 } |
|
337 |
|
338 /* -------------------------------------------------------*/ |
|
339 |
|
340 PyTypeObject PyCursesPanel_Type = { |
|
341 PyVarObject_HEAD_INIT(NULL, 0) |
|
342 "_curses_panel.curses panel", /*tp_name*/ |
|
343 sizeof(PyCursesPanelObject), /*tp_basicsize*/ |
|
344 0, /*tp_itemsize*/ |
|
345 /* methods */ |
|
346 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/ |
|
347 0, /*tp_print*/ |
|
348 (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/ |
|
349 (setattrfunc)0, /*tp_setattr*/ |
|
350 0, /*tp_compare*/ |
|
351 0, /*tp_repr*/ |
|
352 0, /*tp_as_number*/ |
|
353 0, /*tp_as_sequence*/ |
|
354 0, /*tp_as_mapping*/ |
|
355 0, /*tp_hash*/ |
|
356 }; |
|
357 |
|
358 /* Wrapper for panel_above(NULL). This function returns the bottom |
|
359 panel of the stack, so it's renamed to bottom_panel(). |
|
360 panel.above() *requires* a panel object in the first place which |
|
361 may be undesirable. */ |
|
362 static PyObject * |
|
363 PyCurses_bottom_panel(PyObject *self) |
|
364 { |
|
365 PANEL *pan; |
|
366 PyCursesPanelObject *po; |
|
367 |
|
368 PyCursesInitialised; |
|
369 |
|
370 pan = panel_above(NULL); |
|
371 |
|
372 if (pan == NULL) { /* valid output, it means |
|
373 there's no panel at all */ |
|
374 Py_INCREF(Py_None); |
|
375 return Py_None; |
|
376 } |
|
377 po = find_po(pan); |
|
378 if (po == NULL) { |
|
379 PyErr_SetString(PyExc_RuntimeError, |
|
380 "panel_above: can't find Panel Object"); |
|
381 return NULL; |
|
382 } |
|
383 Py_INCREF(po); |
|
384 return (PyObject *)po; |
|
385 } |
|
386 |
|
387 static PyObject * |
|
388 PyCurses_new_panel(PyObject *self, PyObject *args) |
|
389 { |
|
390 PyCursesWindowObject *win; |
|
391 PANEL *pan; |
|
392 |
|
393 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win)) |
|
394 return NULL; |
|
395 pan = new_panel(win->win); |
|
396 if (pan == NULL) { |
|
397 PyErr_SetString(PyCursesError, catchall_NULL); |
|
398 return NULL; |
|
399 } |
|
400 return (PyObject *)PyCursesPanel_New(pan, win); |
|
401 } |
|
402 |
|
403 |
|
404 /* Wrapper for panel_below(NULL). This function returns the top panel |
|
405 of the stack, so it's renamed to top_panel(). panel.below() |
|
406 *requires* a panel object in the first place which may be |
|
407 undesirable. */ |
|
408 static PyObject * |
|
409 PyCurses_top_panel(PyObject *self) |
|
410 { |
|
411 PANEL *pan; |
|
412 PyCursesPanelObject *po; |
|
413 |
|
414 PyCursesInitialised; |
|
415 |
|
416 pan = panel_below(NULL); |
|
417 |
|
418 if (pan == NULL) { /* valid output, it means |
|
419 there's no panel at all */ |
|
420 Py_INCREF(Py_None); |
|
421 return Py_None; |
|
422 } |
|
423 po = find_po(pan); |
|
424 if (po == NULL) { |
|
425 PyErr_SetString(PyExc_RuntimeError, |
|
426 "panel_below: can't find Panel Object"); |
|
427 return NULL; |
|
428 } |
|
429 Py_INCREF(po); |
|
430 return (PyObject *)po; |
|
431 } |
|
432 |
|
433 static PyObject *PyCurses_update_panels(PyObject *self) |
|
434 { |
|
435 PyCursesInitialised; |
|
436 update_panels(); |
|
437 Py_INCREF(Py_None); |
|
438 return Py_None; |
|
439 } |
|
440 |
|
441 |
|
442 /* List of functions defined in the module */ |
|
443 |
|
444 static PyMethodDef PyCurses_methods[] = { |
|
445 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS}, |
|
446 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS}, |
|
447 {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS}, |
|
448 {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS}, |
|
449 {NULL, NULL} /* sentinel */ |
|
450 }; |
|
451 |
|
452 /* Initialization function for the module */ |
|
453 |
|
454 PyMODINIT_FUNC |
|
455 init_curses_panel(void) |
|
456 { |
|
457 PyObject *m, *d, *v; |
|
458 |
|
459 /* Initialize object type */ |
|
460 Py_TYPE(&PyCursesPanel_Type) = &PyType_Type; |
|
461 |
|
462 import_curses(); |
|
463 |
|
464 /* Create the module and add the functions */ |
|
465 m = Py_InitModule("_curses_panel", PyCurses_methods); |
|
466 if (m == NULL) |
|
467 return; |
|
468 d = PyModule_GetDict(m); |
|
469 |
|
470 /* For exception _curses_panel.error */ |
|
471 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL); |
|
472 PyDict_SetItemString(d, "error", PyCursesError); |
|
473 |
|
474 /* Make the version available */ |
|
475 v = PyString_FromString(PyCursesVersion); |
|
476 PyDict_SetItemString(d, "version", v); |
|
477 PyDict_SetItemString(d, "__version__", v); |
|
478 Py_DECREF(v); |
|
479 } |