]> Git Repo - binutils.git/blob - gdb/python/py-tui.c
Change get_objfile_arch to a method on objfile
[binutils.git] / gdb / python / py-tui.c
1 /* TUI windows implemented in Python
2
3    Copyright (C) 2020 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
52 extern PyTypeObject gdbpy_tui_window_object_type
53     CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("gdbpy_tui_window");
54
55 /* A TUI window written in Python.  */
56
57 class tui_py_window : public tui_win_info
58 {
59 public:
60
61   tui_py_window (const char *name, gdbpy_ref<gdbpy_tui_window> wrapper)
62     : m_name (name),
63       m_wrapper (std::move (wrapper))
64   {
65     m_wrapper->window = this;
66   }
67
68   ~tui_py_window ();
69
70   DISABLE_COPY_AND_ASSIGN (tui_py_window);
71
72   /* Set the "user window" to the indicated reference.  The user
73      window is the object returned the by user-defined window
74      constructor.  */
75   void set_user_window (gdbpy_ref<> &&user_window)
76   {
77     m_window = std::move (user_window);
78   }
79
80   const char *name () const override
81   {
82     return m_name.c_str ();
83   }
84
85   void rerender () override;
86   void do_scroll_vertical (int num_to_scroll) override;
87   void do_scroll_horizontal (int num_to_scroll) override;
88
89   /* Erase and re-box the window.  */
90   void erase ()
91   {
92     if (is_visible ())
93       {
94         werase (handle.get ());
95         check_and_display_highlight_if_needed ();
96         cursor_x = 0;
97         cursor_y = 0;
98       }
99   }
100
101   /* Write STR to the window.  */
102   void output (const char *str);
103
104   /* A helper function to compute the viewport width.  */
105   int viewport_width () const
106   {
107     return std::max (0, width - 2);
108   }
109
110   /* A helper function to compute the viewport height.  */
111   int viewport_height () const
112   {
113     return std::max (0, height - 2);
114   }
115
116 private:
117
118   /* Location of the cursor.  */
119   int cursor_x = 0;
120   int cursor_y = 0;
121
122   /* The name of this window.  */
123   std::string m_name;
124
125   /* The underlying Python window object.  */
126   gdbpy_ref<> m_window;
127
128   /* The Python wrapper for this object.  */
129   gdbpy_ref<gdbpy_tui_window> m_wrapper;
130 };
131
132 tui_py_window::~tui_py_window ()
133 {
134   gdbpy_enter enter_py (get_current_arch (), current_language);
135
136   if (PyObject_HasAttrString (m_window.get (), "close"))
137     {
138       gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "close",
139                                                nullptr));
140       if (result == nullptr)
141         gdbpy_print_stack ();
142     }
143
144   /* Unlink.  */
145   m_wrapper->window = nullptr;
146   /* Explicitly free the Python references.  We have to do this
147      manually because we need to hold the GIL while doing so.  */
148   m_wrapper.reset (nullptr);
149   m_window.reset (nullptr);
150 }
151
152 void
153 tui_py_window::rerender ()
154 {
155   gdbpy_enter enter_py (get_current_arch (), current_language);
156
157   if (PyObject_HasAttrString (m_window.get (), "render"))
158     {
159       gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "render",
160                                                nullptr));
161       if (result == nullptr)
162         gdbpy_print_stack ();
163     }
164 }
165
166 void
167 tui_py_window::do_scroll_horizontal (int num_to_scroll)
168 {
169   gdbpy_enter enter_py (get_current_arch (), current_language);
170
171   if (PyObject_HasAttrString (m_window.get (), "hscroll"))
172     {
173       gdbpy_ref<> result (PyObject_CallMethod (m_window.get(), "hscroll",
174                                                "i", num_to_scroll, nullptr));
175       if (result == nullptr)
176         gdbpy_print_stack ();
177     }
178 }
179
180 void
181 tui_py_window::do_scroll_vertical (int num_to_scroll)
182 {
183   gdbpy_enter enter_py (get_current_arch (), current_language);
184
185   if (PyObject_HasAttrString (m_window.get (), "vscroll"))
186     {
187       gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "vscroll",
188                                                "i", num_to_scroll, nullptr));
189       if (result == nullptr)
190         gdbpy_print_stack ();
191     }
192 }
193
194 void
195 tui_py_window::output (const char *text)
196 {
197   int vwidth = viewport_width ();
198
199   while (cursor_y < viewport_height () && *text != '\0')
200     {
201       wmove (handle.get (), cursor_y + 1, cursor_x + 1);
202
203       std::string line = tui_copy_source_line (&text, 0, 0,
204                                                vwidth - cursor_x, 0);
205       tui_puts (line.c_str (), handle.get ());
206
207       if (*text == '\n')
208         {
209           ++text;
210           ++cursor_y;
211           cursor_x = 0;
212         }
213       else
214         cursor_x = getcurx (handle.get ()) - 1;
215     }
216
217   wrefresh (handle.get ());
218 }
219
220 \f
221
222 /* A callable that is used to create a TUI window.  It wraps the
223    user-supplied window constructor.  */
224
225 class gdbpy_tui_window_maker
226 {
227 public:
228
229   explicit gdbpy_tui_window_maker (gdbpy_ref<> &&constr)
230     : m_constr (std::move (constr))
231   {
232   }
233
234   ~gdbpy_tui_window_maker ();
235
236   gdbpy_tui_window_maker (gdbpy_tui_window_maker &&other)
237     : m_constr (std::move (other.m_constr))
238   {
239   }
240
241   gdbpy_tui_window_maker (const gdbpy_tui_window_maker &other)
242   {
243     gdbpy_enter enter_py (get_current_arch (), current_language);
244     m_constr = other.m_constr;
245   }
246
247   gdbpy_tui_window_maker &operator= (gdbpy_tui_window_maker &&other)
248   {
249     m_constr = std::move (other.m_constr);
250     return *this;
251   }
252
253   gdbpy_tui_window_maker &operator= (const gdbpy_tui_window_maker &other)
254   {
255     gdbpy_enter enter_py (get_current_arch (), current_language);
256     m_constr = other.m_constr;
257     return *this;
258   }
259
260   tui_win_info *operator() (const char *name);
261
262 private:
263
264   /* A constructor that is called to make a TUI window.  */
265   gdbpy_ref<> m_constr;
266 };
267
268 gdbpy_tui_window_maker::~gdbpy_tui_window_maker ()
269 {
270   gdbpy_enter enter_py (get_current_arch (), current_language);
271   m_constr.reset (nullptr);
272 }
273
274 tui_win_info *
275 gdbpy_tui_window_maker::operator() (const char *win_name)
276 {
277   gdbpy_enter enter_py (get_current_arch (), current_language);
278
279   gdbpy_ref<gdbpy_tui_window> wrapper
280     (PyObject_New (gdbpy_tui_window, &gdbpy_tui_window_object_type));
281   if (wrapper == nullptr)
282     {
283       gdbpy_print_stack ();
284       return nullptr;
285     }
286
287   std::unique_ptr<tui_py_window> window
288     (new tui_py_window (win_name, wrapper));
289
290   gdbpy_ref<> user_window
291     (PyObject_CallFunctionObjArgs (m_constr.get (),
292                                    (PyObject *) wrapper.get (),
293                                    nullptr));
294   if (user_window == nullptr)
295     {
296       gdbpy_print_stack ();
297       return nullptr;
298     }
299
300   window->set_user_window (std::move (user_window));
301   /* Window is now owned by the TUI.  */
302   return window.release ();
303 }
304
305 /* Implement "gdb.register_window_type".  */
306
307 PyObject *
308 gdbpy_register_tui_window (PyObject *self, PyObject *args, PyObject *kw)
309 {
310   static const char *keywords[] = { "name", "constructor", nullptr };
311
312   const char *name;
313   PyObject *cons_obj;
314
315   if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sO", keywords,
316                                         &name, &cons_obj))
317     return nullptr;
318
319   try
320     {
321       gdbpy_tui_window_maker constr (gdbpy_ref<>::new_reference (cons_obj));
322       tui_register_window (name, constr);
323     }
324   catch (const gdb_exception &except)
325     {
326       gdbpy_convert_exception (except);
327       return nullptr;
328     }
329
330   Py_RETURN_NONE;
331 }
332
333 \f
334
335 /* Require that "Window" be a valid window.  */
336
337 #define REQUIRE_WINDOW(Window)                                  \
338     do {                                                        \
339       if ((Window)->window == nullptr)                          \
340         return PyErr_Format (PyExc_RuntimeError,                \
341                              _("TUI window is invalid."));      \
342     } while (0)
343
344 /* Python function which checks the validity of a TUI window
345    object.  */
346 static PyObject *
347 gdbpy_tui_is_valid (PyObject *self, PyObject *args)
348 {
349   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
350
351   if (win->window != nullptr)
352     Py_RETURN_TRUE;
353   Py_RETURN_FALSE;
354 }
355
356 /* Python function that erases the TUI window.  */
357 static PyObject *
358 gdbpy_tui_erase (PyObject *self, PyObject *args)
359 {
360   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
361
362   REQUIRE_WINDOW (win);
363
364   win->window->erase ();
365
366   Py_RETURN_NONE;
367 }
368
369 /* Python function that writes some text to a TUI window.  */
370 static PyObject *
371 gdbpy_tui_write (PyObject *self, PyObject *args)
372 {
373   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
374   const char *text;
375
376   if (!PyArg_ParseTuple (args, "s", &text))
377     return nullptr;
378
379   REQUIRE_WINDOW (win);
380
381   win->window->output (text);
382
383   Py_RETURN_NONE;
384 }
385
386 /* Return the width of the TUI window.  */
387 static PyObject *
388 gdbpy_tui_width (PyObject *self, void *closure)
389 {
390   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
391   REQUIRE_WINDOW (win);
392   return PyLong_FromLong (win->window->viewport_width ());
393 }
394
395 /* Return the height of the TUI window.  */
396 static PyObject *
397 gdbpy_tui_height (PyObject *self, void *closure)
398 {
399   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
400   REQUIRE_WINDOW (win);
401   return PyLong_FromLong (win->window->viewport_height ());
402 }
403
404 /* Return the title of the TUI window.  */
405 static PyObject *
406 gdbpy_tui_title (PyObject *self, void *closure)
407 {
408   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
409   REQUIRE_WINDOW (win);
410   return host_string_to_python_string (win->window->title.c_str ()).release ();
411 }
412
413 /* Set the title of the TUI window.  */
414 static int
415 gdbpy_tui_set_title (PyObject *self, PyObject *newvalue, void *closure)
416 {
417   gdbpy_tui_window *win = (gdbpy_tui_window *) self;
418
419   if (win->window == nullptr)
420     {
421       PyErr_Format (PyExc_RuntimeError, _("TUI window is invalid."));
422       return -1;
423     }
424
425   if (win->window == nullptr)
426     {
427       PyErr_Format (PyExc_TypeError, _("Cannot delete \"title\" attribute."));
428       return -1;
429     }
430
431   gdb::unique_xmalloc_ptr<char> value
432     = python_string_to_host_string (newvalue);
433   if (value == nullptr)
434     return -1;
435
436   win->window->title = value.get ();
437   return 0;
438 }
439
440 static gdb_PyGetSetDef tui_object_getset[] =
441 {
442   { "width", gdbpy_tui_width, NULL, "Width of the window.", NULL },
443   { "height", gdbpy_tui_height, NULL, "Height of the window.", NULL },
444   { "title", gdbpy_tui_title, gdbpy_tui_set_title, "Title of the window.",
445     NULL },
446   { NULL }  /* Sentinel */
447 };
448
449 static PyMethodDef tui_object_methods[] =
450 {
451   { "is_valid", gdbpy_tui_is_valid, METH_NOARGS,
452     "is_valid () -> Boolean\n\
453 Return true if this TUI window is valid, false if not." },
454   { "erase", gdbpy_tui_erase, METH_NOARGS,
455     "Erase the TUI window." },
456   { "write", (PyCFunction) gdbpy_tui_write, METH_VARARGS,
457     "Append a string to the TUI window." },
458   { NULL } /* Sentinel.  */
459 };
460
461 PyTypeObject gdbpy_tui_window_object_type =
462 {
463   PyVarObject_HEAD_INIT (NULL, 0)
464   "gdb.TuiWindow",                /*tp_name*/
465   sizeof (gdbpy_tui_window),      /*tp_basicsize*/
466   0,                              /*tp_itemsize*/
467   0,                              /*tp_dealloc*/
468   0,                              /*tp_print*/
469   0,                              /*tp_getattr*/
470   0,                              /*tp_setattr*/
471   0,                              /*tp_compare*/
472   0,                              /*tp_repr*/
473   0,                              /*tp_as_number*/
474   0,                              /*tp_as_sequence*/
475   0,                              /*tp_as_mapping*/
476   0,                              /*tp_hash */
477   0,                              /*tp_call*/
478   0,                              /*tp_str*/
479   0,                              /*tp_getattro*/
480   0,                              /*tp_setattro */
481   0,                              /*tp_as_buffer*/
482   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
483   "GDB TUI window object",        /* tp_doc */
484   0,                              /* tp_traverse */
485   0,                              /* tp_clear */
486   0,                              /* tp_richcompare */
487   0,                              /* tp_weaklistoffset */
488   0,                              /* tp_iter */
489   0,                              /* tp_iternext */
490   tui_object_methods,             /* tp_methods */
491   0,                              /* tp_members */
492   tui_object_getset,              /* tp_getset */
493   0,                              /* tp_base */
494   0,                              /* tp_dict */
495   0,                              /* tp_descr_get */
496   0,                              /* tp_descr_set */
497   0,                              /* tp_dictoffset */
498   0,                              /* tp_init */
499   0,                              /* tp_alloc */
500 };
501
502 #endif /* TUI */
503
504 /* Initialize this module.  */
505
506 int
507 gdbpy_initialize_tui ()
508 {
509 #ifdef TUI
510   gdbpy_tui_window_object_type.tp_new = PyType_GenericNew;
511   if (PyType_Ready (&gdbpy_tui_window_object_type) < 0)
512     return -1;
513 #endif  /* TUI */
514
515   return 0;
516 }
This page took 0.05702 seconds and 4 git commands to generate.