Allow TUI windows in Python

This patch adds support for writing new TUI windows in Python.

2020-02-22  Tom Tromey  <tom@tromey.com>

	* NEWS: Add entry for gdb.register_window_type.
	* tui/tui-layout.h (window_factory): New typedef.
	(tui_register_window): Declare.
	* tui/tui-layout.c (saved_tui_windows): New global.
	(tui_apply_current_layout): Use it.
	(tui_register_window): New function.
	* python/python.c (do_start_initialization): Call
	gdbpy_initialize_tui.
	(python_GdbMethods): Add "register_window_type" function.
	* python/python-internal.h (gdbpy_register_tui_window)
	(gdbpy_initialize_tui): Declare.
	* python/py-tui.c: New file.
	* Makefile.in (SUBDIR_PYTHON_SRCS): Add py-tui.c.

gdb/doc/ChangeLog
2020-02-22  Tom Tromey  <tom@tromey.com>

	* python.texi (Python API): Add menu item.
	(TUI Windows In Python): New node.

gdb/testsuite/ChangeLog
2020-02-22  Tom Tromey  <tom@tromey.com>

	* gdb.python/tui-window.exp: New file.
	* gdb.python/tui-window.py: New file.

Change-Id: I85fbfb923a1840450a00a7dce113a05d7f048baa
This commit is contained in:
Tom Tromey 2020-02-22 11:48:26 -07:00
parent fc96d20b2c
commit 01b1af321f
13 changed files with 783 additions and 4 deletions

View File

@ -1,3 +1,19 @@
2020-02-22 Tom Tromey <tom@tromey.com>
* NEWS: Add entry for gdb.register_window_type.
* tui/tui-layout.h (window_factory): New typedef.
(tui_register_window): Declare.
* tui/tui-layout.c (saved_tui_windows): New global.
(tui_apply_current_layout): Use it.
(tui_register_window): New function.
* python/python.c (do_start_initialization): Call
gdbpy_initialize_tui.
(python_GdbMethods): Add "register_window_type" function.
* python/python-internal.h (gdbpy_register_tui_window)
(gdbpy_initialize_tui): Declare.
* python/py-tui.c: New file.
* Makefile.in (SUBDIR_PYTHON_SRCS): Add py-tui.c.
2020-02-22 Tom Tromey <tom@tromey.com>
* tui/tui-io.c (do_tui_putc): Don't omit annotations.

View File

@ -405,6 +405,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-symbol.c \
python/py-symtab.c \
python/py-threadevent.c \
python/py-tui.c \
python/py-type.c \
python/py-unwind.c \
python/py-utils.c \

View File

@ -40,6 +40,11 @@ tui new-layout NAME WINDOW WEIGHT [WINDOW WEIGHT]...
GNU/Linux/RISC-V (gdbserver) riscv*-*-linux*
* Python API
** gdb.register_window_type can be used to implement new TUI windows
in Python.
*** Changes in GDB 9
* 'thread-exited' event is now available in the annotations interface.

View File

@ -1,3 +1,8 @@
2020-02-22 Tom Tromey <tom@tromey.com>
* python.texi (Python API): Add menu item.
(TUI Windows In Python): New node.
2020-02-22 Tom Tromey <tom@tromey.com>
PR tui/17850:

View File

