blob: 5603fe97247558039d48a7d2f4ad17cf49274ef8 [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) \
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000144static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
145{ return PyCursesCheckERR(X(self->pan), # X); }
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000146
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000147#define Panel_NoArgTrueFalseFunction(X) \
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000148static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000149{ \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000150 if (X (self->pan) == FALSE) { Py_INCREF(Py_False); return Py_False; } \
151 else { Py_INCREF(Py_True); return Py_True; } }
152
153#define Panel_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \
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 TYPE arg1, arg2; \
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000157 if (!PyArg_ParseTuple(args, PARSESTR, &arg1, &arg2)) return NULL; \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000158 return PyCursesCheckERR(X(self->pan, arg1, arg2), # X); }
159
160/* ------------- PANEL routines --------------- */
161
162Panel_NoArgNoReturnFunction(bottom_panel)
163Panel_NoArgNoReturnFunction(hide_panel)
164Panel_NoArgNoReturnFunction(show_panel)
165Panel_NoArgNoReturnFunction(top_panel)
166Panel_NoArgTrueFalseFunction(panel_hidden)
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000167Panel_TwoArgNoReturnFunction(move_panel, int, "ii;y,x")
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000168
169/* Allocation and deallocation of Panel Objects */
170
171static PyObject *
172PyCursesPanel_New(PANEL *pan, PyCursesWindowObject *wo)
173{
174 PyCursesPanelObject *po;
175
176 po = PyObject_NEW(PyCursesPanelObject, &PyCursesPanel_Type);
177 if (po == NULL) return NULL;
178 po->pan = pan;
179 po->wo = wo;
180 Py_INCREF(wo);
181 if (insert_lop(po) < 0) {
182 PyObject_DEL(po);
183 return NULL;
184 }
185 return (PyObject *)po;
186}
187
188static void
189PyCursesPanel_Dealloc(PyCursesPanelObject *po)
190{
191 (void)del_panel(po->pan);
192 Py_DECREF(po->wo);
193 remove_lop(po);
Michael W. Hudsoncf6bfe42002-01-30 15:47:34 +0000194 PyObject_DEL(po);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000195}
196
197/* panel_above(NULL) returns the bottom panel in the stack. To get
198 this behaviour we use curses.panel.bottom_panel(). */
199static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000200PyCursesPanel_above(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000201{
202 PANEL *pan;
203 PyCursesPanelObject *po;
204
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000205 pan = panel_above(self->pan);
206
207 if (pan == NULL) { /* valid output, it means the calling panel
208 is on top of the stack */
209 Py_INCREF(Py_None);
210 return Py_None;
211 }
212 po = find_po(pan);
213 if (po == NULL) {
214 PyErr_SetString(PyExc_RuntimeError,
215 "panel_above: can't find Panel Object");
216 return NULL;
217 }
218 Py_INCREF(po);
219 return (PyObject *)po;
220}
221
222/* panel_below(NULL) returns the top panel in the stack. To get
Andrew M. Kuchlingae89af92001-01-19 15:35:26 +0000223 this behaviour we use curses.panel.top_panel(). */
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000224static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000225PyCursesPanel_below(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000226{
227 PANEL *pan;
228 PyCursesPanelObject *po;
229
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000230 pan = panel_below(self->pan);
231
232 if (pan == NULL) { /* valid output, it means the calling panel
233 is on the bottom of the stack */
234 Py_INCREF(Py_None);
235 return Py_None;
236 }
237 po = find_po(pan);
238 if (po == NULL) {
239 PyErr_SetString(PyExc_RuntimeError,
240 "panel_below: can't find Panel Object");
241 return NULL;
242 }
243 Py_INCREF(po);
244 return (PyObject *)po;
245}
246
247static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000248PyCursesPanel_window(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000249{
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000250 Py_INCREF(self->wo);
251 return (PyObject *)self->wo;
252}
253
254static PyObject *
255PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args)
256{
257 PyCursesPanelObject *po;
258 PyCursesWindowObject *temp;
259 int rtn;
260
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000261 if (PyTuple_Size(args) != 1) {
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000262 PyErr_SetString(PyExc_TypeError, "replace requires one argument");
263 return NULL;
264 }
265 if (!PyArg_ParseTuple(args, "O!;window object",
266 &PyCursesWindow_Type, &temp))
267 return NULL;
268
269 po = find_po(self->pan);
270 if (po == NULL) {
271 PyErr_SetString(PyExc_RuntimeError,
272 "replace_panel: can't find Panel Object");
273 return NULL;
274 }
275
276 rtn = replace_panel(self->pan, temp->win);
277 if (rtn == ERR) {
278 PyErr_SetString(PyCursesError, "replace_panel() returned ERR");
279 return NULL;
280 }
281 Py_DECREF(po->wo);
282 po->wo = temp;
283 Py_INCREF(po->wo);
284 Py_INCREF(Py_None);
285 return Py_None;
286}
287
288static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000289PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000290{
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000291 Py_INCREF(obj);
Martin v. Löwisa38d9162001-10-13 08:50:10 +0000292 return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj),
Fred Drake4e36d582000-12-23 05:46:23 +0000293 "set_panel_userptr");
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000294}
295
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000296static PyObject *
297PyCursesPanel_userptr(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000298{
299 PyObject *obj;
300 PyCursesInitialised;
Fred Drake4e36d582000-12-23 05:46:23 +0000301 obj = (PyObject *) panel_userptr(self->pan);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000302 Py_INCREF(obj);
303 return obj;
304}
305
306
307/* Module interface */
308
309static PyMethodDef PyCursesPanel_Methods[] = {
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000310 {"above", (PyCFunction)PyCursesPanel_above, METH_NOARGS},
311 {"below", (PyCFunction)PyCursesPanel_below, METH_NOARGS},
312 {"bottom", (PyCFunction)PyCursesPanel_bottom_panel, METH_NOARGS},
313 {"hidden", (PyCFunction)PyCursesPanel_panel_hidden, METH_NOARGS},
314 {"hide", (PyCFunction)PyCursesPanel_hide_panel, METH_NOARGS},
315 {"move", (PyCFunction)PyCursesPanel_move_panel, METH_VARARGS},
Andrew M. Kuchlingd7d2e192000-12-22 22:03:15 +0000316 {"replace", (PyCFunction)PyCursesPanel_replace_panel,
317 METH_VARARGS},
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000318 {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr, METH_O},
319 {"show", (PyCFunction)PyCursesPanel_show_panel, METH_NOARGS},
320 {"top", (PyCFunction)PyCursesPanel_top_panel, METH_NOARGS},
321 {"userptr", (PyCFunction)PyCursesPanel_userptr, METH_NOARGS},
322 {"window", (PyCFunction)PyCursesPanel_window, METH_NOARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000323 {NULL, NULL} /* sentinel */
324};
325
326static PyObject *
327PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name)
328{
329 return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name);
330}
331
332/* -------------------------------------------------------*/
333
334PyTypeObject PyCursesPanel_Type = {
Guido van Rossuma120ffc2001-01-22 15:29:14 +0000335 PyObject_HEAD_INIT(NULL)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000336 0, /*ob_size*/
Guido van Rossum14648392001-12-08 18:02:58 +0000337 "_curses_panel.curses panel", /*tp_name*/
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000338 sizeof(PyCursesPanelObject), /*tp_basicsize*/
339 0, /*tp_itemsize*/
340 /* methods */
341 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
342 0, /*tp_print*/
343 (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/
344 (setattrfunc)0, /*tp_setattr*/
345 0, /*tp_compare*/
346 0, /*tp_repr*/
347 0, /*tp_as_number*/
348 0, /*tp_as_sequence*/
349 0, /*tp_as_mapping*/
350 0, /*tp_hash*/
351};
352
353/* Wrapper for panel_above(NULL). This function returns the bottom
354 panel of the stack, so it's renamed to bottom_panel().
355 panel.above() *requires* a panel object in the first place which
356 may be undesirable. */
357static PyObject *
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000358PyCurses_bottom_panel(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000359{
360 PANEL *pan;
361 PyCursesPanelObject *po;
362
363 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000364
365 pan = panel_above(NULL);
366
Andrew M. Kuchlingae89af92001-01-19 15:35:26 +0000367 if (pan == NULL) { /* valid output, it means
368 there's no panel at all */
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000369 Py_INCREF(Py_None);
370 return Py_None;
371 }
372 po = find_po(pan);
373 if (po == NULL) {
374 PyErr_SetString(PyExc_RuntimeError,
375 "panel_above: can't find Panel Object");
376 return NULL;
377 }
378 Py_INCREF(po);
379 return (PyObject *)po;
380}
381
382static PyObject *
383PyCurses_new_panel(PyObject *self, PyObject *args)
384{
385 PyCursesWindowObject *win;
386 PANEL *pan;
387
Fred Drake4e36d582000-12-23 05:46:23 +0000388 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
389 return NULL;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000390 pan = new_panel(win->win);
391 if (pan == NULL) {
392 PyErr_SetString(PyCursesError, catchall_NULL);
393 return NULL;
394 }
395 return (PyObject *)PyCursesPanel_New(pan, win);
396}
397
398
399/* Wrapper for panel_below(NULL). This function returns the top panel
400 of the stack, so it's renamed to top_panel(). panel.below()
401 *requires* a panel object in the first place which may be
402 undesirable. */
403static PyObject *
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000404PyCurses_top_panel(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000405{
406 PANEL *pan;
407 PyCursesPanelObject *po;
408
409 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000410
411 pan = panel_below(NULL);
412
413 if (pan == NULL) { /* valid output, it means
414 there's no panel at all */
415 Py_INCREF(Py_None);
416 return Py_None;
417 }
418 po = find_po(pan);
419 if (po == NULL) {
420 PyErr_SetString(PyExc_RuntimeError,
421 "panel_below: can't find Panel Object");
422 return NULL;
423 }
424 Py_INCREF(po);
425 return (PyObject *)po;
426}
427
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000428static PyObject *PyCurses_update_panels(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000429{
430 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000431 update_panels();
432 Py_INCREF(Py_None);
433 return Py_None;
434}
435
436
437/* List of functions defined in the module */
438
439static PyMethodDef PyCurses_methods[] = {
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000440 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS},
441 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
442 {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS},
443 {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000444 {NULL, NULL} /* sentinel */
445};
446
447/* Initialization function for the module */
448
Guido van Rossuma120ffc2001-01-22 15:29:14 +0000449DL_EXPORT(void)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000450init_curses_panel(void)
451{
452 PyObject *m, *d, *v;
453
Fred Drake2174f802001-01-27 18:58:04 +0000454 /* Initialize object type */
455 PyCursesPanel_Type.ob_type = &PyType_Type;
Guido van Rossuma120ffc2001-01-22 15:29:14 +0000456
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000457 import_curses();
Fred Drake2174f802001-01-27 18:58:04 +0000458
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000459 /* Create the module and add the functions */
460 m = Py_InitModule("_curses_panel", PyCurses_methods);
461 d = PyModule_GetDict(m);
462
463 /* For exception _curses_panel.error */
464 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
465 PyDict_SetItemString(d, "error", PyCursesError);
466
467 /* Make the version available */
468 v = PyString_FromString(PyCursesVersion);
469 PyDict_SetItemString(d, "version", v);
470 PyDict_SetItemString(d, "__version__", v);
471 Py_DECREF(v);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000472}