ยปCore Development>Code coverage>Modules/_curses_panel.c

Python code coverage for Modules/_curses_panel.c

#countcontent
1n/a/*
2n/a * Interface to the ncurses panel library
3n/a *
4n/a * Original version by Thomas Gellekum
5n/a */
6n/a
7n/a/* Release Number */
8n/a
9n/astatic const char PyCursesVersion[] = "2.1";
10n/a
11n/a/* Includes */
12n/a
13n/a#include "Python.h"
14n/a
15n/a#include "py_curses.h"
16n/a
17n/a#include <panel.h>
18n/a
19n/atypedef struct {
20n/a PyObject *PyCursesError;
21n/a PyObject *PyCursesPanel_Type;
22n/a} _curses_panelstate;
23n/a
24n/a#define _curses_panelstate(o) ((_curses_panelstate *)PyModule_GetState(o))
25n/a
26n/astatic int
27n/a_curses_panel_clear(PyObject *m)
28n/a{
29n/a Py_CLEAR(_curses_panelstate(m)->PyCursesError);
30n/a return 0;
31n/a}
32n/a
33n/astatic int
34n/a_curses_panel_traverse(PyObject *m, visitproc visit, void *arg)
35n/a{
36n/a Py_VISIT(_curses_panelstate(m)->PyCursesError);
37n/a return 0;
38n/a}
39n/a
40n/astatic void
41n/a_curses_panel_free(void *m)
42n/a{
43n/a _curses_panel_clear((PyObject *) m);
44n/a}
45n/a
46n/astatic struct PyModuleDef _curses_panelmodule;
47n/a
48n/a#define _curses_panelstate_global \
49n/a((_curses_panelstate *) PyModule_GetState(PyState_FindModule(&_curses_panelmodule)))
50n/a
51n/a/* Utility Functions */
52n/a
53n/a/*
54n/a * Check the return code from a curses function and return None
55n/a * or raise an exception as appropriate.
56n/a */
57n/a
58n/astatic PyObject *
59n/aPyCursesCheckERR(int code, const char *fname)
60n/a{
61n/a if (code != ERR) {
62n/a Py_RETURN_NONE;
63n/a } else {
64n/a if (fname == NULL) {
65n/a PyErr_SetString(_curses_panelstate_global->PyCursesError, catchall_ERR);
66n/a } else {
67n/a PyErr_Format(_curses_panelstate_global->PyCursesError, "%s() returned ERR", fname);
68n/a }
69n/a return NULL;
70n/a }
71n/a}
72n/a
73n/a/*****************************************************************************
74n/a The Panel Object
75n/a******************************************************************************/
76n/a
77n/a/* Definition of the panel object and panel type */
78n/a
79n/atypedef struct {
80n/a PyObject_HEAD
81n/a PANEL *pan;
82n/a PyCursesWindowObject *wo; /* for reference counts */
83n/a} PyCursesPanelObject;
84n/a
85n/a#define PyCursesPanel_Check(v) \
86n/a (Py_TYPE(v) == _curses_panelstate_global->PyCursesPanel_Type)
87n/a
88n/a/* Some helper functions. The problem is that there's always a window
89n/a associated with a panel. To ensure that Python's GC doesn't pull
90n/a this window from under our feet we need to keep track of references
91n/a to the corresponding window object within Python. We can't use
92n/a dupwin(oldwin) to keep a copy of the curses WINDOW because the
93n/a contents of oldwin is copied only once; code like
94n/a
95n/a win = newwin(...)
96n/a pan = win.panel()
97n/a win.addstr(some_string)
98n/a pan.window().addstr(other_string)
99n/a
100n/a will fail. */
101n/a
102n/a/* We keep a linked list of PyCursesPanelObjects, lop. A list should
103n/a suffice, I don't expect more than a handful or at most a few
104n/a dozens of panel objects within a typical program. */
105n/atypedef struct _list_of_panels {
106n/a PyCursesPanelObject *po;
107n/a struct _list_of_panels *next;
108n/a} list_of_panels;
109n/a
110n/a/* list anchor */
111n/astatic list_of_panels *lop;
112n/a
113n/a/* Insert a new panel object into lop */
114n/astatic int
115n/ainsert_lop(PyCursesPanelObject *po)
116n/a{
117n/a list_of_panels *new;
118n/a
119n/a if ((new = (list_of_panels *)PyMem_Malloc(sizeof(list_of_panels))) == NULL) {
120n/a PyErr_NoMemory();
121n/a return -1;
122n/a }
123n/a new->po = po;
124n/a new->next = lop;
125n/a lop = new;
126n/a return 0;
127n/a}
128n/a
129n/a/* Remove the panel object from lop */
130n/astatic void
131n/aremove_lop(PyCursesPanelObject *po)
132n/a{
133n/a list_of_panels *temp, *n;
134n/a
135n/a temp = lop;
136n/a if (temp->po == po) {
137n/a lop = temp->next;
138n/a PyMem_Free(temp);
139n/a return;
140n/a }
141n/a while (temp->next == NULL || temp->next->po != po) {
142n/a if (temp->next == NULL) {
143n/a PyErr_SetString(PyExc_RuntimeError,
144n/a "remove_lop: can't find Panel Object");
145n/a return;
146n/a }
147n/a temp = temp->next;
148n/a }
149n/a n = temp->next->next;
150n/a PyMem_Free(temp->next);
151n/a temp->next = n;
152n/a return;
153n/a}
154n/a
155n/a/* Return the panel object that corresponds to pan */
156n/astatic PyCursesPanelObject *
157n/afind_po(PANEL *pan)
158n/a{
159n/a list_of_panels *temp;
160n/a for (temp = lop; temp->po->pan != pan; temp = temp->next)
161n/a if (temp->next == NULL) return NULL; /* not found!? */
162n/a return temp->po;
163n/a}
164n/a
165n/a/* Function Prototype Macros - They are ugly but very, very useful. ;-)
166n/a
167n/a X - function name
168n/a TYPE - parameter Type
169n/a ERGSTR - format string for construction of the return value
170n/a PARSESTR - format string for argument parsing */
171n/a
172n/a#define Panel_NoArgNoReturnFunction(X) \
173n/astatic PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
174n/a{ return PyCursesCheckERR(X(self->pan), # X); }
175n/a
176n/a#define Panel_NoArgTrueFalseFunction(X) \
177n/astatic PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \
178n/a{ \
179n/a if (X (self->pan) == FALSE) { Py_RETURN_FALSE; } \
180n/a else { Py_RETURN_TRUE; } }
181n/a
182n/a#define Panel_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \
183n/astatic PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \
184n/a{ \
185n/a TYPE arg1, arg2; \
186n/a if (!PyArg_ParseTuple(args, PARSESTR, &arg1, &arg2)) return NULL; \
187n/a return PyCursesCheckERR(X(self->pan, arg1, arg2), # X); }
188n/a
189n/a/* ------------- PANEL routines --------------- */
190n/a
191n/aPanel_NoArgNoReturnFunction(bottom_panel)
192n/aPanel_NoArgNoReturnFunction(hide_panel)
193n/aPanel_NoArgNoReturnFunction(show_panel)
194n/aPanel_NoArgNoReturnFunction(top_panel)
195n/aPanel_NoArgTrueFalseFunction(panel_hidden)
196n/aPanel_TwoArgNoReturnFunction(move_panel, int, "ii;y,x")
197n/a
198n/a/* Allocation and deallocation of Panel Objects */
199n/a
200n/astatic PyObject *
201n/aPyCursesPanel_New(PANEL *pan, PyCursesWindowObject *wo)
202n/a{
203n/a PyCursesPanelObject *po;
204n/a
205n/a po = PyObject_NEW(PyCursesPanelObject,
206n/a (PyTypeObject *)(_curses_panelstate_global)->PyCursesPanel_Type);
207n/a if (po == NULL) return NULL;
208n/a po->pan = pan;
209n/a if (insert_lop(po) < 0) {
210n/a po->wo = NULL;
211n/a Py_DECREF(po);
212n/a return NULL;
213n/a }
214n/a po->wo = wo;
215n/a Py_INCREF(wo);
216n/a return (PyObject *)po;
217n/a}
218n/a
219n/astatic void
220n/aPyCursesPanel_Dealloc(PyCursesPanelObject *po)
221n/a{
222n/a PyObject *obj = (PyObject *) panel_userptr(po->pan);
223n/a if (obj) {
224n/a (void)set_panel_userptr(po->pan, NULL);
225n/a Py_DECREF(obj);
226n/a }
227n/a (void)del_panel(po->pan);
228n/a if (po->wo != NULL) {
229n/a Py_DECREF(po->wo);
230n/a remove_lop(po);
231n/a }
232n/a PyObject_DEL(po);
233n/a}
234n/a
235n/a/* panel_above(NULL) returns the bottom panel in the stack. To get
236n/a this behaviour we use curses.panel.bottom_panel(). */
237n/astatic PyObject *
238n/aPyCursesPanel_above(PyCursesPanelObject *self)
239n/a{
240n/a PANEL *pan;
241n/a PyCursesPanelObject *po;
242n/a
243n/a pan = panel_above(self->pan);
244n/a
245n/a if (pan == NULL) { /* valid output, it means the calling panel
246n/a is on top of the stack */
247n/a Py_RETURN_NONE;
248n/a }
249n/a po = find_po(pan);
250n/a if (po == NULL) {
251n/a PyErr_SetString(PyExc_RuntimeError,
252n/a "panel_above: can't find Panel Object");
253n/a return NULL;
254n/a }
255n/a Py_INCREF(po);
256n/a return (PyObject *)po;
257n/a}
258n/a
259n/a/* panel_below(NULL) returns the top panel in the stack. To get
260n/a this behaviour we use curses.panel.top_panel(). */
261n/astatic PyObject *
262n/aPyCursesPanel_below(PyCursesPanelObject *self)
263n/a{
264n/a PANEL *pan;
265n/a PyCursesPanelObject *po;
266n/a
267n/a pan = panel_below(self->pan);
268n/a
269n/a if (pan == NULL) { /* valid output, it means the calling panel
270n/a is on the bottom of the stack */
271n/a Py_RETURN_NONE;
272n/a }
273n/a po = find_po(pan);
274n/a if (po == NULL) {
275n/a PyErr_SetString(PyExc_RuntimeError,
276n/a "panel_below: can't find Panel Object");
277n/a return NULL;
278n/a }
279n/a Py_INCREF(po);
280n/a return (PyObject *)po;
281n/a}
282n/a
283n/astatic PyObject *
284n/aPyCursesPanel_window(PyCursesPanelObject *self)
285n/a{
286n/a Py_INCREF(self->wo);
287n/a return (PyObject *)self->wo;
288n/a}
289n/a
290n/astatic PyObject *
291n/aPyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args)
292n/a{
293n/a PyCursesPanelObject *po;
294n/a PyCursesWindowObject *temp;
295n/a int rtn;
296n/a
297n/a if (PyTuple_Size(args) != 1) {
298n/a PyErr_SetString(PyExc_TypeError, "replace requires one argument");
299n/a return NULL;
300n/a }
301n/a if (!PyArg_ParseTuple(args, "O!;window object",
302n/a &PyCursesWindow_Type, &temp))
303n/a return NULL;
304n/a
305n/a po = find_po(self->pan);
306n/a if (po == NULL) {
307n/a PyErr_SetString(PyExc_RuntimeError,
308n/a "replace_panel: can't find Panel Object");
309n/a return NULL;
310n/a }
311n/a
312n/a rtn = replace_panel(self->pan, temp->win);
313n/a if (rtn == ERR) {
314n/a PyErr_SetString(_curses_panelstate_global->PyCursesError, "replace_panel() returned ERR");
315n/a return NULL;
316n/a }
317n/a Py_INCREF(temp);
318n/a Py_SETREF(po->wo, temp);
319n/a Py_RETURN_NONE;
320n/a}
321n/a
322n/astatic PyObject *
323n/aPyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj)
324n/a{
325n/a PyObject *oldobj;
326n/a int rc;
327n/a PyCursesInitialised;
328n/a Py_INCREF(obj);
329n/a oldobj = (PyObject *) panel_userptr(self->pan);
330n/a rc = set_panel_userptr(self->pan, (void*)obj);
331n/a if (rc == ERR) {
332n/a /* In case of an ncurses error, decref the new object again */
333n/a Py_DECREF(obj);
334n/a }
335n/a Py_XDECREF(oldobj);
336n/a return PyCursesCheckERR(rc, "set_panel_userptr");
337n/a}
338n/a
339n/astatic PyObject *
340n/aPyCursesPanel_userptr(PyCursesPanelObject *self)
341n/a{
342n/a PyObject *obj;
343n/a PyCursesInitialised;
344n/a obj = (PyObject *) panel_userptr(self->pan);
345n/a if (obj == NULL) {
346n/a PyErr_SetString(_curses_panelstate_global->PyCursesError, "no userptr set");
347n/a return NULL;
348n/a }
349n/a
350n/a Py_INCREF(obj);
351n/a return obj;
352n/a}
353n/a
354n/a
355n/a/* Module interface */
356n/a
357n/astatic PyMethodDef PyCursesPanel_Methods[] = {
358n/a {"above", (PyCFunction)PyCursesPanel_above, METH_NOARGS},
359n/a {"below", (PyCFunction)PyCursesPanel_below, METH_NOARGS},
360n/a {"bottom", (PyCFunction)PyCursesPanel_bottom_panel, METH_NOARGS},
361n/a {"hidden", (PyCFunction)PyCursesPanel_panel_hidden, METH_NOARGS},
362n/a {"hide", (PyCFunction)PyCursesPanel_hide_panel, METH_NOARGS},
363n/a {"move", (PyCFunction)PyCursesPanel_move_panel, METH_VARARGS},
364n/a {"replace", (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS},
365n/a {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr, METH_O},
366n/a {"show", (PyCFunction)PyCursesPanel_show_panel, METH_NOARGS},
367n/a {"top", (PyCFunction)PyCursesPanel_top_panel, METH_NOARGS},
368n/a {"userptr", (PyCFunction)PyCursesPanel_userptr, METH_NOARGS},
369n/a {"window", (PyCFunction)PyCursesPanel_window, METH_NOARGS},
370n/a {NULL, NULL} /* sentinel */
371n/a};
372n/a
373n/a/* -------------------------------------------------------*/
374n/a
375n/astatic PyType_Slot PyCursesPanel_Type_slots[] = {
376n/a {Py_tp_dealloc, PyCursesPanel_Dealloc},
377n/a {Py_tp_methods, PyCursesPanel_Methods},
378n/a {0, 0},
379n/a};
380n/a
381n/astatic PyType_Spec PyCursesPanel_Type_spec = {
382n/a "_curses_panel.curses panel",
383n/a sizeof(PyCursesPanelObject),
384n/a 0,
385n/a Py_TPFLAGS_DEFAULT,
386n/a PyCursesPanel_Type_slots
387n/a};
388n/a
389n/a/* Wrapper for panel_above(NULL). This function returns the bottom
390n/a panel of the stack, so it's renamed to bottom_panel().
391n/a panel.above() *requires* a panel object in the first place which
392n/a may be undesirable. */
393n/astatic PyObject *
394n/aPyCurses_bottom_panel(PyObject *self)
395n/a{
396n/a PANEL *pan;
397n/a PyCursesPanelObject *po;
398n/a
399n/a PyCursesInitialised;
400n/a
401n/a pan = panel_above(NULL);
402n/a
403n/a if (pan == NULL) { /* valid output, it means
404n/a there's no panel at all */
405n/a Py_RETURN_NONE;
406n/a }
407n/a po = find_po(pan);
408n/a if (po == NULL) {
409n/a PyErr_SetString(PyExc_RuntimeError,
410n/a "panel_above: can't find Panel Object");
411n/a return NULL;
412n/a }
413n/a Py_INCREF(po);
414n/a return (PyObject *)po;
415n/a}
416n/a
417n/astatic PyObject *
418n/aPyCurses_new_panel(PyObject *self, PyObject *args)
419n/a{
420n/a PyCursesWindowObject *win;
421n/a PANEL *pan;
422n/a
423n/a if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win))
424n/a return NULL;
425n/a pan = new_panel(win->win);
426n/a if (pan == NULL) {
427n/a PyErr_SetString(_curses_panelstate_global->PyCursesError, catchall_NULL);
428n/a return NULL;
429n/a }
430n/a return (PyObject *)PyCursesPanel_New(pan, win);
431n/a}
432n/a
433n/a
434n/a/* Wrapper for panel_below(NULL). This function returns the top panel
435n/a of the stack, so it's renamed to top_panel(). panel.below()
436n/a *requires* a panel object in the first place which may be
437n/a undesirable. */
438n/astatic PyObject *
439n/aPyCurses_top_panel(PyObject *self)
440n/a{
441n/a PANEL *pan;
442n/a PyCursesPanelObject *po;
443n/a
444n/a PyCursesInitialised;
445n/a
446n/a pan = panel_below(NULL);
447n/a
448n/a if (pan == NULL) { /* valid output, it means
449n/a there's no panel at all */
450n/a Py_RETURN_NONE;
451n/a }
452n/a po = find_po(pan);
453n/a if (po == NULL) {
454n/a PyErr_SetString(PyExc_RuntimeError,
455n/a "panel_below: can't find Panel Object");
456n/a return NULL;
457n/a }
458n/a Py_INCREF(po);
459n/a return (PyObject *)po;
460n/a}
461n/a
462n/astatic PyObject *PyCurses_update_panels(PyObject *self)
463n/a{
464n/a PyCursesInitialised;
465n/a update_panels();
466n/a Py_RETURN_NONE;
467n/a}
468n/a
469n/a
470n/a/* List of functions defined in the module */
471n/a
472n/astatic PyMethodDef PyCurses_methods[] = {
473n/a {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS},
474n/a {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS},
475n/a {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS},
476n/a {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS},
477n/a {NULL, NULL} /* sentinel */
478n/a};
479n/a
480n/a/* Initialization function for the module */
481n/a
482n/a
483n/astatic struct PyModuleDef _curses_panelmodule = {
484n/a PyModuleDef_HEAD_INIT,
485n/a "_curses_panel",
486n/a NULL,
487n/a sizeof(_curses_panelstate),
488n/a PyCurses_methods,
489n/a NULL,
490n/a _curses_panel_traverse,
491n/a _curses_panel_clear,
492n/a _curses_panel_free
493n/a};
494n/a
495n/aPyMODINIT_FUNC
496n/aPyInit__curses_panel(void)
497n/a{
498n/a PyObject *m, *d, *v;
499n/a
500n/a /* Create the module and add the functions */
501n/a m = PyModule_Create(&_curses_panelmodule);
502n/a if (m == NULL)
503n/a goto fail;
504n/a d = PyModule_GetDict(m);
505n/a
506n/a /* Initialize object type */
507n/a v = PyType_FromSpec(&PyCursesPanel_Type_spec);
508n/a if (v == NULL)
509n/a goto fail;
510n/a ((PyTypeObject *)v)->tp_new = NULL;
511n/a _curses_panelstate(m)->PyCursesPanel_Type = v;
512n/a
513n/a import_curses();
514n/a if (PyErr_Occurred())
515n/a goto fail;
516n/a
517n/a /* For exception _curses_panel.error */
518n/a _curses_panelstate(m)->PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL);
519n/a PyDict_SetItemString(d, "error", _curses_panelstate(m)->PyCursesError);
520n/a
521n/a /* Make the version available */
522n/a v = PyUnicode_FromString(PyCursesVersion);
523n/a PyDict_SetItemString(d, "version", v);
524n/a PyDict_SetItemString(d, "__version__", v);
525n/a Py_DECREF(v);
526n/a return m;
527n/a fail:
528n/a Py_XDECREF(m);
529n/a return NULL;
530n/a}