Python-2.7.3/Modules/_curses_panel.c

No issues found

  1 /*
  2  *   Interface to the ncurses panel library
  3  *
  4  * Original version by Thomas Gellekum
  5  */
  6 
  7 /* Release Number */
  8 
  9 static char *PyCursesVersion = "2.1";
 10 
 11 /* Includes */
 12 
 13 #include "Python.h"
 14 
 15 #include "py_curses.h"
 16 
 17 #include <panel.h>
 18 
 19 static 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 
 29 static PyObject *
 30 PyCursesCheckERR(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 
 51 typedef struct {
 52     PyObject_HEAD
 53     PANEL *pan;
 54     PyCursesWindowObject *wo;   /* for reference counts */
 55 } PyCursesPanelObject;
 56 
 57 PyTypeObject 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. */
 78 typedef struct _list_of_panels {
 79     PyCursesPanelObject *po;
 80     struct _list_of_panels *next;
 81 } list_of_panels;
 82 
 83 /* list anchor */
 84 static list_of_panels *lop;
 85 
 86 /* Insert a new panel object into lop */
 87 static int
 88 insert_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 */
103 static void
104 remove_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 */
129 static PyCursesPanelObject *
130 find_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) \
146 static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
147 { return PyCursesCheckERR(X(self->pan), # X); }
148 
149 #define Panel_NoArgTrueFalseFunction(X) \
150 static 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) \
156 static 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 
164 Panel_NoArgNoReturnFunction(bottom_panel)
165 Panel_NoArgNoReturnFunction(hide_panel)
166 Panel_NoArgNoReturnFunction(show_panel)
167 Panel_NoArgNoReturnFunction(top_panel)
168 Panel_NoArgTrueFalseFunction(panel_hidden)
169 Panel_TwoArgNoReturnFunction(move_panel, int, "ii;y,x")
170 
171 /* Allocation and deallocation of Panel Objects */
172 
173 static PyObject *
174 PyCursesPanel_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 
191 static void
192 PyCursesPanel_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(). */
204 static PyObject *
205 PyCursesPanel_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(). */
229 static PyObject *
230 PyCursesPanel_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 
252 static PyObject *
253 PyCursesPanel_window(PyCursesPanelObject *self)
254 {
255     Py_INCREF(self->wo);
256     return (PyObject *)self->wo;
257 }
258 
259 static PyObject *
260 PyCursesPanel_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 
293 static PyObject *
294 PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj)
295 {
296     Py_INCREF(obj);
297     return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj),
298                             "set_panel_userptr");
299 }
300 
301 static PyObject *
302 PyCursesPanel_userptr(PyCursesPanelObject *self)
303 {
304     PyObject *obj;
305     PyCursesInitialised;
306     obj = (PyObject *) panel_userptr(self->pan);
307     if (obj == NULL) {
308         PyErr_SetString(PyCursesError, "no userptr set");
309         return NULL;
310     }
311 
312     Py_INCREF(obj);
313     return obj;
314 }
315 
316 
317 /* Module interface */
318 
319 static PyMethodDef PyCursesPanel_Methods[] = {
320     {"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},
326     {"replace",         (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS},
327     {"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},
332     {NULL,              NULL}   /* sentinel */
333 };
334 
335 static PyObject *
336 PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name)
337 {
338     return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name);
339 }
340 
341 /* -------------------------------------------------------*/
342 
343 PyTypeObject PyCursesPanel_Type = {
344     PyVarObject_HEAD_INIT(NULL, 0)
345     "_curses_panel.curses panel",       /*tp_name*/
346     sizeof(PyCursesPanelObject),        /*tp_basicsize*/
347     0,                  /*tp_itemsize*/
348     /* methods */
349     (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/
350     0,                  /*tp_print*/
351     (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/
352     (setattrfunc)0, /*tp_setattr*/
353     0,                  /*tp_compare*/
354     0,                  /*tp_repr*/
355     0,                  /*tp_as_number*/
356     0,                  /*tp_as_sequence*/
357     0,                  /*tp_as_mapping*/
358     0,                  /*tp_hash*/
359 };
360 
361 /* Wrapper for panel_above(NULL). This function returns the bottom
362    panel of the stack, so it's renamed to bottom_panel().
363    panel.above() *requires* a panel object in the first place which
364    may be undesirable. */
365 static PyObject *
366 PyCurses_bottom_panel(PyObject *self)
367 {
368     PANEL *pan;
369     PyCursesPanelObject *po;
370 
371     PyCursesInitialised;
372 
373     pan = panel_above(NULL);
374 
375     if (pan == NULL) {          /* valid output, it means
376                                    there's no panel at all */
377         Py_INCREF(Py_None);
378         return Py_None;
379     }
380     po = find_po(pan);
381     if (po == NULL) {
382         PyErr_SetString(PyExc_RuntimeError,
383                         "panel_above: can't find Panel Object");
384         return NULL;
385     }
386     Py_INCREF(po);
387     return (PyObject *)po;
388 }
389 
390 static PyObject *
391 PyCurses_new_panel(PyObject *self, PyObject *args)
392 {
393     PyCursesWindowObject *win;
394     PANEL *pan;
395 
396     if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
397         return NULL;
398     pan = new_panel(win->win);
399     if (pan == NULL) {
400         PyErr_SetString(PyCursesError, catchall_NULL);
401         return NULL;
402     }
403     return (PyObject *)PyCursesPanel_New(pan, win);
404 }
405 
406 
407 /* Wrapper for panel_below(NULL). This function returns the top panel
408    of the stack, so it's renamed to top_panel(). panel.below()
409    *requires* a panel object in the first place which may be
410    undesirable. */
411 static PyObject *
412 PyCurses_top_panel(PyObject *self)
413 {
414     PANEL *pan;
415     PyCursesPanelObject *po;
416 
417     PyCursesInitialised;
418 
419     pan = panel_below(NULL);
420 
421     if (pan == NULL) {          /* valid output, it means
422                                    there's no panel at all */
423         Py_INCREF(Py_None);
424         return Py_None;
425     }
426     po = find_po(pan);
427     if (po == NULL) {
428         PyErr_SetString(PyExc_RuntimeError,
429                         "panel_below: can't find Panel Object");
430         return NULL;
431     }
432     Py_INCREF(po);
433     return (PyObject *)po;
434 }
435 
436 static PyObject *PyCurses_update_panels(PyObject *self)
437 {
438     PyCursesInitialised;
439     update_panels();
440     Py_INCREF(Py_None);
441     return Py_None;
442 }
443 
444 
445 /* List of functions defined in the module */
446 
447 static PyMethodDef PyCurses_methods[] = {
448     {"bottom_panel",        (PyCFunction)PyCurses_bottom_panel,  METH_NOARGS},
449     {"new_panel",           (PyCFunction)PyCurses_new_panel,     METH_VARARGS},
450     {"top_panel",           (PyCFunction)PyCurses_top_panel,     METH_NOARGS},
451     {"update_panels",       (PyCFunction)PyCurses_update_panels, METH_NOARGS},
452     {NULL,              NULL}           /* sentinel */
453 };
454 
455 /* Initialization function for the module */
456 
457 PyMODINIT_FUNC
458 init_curses_panel(void)
459 {
460     PyObject *m, *d, *v;
461 
462     /* Initialize object type */
463     Py_TYPE(&PyCursesPanel_Type) = &PyType_Type;
464 
465     import_curses();
466 
467     /* Create the module and add the functions */
468     m = Py_InitModule("_curses_panel", PyCurses_methods);
469     if (m == NULL)
470         return;
471     d = PyModule_GetDict(m);
472 
473     /* For exception _curses_panel.error */
474     PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
475     PyDict_SetItemString(d, "error", PyCursesError);
476 
477     /* Make the version available */
478     v = PyString_FromString(PyCursesVersion);
479     PyDict_SetItemString(d, "version", v);
480     PyDict_SetItemString(d, "__version__", v);
481     Py_DECREF(v);
482 }