source: python/trunk/Modules/_curses_panel.c

Last change on this file was 391, checked in by dmik, 11 years ago

python: Merge vendor 2.7.6 to trunk.

  • Property svn:eol-style set to native
File size: 13.3 KB
Line 
1/*
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) (Py_TYPE(v) == &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 == NULL || temp->next->po != po) {
115 if (temp->next == NULL) {
116 PyErr_SetString(PyExc_RuntimeError,
117 "remove_lop: can't find Panel Object");
118 return;
119 }
120 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) \
146static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
147{ return PyCursesCheckERR(X(self->pan), # X); }
148
149#define Panel_NoArgTrueFalseFunction(X) \
150static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
151{ \
152 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) \
156static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
157{ \
158 TYPE arg1, arg2; \
159 if (!PyArg_ParseTuple(args, PARSESTR, &arg1, &arg2)) return NULL; \
160 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)
169Panel_TwoArgNoReturnFunction(move_panel, int, "ii;y,x")
170
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 if (insert_lop(po) < 0) {
182 po->wo = NULL;
183 Py_DECREF(po);
184 return NULL;
185 }
186 po->wo = wo;
187 Py_INCREF(wo);
188 return (PyObject *)po;
189}
190
191static void
192PyCursesPanel_Dealloc(PyCursesPanelObject *po)
193{
194 (void)del_panel(po->pan);
195 if (po->wo != NULL) {
196 Py_DECREF(po->wo);
197 remove_lop(po);
198 }
199 PyObject_DEL(po);
200}
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 *
205PyCursesPanel_above(PyCursesPanelObject *self)
206{
207 PANEL *pan;
208 PyCursesPanelObject *po;
209
210 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
228 this behaviour we use curses.panel.top_panel(). */
229static PyObject *
230PyCursesPanel_below(PyCursesPanelObject *self)
231{
232 PANEL *pan;
233 PyCursesPanelObject *po;
234
235 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 *
253PyCursesPanel_window(PyCursesPanelObject *self)
254{
255 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
266 if (PyTuple_Size(args) != 1) {
267 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 *
294PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj)
295{
296 PyObject *oldobj;
297 int rc;
298 PyCursesInitialised;
299 Py_INCREF(obj);
300 oldobj = (PyObject *) panel_userptr(self->pan);
301 rc = set_panel_userptr(self->pan, (void*)obj);
302 if (rc == ERR) {
303 /* In case of an ncurses error, decref the new object again */
304 Py_DECREF(obj);
305 }
306 Py_XDECREF(oldobj);
307 return PyCursesCheckERR(rc, "set_panel_userptr");
308}
309
310static PyObject *
311PyCursesPanel_userptr(PyCursesPanelObject *self)
312{
313 PyObject *obj;
314 PyCursesInitialised;
315 obj = (PyObject *) panel_userptr(self->pan);
316 if (obj == NULL) {
317 PyErr_SetString(PyCursesError, "no userptr set");
318 return NULL;
319 }
320
321 Py_INCREF(obj);
322 return obj;
323}
324
325
326/* Module interface */
327
328static PyMethodDef PyCursesPanel_Methods[] = {
329 {"above", (PyCFunction)PyCursesPanel_above, METH_NOARGS},
330 {"below", (PyCFunction)PyCursesPanel_below, METH_NOARGS},
331 {"bottom", (PyCFunction)PyCursesPanel_bottom_panel, METH_NOARGS},
332 {"hidden", (PyCFunction)PyCursesPanel_panel_hidden, METH_NOARGS},
333 {"hide", (PyCFunction)PyCursesPanel_hide_panel, METH_NOARGS},
334 {"move", (PyCFunction)PyCursesPanel_move_panel, METH_VARARGS},
335 {"replace", (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS},
336 {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr, METH_O},
337 {"show", (PyCFunction)PyCursesPanel_show_panel, METH_NOARGS},
338 {"top", (PyCFunction)PyCursesPanel_top_panel, METH_NOARGS},
339 {"userptr", (PyCFunction)PyCursesPanel_userptr, METH_NOARGS},
340 {"window", (PyCFunction)PyCursesPanel_window, METH_NOARGS},
341 {NULL, NULL} /* sentinel */
342};
343
344static PyObject *
345PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name)
346{
347 return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name);
348}
349
350/* -------------------------------------------------------*/
351
352PyTypeObject PyCursesPanel_Type = {
353 PyVarObject_HEAD_INIT(NULL, 0)
354 "_curses_panel.curses panel", /*tp_name*/
355 sizeof(PyCursesPanelObject), /*tp_basicsize*/
356 0, /*tp_itemsize*/
357 /* methods */
358 (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
359 0, /*tp_print*/
360 (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/
361 (setattrfunc)0, /*tp_setattr*/
362 0, /*tp_compare*/
363 0, /*tp_repr*/
364 0, /*tp_as_number*/
365 0, /*tp_as_sequence*/
366 0, /*tp_as_mapping*/
367 0, /*tp_hash*/
368};
369
370/* Wrapper for panel_above(NULL). This function returns the bottom
371 panel of the stack, so it's renamed to bottom_panel().
372 panel.above() *requires* a panel object in the first place which
373 may be undesirable. */
374static PyObject *
375PyCurses_bottom_panel(PyObject *self)
376{
377 PANEL *pan;
378 PyCursesPanelObject *po;
379
380 PyCursesInitialised;
381
382 pan = panel_above(NULL);
383
384 if (pan == NULL) { /* valid output, it means
385 there's no panel at all */
386 Py_INCREF(Py_None);
387 return Py_None;
388 }
389 po = find_po(pan);
390 if (po == NULL) {
391 PyErr_SetString(PyExc_RuntimeError,
392 "panel_above: can't find Panel Object");
393 return NULL;
394 }
395 Py_INCREF(po);
396 return (PyObject *)po;
397}
398
399static PyObject *
400PyCurses_new_panel(PyObject *self, PyObject *args)
401{
402 PyCursesWindowObject *win;
403 PANEL *pan;
404
405 if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
406 return NULL;
407 pan = new_panel(win->win);
408 if (pan == NULL) {
409 PyErr_SetString(PyCursesError, catchall_NULL);
410 return NULL;
411 }
412 return (PyObject *)PyCursesPanel_New(pan, win);
413}
414
415
416/* Wrapper for panel_below(NULL). This function returns the top panel
417 of the stack, so it's renamed to top_panel(). panel.below()
418 *requires* a panel object in the first place which may be
419 undesirable. */
420static PyObject *
421PyCurses_top_panel(PyObject *self)
422{
423 PANEL *pan;
424 PyCursesPanelObject *po;
425
426 PyCursesInitialised;
427
428 pan = panel_below(NULL);
429
430 if (pan == NULL) { /* valid output, it means
431 there's no panel at all */
432 Py_INCREF(Py_None);
433 return Py_None;
434 }
435 po = find_po(pan);
436 if (po == NULL) {
437 PyErr_SetString(PyExc_RuntimeError,
438 "panel_below: can't find Panel Object");
439 return NULL;
440 }
441 Py_INCREF(po);
442 return (PyObject *)po;
443}
444
445static PyObject *PyCurses_update_panels(PyObject *self)
446{
447 PyCursesInitialised;
448 update_panels();
449 Py_INCREF(Py_None);
450 return Py_None;
451}
452
453
454/* List of functions defined in the module */
455
456static PyMethodDef PyCurses_methods[] = {
457 {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS},
458 {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
459 {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS},
460 {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS},
461 {NULL, NULL} /* sentinel */
462};
463
464/* Initialization function for the module */
465
466PyMODINIT_FUNC
467init_curses_panel(void)
468{
469 PyObject *m, *d, *v;
470
471 /* Initialize object type */
472 Py_TYPE(&PyCursesPanel_Type) = &PyType_Type;
473
474 import_curses();
475
476 /* Create the module and add the functions */
477 m = Py_InitModule("_curses_panel", PyCurses_methods);
478 if (m == NULL)
479 return;
480 d = PyModule_GetDict(m);
481
482 /* For exception _curses_panel.error */
483 PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
484 PyDict_SetItemString(d, "error", PyCursesError);
485
486 /* Make the version available */
487 v = PyString_FromString(PyCursesVersion);
488 PyDict_SetItemString(d, "version", v);
489 PyDict_SetItemString(d, "__version__", v);
490 Py_DECREF(v);
491}
Note: See TracBrowser for help on using the repository browser.