arm, objdump: print obsolote warning when 26-bit set in instructions
[binutils-gdb.git] / gdb / python / py-tui.c
blob09f0a603ea0a3d0b61a87b97286eb38e62f8ff9f
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"
39 #include "observable.h"
40 #include "py-events.h"
41 #include "py-event.h"
43 class tui_py_window;
45 /* A PyObject representing a TUI window. */
47 struct gdbpy_tui_window
49 PyObject_HEAD
51 /* The TUI window, or nullptr if the window has been deleted. */
52 tui_py_window *window;
54 /* Return true if this object is valid. */
55 bool is_valid () const;
58 extern PyTypeObject gdbpy_tui_window_object_type
59 CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("gdbpy_tui_window");
61 /* A TUI window written in Python. */
63 class tui_py_window : public tui_win_info
65 public:
67 tui_py_window (const char *name, gdbpy_ref<gdbpy_tui_window> wrapper)
68 : m_name (name),
69 m_wrapper (std::move (wrapper))
71 m_wrapper->window = this;
74 ~tui_py_window ();
76 DISABLE_COPY_AND_ASSIGN (tui_py_window);
78 /* Set the "user window" to the indicated reference. The user
79 window is the object returned the by user-defined window
80 constructor. */
81 void set_user_window (gdbpy_ref<> &&user_window)
83 m_window = std::move (user_window);
86 const char *name () const override
88 return m_name.c_str ();
91 void rerender () override;
92 void do_scroll_vertical (int num_to_scroll) override;
93 void do_scroll_horizontal (int num_to_scroll) override;
95 void refresh_window () override
97 if (m_inner_window != nullptr)
99 wnoutrefresh (handle.get ());
100 touchwin (m_inner_window.get ());
101 wnoutrefresh (m_inner_window.get ());
103 else
104 tui_win_info::refresh_window ();
107 void resize (int height, int width, int origin_x, int origin_y) override;
109 void click (int mouse_x, int mouse_y, int mouse_button) override;
111 /* Erase and re-box the window. */
112 void erase ()
114 if (is_visible () && m_inner_window != nullptr)
116 werase (m_inner_window.get ());
117 check_and_display_highlight_if_needed ();
121 /* Write STR to the window. FULL_WINDOW is true to erase the window
122 contents beforehand. */
123 void output (const char *str, bool full_window);
125 /* A helper function to compute the viewport width. */
126 int viewport_width () const
128 return std::max (0, width - 2);
131 /* A helper function to compute the viewport height. */
132 int viewport_height () const
134 return std::max (0, height - 2);
137 private:
139 /* The name of this window. */
140 std::string m_name;
142 /* We make our own inner window, so that it is easy to print without
143 overwriting the border. */
144 std::unique_ptr<WINDOW, curses_deleter> m_inner_window;
146 /* The underlying Python window object. */
147 gdbpy_ref<> m_window;
149 /* The Python wrapper for this object. */
150 gdbpy_ref<gdbpy_tui_window> m_wrapper;
153 /* See gdbpy_tui_window declaration above. */
155 bool
156 gdbpy_tui_window::is_valid () const
158 return window != nullptr && tui_active;
161 tui_py_window::~tui_py_window ()
163 gdbpy_enter enter_py;
165 /* This can be null if the user-provided Python construction
166 function failed. */
167 if (m_window != nullptr
168 && PyObject_HasAttrString (m_window.get (), "close"))
170 gdbpy_ref<> result = gdbpy_call_method (m_window, "close");
171 if (result == nullptr)
172 gdbpy_print_stack ();
175 /* Unlink. */
176 m_wrapper->window = nullptr;
177 /* Explicitly free the Python references. We have to do this
178 manually because we need to hold the GIL while doing so. */
179 m_wrapper.reset (nullptr);
180 m_window.reset (nullptr);
183 void
184 tui_py_window::rerender ()
186 tui_batch_rendering batch;
188 tui_win_info::rerender ();
190 gdbpy_enter enter_py;
192 int h = viewport_height ();
193 int w = viewport_width ();
194 if (h == 0 || w == 0)
196 /* The window would be too small, so just remove the
197 contents. */
198 m_inner_window.reset (nullptr);
199 return;
201 m_inner_window.reset (newwin (h, w, y + 1, x + 1));
203 if (PyObject_HasAttrString (m_window.get (), "render"))
205 gdbpy_ref<> result = gdbpy_call_method (m_window, "render");
206 if (result == nullptr)
207 gdbpy_print_stack ();
211 void
212 tui_py_window::do_scroll_horizontal (int num_to_scroll)
214 tui_batch_rendering batch;
216 gdbpy_enter enter_py;
218 if (PyObject_HasAttrString (m_window.get (), "hscroll"))
220 gdbpy_ref<> result = gdbpy_call_method (m_window, "hscroll",
221 num_to_scroll);
222 if (result == nullptr)
223 gdbpy_print_stack ();
227 void
228 tui_py_window::do_scroll_vertical (int num_to_scroll)
230 tui_batch_rendering batch;
232 gdbpy_enter enter_py;
234 if (PyObject_HasAttrString (m_window.get (), "vscroll"))
236 gdbpy_ref<> result = gdbpy_call_method (m_window, "vscroll",
237 num_to_scroll);
238 if (result == nullptr)
239 gdbpy_print_stack ();
243 void
244 tui_py_window::resize (int height_, int width_, int origin_x_, int origin_y_)
246 m_inner_window.reset (nullptr);
248 tui_win_info::resize (height_, width_, origin_x_, origin_y_);
251 void
252 tui_py_window::click (int mouse_x, int mouse_y, int mouse_button)
254 tui_batch_rendering batch;
256 gdbpy_enter enter_py;
258 if (PyObject_HasAttrString (m_window.get (), "click"))
260 gdbpy_ref<> result = gdbpy_call_method (m_window, "click",
261 mouse_x, mouse_y, mouse_button);
262 if (result == nullptr)
263 gdbpy_print_stack ();
267 void
268 tui_py_window::output (const char *text, bool full_window)
270 if (m_inner_window != nullptr)
272 tui_batch_rendering batch;
274 if (full_window)
275 werase (m_inner_window.get ());
277 tui_puts (text, m_inner_window.get ());
278 if (full_window)
279 check_and_display_highlight_if_needed ();
280 else
281 wnoutrefresh (m_inner_window.get ());
287 /* A callable that is used to create a TUI window. It wraps the
288 user-supplied window constructor. */
290 class gdbpy_tui_window_maker
291 : public intrusive_list_node<gdbpy_tui_window_maker>
293 public:
295 explicit gdbpy_tui_window_maker (gdbpy_ref<> &&constr)
296 : m_constr (std::move (constr))
298 m_window_maker_list.push_back (*this);
301 ~gdbpy_tui_window_maker ();
303 gdbpy_tui_window_maker (gdbpy_tui_window_maker &&other) noexcept
304 : m_constr (std::move (other.m_constr))
306 m_window_maker_list.push_back (*this);
309 gdbpy_tui_window_maker (const gdbpy_tui_window_maker &other)
311 gdbpy_enter enter_py;
312 m_constr = other.m_constr;
313 m_window_maker_list.push_back (*this);
316 gdbpy_tui_window_maker &operator= (gdbpy_tui_window_maker &&other)
318 m_constr = std::move (other.m_constr);
319 return *this;
322 gdbpy_tui_window_maker &operator= (const gdbpy_tui_window_maker &other)
324 gdbpy_enter enter_py;
325 m_constr = other.m_constr;
326 return *this;
329 tui_win_info *operator() (const char *name);
331 /* Reset the m_constr field of all gdbpy_tui_window_maker objects back to
332 nullptr, this will allow the Python object referenced to be
333 deallocated. This function is intended to be called when GDB is
334 shutting down the Python interpreter to allow all Python objects to be
335 deallocated and cleaned up. */
336 static void
337 invalidate_all ()
339 gdbpy_enter enter_py;
340 for (gdbpy_tui_window_maker &f : m_window_maker_list)
341 f.m_constr.reset (nullptr);
344 private:
346 /* A constructor that is called to make a TUI window. */
347 gdbpy_ref<> m_constr;
349 /* A global list of all gdbpy_tui_window_maker objects. */
350 static intrusive_list<gdbpy_tui_window_maker> m_window_maker_list;
353 /* See comment in class declaration above. */
355 intrusive_list<gdbpy_tui_window_maker>
356 gdbpy_tui_window_maker::m_window_maker_list;
358 gdbpy_tui_window_maker::~gdbpy_tui_window_maker ()
360 /* Remove this gdbpy_tui_window_maker from the global list. */
361 if (is_linked ())
362 m_window_maker_list.erase (m_window_maker_list.iterator_to (*this));
364 if (m_constr != nullptr)
366 gdbpy_enter enter_py;
367 m_constr.reset (nullptr);
371 tui_win_info *
372 gdbpy_tui_window_maker::operator() (const char *win_name)
374 gdbpy_enter enter_py;
376 gdbpy_ref<gdbpy_tui_window> wrapper
377 (PyObject_New (gdbpy_tui_window, &gdbpy_tui_window_object_type));
378 if (wrapper == nullptr)
380 gdbpy_print_stack ();
381 return nullptr;
384 std::unique_ptr<tui_py_window> window
385 (new tui_py_window (win_name, wrapper));
387 /* There's only two ways that m_constr can be reset back to nullptr,
388 first when the parent gdbpy_tui_window_maker object is deleted, in
389 which case it should be impossible to call this method, or second, as
390 a result of a gdbpy_tui_window_maker::invalidate_all call, but this is
391 only called when GDB's Python interpreter is being shut down, after
392 which, this method should not be called. */
393 gdb_assert (m_constr != nullptr);
395 gdbpy_ref<> user_window
396 (PyObject_CallFunctionObjArgs (m_constr.get (),
397 (PyObject *) wrapper.get (),
398 nullptr));
399 if (user_window == nullptr)
401 gdbpy_print_stack ();
402 return nullptr;
405 window->set_user_window (std::move (user_window));
406 /* Window is now owned by the TUI. */
407 return window.release ();
410 /* Implement "gdb.register_window_type". */
412 PyObject *
413 gdbpy_register_tui_window (PyObject *self, PyObject *args, PyObject *kw)
415 static const char *keywords[] = { "name", "constructor", nullptr };
417 const char *name;
418 PyObject *cons_obj;
420 if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sO", keywords,
421 &name, &cons_obj))
422 return nullptr;
426 gdbpy_tui_window_maker constr (gdbpy_ref<>::new_reference (cons_obj));
427 tui_register_window (name, constr);
429 catch (const gdb_exception &except)
431 return gdbpy_handle_gdb_exception (nullptr, except);
434 Py_RETURN_NONE;
439 /* Require that "Window" be a valid window. */
441 #define REQUIRE_WINDOW(Window) \
442 do { \
443 if (!(Window)->is_valid ()) \
444 return PyErr_Format (PyExc_RuntimeError, \
445 _("TUI window is invalid.")); \
446 } while (0)
448 /* Require that "Window" be a valid window. */
450 #define REQUIRE_WINDOW_FOR_SETTER(Window) \
451 do { \
452 if (!(Window)->is_valid ()) \
454 PyErr_Format (PyExc_RuntimeError, \
455 _("TUI window is invalid.")); \
456 return -1; \
458 } while (0)
460 /* Python function which checks the validity of a TUI window
461 object. */
462 static PyObject *
463 gdbpy_tui_is_valid (PyObject *self, PyObject *args)
465 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
467 if (win->is_valid ())
468 Py_RETURN_TRUE;
469 Py_RETURN_FALSE;
472 /* Python function that erases the TUI window. */
473 static PyObject *
474 gdbpy_tui_erase (PyObject *self, PyObject *args)
476 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
478 REQUIRE_WINDOW (win);
480 win->window->erase ();
482 Py_RETURN_NONE;
485 /* Python function that writes some text to a TUI window. */
486 static PyObject *
487 gdbpy_tui_write (PyObject *self, PyObject *args, PyObject *kw)
489 static const char *keywords[] = { "string", "full_window", nullptr };
491 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
492 const char *text;
493 int full_window = 0;
495 if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|i", keywords,
496 &text, &full_window))
497 return nullptr;
499 REQUIRE_WINDOW (win);
501 win->window->output (text, full_window);
503 Py_RETURN_NONE;
506 /* Return the width of the TUI window. */
507 static PyObject *
508 gdbpy_tui_width (PyObject *self, void *closure)
510 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
511 REQUIRE_WINDOW (win);
512 gdbpy_ref<> result
513 = gdb_py_object_from_longest (win->window->viewport_width ());
514 return result.release ();
517 /* Return the height of the TUI window. */
518 static PyObject *
519 gdbpy_tui_height (PyObject *self, void *closure)
521 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
522 REQUIRE_WINDOW (win);
523 gdbpy_ref<> result
524 = gdb_py_object_from_longest (win->window->viewport_height ());
525 return result.release ();
528 /* Return the title of the TUI window. */
529 static PyObject *
530 gdbpy_tui_title (PyObject *self, void *closure)
532 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
533 REQUIRE_WINDOW (win);
534 return host_string_to_python_string (win->window->title ().c_str ()).release ();
537 /* Set the title of the TUI window. */
538 static int
539 gdbpy_tui_set_title (PyObject *self, PyObject *newvalue, void *closure)
541 gdbpy_tui_window *win = (gdbpy_tui_window *) self;
543 REQUIRE_WINDOW_FOR_SETTER (win);
545 if (newvalue == nullptr)
547 PyErr_Format (PyExc_TypeError, _("Cannot delete \"title\" attribute."));
548 return -1;
551 gdb::unique_xmalloc_ptr<char> value
552 = python_string_to_host_string (newvalue);
553 if (value == nullptr)
554 return -1;
556 win->window->set_title (value.get ());
557 return 0;
560 static gdb_PyGetSetDef tui_object_getset[] =
562 { "width", gdbpy_tui_width, NULL, "Width of the window.", NULL },
563 { "height", gdbpy_tui_height, NULL, "Height of the window.", NULL },
564 { "title", gdbpy_tui_title, gdbpy_tui_set_title, "Title of the window.",
565 NULL },
566 { NULL } /* Sentinel */
569 static PyMethodDef tui_object_methods[] =
571 { "is_valid", gdbpy_tui_is_valid, METH_NOARGS,
572 "is_valid () -> Boolean\n\
573 Return true if this TUI window is valid, false if not." },
574 { "erase", gdbpy_tui_erase, METH_NOARGS,
575 "Erase the TUI window." },
576 { "write", (PyCFunction) gdbpy_tui_write, METH_VARARGS | METH_KEYWORDS,
577 "Append a string to the TUI window." },
578 { NULL } /* Sentinel. */
581 PyTypeObject gdbpy_tui_window_object_type =
583 PyVarObject_HEAD_INIT (NULL, 0)
584 "gdb.TuiWindow", /*tp_name*/
585 sizeof (gdbpy_tui_window), /*tp_basicsize*/
586 0, /*tp_itemsize*/
587 0, /*tp_dealloc*/
588 0, /*tp_print*/
589 0, /*tp_getattr*/
590 0, /*tp_setattr*/
591 0, /*tp_compare*/
592 0, /*tp_repr*/
593 0, /*tp_as_number*/
594 0, /*tp_as_sequence*/
595 0, /*tp_as_mapping*/
596 0, /*tp_hash */
597 0, /*tp_call*/
598 0, /*tp_str*/
599 0, /*tp_getattro*/
600 0, /*tp_setattro */
601 0, /*tp_as_buffer*/
602 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
603 "GDB TUI window object", /* tp_doc */
604 0, /* tp_traverse */
605 0, /* tp_clear */
606 0, /* tp_richcompare */
607 0, /* tp_weaklistoffset */
608 0, /* tp_iter */
609 0, /* tp_iternext */
610 tui_object_methods, /* tp_methods */
611 0, /* tp_members */
612 tui_object_getset, /* tp_getset */
613 0, /* tp_base */
614 0, /* tp_dict */
615 0, /* tp_descr_get */
616 0, /* tp_descr_set */
617 0, /* tp_dictoffset */
618 0, /* tp_init */
619 0, /* tp_alloc */
622 /* Called when TUI is enabled or disabled. */
624 static void
625 gdbpy_tui_enabled (bool state)
627 gdbpy_enter enter_py;
629 if (evregpy_no_listeners_p (gdb_py_events.tui_enabled))
630 return;
632 gdbpy_ref<> event_obj = create_event_object (&tui_enabled_event_object_type);
633 if (event_obj == nullptr)
635 gdbpy_print_stack ();
636 return;
639 gdbpy_ref<> code (PyBool_FromLong (state));
640 if (evpy_add_attribute (event_obj.get (), "enabled", code.get ()) < 0
641 || evpy_emit_event (event_obj.get (), gdb_py_events.tui_enabled) < 0)
642 gdbpy_print_stack ();
645 #endif /* TUI */
647 /* Initialize this module. */
649 static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
650 gdbpy_initialize_tui ()
652 #ifdef TUI
653 gdbpy_tui_window_object_type.tp_new = PyType_GenericNew;
654 if (gdbpy_type_ready (&gdbpy_tui_window_object_type) < 0)
655 return -1;
657 gdb::observers::tui_enabled.attach (gdbpy_tui_enabled, "py-tui");
658 #endif /* TUI */
660 return 0;
663 /* Finalize this module. */
665 static void
666 gdbpy_finalize_tui ()
668 #ifdef TUI
669 gdbpy_tui_window_maker::invalidate_all ();
670 #endif /* TUI */
673 GDBPY_INITIALIZE_FILE (gdbpy_initialize_tui, gdbpy_finalize_tui);