Update readelf's display of RELR sections to include the number of locations relocated
[binutils-gdb.git] / gdb / python / py-tui.c
blob5dcec4bd2b11cf82c33c56b7c1cf7de2e9a51924
1 /* TUI windows implemented in Python
3 Copyright (C) 2020-2024 Free Software Foundation, Inc.
5 This file is part of GDB.
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.
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.
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/>. */
21 #include "arch-utils.h"
22 #include "python-internal.h"
23 #include "gdbsupport/intrusive_list.h"
25 #ifdef TUI
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"
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"
40 class tui_py_window;
42 /* A PyObject representing a TUI window. */
44 struct gdbpy_tui_window
46 PyObject_HEAD
48 /* The TUI window, or nullptr if the window has been deleted. */
49 tui_py_window *window;
51 /* Return true if this object is valid. */
52 bool is_valid () const;
55 extern PyTypeObject gdbpy_tui_window_object_type
56 CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("gdbpy_tui_window");
58 /* A TUI window written in Python. */
60 class tui_py_window : public tui_win_info
62 public:
64 tui_py_window (const char *name, gdbpy_ref<gdbpy_tui_window> wrapper)
65 : m_name (name),
66 m_wrapper (std::move (wrapper))
68 m_wrapper->window = this;
71 ~tui_py_window ();
73 DISABLE_COPY_AND_ASSIGN (tui_py_window);
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)
80 m_window = std::move (user_window);
83 const char *name () const override
85 return m_name.c_str ();
88 void rerender () override;
89 void do_scroll_vertical (int num_to_scroll) override;
90 void do_scroll_horizontal (int num_to_scroll) override;
92 void refresh_window () override
94 if (m_inner_window != nullptr)
96 wnoutrefresh (handle.get ());
97 touchwin (m_inner_window.get ());
98 tui_wrefresh (m_inner_window.get ());
100 else
101 tui_win_info::refresh_window ();
104 void resize (int height, int width, int origin_x, int origin_y) override;
106 void click (int mouse_x, int mouse_y, int mouse_button) override;
108 /* Erase and re-box the window. */
109 void erase ()
111 if (is_visible () && m_inner_window != nullptr)
113 werase (m_inner_window.get ());
114 check_and_display_highlight_if_needed ();
118 /* Write STR to the window. FULL_WINDOW is true to erase the window
119 contents beforehand. */
120 void output (const char *str, bool full_window);
122 /* A helper function to compute the viewport width. */
123 int viewport_width () const
125 return std::max (0, width - 2);
128 /* A helper function to compute the viewport height. */
129 int viewport_height () const
131 return std::max (0, height - 2);
134 private:
136 /* The name of this window. */
137 std::string m_name;
139 /* We make our own inner window, so that it is easy to print without
140 overwriting the border. */
141 std::unique_ptr<WINDOW, curses_deleter> m_inner_window;
143 /* The underlying Python window object. */
144 gdbpy_ref<> m_window;
146 /* The Python wrapper for this object. */
147 gdbpy_ref<gdbpy_tui_window> m_wrapper;
150 /* See gdbpy_tui_window declaration above. */
152 bool
153 gdbpy_tui_window::is_valid () const
155 return window != nullptr && tui_active;
158 tui_py_window::~tui_py_window ()
160 gdbpy_enter enter_py;
162 /* This can be null if the user-provided Python construction
163 function failed. */
164 if (m_window != nullptr
165 && PyObject_HasAttrString (m_window.get (), "close"))
167 gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "close",
168 nullptr));
169 if (result == nullptr)
170 gdbpy_print_stack ();
173 /* Unlink. */
174 m_wrapper->window = nullptr;
175 /* Explicitly free the Python references. We have to do this
176 manually because we need to hold the GIL while doing so. */
177 m_wrapper.reset (nullptr);
178 m_window.reset (nullptr);
181 void
182 tui_py_window::rerender ()
184 tui_win_info::rerender ();
186 gdbpy_enter enter_py;
188 int h = viewport_height ();
189 int w = viewport_width ();
190 if (h == 0 || w == 0)
192 /* The window would be too small, so just remove the
193 contents. */
194 m_inner_window.reset (nullptr);
195 return;
197 m_inner_window.reset (newwin (h, w, y + 1, x + 1));
199 if (PyObject_HasAttrString (m_window.get (), "render"))
201 gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "render",
202 nullptr));
203 if (result == nullptr)
204 gdbpy_print_stack ();
208 void
209 tui_py_window::do_scroll_horizontal (int num_to_scroll)
211 gdbpy_enter enter_py;
213 if (PyObject_HasAttrString (m_window.get (), "hscroll"))
215 gdbpy_ref<> result (PyObject_CallMethod (m_window.get(), "hscroll",
216 "i", num_to_scroll, nullptr));
217 if (result == nullptr)
218 gdbpy_print_stack ();
222 void
223 tui_py_window::do_scroll_vertical (int num_to_scroll)
225 gdbpy_enter enter_py;
227 if (PyObject_HasAttrString (m_window.get (), "vscroll"))
229 gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "vscroll",
230 "i", num_to_scroll, nullptr));
231 if (result == nullptr)
232 gdbpy_print_stack ();
236 void
237 tui_py_window::resize (int height_, int width_, int origin_x_, int origin_y_)
239 m_inner_window.reset (nullptr);
241 tui_win_info::resize (height_, width_, origin_x_, origin_y_);
244 void
245 tui_py_window::click (int mouse_x, int mouse_y, int mouse_button)
247 gdbpy_enter enter_py;
249 if (PyObject_HasAttrString (m_window.get (), "click"))
251 gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "click",
252 "iii", mouse_x, mouse_y,
253 mouse_button));
254 if (result == nullptr)
255 gdbpy_print_stack ();
259 void
260 tui_py_window::output (const char *text, bool full_window)
262 if (m_inner_window != nullptr)
264 if (full_window)
265 werase (m_inner_window.get ());
267 tui_puts (text, m_inner_window.get ());
268 if (full_window)
269 check_and_display_highlight_if_needed ();
270 else
271 tui_wrefresh (m_inner_window.get ());
277 /* A callable that is used to create a TUI window. It wraps the
278 user-supplied window constructor. */
280 class gdbpy_tui_window_maker
281 : public intrusive_list_node<gdbpy_tui_window_maker>
283 public:
285 explicit gdbpy_tui_window_maker (gdbpy_ref<> &&constr)
286 : m_constr (std::move (constr))
288 m_window_maker_list.push_back (*this);
291 ~gdbpy_tui_window_maker ();
293 gdbpy_tui_window_maker (gdbpy_tui_window_maker &&other) noexcept
294 : m_constr (std::move (other.m_constr))
296 m_window_maker_list.push_back (*this);
299 gdbpy_tui_window_maker (const gdbpy_tui_window_maker &other)
301 gdbpy_enter enter_py;
302 m_constr = other.m_constr;
303 m_window_maker_list.push_back (*this);
306 gdbpy_tui_window_maker &operator= (gdbpy_tui_window_maker &&other)
308 m_constr = std::move (other.m_constr);
309 return *this;
312 gdbpy_tui_window_maker &operator= (const gdbpy_tui_window_maker &other)
314 gdbpy_enter enter_py;
315 m_constr = other.m_constr;
316 return *this;
319 tui_win_info *operator() (const char *name);
321 /* Reset the m_constr field of all gdbpy_tui_window_maker objects back to
322 nullptr, this will allow the Python object referenced to be
323 deallocated. This function is intended to be called when GDB is
324 shutting down the Python interpreter to allow all Python objects to be
325 deallocated and cleaned up. */
326 static void
327 invalidate_all ()
329 gdbpy_enter enter_py;
330 for (gdbpy_tui_window_maker &f : m_window_maker_list)
331 f.m_constr.reset (nullptr);
334 private:
336 /* A constructor that is called to make a TUI window. */
337 gdbpy_ref<> m_constr;
339 /* A global list of all gdbpy_tui_window_maker objects. */
340 static intrusive_list<gdbpy_tui_window_maker> m_window_maker_list;
343 /* See comment in class declaration above. */
345 intrusive_list<gdbpy_tui_window_maker>
346 gdbpy_tui_window_maker::m_window_maker_list;
348 gdbpy_tui_window_maker::~gdbpy_tui_window_maker ()
350 /* Remove this gdbpy_tui_window_maker from the global list. */
351 if (is_linked ())
352 m_window_maker_list.erase (m_window_maker_list.iterator_to (*this));
354 if (m_constr != nullptr)
356 gdbpy_enter enter_py;
357 m_constr.reset (nullptr);
361 tui_win_info *
362 gdbpy_tui_window_maker::operator() (const char *win_name)
364 gdbpy_enter enter_py;
366 gdbpy_ref<gdbpy_tui_window> wrapper
367 (PyObject_New (gdbpy_tui_window, &gdbpy_tui_window_object_type));
368 if (wrapper == nullptr)
370 gdbpy_print_stack ();
371 return nullptr;
374 std::unique_ptr<tui_py_window> window
375 (new tui_py_window (win_name, wrapper));
377 /* There's only two ways that m_constr can be reset back to nullptr,
378 first when the parent gdbpy_tui_window_maker object is deleted, in
379 which case it should be impossible to call this method, or second, as
380 a result of a gdbpy_tui_window_maker::invalidate_all call, but this is
381 only called when GDB's Python interpreter is being shut down, after
382 which, this method should not be called. */
383 gdb_assert (m_constr != nullptr);
385 gdbpy_ref<> user_window
386 (PyObject_CallFunctionObjArgs (m_constr.get (),
387 (PyObject *) wrapper.get (),
388 nullptr));
389 if (user_window == nullptr)
391 gdbpy_print_stack ();
392 return nullptr;
395 window->set_user_window (std::move (user_window));
396 /* Window is now owned by the TUI. */
397 return window.release ();
400 /* Implement "gdb.register_window_type". */
402 PyObject *
403 gdbpy_register_tui_window (PyObject *self, PyObject *args, PyObject *kw)
405 static const char *keywords[] = { "name", "constructor", nullptr };
407 const char *name;
408 PyObject *cons_obj;
410 if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sO", keywords,
411 &name, &cons_obj))
412 return nullptr;
416 gdbpy_tui_window_maker constr (gdbpy_ref<>::new_reference (cons_obj));
417 tui_register_window (name, constr);
419 catch (const gdb_exception &except)
421 gdbpy_convert_exception (except);
422 return nullptr;
425 Py_RETURN_NONE;
430 /* Require that "Window" be a valid window. */
432 #define REQUIRE_WINDOW(Window) \
433 do { \
434 if (!(Window)->is_valid ()) \
435 return PyErr_Format (PyExc_RuntimeError, \
436 _("TUI window is invalid.")); \
437 } while (0)
439 /* Require that "Window" be a valid window. */
441 #define REQUIRE_WINDOW_FOR_SETTER(Window) \
442 do { \
443 if (!(Window)->is_valid ()) \
445 PyErr_Format (PyExc_RuntimeError, \
446 _("TUI window is invalid.")); \
447 return -1; \
449 } while (0)
451 /* Python function which checks the validity of a TUI window
452 object. */
453 static PyObject *
454 gdbpy_tui_is_valid (PyObject *self, PyObject *args)
456 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
458 if (win->is_valid ())
459 Py_RETURN_TRUE;
460 Py_RETURN_FALSE;
463 /* Python function that erases the TUI window. */
464 static PyObject *
465 gdbpy_tui_erase (PyObject *self, PyObject *args)
467 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
469 REQUIRE_WINDOW (win);
471 win->window->erase ();
473 Py_RETURN_NONE;
476 /* Python function that writes some text to a TUI window. */
477 static PyObject *
478 gdbpy_tui_write (PyObject *self, PyObject *args, PyObject *kw)
480 static const char *keywords[] = { "string", "full_window", nullptr };
482 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
483 const char *text;
484 int full_window = 0;
486 if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|i", keywords,
487 &text, &full_window))
488 return nullptr;
490 REQUIRE_WINDOW (win);
492 win->window->output (text, full_window);
494 Py_RETURN_NONE;
497 /* Return the width of the TUI window. */
498 static PyObject *
499 gdbpy_tui_width (PyObject *self, void *closure)
501 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
502 REQUIRE_WINDOW (win);
503 gdbpy_ref<> result
504 = gdb_py_object_from_longest (win->window->viewport_width ());
505 return result.release ();
508 /* Return the height of the TUI window. */
509 static PyObject *
510 gdbpy_tui_height (PyObject *self, void *closure)
512 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
513 REQUIRE_WINDOW (win);
514 gdbpy_ref<> result
515 = gdb_py_object_from_longest (win->window->viewport_height ());
516 return result.release ();
519 /* Return the title of the TUI window. */
520 static PyObject *
521 gdbpy_tui_title (PyObject *self, void *closure)
523 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
524 REQUIRE_WINDOW (win);
525 return host_string_to_python_string (win->window->title ().c_str ()).release ();
528 /* Set the title of the TUI window. */
529 static int
530 gdbpy_tui_set_title (PyObject *self, PyObject *newvalue, void *closure)
532 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
534 REQUIRE_WINDOW_FOR_SETTER (win);
536 if (newvalue == nullptr)
538 PyErr_Format (PyExc_TypeError, _("Cannot delete \"title\" attribute."));
539 return -1;
542 gdb::unique_xmalloc_ptr<char> value
543 = python_string_to_host_string (newvalue);
544 if (value == nullptr)
545 return -1;
547 win->window->set_title (value.get ());
548 return 0;
551 static gdb_PyGetSetDef tui_object_getset[] =
553 { "width", gdbpy_tui_width, NULL, "Width of the window.", NULL },
554 { "height", gdbpy_tui_height, NULL, "Height of the window.", NULL },
555 { "title", gdbpy_tui_title, gdbpy_tui_set_title, "Title of the window.",
556 NULL },
557 { NULL } /* Sentinel */
560 static PyMethodDef tui_object_methods[] =
562 { "is_valid", gdbpy_tui_is_valid, METH_NOARGS,
563 "is_valid () -> Boolean\n\
564 Return true if this TUI window is valid, false if not." },
565 { "erase", gdbpy_tui_erase, METH_NOARGS,
566 "Erase the TUI window." },
567 { "write", (PyCFunction) gdbpy_tui_write, METH_VARARGS | METH_KEYWORDS,
568 "Append a string to the TUI window." },
569 { NULL } /* Sentinel. */
572 PyTypeObject gdbpy_tui_window_object_type =
574 PyVarObject_HEAD_INIT (NULL, 0)
575 "gdb.TuiWindow", /*tp_name*/
576 sizeof (gdbpy_tui_window), /*tp_basicsize*/
577 0, /*tp_itemsize*/
578 0, /*tp_dealloc*/
579 0, /*tp_print*/
580 0, /*tp_getattr*/
581 0, /*tp_setattr*/
582 0, /*tp_compare*/
583 0, /*tp_repr*/
584 0, /*tp_as_number*/
585 0, /*tp_as_sequence*/
586 0, /*tp_as_mapping*/
587 0, /*tp_hash */
588 0, /*tp_call*/
589 0, /*tp_str*/
590 0, /*tp_getattro*/
591 0, /*tp_setattro */
592 0, /*tp_as_buffer*/
593 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
594 "GDB TUI window object", /* tp_doc */
595 0, /* tp_traverse */
596 0, /* tp_clear */
597 0, /* tp_richcompare */
598 0, /* tp_weaklistoffset */
599 0, /* tp_iter */
600 0, /* tp_iternext */
601 tui_object_methods, /* tp_methods */
602 0, /* tp_members */
603 tui_object_getset, /* tp_getset */
604 0, /* tp_base */
605 0, /* tp_dict */
606 0, /* tp_descr_get */
607 0, /* tp_descr_set */
608 0, /* tp_dictoffset */
609 0, /* tp_init */
610 0, /* tp_alloc */
613 #endif /* TUI */
615 /* Initialize this module. */
617 static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
618 gdbpy_initialize_tui ()
620 #ifdef TUI
621 gdbpy_tui_window_object_type.tp_new = PyType_GenericNew;
622 if (PyType_Ready (&gdbpy_tui_window_object_type) < 0)
623 return -1;
624 #endif /* TUI */
626 return 0;
629 /* Finalize this module. */
631 static void
632 gdbpy_finalize_tui ()
634 #ifdef TUI
635 gdbpy_tui_window_maker::invalidate_all ();
636 #endif /* TUI */
639 GDBPY_INITIALIZE_FILE (gdbpy_initialize_tui, gdbpy_finalize_tui);