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