@ -163,6 +163,7 @@ optional arguments while skipping others. Example:
using Python.
* Lazy Strings In Python:: Python representation of lazy strings.
* Architectures In Python:: Python representation of architectures.
* TUI Windows In Python:: Implementing new TUI windows.
@end menu
@node Basic Python
@ -5673,6 +5674,110 @@ instruction in bytes.
@end table
@end defun
@node TUI Windows In Python
@subsubsection Implementing new TUI windows
@cindex Python TUI Windows
New TUI (@pxref{TUI}) windows can be implemented in Python.
@findex gdb.register_window_type
@defun gdb.register_window_type (@var{name}, @var{factory})
Because TUI windows are created and destroyed depending on the layout
the user chooses, new window types are implemented by registering a
factory function with @value{GDBN}.
@var{name} is the name of the new window. It's an error to try to
replace one of the built-in windows, but other window types can be
replaced.
@var{function} is a factory function that is called to create the TUI
window. This is called with a single argument of type
@code{gdb.TuiWindow}, described below. It should return an object
that implements the TUI window protocol, also described below.
@end defun
As mentioned above, when a factory function is called, it is passed a
an object of type @code{gdb.TuiWindow}. This object has these
methods and attributes:
@defun TuiWindow.is_valid ()
This method returns @code{True} when this window is valid. When the
user changes the TUI layout, windows no longer visible in the new
layout will be destroyed. At this point, the @code{gdb.TuiWindow}
will no longer be valid, and methods (and attributes) other than
@code{is_valid} will throw an exception.
@end defun
@defvar TuiWindow.width
This attribute holds the width of the window. It is not writable.
@end defvar
@defvar TuiWindow.height
This attribute holds the height of the window. It is not writable.
@end defvar
@defvar TuiWindow.title
This attribute holds the window's title, a string. This is normally
displayed above the window. This attribute can be modified.
@end defvar
@defun TuiWindow.erase ()
Remove all the contents of the window.
@end defun
@defun TuiWindow.write (@var{string})
Write @var{string} to the window. @var{string} can contain ANSI
terminal escape styling sequences; @value{GDBN} will translate these
as appropriate for the terminal.
@end defun
The factory function that you supply should return an object
conforming to the TUI window protocol. These are the method that can
be called on this object, which is referred to below as the ``window
object''. The methods documented below are optional; if the object
does not implement one of these methods, @value{GDBN} will not attempt
to call it. Additional new methods may be added to the window
protocol in the future. @value{GDBN} guarantees that they will begin
with a lower-case letter, so you can start implementation methods with
upper-case letters or underscore to avoid any future conflicts.
@defun Window.close ()
When the TUI window is closed, the @code{gdb.TuiWindow} object will be
put into an invalid state. At this time, @value{GDBN} will call
@code{close} method on the window object.
After this method is called, @value{GDBN} will discard any references
it holds on this window object, and will no longer call methods on
this object.
@end defun
@defun Window.render ()
In some situations, a TUI window can change size. For example, this
can happen if the user resizes the terminal, or changes the layout.
When this happens, @value{GDBN} will call the @code{render} method on
the window object.
If your window is intended to update in response to changes in the
inferior, you will probably also want to register event listeners and
send output to the @code{gdb.TuiWindow}.
@end defun
@defun Window.hscroll (@var{num})
This is a request to scroll the window horizontally. @var{num} is the
amount by which to scroll, with negative numbers meaning to scroll
right. In the TUI model, it is the viewport that moves, not the
contents. A positive argument should cause the viewport to move
right, and so the content should appear to move to the left.
@end defun
@defun Window.vscroll (@var{num})
This is a request to scroll the window vertically. @var{num} is the
amount by which to scroll, with negative numbers meaning to scroll
backward. In the TUI model, it is the viewport that moves, not the
contents. A positive argument should cause the viewport to move down,
and so the content should appear to move up.
@end defun
@node Python Auto-loading
@subsection Python Auto-loading
@cindex Python auto-loading

510
gdb/python/py-tui.c Normal file
View File

