blob: b5f30cb85efcbbe8156fef092ac3384a1e261f73 [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);
Neal Norwitz5e3d8622006-01-09 06:24:35 +0000302 if (obj == NULL) {
303 PyErr_SetString(PyCursesError, "no userptr set");
304 return NULL;
305 }
306
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000307 Py_INCREF(obj);
308 return obj;
309}
310
311
312/* Module interface */
313
314static PyMethodDef PyCursesPanel_Methods[] = {
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000315 {"above", (PyCFunction)PyCursesPanel_above, METH_NOARGS},
316 {"below", (PyCFunction)PyCursesPanel_below, METH_NOARGS},
317 {"bottom", (PyCFunction)PyCursesPanel_bottom_panel, METH_NOARGS},
318 {"hidden", (PyCFunction)PyCursesPanel_panel_hidden, METH_NOARGS},
319 {"hide", (PyCFunction)PyCursesPanel_hide_panel, METH_NOARGS},
320 {"move", (PyCFunction)PyCursesPanel_move_panel, METH_VARARGS},
Neal Norwitz01b26942002-03-31 14:55:17 +0000321 {"replace", (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS},
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000322 {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr, METH_O},
323 {"show", (PyCFunction)PyCursesPanel_show_panel, METH_NOARGS},
324 {"top", (PyCFunction)PyCursesPanel_top_panel, METH_NOARGS},
325 {"userptr", (PyCFunction)PyCursesPanel_userptr, METH_NOARGS},
326 {"window", (PyCFunction)PyCursesPanel_window, METH_NOARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000327 {NULL, NULL} /* sentinel */
328};
329
330static PyObject *
331PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name)
332{
333 return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name);
334}
335
336/* -------------------------------------------------------*/
337
338PyTypeObject PyCursesPanel_Type = {
Guido van Rossuma120ffc2001-01-22 15:29:14 +0000339 PyObject_HEAD_INIT(NULL)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000340 0, /*ob_size*/
Guido van Rossum14648392001-12-08 18:02:58 +0000341 "_curses_panel.curses panel", /*tp_name*/
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000342 sizeof(PyCursesPanelObject), /*tp_basicsize*/
343 0, /*tp_itemsize*/
344 /* methods */
345 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
346 0, /*tp_print*/
347 (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/
348 (setattrfunc)0, /*tp_setattr*/
349 0, /*tp_compare*/
350 0, /*tp_repr*/
351 0, /*tp_as_number*/
352 0, /*tp_as_sequence*/
353 0, /*tp_as_mapping*/
354 0, /*tp_hash*/
355};
356
357/* Wrapper for panel_above(NULL). This function returns the bottom
358 panel of the stack, so it's renamed to bottom_panel().
359 panel.above() *requires* a panel object in the first place which
360 may be undesirable. */
361static PyObject *
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000362PyCurses_bottom_panel(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000363{
364 PANEL *pan;
365 PyCursesPanelObject *po;
366
367 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000368
369 pan = panel_above(NULL);
370
Andrew M. Kuchlingae89af92001-01-19 15:35:26 +0000371 if (pan == NULL) { /* valid output, it means
372 there's no panel at all */
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000373 Py_INCREF(Py_None);
374 return Py_None;
375 }
376 po = find_po(pan);
377 if (po == NULL) {
378 PyErr_SetString(PyExc_RuntimeError,
379 "panel_above: can't find Panel Object");
380 return NULL;
381 }
382 Py_INCREF(po);
383 return (PyObject *)po;
384}
385
386static PyObject *
387PyCurses_new_panel(PyObject *self, PyObject *args)
388{
389 PyCursesWindowObject *win;
390 PANEL *pan;
391
Fred Drake4e36d582000-12-23 05:46:23 +0000392 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
393 return NULL;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000394 pan = new_panel(win->win);
395 if (pan == NULL) {
396 PyErr_SetString(PyCursesError, catchall_NULL);
397 return NULL;
398 }
399 return (PyObject *)PyCursesPanel_New(pan, win);
400}
401
402
403/* Wrapper for panel_below(NULL). This function returns the top panel
404 of the stack, so it's renamed to top_panel(). panel.below()
405 *requires* a panel object in the first place which may be
406 undesirable. */
407static PyObject *
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000408PyCurses_top_panel(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000409{
410 PANEL *pan;
411 PyCursesPanelObject *po;
412
413 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000414
415 pan = panel_below(NULL);
416
417 if (pan == NULL) { /* valid output, it means
418 there's no panel at all */
419 Py_INCREF(Py_None);
420 return Py_None;
421 }
422 po = find_po(pan);
423 if (po == NULL) {
424 PyErr_SetString(PyExc_RuntimeError,
425 "panel_below: can't find Panel Object");
426 return NULL;
427 }
428 Py_INCREF(po);
429 return (PyObject *)po;
430}
431
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000432static PyObject *PyCurses_update_panels(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000433{
434 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000435 update_panels();
436 Py_INCREF(Py_None);
437 return Py_None;
438}
439
440
441/* List of functions defined in the module */
442
443static PyMethodDef PyCurses_methods[] = {
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000444 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS},
445 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
446 {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS},
447 {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000448 {NULL, NULL} /* sentinel */
449};
450
451/* Initialization function for the module */
452
Mark Hammondfe51c6d2002-08-02 02:27:13 +0000453PyMODINIT_FUNC
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000454init_curses_panel(void)
455{
456 PyObject *m, *d, *v;
457
Fred Drake2174f802001-01-27 18:58:04 +0000458 /* Initialize object type */
459 PyCursesPanel_Type.ob_type = &PyType_Type;
Guido van Rossuma120ffc2001-01-22 15:29:14 +0000460
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000461 import_curses();
Fred Drake2174f802001-01-27 18:58:04 +0000462
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000463 /* Create the module and add the functions */
464 m = Py_InitModule("_curses_panel", PyCurses_methods);
465 d = PyModule_GetDict(m);
466
467 /* For exception _curses_panel.error */
468 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
469 PyDict_SetItemString(d, "error", PyCursesError);
470
471 /* Make the version available */
472 v = PyString_FromString(PyCursesVersion);
473 PyDict_SetItemString(d, "version", v);
474 PyDict_SetItemString(d, "__version__", v);
475 Py_DECREF(v);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000476}