]> Git Repo - binutils.git/blob - gdb/python/py-tui.c
gdb: fix some indentation issues
[binutils.git] / gdb / python / py-tui.c
1 /* TUI windows implemented in Python
2
3    Copyright (C) 2020-2021 Free Software Foundation, Inc.
4
5    This file is part of GDB.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20
21 #include "defs.h"
22 #include "arch-utils.h"
23 #include "python-internal.h"
24
25 #ifdef TUI
26
27 /* Note that Python's public headers may define HAVE_NCURSES_H, so if
28    we unconditionally include this (outside the #ifdef above), then we
29    can get a compile error when ncurses is not in fact installed.  See
30    PR tui/25597; or the upstream Python bug
31    https://bugs.python.org/issue20768.  */
32 #include "gdb_curses.h"
33
34 #include "tui/tui-data.h"
35 #include "tui/tui-io.h"
36 #include "tui/tui-layout.h"
37 #include "tui/tui-wingeneral.h"
38 #include "tui/tui-winsource.h"
39
40 class tui_py_window;
41
42 /* A PyObject representing a TUI window.  */
43
44 struct gdbpy_tui_window
45 {
46   PyObject_HEAD
47
48   /* The TUI window, or nullptr if the window has been deleted.  */
49   tui_py_window *window;
50
51   /* Return true if this object is valid.  */
52   bool is_valid () const;
53 };
54
55 extern PyTypeObject gdbpy_tui_window_object_type
56     CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("gdbpy_tui_window");
57
58 /* A TUI window written in Python.  */
59
60 class tui_py_window : public tui_win_info
61 {
62 public:
63
64   tui_py_window (const char *name, gdbpy_ref<gdbpy_tui_window> wrapper)
65     : m_name (name),
66       m_wrapper (std::move (wrapper))
67   {
68     m_wrapper->window = this;
69   }
70
71   ~tui_py_window ();
72
73   DISABLE_COPY_AND_ASSIGN (tui_py_window);
74
75   /* Set the "user window" to the indicated reference.  The user
76      window is the object returned the by user-defined window
77      constructor.  */
78   void set_user_window (gdbpy_ref<> &&user_window)
79   {
80     m_window = std::move (user_window);
81   }
82
83   const char *name () const override
84   {
85     return m_name.c_str ();
86   }
87
88   void rerender () override;
89   void do_scroll_vertical (int num_to_scroll) override;
90   void do_scroll_horizontal (int num_to_scroll) override;
91
92   void refresh_window () override
93   {
94     if (m_inner_window != nullptr)
95       {
96         wnoutrefresh (handle.get ());
97         touchwin (m_inner_window.get ());
98         tui_wrefresh (m_inner_window.get ());
99       }
100     else
101       tui_win_info::refresh_window ();
102   }
103
104   /* Erase and re-box the window.  */
105   void erase ()
106   {
107     if (is_visible () && m_inner_window != nullptr)
108       {
109         werase (m_inner_window.get ());
110         check_and_display_highlight_if_needed ();
111       }
112   }
113
114   /* Write STR to the window.  FULL_WINDOW is true to erase the window
115      contents beforehand.  */
116   void output (const char *str, bool full_window);
117
118   /* A helper function to compute the viewport width.  */
119   int viewport_width () const
120   {
121     return std::max (0, width - 2);
122   }
123
124   /* A helper function to compute the viewport height.  */
125   int viewport_height () const
126   {
127     return std::max (0, height - 2);
128   }
129
130 private:
131
132   /* The name of this window.  */
133   std::string m_name;
134
135   /* We make our own inner window, so that it is easy to print without
136      overwriting the border.  */
137   std::unique_ptr<WINDOW, curses_deleter> m_inner_window;
138
139   /* The underlying Python window object.  */
140   gdbpy_ref<> m_window;
141
142   /* The Python wrapper for this object.  */
143   gdbpy_ref<gdbpy_tui_window> m_wrapper;
144 };
145
146 /* See gdbpy_tui_window declaration above.  */
147
148 bool
149 gdbpy_tui_window::is_valid () const
150 {
151   return window != nullptr && tui_active;
152 }
153
154 tui_py_window::~tui_py_window ()
155 {
156   gdbpy_enter enter_py (get_current_arch (), current_language);
157
158   /* This can be null if the user-provided Python construction
159      function failed.  */
160   if (m_window != nullptr
161       && PyObject_HasAttrString (m_window.get (), "close"))
162     {
163       gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "close",
164                                                nullptr));
165       if (result == nullptr)
166         gdbpy_print_stack ();
167     }
168
169   /* Unlink.  */
170   m_wrapper->window = nullptr;
171   /* Explicitly free the Python references.  We have to do this
172      manually because we need to hold the GIL while doing so.  */
173   m_wrapper.reset (nullptr);
174   m_window.reset (nullptr);
175 }
176
177 void
178 tui_py_window::rerender ()
179 {
180   tui_win_info::rerender ();
181
182   gdbpy_enter enter_py (get_current_arch (), current_language);
183
184   int h = viewport_height ();
185   int w = viewport_width ();
186   if (h == 0 || w == 0)
187     {
188       /* The window would be too small, so just remove the
189          contents.  */
190       m_inner_window.reset (nullptr);
191       return;
192     }
193   m_inner_window.reset (newwin (h, w, y + 1, x + 1));
194
195   if (PyObject_HasAttrString (m_window.get (), "render"))
196     {
197       gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "render",
198                                                nullptr));
199       if (result == nullptr)
200         gdbpy_print_stack ();
201     }
202 }
203
204 void
205 tui_py_window::do_scroll_horizontal (int num_to_scroll)
206 {
207   gdbpy_enter enter_py (get_current_arch (), current_language);
208
209   if (PyObject_HasAttrString (m_window.get (), "hscroll"))
210     {
211       gdbpy_ref<> result (PyObject_CallMethod (m_window.get(), "hscroll",
212                                                "i", num_to_scroll, nullptr));
213       if (result == nullptr)
214         gdbpy_print_stack ();
215     }
216 }
217
218 void
219 tui_py_window::do_scroll_vertical (int num_to_scroll)
220 {
221   gdbpy_enter enter_py (get_current_arch (), current_language);
222
223   if (PyObject_HasAttrString (m_window.get (), "vscroll"))
224     {
225       gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "vscroll",
226                                                "i", num_to_scroll, nullptr));
227       if (result == nullptr)
228         gdbpy_print_stack ();
229     }
230 }
231
232 void
233 tui_py_window::output (const char *text, bool full_window)
234 {
235   if (m_inner_window != nullptr)
236     {
237       if (full_window)
238         werase (m_inner_window.get ());
239
240       tui_puts (text, m_inner_window.get ());
241       if (full_window)
242         check_and_display_highlight_if_needed ();
243       else
244         tui_wrefresh (m_inner_window.get ());
245     }
246 }
247
248 \f
249
250 /* A callable that is used to create a TUI window.  It wraps the
251    user-supplied window constructor.  */
252
253 class gdbpy_tui_window_maker
254 {
255 public:
256
257   explicit gdbpy_tui_window_maker (gdbpy_ref<> &&constr)
258     : m_constr (std::move (constr))
259   {
260   }
261
262   ~gdbpy_tui_window_maker ();
263
264   gdbpy_tui_window_maker (gdbpy_tui_window_maker &&other) noexcept
265     : m_constr (std::move (other.m_constr))
266   {
267   }
268
269   gdbpy_tui_window_maker (const gdbpy_tui_window_maker &other)
270   {
271     gdbpy_enter enter_py (get_current_arch (), current_language);
272     m_constr = other.m_constr;
273   }
274
275   gdbpy_tui_window_maker &operator= (gdbpy_tui_window_maker &&other)
276   {
277     m_constr = std::move (other.m_constr);
278     return *this;
279   }
280
281   gdbpy_tui_window_maker &operator= (const gdbpy_tui_window_maker &other)
282   {
283     gdbpy_enter enter_py (get_current_arch (), current_language);
284     m_constr = other.m_constr;
285     return *this;
286   }
287
288   tui_win_info *operator() (const char *name);
289
290 private:
291
292   /* A constructor that is called to make a TUI window.  */
293   gdbpy_ref<> m_constr;
294 };
295
296 gdbpy_tui_window_maker::~gdbpy_tui_window_maker ()
297 {
298   gdbpy_enter enter_py (get_current_arch (), current_language);
299   m_constr.reset (nullptr);
300 }
301
302 tui_win_info *
303 gdbpy_tui_window_maker::operator() (const char *win_name)
304 {
305   gdbpy_enter enter_py (get_current_arch (), current_language);
306
307   gdbpy_ref<gdbpy_tui_window> wrapper
308     (PyObject_New (gdbpy_tui_window, &gdbpy_tui_window_object_type));
309   if (wrapper == nullptr)
310     {
311       gdbpy_print_stack ();
312       return nullptr;
313     }
314
315   std::unique_ptr<tui_py_window> window
316     (new tui_py_window (win_name, wrapper));
317
318   gdbpy_ref<> user_window
319     (PyObject_CallFunctionObjArgs (m_constr.get (),
320                                    (PyObject *) wrapper.get (),
321                                    nullptr));
322   if (user_window == nullptr)
323     {
324       gdbpy_print_stack ();
325       return nullptr;
326     }
327
328   window->set_user_window (std::move (user_window));
329   /* Window is now owned by the TUI.  */
330   return window.release ();
331 }
332
333 /* Implement "gdb.register_window_type".  */
334
335 PyObject *
336 gdbpy_register_tui_window (PyObject *self, PyObject *args, PyObject *kw)
337 {
338   static const char *keywords[] = { "name", "constructor", nullptr };
339
340   const char *name;
341   PyObject *cons_obj;
342
343   if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sO", keywords,
344                                         &name, &cons_obj))
345     return nullptr;
346
347   try
348     {
349       gdbpy_tui_window_maker constr (gdbpy_ref<>::new_reference (cons_obj));
350       tui_register_window (name, constr);
351     }
352   catch (const gdb_exception &except)
353     {
354       gdbpy_convert_exception (except);
355       return nullptr;
356     }
357
358   Py_RETURN_NONE;
359 }
360
361 \f
362
363 /* Require that "Window" be a valid window.  */
364
365 #define REQUIRE_WINDOW(Window)                                  \
366     do {                                                        \
367       if (!(Window)->is_valid ())                               \
368         return PyErr_Format (PyExc_RuntimeError,                \
369                              _("TUI window is invalid."));      \
370     } while (0)
371
372 /* Require that "Window" be a valid window.  */
373
374 #define REQUIRE_WINDOW_FOR_SETTER(Window)                       \
375     do {                                                        \
376       if (!(Window)->is_valid ())                               \
377         {                                                       \
378           PyErr_Format (PyExc_RuntimeError,                     \
379                         _("TUI window is invalid."));           \
380           return -1;                                            \
381         }                                                       \
382     } while (0)
383
384 /* Python function which checks the validity of a TUI window
385    object.  */
386 static PyObject *
387 gdbpy_tui_is_valid (PyObject *self, PyObject *args)
388 {
389   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
390
391   if (win->is_valid ())
392     Py_RETURN_TRUE;
393   Py_RETURN_FALSE;
394 }
395
396 /* Python function that erases the TUI window.  */
397 static PyObject *
398 gdbpy_tui_erase (PyObject *self, PyObject *args)
399 {
400   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
401
402   REQUIRE_WINDOW (win);
403
404   win->window->erase ();
405
406   Py_RETURN_NONE;
407 }
408
409 /* Python function that writes some text to a TUI window.  */
410 static PyObject *
411 gdbpy_tui_write (PyObject *self, PyObject *args)
412 {
413   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
414   const char *text;
415   int full_window = 0;
416
417   if (!PyArg_ParseTuple (args, "s|i", &text, &full_window))
418     return nullptr;
419
420   REQUIRE_WINDOW (win);
421
422   win->window->output (text, full_window);
423
424   Py_RETURN_NONE;
425 }
426
427 /* Return the width of the TUI window.  */
428 static PyObject *
429 gdbpy_tui_width (PyObject *self, void *closure)
430 {
431   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
432   REQUIRE_WINDOW (win);
433   gdbpy_ref<> result
434     = gdb_py_object_from_longest (win->window->viewport_width ());
435   return result.release ();
436 }
437
438 /* Return the height of the TUI window.  */
439 static PyObject *
440 gdbpy_tui_height (PyObject *self, void *closure)
441 {
442   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
443   REQUIRE_WINDOW (win);
444   gdbpy_ref<> result
445     = gdb_py_object_from_longest (win->window->viewport_height ());
446   return result.release ();
447 }
448
449 /* Return the title of the TUI window.  */
450 static PyObject *
451 gdbpy_tui_title (PyObject *self, void *closure)
452 {
453   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
454   REQUIRE_WINDOW (win);
455   return host_string_to_python_string (win->window->title.c_str ()).release ();
456 }
457
458 /* Set the title of the TUI window.  */
459 static int
460 gdbpy_tui_set_title (PyObject *self, PyObject *newvalue, void *closure)
461 {
462   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
463
464   REQUIRE_WINDOW_FOR_SETTER (win);
465
466   if (newvalue == nullptr)
467     {
468       PyErr_Format (PyExc_TypeError, _("Cannot delete \"title\" attribute."));
469       return -1;
470     }
471
472   gdb::unique_xmalloc_ptr<char> value
473     = python_string_to_host_string (newvalue);
474   if (value == nullptr)
475     return -1;
476
477   win->window->title = value.get ();
478   return 0;
479 }
480
481 static gdb_PyGetSetDef tui_object_getset[] =
482 {
483   { "width", gdbpy_tui_width, NULL, "Width of the window.", NULL },
484   { "height", gdbpy_tui_height, NULL, "Height of the window.", NULL },
485   { "title", gdbpy_tui_title, gdbpy_tui_set_title, "Title of the window.",
486     NULL },
487   { NULL }  /* Sentinel */
488 };
489
490 static PyMethodDef tui_object_methods[] =
491 {
492   { "is_valid", gdbpy_tui_is_valid, METH_NOARGS,
493     "is_valid () -> Boolean\n\
494 Return true if this TUI window is valid, false if not." },
495   { "erase", gdbpy_tui_erase, METH_NOARGS,
496     "Erase the TUI window." },
497   { "write", (PyCFunction) gdbpy_tui_write, METH_VARARGS,
498     "Append a string to the TUI window." },
499   { NULL } /* Sentinel.  */
500 };
501
502 PyTypeObject gdbpy_tui_window_object_type =
503 {
504   PyVarObject_HEAD_INIT (NULL, 0)
505   "gdb.TuiWindow",                /*tp_name*/
506   sizeof (gdbpy_tui_window),      /*tp_basicsize*/
507   0,                              /*tp_itemsize*/
508   0,                              /*tp_dealloc*/
509   0,                              /*tp_print*/
510   0,                              /*tp_getattr*/
511   0,                              /*tp_setattr*/
512   0,                              /*tp_compare*/
513   0,                              /*tp_repr*/
514   0,                              /*tp_as_number*/
515   0,                              /*tp_as_sequence*/
516   0,                              /*tp_as_mapping*/
517   0,                              /*tp_hash */
518   0,                              /*tp_call*/
519   0,                              /*tp_str*/
520   0,                              /*tp_getattro*/
521   0,                              /*tp_setattro */
522   0,                              /*tp_as_buffer*/
523   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
524   "GDB TUI window object",        /* tp_doc */
525   0,                              /* tp_traverse */
526   0,                              /* tp_clear */
527   0,                              /* tp_richcompare */
528   0,                              /* tp_weaklistoffset */
529   0,                              /* tp_iter */
530   0,                              /* tp_iternext */
531   tui_object_methods,             /* tp_methods */
532   0,                              /* tp_members */
533   tui_object_getset,              /* tp_getset */
534   0,                              /* tp_base */
535   0,                              /* tp_dict */
536   0,                              /* tp_descr_get */
537   0,                              /* tp_descr_set */
538   0,                              /* tp_dictoffset */
539   0,                              /* tp_init */
540   0,                              /* tp_alloc */
541 };
542
543 #endif /* TUI */
544
545 /* Initialize this module.  */
546
547 int
548 gdbpy_initialize_tui ()
549 {
550 #ifdef TUI
551   gdbpy_tui_window_object_type.tp_new = PyType_GenericNew;
552   if (PyType_Ready (&gdbpy_tui_window_object_type) < 0)
553     return -1;
554 #endif  /* TUI */
555
556   return 0;
557 }
This page took 0.060868 seconds and 4 git commands to generate.