@ -0,0 +1,510 @@
/* TUI windows implemented in Python
Copyright (C) 2020 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "arch-utils.h"
#include "python-internal.h"
#include "gdb_curses.h"
#ifdef TUI
#include "tui/tui-data.h"
#include "tui/tui-io.h"
#include "tui/tui-layout.h"
#include "tui/tui-wingeneral.h"
#include "tui/tui-winsource.h"
class tui_py_window;
/* A PyObject representing a TUI window. */
struct gdbpy_tui_window
{
PyObject_HEAD
/* The TUI window, or nullptr if the window has been deleted. */
tui_py_window *window;
};
extern PyTypeObject gdbpy_tui_window_object_type
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("gdbpy_tui_window");
/* A TUI window written in Python. */
class tui_py_window : public tui_win_info
{
public:
tui_py_window (const char *name, gdbpy_ref<gdbpy_tui_window> wrapper)
: m_name (name),
m_wrapper (std::move (wrapper))
{
m_wrapper->window = this;
}
~tui_py_window ();
DISABLE_COPY_AND_ASSIGN (tui_py_window);
/* Set the "user window" to the indicated reference. The user
window is the object returned the by user-defined window
constructor. */
void set_user_window (gdbpy_ref<> &&user_window)
{
m_window = std::move (user_window);
}
const char *name () const override
{
return m_name.c_str ();
}
void rerender () override;
void do_scroll_vertical (int num_to_scroll) override;
void do_scroll_horizontal (int num_to_scroll) override;
/* Erase and re-box the window. */
void erase ()
{
if (is_visible ())
{
werase (handle.get ());
check_and_display_highlight_if_needed ();
cursor_x = 0;
cursor_y = 0;
}
}
/* Write STR to the window. */
void output (const char *str);
/* A helper function to compute the viewport width. */
int viewport_width () const
{
return std::max (0, width - 2);
}
/* A helper function to compute the viewport height. */
int viewport_height () const
{
return std::max (0, height - 2);
}
private:
/* Location of the cursor. */
int cursor_x = 0;
int cursor_y = 0;
/* The name of this window. */
std::string m_name;
/* The underlying Python window object. */
gdbpy_ref<> m_window;
/* The Python wrapper for this object. */
gdbpy_ref<gdbpy_tui_window> m_wrapper;
};
tui_py_window::~tui_py_window ()
{
gdbpy_enter enter_py (get_current_arch (), current_language);
if (PyObject_HasAttrString (m_window.get (), "close"))
{
gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "close",
nullptr));
if (result == nullptr)
gdbpy_print_stack ();
}
/* Unlink. */
m_wrapper->window = nullptr;
/* Explicitly free the Python references. We have to do this
manually because we need to hold the GIL while doing so. */
m_wrapper.reset (nullptr);
m_window.reset (nullptr);
}
void
tui_py_window::rerender ()
{
gdbpy_enter enter_py (get_current_arch (), current_language);
if (PyObject_HasAttrString (m_window.get (), "render"))
{
gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "render",
nullptr));
if (result == nullptr)
gdbpy_print_stack ();
}
}
void
tui_py_window::do_scroll_horizontal (int num_to_scroll)
{
gdbpy_enter enter_py (get_current_arch (), current_language);
if (PyObject_HasAttrString (m_window.get (), "hscroll"))
{
gdbpy_ref<> result (PyObject_CallMethod (m_window.get(), "hscroll",
"i", num_to_scroll, nullptr));
if (result == nullptr)
gdbpy_print_stack ();
}
}
void
tui_py_window::do_scroll_vertical (int num_to_scroll)
{
gdbpy_enter enter_py (get_current_arch (), current_language);
if (PyObject_HasAttrString (m_window.get (), "vscroll"))
{
gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "vscroll",
"i", num_to_scroll, nullptr));
if (result == nullptr)
gdbpy_print_stack ();
}
}
void
tui_py_window::output (const char *text)
{
int vwidth = viewport_width ();
while (cursor_y < viewport_height () && *text != '\0')
{
wmove (handle.get (), cursor_y + 1, cursor_x + 1);
std::string line = tui_copy_source_line (&text, 0, 0,
vwidth - cursor_x, 0);
tui_puts (line.c_str (), handle.get ());
if (*text == '\n')
{
++text;
++cursor_y;
cursor_x = 0;
}
else
cursor_x = getcurx (handle.get ()) - 1;
}
wrefresh (handle.get ());
}
/* A callable that is used to create a TUI window. It wraps the
user-supplied window constructor. */
class gdbpy_tui_window_maker
{
public:
explicit gdbpy_tui_window_maker (gdbpy_ref<> &&constr)
: m_constr (std::move (constr))
{
}
~gdbpy_tui_window_maker ();
gdbpy_tui_window_maker (gdbpy_tui_window_maker &&other)
: m_constr (std::move (other.m_constr))
{
}
gdbpy_tui_window_maker (const gdbpy_tui_window_maker &other)
{
gdbpy_enter enter_py (get_current_arch (), current_language);
m_constr = other.m_constr;
}
gdbpy_tui_window_maker &operator= (gdbpy_tui_window_maker &&other)
{
m_constr = std::move (other.m_constr);
return *this;
}
gdbpy_tui_window_maker &operator= (const gdbpy_tui_window_maker &other)
{
gdbpy_enter enter_py (get_current_arch (), current_language);
m_constr = other.m_constr;
return *this;
}
tui_win_info *operator() (const char *name);
private:
/* A constructor that is called to make a TUI window. */
gdbpy_ref<> m_constr;
};
gdbpy_tui_window_maker::~gdbpy_tui_window_maker ()
{
gdbpy_enter enter_py (get_current_arch (), current_language);
m_constr.reset (nullptr);
}
tui_win_info *
gdbpy_tui_window_maker::operator() (const char *win_name)
{
gdbpy_enter enter_py (get_current_arch (), current_language);
gdbpy_ref<gdbpy_tui_window> wrapper
(PyObject_New (gdbpy_tui_window, &gdbpy_tui_window_object_type));
if (wrapper == nullptr)
{
gdbpy_print_stack ();
return nullptr;
}
std::unique_ptr<tui_py_window> window
(new tui_py_window (win_name, wrapper));
gdbpy_ref<> user_window
(PyObject_CallFunctionObjArgs (m_constr.get (),
(PyObject *) wrapper.get (),
nullptr));
if (user_window == nullptr)
{
gdbpy_print_stack ();
return nullptr;
}
window->set_user_window (std::move (user_window));
/* Window is now owned by the TUI. */
return window.release ();
}
/* Implement "gdb.register_window_type". */
PyObject *
gdbpy_register_tui_window (PyObject *self, PyObject *args, PyObject *kw)
{
static const char *keywords[] = { "name", "constructor", nullptr };
const char *name;
PyObject *cons_obj;
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "sO", keywords,
&name, &cons_obj))
return nullptr;
try
{
gdbpy_tui_window_maker constr (gdbpy_ref<>::new_reference (cons_obj));
tui_register_window (name, constr);
}
catch (const gdb_exception &except)
{
gdbpy_convert_exception (except);
return nullptr;
}
Py_RETURN_NONE;
}
/* Require that "Window" be a valid window. */
#define REQUIRE_WINDOW(Window) \
do { \
if ((Window)->window == nullptr) \
return PyErr_Format (PyExc_RuntimeError, \
_("TUI window is invalid.")); \
} while (0)
/* Python function which checks the validity of a TUI window
object. */
static PyObject *
gdbpy_tui_is_valid (PyObject *self, PyObject *args)
{
gdbpy_tui_window *win = (gdbpy_tui_window *) self;
if (win->window != nullptr)
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
/* Python function that erases the TUI window. */
static PyObject *
gdbpy_tui_erase (PyObject *self, PyObject *args)
{
gdbpy_tui_window *win = (gdbpy_tui_window *) self;
REQUIRE_WINDOW (win);
win->window->erase ();
Py_RETURN_NONE;
}
/* Python function that writes some text to a TUI window. */
static PyObject *
gdbpy_tui_write (PyObject *self, PyObject *args)
{
gdbpy_tui_window *win = (gdbpy_tui_window *) self;
const char *text;
if (!PyArg_ParseTuple (args, "s", &text))
return nullptr;
REQUIRE_WINDOW (win);
win->window->output (text);
Py_RETURN_NONE;
}
/* Return the width of the TUI window. */
static PyObject *
gdbpy_tui_width (PyObject *self, void *closure)
{
gdbpy_tui_window *win = (gdbpy_tui_window *) self;
REQUIRE_WINDOW (win);
return PyLong_FromLong (win->window->viewport_width ());
}
/* Return the height of the TUI window. */
static PyObject *
gdbpy_tui_height (PyObject *self, void *closure)
{
gdbpy_tui_window *win = (gdbpy_tui_window *) self;
REQUIRE_WINDOW (win);
return PyLong_FromLong (win->window->viewport_height ());
}
/* Return the title of the TUI window. */
static PyObject *
gdbpy_tui_title (PyObject *self, void *closure)
{
gdbpy_tui_window *win = (gdbpy_tui_window *) self;
REQUIRE_WINDOW (win);
return host_string_to_python_string (win->window->title.c_str ()).release ();
}
/* Set the title of the TUI window. */
static int
gdbpy_tui_set_title (PyObject *self, PyObject *newvalue, void *closure)
{
gdbpy_tui_window *win = (gdbpy_tui_window *) self;
if (win->window == nullptr)
{
PyErr_Format (PyExc_RuntimeError, _("TUI window is invalid."));
return -1;
}
if (win->window == nullptr)
{
PyErr_Format (PyExc_TypeError, _("Cannot delete \"title\" attribute."));
return -1;
}
gdb::unique_xmalloc_ptr<char> value
= python_string_to_host_string (newvalue);
if (value == nullptr)
return -1;
win->window->title = value.get ();
return 0;
}
static gdb_PyGetSetDef tui_object_getset[] =
{
{ "width", gdbpy_tui_width, NULL, "Width of the window.", NULL },
{ "height", gdbpy_tui_height, NULL, "Height of the window.", NULL },
{ "title", gdbpy_tui_title, gdbpy_tui_set_title, "Title of the window.",
NULL },
{ NULL } /* Sentinel */
};
static PyMethodDef tui_object_methods[] =
{
{ "is_valid", gdbpy_tui_is_valid, METH_NOARGS,
"is_valid () -> Boolean\n\
Return true if this TUI window is valid, false if not." },
{ "erase", gdbpy_tui_erase, METH_NOARGS,
"Erase the TUI window." },
{ "write", (PyCFunction) gdbpy_tui_write, METH_VARARGS,
"Append a string to the TUI window." },
{ NULL } /* Sentinel. */
};
PyTypeObject gdbpy_tui_window_object_type =
{
PyVarObject_HEAD_INIT (NULL, 0)
"gdb.TuiWindow", /*tp_name*/
sizeof (gdbpy_tui_window), /*tp_basicsize*/
0, /*tp_itemsize*/
0, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro */
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"GDB TUI window object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
tui_object_methods, /* tp_methods */
0, /* tp_members */
tui_object_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
};
#endif /* TUI */
/* Initialize this module. */
int
gdbpy_initialize_tui ()
{
#ifdef TUI
gdbpy_tui_window_object_type.tp_new = PyType_GenericNew;
if (PyType_Ready (&gdbpy_tui_window_object_type) < 0)
return -1;
#endif /* TUI */
return 0;
}

View File

@ -447,6 +447,8 @@ PyObject *gdbpy_parameter_value (enum var_types type, void *var);
char *gdbpy_parse_command_name (const char *name,
struct cmd_list_element ***base_list,
struct cmd_list_element **start_list);
PyObject *gdbpy_register_tui_window (PyObject *self, PyObject *args,
PyObject *kw);
PyObject *symtab_and_line_to_sal_object (struct symtab_and_line sal);
PyObject *symtab_to_symtab_object (struct symtab *symtab);
@ -543,6 +545,8 @@ int gdbpy_initialize_xmethods (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_unwind (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_tui ()
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
/* A wrapper for PyErr_Fetch that handles reference counting for the
caller. */

View File

@ -1769,7 +1769,8 @@ do_start_initialization ()
|| gdbpy_initialize_event () < 0
|| gdbpy_initialize_arch () < 0
|| gdbpy_initialize_xmethods () < 0
|| gdbpy_initialize_unwind () < 0)
|| gdbpy_initialize_unwind () < 0
|| gdbpy_initialize_tui () < 0)
return false;
#define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base) \
@ -2122,6 +2123,13 @@ or None if not set." },
"convenience_variable (NAME, VALUE) -> None.\n\
Set the value of the convenience variable $NAME." },
#ifdef TUI
{ "register_window_type", (PyCFunction) gdbpy_register_tui_window,
METH_VARARGS | METH_KEYWORDS,
"register_window_type (NAME, CONSTRUCSTOR) -> None\n\
Register a TUI window constructor." },
#endif /* TUI */
{NULL, NULL, 0, NULL}
};

