blob: 02e064fe7c867437dbdd1a246366e00b6ab4e432 [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
Christian Heimes90aa7642007-12-19 02:45:37 +000059#define PyCursesPanel_Check(v) (Py_TYPE(v) == &PyCursesPanel_Type)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +000060
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 }
Thomas Wouters47f003d2006-03-07 13:38:14 +0000114 while (temp->next == NULL || temp->next->po != po) {
115 if (temp->next == NULL) {
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000116 PyErr_SetString(PyExc_RuntimeError,
117 "remove_lop: can't find Panel Object");
Thomas Wouters47f003d2006-03-07 13:38:14 +0000118 return;
119 }
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000120 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 */
129static PyCursesPanelObject *
130find_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) \
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000146static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
147{ return PyCursesCheckERR(X(self->pan), # X); }
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000148
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000149#define Panel_NoArgTrueFalseFunction(X) \
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000150static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000151{ \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000152 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) \
Fred Drake4e36d582000-12-23 05:46:23 +0000156static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000157{ \
158 TYPE arg1, arg2; \
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000159 if (!PyArg_ParseTuple(args, PARSESTR, &arg1, &arg2)) return NULL; \
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000160 return PyCursesCheckERR(X(self->pan, arg1, arg2), # X); }
161
162/* ------------- PANEL routines --------------- */
163
164Panel_NoArgNoReturnFunction(bottom_panel)
165Panel_NoArgNoReturnFunction(hide_panel)
166Panel_NoArgNoReturnFunction(show_panel)
167Panel_NoArgNoReturnFunction(top_panel)
168Panel_NoArgTrueFalseFunction(panel_hidden)
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000169Panel_TwoArgNoReturnFunction(move_panel, int, "ii;y,x")
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000170
171/* Allocation and deallocation of Panel Objects */
172
173static PyObject *
174PyCursesPanel_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
190static void
191PyCursesPanel_Dealloc(PyCursesPanelObject *po)
192{
193 (void)del_panel(po->pan);
194 Py_DECREF(po->wo);
195 remove_lop(po);
Michael W. Hudsoncf6bfe42002-01-30 15:47:34 +0000196 PyObject_DEL(po);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000197}
198
199/* panel_above(NULL) returns the bottom panel in the stack. To get
200 this behaviour we use curses.panel.bottom_panel(). */
201static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000202PyCursesPanel_above(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000203{
204 PANEL *pan;
205 PyCursesPanelObject *po;
206
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000207 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
Andrew M. Kuchlingae89af92001-01-19 15:35:26 +0000225 this behaviour we use curses.panel.top_panel(). */
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000226static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000227PyCursesPanel_below(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000228{
229 PANEL *pan;
230 PyCursesPanelObject *po;
231
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000232 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
249static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000250PyCursesPanel_window(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000251{
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000252 Py_INCREF(self->wo);
253 return (PyObject *)self->wo;
254}
255
256static PyObject *
257PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args)
258{
259 PyCursesPanelObject *po;
260 PyCursesWindowObject *temp;
261 int rtn;
262
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000263 if (PyTuple_Size(args) != 1) {
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000264 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
290static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000291PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000292{
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000293 Py_INCREF(obj);
Martin v. Löwisa38d9162001-10-13 08:50:10 +0000294 return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj),
Fred Drake4e36d582000-12-23 05:46:23 +0000295 "set_panel_userptr");
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000296}
297
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000298static PyObject *
299PyCursesPanel_userptr(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000300{
301 PyObject *obj;
302 PyCursesInitialised;
Fred Drake4e36d582000-12-23 05:46:23 +0000303 obj = (PyObject *) panel_userptr(self->pan);
Neal Norwitz5e3d8622006-01-09 06:24:35 +0000304 if (obj == NULL) {
305 PyErr_SetString(PyCursesError, "no userptr set");
306 return NULL;
307 }
308
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000309 Py_INCREF(obj);
310 return obj;
311}
312
313
314/* Module interface */
315
316static PyMethodDef PyCursesPanel_Methods[] = {
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000317 {"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},
Neal Norwitz01b26942002-03-31 14:55:17 +0000323 {"replace", (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS},
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000324 {"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},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000329 {NULL, NULL} /* sentinel */
330};
331
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000332/* -------------------------------------------------------*/
333
334PyTypeObject PyCursesPanel_Type = {
Martin v. Löwis9f2e3462007-07-21 17:22:18 +0000335 PyVarObject_HEAD_INIT(NULL, 0)
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*/
Amaury Forgeot d'Arc1f900f12008-07-02 22:38:47 +0000342 0, /*tp_getattr*/
343 0, /*tp_setattr*/
Mark Dickinsone94c6792009-02-02 20:36:42 +0000344 0, /*tp_reserved*/
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000345 0, /*tp_repr*/
346 0, /*tp_as_number*/
347 0, /*tp_as_sequence*/
348 0, /*tp_as_mapping*/
349 0, /*tp_hash*/
Amaury Forgeot d'Arc1f900f12008-07-02 22:38:47 +0000350 0, /*tp_call*/
351 0, /*tp_str*/
352 0, /*tp_getattro*/
353 0, /*tp_setattro*/
354 0, /*tp_as_buffer*/
355 Py_TPFLAGS_DEFAULT, /*tp_flags*/
356 0, /*tp_doc*/
357 0, /*tp_traverse*/
358 0, /*tp_clear*/
359 0, /*tp_richcompare*/
360 0, /*tp_weaklistoffset*/
361 0, /*tp_iter*/
362 0, /*tp_iternext*/
363 PyCursesPanel_Methods, /*tp_methods*/
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000364};
365
366/* Wrapper for panel_above(NULL). This function returns the bottom
367 panel of the stack, so it's renamed to bottom_panel().
368 panel.above() *requires* a panel object in the first place which
369 may be undesirable. */
370static PyObject *
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000371PyCurses_bottom_panel(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000372{
373 PANEL *pan;
374 PyCursesPanelObject *po;
375
376 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000377
378 pan = panel_above(NULL);
379
Andrew M. Kuchlingae89af92001-01-19 15:35:26 +0000380 if (pan == NULL) { /* valid output, it means
381 there's no panel at all */
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000382 Py_INCREF(Py_None);
383 return Py_None;
384 }
385 po = find_po(pan);
386 if (po == NULL) {
387 PyErr_SetString(PyExc_RuntimeError,
388 "panel_above: can't find Panel Object");
389 return NULL;
390 }
391 Py_INCREF(po);
392 return (PyObject *)po;
393}
394
395static PyObject *
396PyCurses_new_panel(PyObject *self, PyObject *args)
397{
398 PyCursesWindowObject *win;
399 PANEL *pan;
400
Fred Drake4e36d582000-12-23 05:46:23 +0000401 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
402 return NULL;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000403 pan = new_panel(win->win);
404 if (pan == NULL) {
405 PyErr_SetString(PyCursesError, catchall_NULL);
406 return NULL;
407 }
408 return (PyObject *)PyCursesPanel_New(pan, win);
409}
410
411
412/* Wrapper for panel_below(NULL). This function returns the top panel
413 of the stack, so it's renamed to top_panel(). panel.below()
414 *requires* a panel object in the first place which may be
415 undesirable. */
416static PyObject *
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000417PyCurses_top_panel(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000418{
419 PANEL *pan;
420 PyCursesPanelObject *po;
421
422 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000423
424 pan = panel_below(NULL);
425
426 if (pan == NULL) { /* valid output, it means
427 there's no panel at all */
428 Py_INCREF(Py_None);
429 return Py_None;
430 }
431 po = find_po(pan);
432 if (po == NULL) {
433 PyErr_SetString(PyExc_RuntimeError,
434 "panel_below: can't find Panel Object");
435 return NULL;
436 }
437 Py_INCREF(po);
438 return (PyObject *)po;
439}
440
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000441static PyObject *PyCurses_update_panels(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000442{
443 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000444 update_panels();
445 Py_INCREF(Py_None);
446 return Py_None;
447}
448
449
450/* List of functions defined in the module */
451
452static PyMethodDef PyCurses_methods[] = {
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000453 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS},
454 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
455 {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS},
456 {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000457 {NULL, NULL} /* sentinel */
458};
459
460/* Initialization function for the module */
461
Martin v. Löwis1a214512008-06-11 05:26:20 +0000462
463static struct PyModuleDef _curses_panelmodule = {
464 PyModuleDef_HEAD_INIT,
465 "_curses_panel",
466 NULL,
467 -1,
468 PyCurses_methods,
469 NULL,
470 NULL,
471 NULL,
472 NULL
473};
474
Mark Hammondfe51c6d2002-08-02 02:27:13 +0000475PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +0000476PyInit__curses_panel(void)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000477{
478 PyObject *m, *d, *v;
479
Fred Drake2174f802001-01-27 18:58:04 +0000480 /* Initialize object type */
Amaury Forgeot d'Arc1f900f12008-07-02 22:38:47 +0000481 if (PyType_Ready(&PyCursesPanel_Type) < 0)
482 return NULL;
Guido van Rossuma120ffc2001-01-22 15:29:14 +0000483
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000484 import_curses();
Fred Drake2174f802001-01-27 18:58:04 +0000485
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000486 /* Create the module and add the functions */
Martin v. Löwis1a214512008-06-11 05:26:20 +0000487 m = PyModule_Create(&_curses_panelmodule);
Neal Norwitz1ac754f2006-01-19 06:09:39 +0000488 if (m == NULL)
Martin v. Löwis1a214512008-06-11 05:26:20 +0000489 return NULL;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000490 d = PyModule_GetDict(m);
491
492 /* For exception _curses_panel.error */
493 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
494 PyDict_SetItemString(d, "error", PyCursesError);
495
496 /* Make the version available */
Neal Norwitz53cbdaa2007-08-23 21:42:55 +0000497 v = PyUnicode_FromString(PyCursesVersion);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000498 PyDict_SetItemString(d, "version", v);
499 PyDict_SetItemString(d, "__version__", v);
500 Py_DECREF(v);
Martin v. Löwis1a214512008-06-11 05:26:20 +0000501 return m;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000502}