blob: 08c5f09ade1930928492a0667addf8e705ff29c3 [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},
Neal Norwitz01b26942002-03-31 14:55:17 +0000316 {"replace", (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS},
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000317 {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr, METH_O},
318 {"show", (PyCFunction)PyCursesPanel_show_panel, METH_NOARGS},
319 {"top", (PyCFunction)PyCursesPanel_top_panel, METH_NOARGS},
320 {"userptr", (PyCFunction)PyCursesPanel_userptr, METH_NOARGS},
321 {"window", (PyCFunction)PyCursesPanel_window, METH_NOARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000322 {NULL, NULL} /* sentinel */
323};
324
325static PyObject *
326PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name)
327{
328 return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name);
329}
330
331/* -------------------------------------------------------*/
332
333PyTypeObject PyCursesPanel_Type = {
Guido van Rossuma120ffc2001-01-22 15:29:14 +0000334 PyObject_HEAD_INIT(NULL)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000335 0, /*ob_size*/
Guido van Rossum14648392001-12-08 18:02:58 +0000336 "_curses_panel.curses panel", /*tp_name*/
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000337 sizeof(PyCursesPanelObject), /*tp_basicsize*/
338 0, /*tp_itemsize*/
339 /* methods */
340 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
341 0, /*tp_print*/
342 (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/
343 (setattrfunc)0, /*tp_setattr*/
344 0, /*tp_compare*/
345 0, /*tp_repr*/
346 0, /*tp_as_number*/
347 0, /*tp_as_sequence*/
348 0, /*tp_as_mapping*/
349 0, /*tp_hash*/
350};
351
352/* Wrapper for panel_above(NULL). This function returns the bottom
353 panel of the stack, so it's renamed to bottom_panel().
354 panel.above() *requires* a panel object in the first place which
355 may be undesirable. */
356static PyObject *
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000357PyCurses_bottom_panel(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000358{
359 PANEL *pan;
360 PyCursesPanelObject *po;
361
362 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000363
364 pan = panel_above(NULL);
365
Andrew M. Kuchlingae89af92001-01-19 15:35:26 +0000366 if (pan == NULL) { /* valid output, it means
367 there's no panel at all */
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000368 Py_INCREF(Py_None);
369 return Py_None;
370 }
371 po = find_po(pan);
372 if (po == NULL) {
373 PyErr_SetString(PyExc_RuntimeError,
374 "panel_above: can't find Panel Object");
375 return NULL;
376 }
377 Py_INCREF(po);
378 return (PyObject *)po;
379}
380
381static PyObject *
382PyCurses_new_panel(PyObject *self, PyObject *args)
383{
384 PyCursesWindowObject *win;
385 PANEL *pan;
386
Fred Drake4e36d582000-12-23 05:46:23 +0000387 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
388 return NULL;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000389 pan = new_panel(win->win);
390 if (pan == NULL) {
391 PyErr_SetString(PyCursesError, catchall_NULL);
392 return NULL;
393 }
394 return (PyObject *)PyCursesPanel_New(pan, win);
395}
396
397
398/* Wrapper for panel_below(NULL). This function returns the top panel
399 of the stack, so it's renamed to top_panel(). panel.below()
400 *requires* a panel object in the first place which may be
401 undesirable. */
402static PyObject *
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000403PyCurses_top_panel(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000404{
405 PANEL *pan;
406 PyCursesPanelObject *po;
407
408 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000409
410 pan = panel_below(NULL);
411
412 if (pan == NULL) { /* valid output, it means
413 there's no panel at all */
414 Py_INCREF(Py_None);
415 return Py_None;
416 }
417 po = find_po(pan);
418 if (po == NULL) {
419 PyErr_SetString(PyExc_RuntimeError,
420 "panel_below: can't find Panel Object");
421 return NULL;
422 }
423 Py_INCREF(po);
424 return (PyObject *)po;
425}
426
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000427static PyObject *PyCurses_update_panels(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000428{
429 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000430 update_panels();
431 Py_INCREF(Py_None);
432 return Py_None;
433}
434
435
436/* List of functions defined in the module */
437
438static PyMethodDef PyCurses_methods[] = {
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000439 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS},
440 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
441 {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS},
442 {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000443 {NULL, NULL} /* sentinel */
444};
445
446/* Initialization function for the module */
447
Mark Hammondfe51c6d2002-08-02 02:27:13 +0000448PyMODINIT_FUNC
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000449init_curses_panel(void)
450{
451 PyObject *m, *d, *v;
452
Fred Drake2174f802001-01-27 18:58:04 +0000453 /* Initialize object type */
454 PyCursesPanel_Type.ob_type = &PyType_Type;
Guido van Rossuma120ffc2001-01-22 15:29:14 +0000455
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000456 import_curses();
Fred Drake2174f802001-01-27 18:58:04 +0000457
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000458 /* Create the module and add the functions */
459 m = Py_InitModule("_curses_panel", PyCurses_methods);
460 d = PyModule_GetDict(m);
461
462 /* For exception _curses_panel.error */
463 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
464 PyDict_SetItemString(d, "error", PyCursesError);
465
466 /* Make the version available */
467 v = PyString_FromString(PyCursesVersion);
468 PyDict_SetItemString(d, "version", v);
469 PyDict_SetItemString(d, "__version__", v);
470 Py_DECREF(v);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000471}