View File

@ -1,3 +1,8 @@
2020-02-22 Tom Tromey <tom@tromey.com>
* gdb.python/tui-window.exp: New file.
* gdb.python/tui-window.py: New file.
2020-02-22 Tom Tromey <tom@tromey.com>
PR tui/17850:

View File

@ -0,0 +1,51 @@
# Copyright (C) 2020 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Test a TUI window implemented in Python.
load_lib gdb-python.exp
load_lib tuiterm.exp
# This test doesn't care about the inferior.
standard_testfile py-arch.c
if {[build_executable "failed to prepare" ${testfile} ${srcfile}] == -1} {
return -1
}
Term::clean_restart 24 80 $testfile
# Skip all tests if Python scripting is not enabled.
if { [skip_python_tests] } { continue }
set remote_python_file [gdb_remote_download host \
${srcdir}/${subdir}/${testfile}.py]
gdb_test_no_output "source ${remote_python_file}" \
"source ${testfile}.py"
gdb_test_no_output "tui new-layout test test 1 status 0 cmd 1"
if {![Term::enter_tui]} {
unsupported "TUI not supported"
}
Term::command "layout test"
Term::check_contents "test title" \
"This Is The Title"
Term::check_contents "Window display" "Test: 0"
Term::resize 51 51
# Remember that a resize request actually does two resizes...
Term::check_contents "Window was updated" "Test: 2"

