blob: a2817023ade6305bbb51e646379b39bd069605f2 [file] [log] [blame]
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +00001/*
2 * Interface to the ncurses panel library
3 *
4 * Original version by Thomas Gellekum
5 */
6
7/* Release Number */
8
9static char *PyCursesVersion = "2.1";
10
11/* Includes */
12
13#include "Python.h"
14
15#include "py_curses.h"
16
17#include <panel.h>
18
19static 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
29static PyObject *
30PyCursesCheckERR(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
51typedef struct {
52 PyObject_HEAD
53 PANEL *pan;
54 PyCursesWindowObject *wo; /* for reference counts */
55} PyCursesPanelObject;
56
57PyTypeObject PyCursesPanel_Type;
58
59#define PyCursesPanel_Check(v) ((v)->ob_type == &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. */
78typedef struct _list_of_panels {
79 PyCursesPanelObject *po;
80 struct _list_of_panels *next;
81} list_of_panels;
82
83/* list anchor */
84static list_of_panels *lop;
85
86/* Insert a new panel object into lop */
87static int
88insert_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 */
103static void
104remove_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->po != po) {
115 if (temp->next == NULL)
116 PyErr_SetString(PyExc_RuntimeError,
117 "remove_lop: can't find Panel Object");
118 temp = temp->next;
119 }
120 n = temp->next->next;
121 free(temp->next);
122 temp->next = n;
123 return;
124}
125
126/* Return the panel object that corresponds to pan */
127static PyCursesPanelObject *
128find_po(PANEL *pan)
129{
130 list_of_panels *temp;
131 for (temp = lop; temp->po->pan != pan; temp = temp->next)
132 if (temp->next == NULL) return NULL; /* not found!? */
133 return temp->po;
134}
135
136/* Function Prototype Macros - They are ugly but very, very useful. ;-)
137
138 X - function name
139 TYPE - parameter Type
140 ERGSTR - format string for construction of the return value
141 PARSESTR - format string for argument parsing */
142
143#define Panel_NoArgNoReturnFunction(X) \
Fred Drake4e36d582000-12-23 05:46:23 +0000144static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000145{ if (!PyArg_NoArgs(args)) return NULL; \
146 return PyCursesCheckERR(X(self->pan), # X); }
147
148#define Panel_NoArgReturnStringFunction(X) \
Fred Drake4e36d582000-12-23 05:46:23 +0000149static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000150{ if (!PyArg_NoArgs(args)) return NULL; \
151 return PyString_FromString(X(self->pan)); }
152
153#define Panel_NoArgTrueFalseFunction(X) \
Fred Drake4e36d582000-12-23 05:46:23 +0000154static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000155{ \
156 if (!PyArg_NoArgs(args)) return NULL; \
157 if (X (self->pan) == FALSE) { Py_INCREF(Py_False); return Py_False; } \
158 else { Py_INCREF(Py_True); return Py_True; } }
159
160#define Panel_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \
Fred Drake4e36d582000-12-23 05:46:23 +0000161static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000162{ \
163 TYPE arg1, arg2; \
164 if (!PyArg_Parse(args,PARSESTR, &arg1, &arg2)) return NULL; \
165 return PyCursesCheckERR(X(self->pan, arg1, arg2), # X); }
166
167/* ------------- PANEL routines --------------- */
168
169Panel_NoArgNoReturnFunction(bottom_panel)
170Panel_NoArgNoReturnFunction(hide_panel)
171Panel_NoArgNoReturnFunction(show_panel)
172Panel_NoArgNoReturnFunction(top_panel)
173Panel_NoArgTrueFalseFunction(panel_hidden)
174Panel_TwoArgNoReturnFunction(move_panel, int, "(ii);y,x")
175
176/* Allocation and deallocation of Panel Objects */
177
178static PyObject *
179PyCursesPanel_New(PANEL *pan, PyCursesWindowObject *wo)
180{
181 PyCursesPanelObject *po;
182
183 po = PyObject_NEW(PyCursesPanelObject, &PyCursesPanel_Type);
184 if (po == NULL) return NULL;
185 po->pan = pan;
186 po->wo = wo;
187 Py_INCREF(wo);
188 if (insert_lop(po) < 0) {
189 PyObject_DEL(po);
190 return NULL;
191 }
192 return (PyObject *)po;
193}
194
195static void
196PyCursesPanel_Dealloc(PyCursesPanelObject *po)
197{
198 (void)del_panel(po->pan);
199 Py_DECREF(po->wo);
200 remove_lop(po);
201 PyMem_DEL(po);
202}
203
204/* panel_above(NULL) returns the bottom panel in the stack. To get
205 this behaviour we use curses.panel.bottom_panel(). */
206static PyObject *
207PyCursesPanel_above(PyCursesPanelObject *self, PyObject *args)
208{
209 PANEL *pan;
210 PyCursesPanelObject *po;
211
212 if (!PyArg_NoArgs(args)) return NULL;
213
214 pan = panel_above(self->pan);
215
216 if (pan == NULL) { /* valid output, it means the calling panel
217 is on top of the stack */
218 Py_INCREF(Py_None);
219 return Py_None;
220 }
221 po = find_po(pan);
222 if (po == NULL) {
223 PyErr_SetString(PyExc_RuntimeError,
224 "panel_above: can't find Panel Object");
225 return NULL;
226 }
227 Py_INCREF(po);
228 return (PyObject *)po;
229}
230
231/* panel_below(NULL) returns the top panel in the stack. To get
232 this behaviour we use curses.panel_below(). */
233static PyObject *
234PyCursesPanel_below(PyCursesPanelObject *self, PyObject *args)
235{
236 PANEL *pan;
237 PyCursesPanelObject *po;
238
239 if (!PyArg_NoArgs(args)) return NULL;
240
241 pan = panel_below(self->pan);
242
243 if (pan == NULL) { /* valid output, it means the calling panel
244 is on the bottom of the stack */
245 Py_INCREF(Py_None);
246 return Py_None;
247 }
248 po = find_po(pan);
249 if (po == NULL) {
250 PyErr_SetString(PyExc_RuntimeError,
251 "panel_below: can't find Panel Object");
252 return NULL;
253 }
254 Py_INCREF(po);
255 return (PyObject *)po;
256}
257
258static PyObject *
259PyCursesPanel_window(PyCursesPanelObject *self, PyObject *args)
260{
261 if (!PyArg_NoArgs(args)) return NULL;
262
263 Py_INCREF(self->wo);
264 return (PyObject *)self->wo;
265}
266
267static PyObject *
268PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args)
269{
270 PyCursesPanelObject *po;
271 PyCursesWindowObject *temp;
272 int rtn;
273
274 if (ARG_COUNT(args) != 1) {
275 PyErr_SetString(PyExc_TypeError, "replace requires one argument");
276 return NULL;
277 }
278 if (!PyArg_ParseTuple(args, "O!;window object",
279 &PyCursesWindow_Type, &temp))
280 return NULL;
281
282 po = find_po(self->pan);
283 if (po == NULL) {
284 PyErr_SetString(PyExc_RuntimeError,
285 "replace_panel: can't find Panel Object");
286 return NULL;
287 }
288
289 rtn = replace_panel(self->pan, temp->win);
290 if (rtn == ERR) {
291 PyErr_SetString(PyCursesError, "replace_panel() returned ERR");
292 return NULL;
293 }
294 Py_DECREF(po->wo);
295 po->wo = temp;
296 Py_INCREF(po->wo);
297 Py_INCREF(Py_None);
298 return Py_None;
299}
300
301static PyObject *
302PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *args)
303{
304 PyObject *obj;
305
306 if (ARG_COUNT(args) != 1) {
307 PyErr_SetString(PyExc_TypeError, "set_userptr requires one argument");
308 return NULL;
309 }
310 obj = PyTuple_GetItem(args, 0);
311 Py_INCREF(obj);
Fred Drake4e36d582000-12-23 05:46:23 +0000312 return PyCursesCheckERR(set_panel_userptr(self->pan, obj),
313 "set_panel_userptr");
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000314}
315
316static PyObject *PyCursesPanel_userptr
317(PyCursesPanelObject *self, PyObject *args)
318{
319 PyObject *obj;
320 PyCursesInitialised;
Fred Drake4e36d582000-12-23 05:46:23 +0000321 if (!PyArg_NoArgs(args))
322 return NULL;
323 obj = (PyObject *) panel_userptr(self->pan);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000324 Py_INCREF(obj);
325 return obj;
326}
327
328
329/* Module interface */
330
331static PyMethodDef PyCursesPanel_Methods[] = {
332 {"above", (PyCFunction)PyCursesPanel_above},
333 {"below", (PyCFunction)PyCursesPanel_below},
334 {"bottom", (PyCFunction)PyCursesPanel_bottom_panel},
335 {"hidden", (PyCFunction)PyCursesPanel_panel_hidden},
336 {"hide", (PyCFunction)PyCursesPanel_hide_panel},
337 {"move", (PyCFunction)PyCursesPanel_move_panel},
Andrew M. Kuchlingd7d2e192000-12-22 22:03:15 +0000338 {"replace", (PyCFunction)PyCursesPanel_replace_panel,
339 METH_VARARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000340 {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr,
341 METH_VARARGS},
342 {"show", (PyCFunction)PyCursesPanel_show_panel},
343 {"top", (PyCFunction)PyCursesPanel_top_panel},
344 {"userptr", (PyCFunction)PyCursesPanel_userptr},
345 {"window", (PyCFunction)PyCursesPanel_window},
346 {NULL, NULL} /* sentinel */
347};
348
349static PyObject *
350PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name)
351{
352 return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name);
353}
354
355/* -------------------------------------------------------*/
356
357PyTypeObject PyCursesPanel_Type = {
358 PyObject_HEAD_INIT(&PyType_Type)
359 0, /*ob_size*/
360 "curses panel", /*tp_name*/
361 sizeof(PyCursesPanelObject), /*tp_basicsize*/
362 0, /*tp_itemsize*/
363 /* methods */
364 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
365 0, /*tp_print*/
366 (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/
367 (setattrfunc)0, /*tp_setattr*/
368 0, /*tp_compare*/
369 0, /*tp_repr*/
370 0, /*tp_as_number*/
371 0, /*tp_as_sequence*/
372 0, /*tp_as_mapping*/
373 0, /*tp_hash*/
374};
375
376/* Wrapper for panel_above(NULL). This function returns the bottom
377 panel of the stack, so it's renamed to bottom_panel().
378 panel.above() *requires* a panel object in the first place which
379 may be undesirable. */
380static PyObject *
381PyCurses_bottom_panel(PyObject *self, PyObject *args)
382{
383 PANEL *pan;
384 PyCursesPanelObject *po;
385
386 PyCursesInitialised;
387
388 if (!PyArg_NoArgs(args)) return NULL;
389
390 pan = panel_above(NULL);
391
392 if (pan == NULL) { /* valid output, it means there's no panel at
393 all */
394 Py_INCREF(Py_None);
395 return Py_None;
396 }
397 po = find_po(pan);
398 if (po == NULL) {
399 PyErr_SetString(PyExc_RuntimeError,
400 "panel_above: can't find Panel Object");
401 return NULL;
402 }
403 Py_INCREF(po);
404 return (PyObject *)po;
405}
406
407static PyObject *
408PyCurses_new_panel(PyObject *self, PyObject *args)
409{
410 PyCursesWindowObject *win;
411 PANEL *pan;
412
Fred Drake4e36d582000-12-23 05:46:23 +0000413 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
414 return NULL;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000415 pan = new_panel(win->win);
416 if (pan == NULL) {
417 PyErr_SetString(PyCursesError, catchall_NULL);
418 return NULL;
419 }
420 return (PyObject *)PyCursesPanel_New(pan, win);
421}
422
423
424/* Wrapper for panel_below(NULL). This function returns the top panel
425 of the stack, so it's renamed to top_panel(). panel.below()
426 *requires* a panel object in the first place which may be
427 undesirable. */
428static PyObject *
429PyCurses_top_panel(PyObject *self, PyObject *args)
430{
431 PANEL *pan;
432 PyCursesPanelObject *po;
433
434 PyCursesInitialised;
435
436 if (!PyArg_NoArgs(args)) return NULL;
437
438 pan = panel_below(NULL);
439
440 if (pan == NULL) { /* valid output, it means
441 there's no panel at all */
442 Py_INCREF(Py_None);
443 return Py_None;
444 }
445 po = find_po(pan);
446 if (po == NULL) {
447 PyErr_SetString(PyExc_RuntimeError,
448 "panel_below: can't find Panel Object");
449 return NULL;
450 }
451 Py_INCREF(po);
452 return (PyObject *)po;
453}
454
455static PyObject *PyCurses_update_panels(PyObject *self, PyObject *args)
456{
457 PyCursesInitialised;
458 if (!PyArg_NoArgs(args)) return NULL;
459 update_panels();
460 Py_INCREF(Py_None);
461 return Py_None;
462}
463
464
465/* List of functions defined in the module */
466
467static PyMethodDef PyCurses_methods[] = {
468 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel},
469 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
470 {"top_panel", (PyCFunction)PyCurses_top_panel},
471 {"update_panels", (PyCFunction)PyCurses_update_panels},
472 {NULL, NULL} /* sentinel */
473};
474
475/* Initialization function for the module */
476
477void
478init_curses_panel(void)
479{
480 PyObject *m, *d, *v;
481
482 import_curses();
483
484 /* Create the module and add the functions */
485 m = Py_InitModule("_curses_panel", PyCurses_methods);
486 d = PyModule_GetDict(m);
487
488 /* For exception _curses_panel.error */
489 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
490 PyDict_SetItemString(d, "error", PyCursesError);
491
492 /* Make the version available */
493 v = PyString_FromString(PyCursesVersion);
494 PyDict_SetItemString(d, "version", v);
495 PyDict_SetItemString(d, "__version__", v);
496 Py_DECREF(v);
497
498}
499