(root)/
Python-3.11.7/
Modules/
_curses_panel.c
       1  /*
       2   *   Interface to the ncurses panel library
       3   *
       4   * Original version by Thomas Gellekum
       5   */
       6  
       7  /* Release Number */
       8  
       9  static const 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  typedef struct {
      20      PyObject *PyCursesError;
      21      PyTypeObject *PyCursesPanel_Type;
      22  } _curses_panel_state;
      23  
      24  static inline _curses_panel_state *
      25  get_curses_panel_state(PyObject *module)
      26  {
      27      void *state = PyModule_GetState(module);
      28      assert(state != NULL);
      29      return (_curses_panel_state *)state;
      30  }
      31  
      32  static int
      33  _curses_panel_clear(PyObject *mod)
      34  {
      35      _curses_panel_state *state = get_curses_panel_state(mod);
      36      Py_CLEAR(state->PyCursesError);
      37      Py_CLEAR(state->PyCursesPanel_Type);
      38      return 0;
      39  }
      40  
      41  static int
      42  _curses_panel_traverse(PyObject *mod, visitproc visit, void *arg)
      43  {
      44      Py_VISIT(Py_TYPE(mod));
      45      _curses_panel_state *state = get_curses_panel_state(mod);
      46      Py_VISIT(state->PyCursesError);
      47      Py_VISIT(state->PyCursesPanel_Type);
      48      return 0;
      49  }
      50  
      51  static void
      52  _curses_panel_free(void *mod)
      53  {
      54      _curses_panel_clear((PyObject *) mod);
      55  }
      56  
      57  /* Utility Functions */
      58  
      59  /*
      60   * Check the return code from a curses function and return None
      61   * or raise an exception as appropriate.
      62   */
      63  
      64  static PyObject *
      65  PyCursesCheckERR(_curses_panel_state *state, int code, const char *fname)
      66  {
      67      if (code != ERR) {
      68          Py_RETURN_NONE;
      69      }
      70      else {
      71          if (fname == NULL) {
      72              PyErr_SetString(state->PyCursesError, catchall_ERR);
      73          }
      74          else {
      75              PyErr_Format(state->PyCursesError, "%s() returned ERR", fname);
      76          }
      77          return NULL;
      78      }
      79  }
      80  
      81  /*****************************************************************************
      82   The Panel Object
      83  ******************************************************************************/
      84  
      85  /* Definition of the panel object and panel type */
      86  
      87  typedef struct {
      88      PyObject_HEAD
      89      PANEL *pan;
      90      PyCursesWindowObject *wo;   /* for reference counts */
      91  } PyCursesPanelObject;
      92  
      93  /* Some helper functions. The problem is that there's always a window
      94     associated with a panel. To ensure that Python's GC doesn't pull
      95     this window from under our feet we need to keep track of references
      96     to the corresponding window object within Python. We can't use
      97     dupwin(oldwin) to keep a copy of the curses WINDOW because the
      98     contents of oldwin is copied only once; code like
      99  
     100     win = newwin(...)
     101     pan = win.panel()
     102     win.addstr(some_string)
     103     pan.window().addstr(other_string)
     104  
     105     will fail. */
     106  
     107  /* We keep a linked list of PyCursesPanelObjects, lop. A list should
     108     suffice, I don't expect more than a handful or at most a few
     109     dozens of panel objects within a typical program. */
     110  typedef struct _list_of_panels {
     111      PyCursesPanelObject *po;
     112      struct _list_of_panels *next;
     113  } list_of_panels;
     114  
     115  /* list anchor */
     116  static list_of_panels *lop;
     117  
     118  /* Insert a new panel object into lop */
     119  static int
     120  insert_lop(PyCursesPanelObject *po)
     121  {
     122      list_of_panels *new;
     123  
     124      if ((new = (list_of_panels *)PyMem_Malloc(sizeof(list_of_panels))) == NULL) {
     125          PyErr_NoMemory();
     126          return -1;
     127      }
     128      new->po = po;
     129      new->next = lop;
     130      lop = new;
     131      return 0;
     132  }
     133  
     134  /* Remove the panel object from lop */
     135  static void
     136  remove_lop(PyCursesPanelObject *po)
     137  {
     138      list_of_panels *temp, *n;
     139  
     140      temp = lop;
     141      if (temp->po == po) {
     142          lop = temp->next;
     143          PyMem_Free(temp);
     144          return;
     145      }
     146      while (temp->next == NULL || temp->next->po != po) {
     147          if (temp->next == NULL) {
     148              PyErr_SetString(PyExc_RuntimeError,
     149                              "remove_lop: can't find Panel Object");
     150              return;
     151          }
     152          temp = temp->next;
     153      }
     154      n = temp->next->next;
     155      PyMem_Free(temp->next);
     156      temp->next = n;
     157      return;
     158  }
     159  
     160  /* Return the panel object that corresponds to pan */
     161  static PyCursesPanelObject *
     162  find_po(PANEL *pan)
     163  {
     164      list_of_panels *temp;
     165      for (temp = lop; temp->po->pan != pan; temp = temp->next)
     166          if (temp->next == NULL) return NULL;    /* not found!? */
     167      return temp->po;
     168  }
     169  
     170  /*[clinic input]
     171  module _curses_panel
     172  class _curses_panel.panel "PyCursesPanelObject *" "&PyCursesPanel_Type"
     173  [clinic start generated code]*/
     174  /*[clinic end generated code: output=da39a3ee5e6b4b0d input=2f4ef263ca850a31]*/
     175  
     176  #include "clinic/_curses_panel.c.h"
     177  
     178  /* ------------- PANEL routines --------------- */
     179  
     180  /*[clinic input]
     181  _curses_panel.panel.bottom
     182  
     183      cls: defining_class
     184  
     185  Push the panel to the bottom of the stack.
     186  [clinic start generated code]*/
     187  
     188  static PyObject *
     189  _curses_panel_panel_bottom_impl(PyCursesPanelObject *self, PyTypeObject *cls)
     190  /*[clinic end generated code: output=8ec7fbbc08554021 input=6b7d2c0578b5a1c4]*/
     191  {
     192      _curses_panel_state *state = PyType_GetModuleState(cls);
     193      return PyCursesCheckERR(state, bottom_panel(self->pan), "bottom");
     194  }
     195  
     196  /*[clinic input]
     197  _curses_panel.panel.hide
     198  
     199      cls: defining_class
     200  
     201  Hide the panel.
     202  
     203  This does not delete the object, it just makes the window on screen invisible.
     204  [clinic start generated code]*/
     205  
     206  static PyObject *
     207  _curses_panel_panel_hide_impl(PyCursesPanelObject *self, PyTypeObject *cls)
     208  /*[clinic end generated code: output=cc6ab7203cdc1450 input=1bfc741f473e6055]*/
     209  {
     210      _curses_panel_state *state = PyType_GetModuleState(cls);
     211      return PyCursesCheckERR(state, hide_panel(self->pan), "hide");
     212  }
     213  
     214  /*[clinic input]
     215  _curses_panel.panel.show
     216  
     217      cls: defining_class
     218  
     219  Display the panel (which might have been hidden).
     220  [clinic start generated code]*/
     221  
     222  static PyObject *
     223  _curses_panel_panel_show_impl(PyCursesPanelObject *self, PyTypeObject *cls)
     224  /*[clinic end generated code: output=dc3421de375f0409 input=8122e80151cb4379]*/
     225  {
     226      _curses_panel_state *state = PyType_GetModuleState(cls);
     227      return PyCursesCheckERR(state, show_panel(self->pan), "show");
     228  }
     229  
     230  /*[clinic input]
     231  _curses_panel.panel.top
     232  
     233      cls: defining_class
     234  
     235  Push panel to the top of the stack.
     236  [clinic start generated code]*/
     237  
     238  static PyObject *
     239  _curses_panel_panel_top_impl(PyCursesPanelObject *self, PyTypeObject *cls)
     240  /*[clinic end generated code: output=10a072e511e873f7 input=1f372d597dda3379]*/
     241  {
     242      _curses_panel_state *state = PyType_GetModuleState(cls);
     243      return PyCursesCheckERR(state, top_panel(self->pan), "top");
     244  }
     245  
     246  /* Allocation and deallocation of Panel Objects */
     247  
     248  static PyObject *
     249  PyCursesPanel_New(_curses_panel_state *state, PANEL *pan,
     250                    PyCursesWindowObject *wo)
     251  {
     252      PyCursesPanelObject *po = PyObject_New(PyCursesPanelObject,
     253                                             state->PyCursesPanel_Type);
     254      if (po == NULL) {
     255          return NULL;
     256      }
     257  
     258      po->pan = pan;
     259      if (insert_lop(po) < 0) {
     260          po->wo = NULL;
     261          Py_DECREF(po);
     262          return NULL;
     263      }
     264      po->wo = wo;
     265      Py_INCREF(wo);
     266      return (PyObject *)po;
     267  }
     268  
     269  static void
     270  PyCursesPanel_Dealloc(PyCursesPanelObject *po)
     271  {
     272      PyObject *tp, *obj;
     273  
     274      tp = (PyObject *) Py_TYPE(po);
     275      obj = (PyObject *) panel_userptr(po->pan);
     276      if (obj) {
     277          (void)set_panel_userptr(po->pan, NULL);
     278          Py_DECREF(obj);
     279      }
     280      (void)del_panel(po->pan);
     281      if (po->wo != NULL) {
     282          Py_DECREF(po->wo);
     283          remove_lop(po);
     284      }
     285      PyObject_Free(po);
     286      Py_DECREF(tp);
     287  }
     288  
     289  /* panel_above(NULL) returns the bottom panel in the stack. To get
     290     this behaviour we use curses.panel.bottom_panel(). */
     291  /*[clinic input]
     292  _curses_panel.panel.above
     293  
     294  Return the panel above the current panel.
     295  [clinic start generated code]*/
     296  
     297  static PyObject *
     298  _curses_panel_panel_above_impl(PyCursesPanelObject *self)
     299  /*[clinic end generated code: output=70ac06d25fd3b4da input=c059994022976788]*/
     300  {
     301      PANEL *pan;
     302      PyCursesPanelObject *po;
     303  
     304      pan = panel_above(self->pan);
     305  
     306      if (pan == NULL) {          /* valid output, it means the calling panel
     307                                     is on top of the stack */
     308          Py_RETURN_NONE;
     309      }
     310      po = find_po(pan);
     311      if (po == NULL) {
     312          PyErr_SetString(PyExc_RuntimeError,
     313                          "panel_above: can't find Panel Object");
     314          return NULL;
     315      }
     316      Py_INCREF(po);
     317      return (PyObject *)po;
     318  }
     319  
     320  /* panel_below(NULL) returns the top panel in the stack. To get
     321     this behaviour we use curses.panel.top_panel(). */
     322  /*[clinic input]
     323  _curses_panel.panel.below
     324  
     325  Return the panel below the current panel.
     326  [clinic start generated code]*/
     327  
     328  static PyObject *
     329  _curses_panel_panel_below_impl(PyCursesPanelObject *self)
     330  /*[clinic end generated code: output=282861122e06e3de input=cc08f61936d297c6]*/
     331  {
     332      PANEL *pan;
     333      PyCursesPanelObject *po;
     334  
     335      pan = panel_below(self->pan);
     336  
     337      if (pan == NULL) {          /* valid output, it means the calling panel
     338                                     is on the bottom of the stack */
     339          Py_RETURN_NONE;
     340      }
     341      po = find_po(pan);
     342      if (po == NULL) {
     343          PyErr_SetString(PyExc_RuntimeError,
     344                          "panel_below: can't find Panel Object");
     345          return NULL;
     346      }
     347      Py_INCREF(po);
     348      return (PyObject *)po;
     349  }
     350  
     351  /*[clinic input]
     352  _curses_panel.panel.hidden
     353  
     354  Return True if the panel is hidden (not visible), False otherwise.
     355  [clinic start generated code]*/
     356  
     357  static PyObject *
     358  _curses_panel_panel_hidden_impl(PyCursesPanelObject *self)
     359  /*[clinic end generated code: output=66eebd1ab4501a71 input=453d4b4fce25e21a]*/
     360  {
     361      if (panel_hidden(self->pan))
     362          Py_RETURN_TRUE;
     363      else
     364          Py_RETURN_FALSE;
     365  }
     366  
     367  /*[clinic input]
     368  _curses_panel.panel.move
     369  
     370      cls: defining_class
     371      y: int
     372      x: int
     373      /
     374  
     375  Move the panel to the screen coordinates (y, x).
     376  [clinic start generated code]*/
     377  
     378  static PyObject *
     379  _curses_panel_panel_move_impl(PyCursesPanelObject *self, PyTypeObject *cls,
     380                                int y, int x)
     381  /*[clinic end generated code: output=ce546c93e56867da input=60a0e7912ff99849]*/
     382  {
     383      _curses_panel_state *state = PyType_GetModuleState(cls);
     384      return PyCursesCheckERR(state, move_panel(self->pan, y, x), "move_panel");
     385  }
     386  
     387  /*[clinic input]
     388  _curses_panel.panel.window
     389  
     390  Return the window object associated with the panel.
     391  [clinic start generated code]*/
     392  
     393  static PyObject *
     394  _curses_panel_panel_window_impl(PyCursesPanelObject *self)
     395  /*[clinic end generated code: output=5f05940d4106b4cb input=6067353d2c307901]*/
     396  {
     397      Py_INCREF(self->wo);
     398      return (PyObject *)self->wo;
     399  }
     400  
     401  /*[clinic input]
     402  _curses_panel.panel.replace
     403  
     404      cls: defining_class
     405      win: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
     406      /
     407  
     408  Change the window associated with the panel to the window win.
     409  [clinic start generated code]*/
     410  
     411  static PyObject *
     412  _curses_panel_panel_replace_impl(PyCursesPanelObject *self,
     413                                   PyTypeObject *cls,
     414                                   PyCursesWindowObject *win)
     415  /*[clinic end generated code: output=c71f95c212d58ae7 input=dbec7180ece41ff5]*/
     416  {
     417      _curses_panel_state *state = PyType_GetModuleState(cls);
     418  
     419      PyCursesPanelObject *po = find_po(self->pan);
     420      if (po == NULL) {
     421          PyErr_SetString(PyExc_RuntimeError,
     422                          "replace_panel: can't find Panel Object");
     423          return NULL;
     424      }
     425  
     426      int rtn = replace_panel(self->pan, win->win);
     427      if (rtn == ERR) {
     428          PyErr_SetString(state->PyCursesError, "replace_panel() returned ERR");
     429          return NULL;
     430      }
     431      Py_INCREF(win);
     432      Py_SETREF(po->wo, win);
     433      Py_RETURN_NONE;
     434  }
     435  
     436  /*[clinic input]
     437  _curses_panel.panel.set_userptr
     438  
     439      cls: defining_class
     440      obj: object
     441      /
     442  
     443  Set the panel's user pointer to obj.
     444  [clinic start generated code]*/
     445  
     446  static PyObject *
     447  _curses_panel_panel_set_userptr_impl(PyCursesPanelObject *self,
     448                                       PyTypeObject *cls, PyObject *obj)
     449  /*[clinic end generated code: output=db74f3db07b28080 input=e3fee2ff7b1b8e48]*/
     450  {
     451      PyCursesInitialised;
     452      Py_INCREF(obj);
     453      PyObject *oldobj = (PyObject *) panel_userptr(self->pan);
     454      int rc = set_panel_userptr(self->pan, (void*)obj);
     455      if (rc == ERR) {
     456          /* In case of an ncurses error, decref the new object again */
     457          Py_DECREF(obj);
     458      }
     459      else {
     460          Py_XDECREF(oldobj);
     461      }
     462  
     463      _curses_panel_state *state = PyType_GetModuleState(cls);
     464      return PyCursesCheckERR(state, rc, "set_panel_userptr");
     465  }
     466  
     467  /*[clinic input]
     468  _curses_panel.panel.userptr
     469  
     470      cls: defining_class
     471  
     472  Return the user pointer for the panel.
     473  [clinic start generated code]*/
     474  
     475  static PyObject *
     476  _curses_panel_panel_userptr_impl(PyCursesPanelObject *self,
     477                                   PyTypeObject *cls)
     478  /*[clinic end generated code: output=eea6e6f39ffc0179 input=f22ca4f115e30a80]*/
     479  {
     480      _curses_panel_state *state = PyType_GetModuleState(cls);
     481  
     482      PyCursesInitialised;
     483      PyObject *obj = (PyObject *) panel_userptr(self->pan);
     484      if (obj == NULL) {
     485          PyErr_SetString(state->PyCursesError, "no userptr set");
     486          return NULL;
     487      }
     488  
     489      Py_INCREF(obj);
     490      return obj;
     491  }
     492  
     493  
     494  /* Module interface */
     495  
     496  static PyMethodDef PyCursesPanel_Methods[] = {
     497      _CURSES_PANEL_PANEL_ABOVE_METHODDEF
     498      _CURSES_PANEL_PANEL_BELOW_METHODDEF
     499      _CURSES_PANEL_PANEL_BOTTOM_METHODDEF
     500      _CURSES_PANEL_PANEL_HIDDEN_METHODDEF
     501      _CURSES_PANEL_PANEL_HIDE_METHODDEF
     502      _CURSES_PANEL_PANEL_MOVE_METHODDEF
     503      _CURSES_PANEL_PANEL_REPLACE_METHODDEF
     504      _CURSES_PANEL_PANEL_SET_USERPTR_METHODDEF
     505      _CURSES_PANEL_PANEL_SHOW_METHODDEF
     506      _CURSES_PANEL_PANEL_TOP_METHODDEF
     507      _CURSES_PANEL_PANEL_USERPTR_METHODDEF
     508      _CURSES_PANEL_PANEL_WINDOW_METHODDEF
     509      {NULL,              NULL}   /* sentinel */
     510  };
     511  
     512  /* -------------------------------------------------------*/
     513  
     514  static PyType_Slot PyCursesPanel_Type_slots[] = {
     515      {Py_tp_dealloc, PyCursesPanel_Dealloc},
     516      {Py_tp_methods, PyCursesPanel_Methods},
     517      {0, 0},
     518  };
     519  
     520  static PyType_Spec PyCursesPanel_Type_spec = {
     521      .name = "_curses_panel.panel",
     522      .basicsize = sizeof(PyCursesPanelObject),
     523      .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
     524      .slots = PyCursesPanel_Type_slots
     525  };
     526  
     527  /* Wrapper for panel_above(NULL). This function returns the bottom
     528     panel of the stack, so it's renamed to bottom_panel().
     529     panel.above() *requires* a panel object in the first place which
     530     may be undesirable. */
     531  /*[clinic input]
     532  _curses_panel.bottom_panel
     533  
     534  Return the bottom panel in the panel stack.
     535  [clinic start generated code]*/
     536  
     537  static PyObject *
     538  _curses_panel_bottom_panel_impl(PyObject *module)
     539  /*[clinic end generated code: output=3aba9f985f4c2bd0 input=634c2a8078b3d7e4]*/
     540  {
     541      PANEL *pan;
     542      PyCursesPanelObject *po;
     543  
     544      PyCursesInitialised;
     545  
     546      pan = panel_above(NULL);
     547  
     548      if (pan == NULL) {          /* valid output, it means
     549                                     there's no panel at all */
     550          Py_RETURN_NONE;
     551      }
     552      po = find_po(pan);
     553      if (po == NULL) {
     554          PyErr_SetString(PyExc_RuntimeError,
     555                          "panel_above: can't find Panel Object");
     556          return NULL;
     557      }
     558      Py_INCREF(po);
     559      return (PyObject *)po;
     560  }
     561  
     562  /*[clinic input]
     563  _curses_panel.new_panel
     564  
     565      win: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
     566      /
     567  
     568  Return a panel object, associating it with the given window win.
     569  [clinic start generated code]*/
     570  
     571  static PyObject *
     572  _curses_panel_new_panel_impl(PyObject *module, PyCursesWindowObject *win)
     573  /*[clinic end generated code: output=45e948e0176a9bd2 input=74d4754e0ebe4800]*/
     574  {
     575      _curses_panel_state *state = get_curses_panel_state(module);
     576  
     577      PANEL *pan = new_panel(win->win);
     578      if (pan == NULL) {
     579          PyErr_SetString(state->PyCursesError, catchall_NULL);
     580          return NULL;
     581      }
     582      return (PyObject *)PyCursesPanel_New(state, pan, win);
     583  }
     584  
     585  
     586  /* Wrapper for panel_below(NULL). This function returns the top panel
     587     of the stack, so it's renamed to top_panel(). panel.below()
     588     *requires* a panel object in the first place which may be
     589     undesirable. */
     590  /*[clinic input]
     591  _curses_panel.top_panel
     592  
     593  Return the top panel in the panel stack.
     594  [clinic start generated code]*/
     595  
     596  static PyObject *
     597  _curses_panel_top_panel_impl(PyObject *module)
     598  /*[clinic end generated code: output=86704988bea8508e input=e62d6278dba39e79]*/
     599  {
     600      PANEL *pan;
     601      PyCursesPanelObject *po;
     602  
     603      PyCursesInitialised;
     604  
     605      pan = panel_below(NULL);
     606  
     607      if (pan == NULL) {          /* valid output, it means
     608                                     there's no panel at all */
     609          Py_RETURN_NONE;
     610      }
     611      po = find_po(pan);
     612      if (po == NULL) {
     613          PyErr_SetString(PyExc_RuntimeError,
     614                          "panel_below: can't find Panel Object");
     615          return NULL;
     616      }
     617      Py_INCREF(po);
     618      return (PyObject *)po;
     619  }
     620  
     621  /*[clinic input]
     622  _curses_panel.update_panels
     623  
     624  Updates the virtual screen after changes in the panel stack.
     625  
     626  This does not call curses.doupdate(), so you'll have to do this yourself.
     627  [clinic start generated code]*/
     628  
     629  static PyObject *
     630  _curses_panel_update_panels_impl(PyObject *module)
     631  /*[clinic end generated code: output=2f3b4c2e03d90ded input=5299624c9a708621]*/
     632  {
     633      PyCursesInitialised;
     634      update_panels();
     635      Py_RETURN_NONE;
     636  }
     637  
     638  /* List of functions defined in the module */
     639  
     640  static PyMethodDef PyCurses_methods[] = {
     641      _CURSES_PANEL_BOTTOM_PANEL_METHODDEF
     642      _CURSES_PANEL_NEW_PANEL_METHODDEF
     643      _CURSES_PANEL_TOP_PANEL_METHODDEF
     644      _CURSES_PANEL_UPDATE_PANELS_METHODDEF
     645      {NULL,              NULL}           /* sentinel */
     646  };
     647  
     648  /* Initialization function for the module */
     649  static int
     650  _curses_panel_exec(PyObject *mod)
     651  {
     652      _curses_panel_state *state = get_curses_panel_state(mod);
     653      /* Initialize object type */
     654      state->PyCursesPanel_Type = (PyTypeObject *)PyType_FromModuleAndSpec(
     655          mod, &PyCursesPanel_Type_spec, NULL);
     656      if (state->PyCursesPanel_Type == NULL) {
     657          return -1;
     658      }
     659  
     660      if (PyModule_AddType(mod, state->PyCursesPanel_Type) < 0) {
     661          return -1;
     662      }
     663  
     664      import_curses();
     665      if (PyErr_Occurred()) {
     666          return -1;
     667      }
     668  
     669      /* For exception _curses_panel.error */
     670      state->PyCursesError = PyErr_NewException(
     671          "_curses_panel.error", NULL, NULL);
     672  
     673      if (PyModule_AddObjectRef(mod, "error", state->PyCursesError) < 0) {
     674          return -1;
     675      }
     676  
     677      /* Make the version available */
     678      PyObject *v = PyUnicode_FromString(PyCursesVersion);
     679      if (v == NULL) {
     680          return -1;
     681      }
     682  
     683      PyObject *d = PyModule_GetDict(mod);
     684      if (PyDict_SetItemString(d, "version", v) < 0) {
     685          Py_DECREF(v);
     686          return -1;
     687      }
     688      if (PyDict_SetItemString(d, "__version__", v) < 0) {
     689          Py_DECREF(v);
     690          return -1;
     691      }
     692  
     693      Py_DECREF(v);
     694  
     695      return 0;
     696  }
     697  
     698  static PyModuleDef_Slot _curses_slots[] = {
     699      {Py_mod_exec, _curses_panel_exec},
     700      {0, NULL}
     701  };
     702  
     703  static struct PyModuleDef _curses_panelmodule = {
     704      PyModuleDef_HEAD_INIT,
     705      .m_name = "_curses_panel",
     706      .m_size = sizeof(_curses_panel_state),
     707      .m_methods = PyCurses_methods,
     708      .m_slots = _curses_slots,
     709      .m_traverse = _curses_panel_traverse,
     710      .m_clear = _curses_panel_clear,
     711      .m_free = _curses_panel_free
     712  };
     713  
     714  PyMODINIT_FUNC
     715  PyInit__curses_panel(void)
     716  {
     717      return PyModuleDef_Init(&_curses_panelmodule);
     718  }