View File

@ -0,0 +1,37 @@
# Copyright (C) 2020 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# A TUI window implemented in Python.
import gdb
the_window = None
class TestWindow:
def __init__(self, win):
global the_window
the_window = win
self.count = 0
self.win = win
win.title = "This Is The Title"
def render(self):
self.win.erase()
w = self.win.width
h = self.win.height
self.win.write("Test: " + str(self.count) + " " + str(w) + "x" + str(h))
self.count = self.count + 1
gdb.register_window_type("test", TestWindow)

View File

@ -65,6 +65,11 @@ static tui_layout_split *asm_regs_layout;
/* See tui-data.h. */
std::vector<tui_win_info *> tui_windows;
/* When applying a layout, this is the list of all windows that were
in the previous layout. This is used to re-use windows when
changing a layout. */
static std::vector<tui_win_info *> saved_tui_windows;
/* See tui-layout.h. */
void
@ -75,10 +80,10 @@ tui_apply_current_layout ()
extract_display_start_addr (&gdbarch, &addr);
std::vector<tui_win_info *> saved_windows = std::move (tui_windows);
saved_tui_windows = std::move (tui_windows);
tui_windows.clear ();
for (tui_win_info *win_info : saved_windows)
for (tui_win_info *win_info : saved_tui_windows)
win_info->make_visible (false);
applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
@ -94,7 +99,7 @@ tui_apply_current_layout ()
/* Now delete any window that was not re-applied. */
tui_win_info *focus = tui_win_with_focus ();
for (tui_win_info *win_info : saved_windows)
for (tui_win_info *win_info : saved_tui_windows)
{
if (!win_info->is_visible ())
{
@ -107,6 +112,8 @@ tui_apply_current_layout ()
if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
tui_get_begin_asm_address (&gdbarch, &addr);
tui_update_source_windows_with_addr (gdbarch, addr);
saved_tui_windows.clear ();
}
/* See tui-layout. */
@ -395,6 +402,21 @@ initialize_known_windows ()
/* See tui-layout.h. */
void
tui_register_window (const char *name, window_factory &&factory)
{
std::string name_copy = name;
if (name_copy == "src" || name_copy == "cmd" || name_copy == "regs"
|| name_copy == "asm" || name_copy == "status")
error (_("Window type \"%s\" is built-in"), name);
known_window_types->emplace (std::move (name_copy),
std::move (factory));
}
/* See tui-layout.h. */
std::unique_ptr<tui_layout_base>
tui_layout_window::clone () const
{

View File

@ -249,4 +249,14 @@ extern void tui_apply_current_layout ();
extern void tui_adjust_window_height (struct tui_win_info *win,
int new_height);
/* The type of a function that is used to create a TUI window. */
typedef std::function<tui_gen_win_info * (const char *name)> window_factory;
/* Register a new TUI window type. NAME is the name of the window
type. FACTORY is a function that can be called to instantiate the
window. */
extern void tui_register_window (const char *name, window_factory &&factory);
#endif /* TUI_TUI_LAYOUT_H */