blob: ff1276d0f48bf385a34140c26358a9901fba99c6 [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;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000181 if (insert_lop(po) < 0) {
Victor Stinnera7612272010-03-03 21:56:53 +0000182 po->wo = NULL;
183 Py_DECREF(po);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000184 return NULL;
185 }
Victor Stinnera7612272010-03-03 21:56:53 +0000186 po->wo = wo;
187 Py_INCREF(wo);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000188 return (PyObject *)po;
189}
190
191static void
192PyCursesPanel_Dealloc(PyCursesPanelObject *po)
193{
194 (void)del_panel(po->pan);
Victor Stinnera7612272010-03-03 21:56:53 +0000195 if (po->wo != NULL) {
196 Py_DECREF(po->wo);
197 remove_lop(po);
198 }
Michael W. Hudsoncf6bfe42002-01-30 15:47:34 +0000199 PyObject_DEL(po);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000200}
201
202/* panel_above(NULL) returns the bottom panel in the stack. To get
203 this behaviour we use curses.panel.bottom_panel(). */
204static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000205PyCursesPanel_above(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000206{
207 PANEL *pan;
208 PyCursesPanelObject *po;
209
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000210 pan = panel_above(self->pan);
211
212 if (pan == NULL) { /* valid output, it means the calling panel
213 is on top of the stack */
214 Py_INCREF(Py_None);
215 return Py_None;
216 }
217 po = find_po(pan);
218 if (po == NULL) {
219 PyErr_SetString(PyExc_RuntimeError,
220 "panel_above: can't find Panel Object");
221 return NULL;
222 }
223 Py_INCREF(po);
224 return (PyObject *)po;
225}
226
227/* panel_below(NULL) returns the top panel in the stack. To get
Andrew M. Kuchlingae89af92001-01-19 15:35:26 +0000228 this behaviour we use curses.panel.top_panel(). */
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000229static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000230PyCursesPanel_below(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000231{
232 PANEL *pan;
233 PyCursesPanelObject *po;
234
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000235 pan = panel_below(self->pan);
236
237 if (pan == NULL) { /* valid output, it means the calling panel
238 is on the bottom of the stack */
239 Py_INCREF(Py_None);
240 return Py_None;
241 }
242 po = find_po(pan);
243 if (po == NULL) {
244 PyErr_SetString(PyExc_RuntimeError,
245 "panel_below: can't find Panel Object");
246 return NULL;
247 }
248 Py_INCREF(po);
249 return (PyObject *)po;
250}
251
252static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000253PyCursesPanel_window(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000254{
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000255 Py_INCREF(self->wo);
256 return (PyObject *)self->wo;
257}
258
259static PyObject *
260PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args)
261{
262 PyCursesPanelObject *po;
263 PyCursesWindowObject *temp;
264 int rtn;
265
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000266 if (PyTuple_Size(args) != 1) {
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000267 PyErr_SetString(PyExc_TypeError, "replace requires one argument");
268 return NULL;
269 }
270 if (!PyArg_ParseTuple(args, "O!;window object",
271 &PyCursesWindow_Type, &temp))
272 return NULL;
273
274 po = find_po(self->pan);
275 if (po == NULL) {
276 PyErr_SetString(PyExc_RuntimeError,
277 "replace_panel: can't find Panel Object");
278 return NULL;
279 }
280
281 rtn = replace_panel(self->pan, temp->win);
282 if (rtn == ERR) {
283 PyErr_SetString(PyCursesError, "replace_panel() returned ERR");
284 return NULL;
285 }
286 Py_DECREF(po->wo);
287 po->wo = temp;
288 Py_INCREF(po->wo);
289 Py_INCREF(Py_None);
290 return Py_None;
291}
292
293static PyObject *
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000294PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000295{
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000296 Py_INCREF(obj);
Martin v. Löwisa38d9162001-10-13 08:50:10 +0000297 return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj),
Fred Drake4e36d582000-12-23 05:46:23 +0000298 "set_panel_userptr");
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000299}
300
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000301static PyObject *
302PyCursesPanel_userptr(PyCursesPanelObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000303{
304 PyObject *obj;
305 PyCursesInitialised;
Fred Drake4e36d582000-12-23 05:46:23 +0000306 obj = (PyObject *) panel_userptr(self->pan);
Neal Norwitz5e3d8622006-01-09 06:24:35 +0000307 if (obj == NULL) {
308 PyErr_SetString(PyCursesError, "no userptr set");
309 return NULL;
310 }
311
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000312 Py_INCREF(obj);
313 return obj;
314}
315
316
317/* Module interface */
318
319static PyMethodDef PyCursesPanel_Methods[] = {
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000320 {"above", (PyCFunction)PyCursesPanel_above, METH_NOARGS},
321 {"below", (PyCFunction)PyCursesPanel_below, METH_NOARGS},
322 {"bottom", (PyCFunction)PyCursesPanel_bottom_panel, METH_NOARGS},
323 {"hidden", (PyCFunction)PyCursesPanel_panel_hidden, METH_NOARGS},
324 {"hide", (PyCFunction)PyCursesPanel_hide_panel, METH_NOARGS},
325 {"move", (PyCFunction)PyCursesPanel_move_panel, METH_VARARGS},
Neal Norwitz01b26942002-03-31 14:55:17 +0000326 {"replace", (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS},
Martin v. Löwisc0e16712002-01-17 23:08:27 +0000327 {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr, METH_O},
328 {"show", (PyCFunction)PyCursesPanel_show_panel, METH_NOARGS},
329 {"top", (PyCFunction)PyCursesPanel_top_panel, METH_NOARGS},
330 {"userptr", (PyCFunction)PyCursesPanel_userptr, METH_NOARGS},
331 {"window", (PyCFunction)PyCursesPanel_window, METH_NOARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000332 {NULL, NULL} /* sentinel */
333};
334
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000335/* -------------------------------------------------------*/
336
337PyTypeObject PyCursesPanel_Type = {
Martin v. Löwis9f2e3462007-07-21 17:22:18 +0000338 PyVarObject_HEAD_INIT(NULL, 0)
Guido van Rossum14648392001-12-08 18:02:58 +0000339 "_curses_panel.curses panel", /*tp_name*/
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000340 sizeof(PyCursesPanelObject), /*tp_basicsize*/
341 0, /*tp_itemsize*/
342 /* methods */
343 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
344 0, /*tp_print*/
Amaury Forgeot d'Arc1f900f12008-07-02 22:38:47 +0000345 0, /*tp_getattr*/
346 0, /*tp_setattr*/
Mark Dickinsone94c6792009-02-02 20:36:42 +0000347 0, /*tp_reserved*/
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000348 0, /*tp_repr*/
349 0, /*tp_as_number*/
350 0, /*tp_as_sequence*/
351 0, /*tp_as_mapping*/
352 0, /*tp_hash*/
Amaury Forgeot d'Arc1f900f12008-07-02 22:38:47 +0000353 0, /*tp_call*/
354 0, /*tp_str*/
355 0, /*tp_getattro*/
356 0, /*tp_setattro*/
357 0, /*tp_as_buffer*/
358 Py_TPFLAGS_DEFAULT, /*tp_flags*/
359 0, /*tp_doc*/
360 0, /*tp_traverse*/
361 0, /*tp_clear*/
362 0, /*tp_richcompare*/
363 0, /*tp_weaklistoffset*/
364 0, /*tp_iter*/
365 0, /*tp_iternext*/
366 PyCursesPanel_Methods, /*tp_methods*/
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000367};
368
369/* Wrapper for panel_above(NULL). This function returns the bottom
370 panel of the stack, so it's renamed to bottom_panel().
371 panel.above() *requires* a panel object in the first place which
372 may be undesirable. */
373static PyObject *
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000374PyCurses_bottom_panel(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000375{
376 PANEL *pan;
377 PyCursesPanelObject *po;
378
379 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000380
381 pan = panel_above(NULL);
382
Andrew M. Kuchlingae89af92001-01-19 15:35:26 +0000383 if (pan == NULL) { /* valid output, it means
384 there's no panel at all */
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000385 Py_INCREF(Py_None);
386 return Py_None;
387 }
388 po = find_po(pan);
389 if (po == NULL) {
390 PyErr_SetString(PyExc_RuntimeError,
391 "panel_above: can't find Panel Object");
392 return NULL;
393 }
394 Py_INCREF(po);
395 return (PyObject *)po;
396}
397
398static PyObject *
399PyCurses_new_panel(PyObject *self, PyObject *args)
400{
401 PyCursesWindowObject *win;
402 PANEL *pan;
403
Fred Drake4e36d582000-12-23 05:46:23 +0000404 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
405 return NULL;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000406 pan = new_panel(win->win);
407 if (pan == NULL) {
408 PyErr_SetString(PyCursesError, catchall_NULL);
409 return NULL;
410 }
411 return (PyObject *)PyCursesPanel_New(pan, win);
412}
413
414
415/* Wrapper for panel_below(NULL). This function returns the top panel
416 of the stack, so it's renamed to top_panel(). panel.below()
417 *requires* a panel object in the first place which may be
418 undesirable. */
419static PyObject *
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000420PyCurses_top_panel(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000421{
422 PANEL *pan;
423 PyCursesPanelObject *po;
424
425 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000426
427 pan = panel_below(NULL);
428
429 if (pan == NULL) { /* valid output, it means
430 there's no panel at all */
431 Py_INCREF(Py_None);
432 return Py_None;
433 }
434 po = find_po(pan);
435 if (po == NULL) {
436 PyErr_SetString(PyExc_RuntimeError,
437 "panel_below: can't find Panel Object");
438 return NULL;
439 }
440 Py_INCREF(po);
441 return (PyObject *)po;
442}
443
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000444static PyObject *PyCurses_update_panels(PyObject *self)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000445{
446 PyCursesInitialised;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000447 update_panels();
448 Py_INCREF(Py_None);
449 return Py_None;
450}
451
452
453/* List of functions defined in the module */
454
455static PyMethodDef PyCurses_methods[] = {
Neal Norwitz3a6f9782002-03-25 20:46:46 +0000456 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS},
457 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
458 {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS},
459 {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS},
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000460 {NULL, NULL} /* sentinel */
461};
462
463/* Initialization function for the module */
464
Martin v. Löwis1a214512008-06-11 05:26:20 +0000465
466static struct PyModuleDef _curses_panelmodule = {
467 PyModuleDef_HEAD_INIT,
468 "_curses_panel",
469 NULL,
470 -1,
471 PyCurses_methods,
472 NULL,
473 NULL,
474 NULL,
475 NULL
476};
477
Mark Hammondfe51c6d2002-08-02 02:27:13 +0000478PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +0000479PyInit__curses_panel(void)
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000480{
481 PyObject *m, *d, *v;
482
Fred Drake2174f802001-01-27 18:58:04 +0000483 /* Initialize object type */
Amaury Forgeot d'Arc1f900f12008-07-02 22:38:47 +0000484 if (PyType_Ready(&PyCursesPanel_Type) < 0)
485 return NULL;
Guido van Rossuma120ffc2001-01-22 15:29:14 +0000486
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000487 import_curses();
Fred Drake2174f802001-01-27 18:58:04 +0000488
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000489 /* Create the module and add the functions */
Martin v. Löwis1a214512008-06-11 05:26:20 +0000490 m = PyModule_Create(&_curses_panelmodule);
Neal Norwitz1ac754f2006-01-19 06:09:39 +0000491 if (m == NULL)
Martin v. Löwis1a214512008-06-11 05:26:20 +0000492 return NULL;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000493 d = PyModule_GetDict(m);
494
495 /* For exception _curses_panel.error */
496 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
497 PyDict_SetItemString(d, "error", PyCursesError);
498
499 /* Make the version available */
Neal Norwitz53cbdaa2007-08-23 21:42:55 +0000500 v = PyUnicode_FromString(PyCursesVersion);
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000501 PyDict_SetItemString(d, "version", v);
502 PyDict_SetItemString(d, "__version__", v);
503 Py_DECREF(v);
Martin v. Löwis1a214512008-06-11 05:26:20 +0000504 return m;
Andrew M. Kuchling7b59ed22000-12-22 21:54:12 +0000505}