mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-05-25 11:16:56 +08:00
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:
@ -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>
|
2020-02-22 Tom Tromey <tom@tromey.com>
|
||||||
|
|
||||||
* tui/tui-io.c (do_tui_putc): Don't omit annotations.
|
* tui/tui-io.c (do_tui_putc): Don't omit annotations.
|
||||||
|
@ -405,6 +405,7 @@ SUBDIR_PYTHON_SRCS = \
|
|||||||
python/py-symbol.c \
|
python/py-symbol.c \
|
||||||
python/py-symtab.c \
|
python/py-symtab.c \
|
||||||
python/py-threadevent.c \
|
python/py-threadevent.c \
|
||||||
|
python/py-tui.c \
|
||||||
python/py-type.c \
|
python/py-type.c \
|
||||||
python/py-unwind.c \
|
python/py-unwind.c \
|
||||||
python/py-utils.c \
|
python/py-utils.c \
|
||||||
|
5
gdb/NEWS
5
gdb/NEWS
@ -40,6 +40,11 @@ tui new-layout NAME WINDOW WEIGHT [WINDOW WEIGHT]...
|
|||||||
|
|
||||||
GNU/Linux/RISC-V (gdbserver) riscv*-*-linux*
|
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
|
*** Changes in GDB 9
|
||||||
|
|
||||||
* 'thread-exited' event is now available in the annotations interface.
|
* 'thread-exited' event is now available in the annotations interface.
|
||||||
|
@ -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>
|
2020-02-22 Tom Tromey <tom@tromey.com>
|
||||||
|
|
||||||
PR tui/17850:
|
PR tui/17850:
|
||||||
|
@ -163,6 +163,7 @@ optional arguments while skipping others. Example:
|
|||||||
using Python.
|
using Python.
|
||||||
* Lazy Strings In Python:: Python representation of lazy strings.
|
* Lazy Strings In Python:: Python representation of lazy strings.
|
||||||
* Architectures In Python:: Python representation of architectures.
|
* Architectures In Python:: Python representation of architectures.
|
||||||
|
* TUI Windows In Python:: Implementing new TUI windows.
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@node Basic Python
|
@node Basic Python
|
||||||
@ -5673,6 +5674,110 @@ instruction in bytes.
|
|||||||
@end table
|
@end table
|
||||||
@end defun
|
@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
|
@node Python Auto-loading
|
||||||
@subsection Python Auto-loading
|
@subsection Python Auto-loading
|
||||||
@cindex Python auto-loading
|
@cindex Python auto-loading
|
||||||
|
510
gdb/python/py-tui.c
Normal file
510
gdb/python/py-tui.c
Normal 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;
|
||||||
|
}
|
@ -447,6 +447,8 @@ PyObject *gdbpy_parameter_value (enum var_types type, void *var);
|
|||||||
char *gdbpy_parse_command_name (const char *name,
|
char *gdbpy_parse_command_name (const char *name,
|
||||||
struct cmd_list_element ***base_list,
|
struct cmd_list_element ***base_list,
|
||||||
struct cmd_list_element **start_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_and_line_to_sal_object (struct symtab_and_line sal);
|
||||||
PyObject *symtab_to_symtab_object (struct symtab *symtab);
|
PyObject *symtab_to_symtab_object (struct symtab *symtab);
|
||||||
@ -543,6 +545,8 @@ int gdbpy_initialize_xmethods (void)
|
|||||||
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
|
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
|
||||||
int gdbpy_initialize_unwind (void)
|
int gdbpy_initialize_unwind (void)
|
||||||
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
|
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
|
/* A wrapper for PyErr_Fetch that handles reference counting for the
|
||||||
caller. */
|
caller. */
|
||||||
|
@ -1769,7 +1769,8 @@ do_start_initialization ()
|
|||||||
|| gdbpy_initialize_event () < 0
|
|| gdbpy_initialize_event () < 0
|
||||||
|| gdbpy_initialize_arch () < 0
|
|| gdbpy_initialize_arch () < 0
|
||||||
|| gdbpy_initialize_xmethods () < 0
|
|| gdbpy_initialize_xmethods () < 0
|
||||||
|| gdbpy_initialize_unwind () < 0)
|
|| gdbpy_initialize_unwind () < 0
|
||||||
|
|| gdbpy_initialize_tui () < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base) \
|
#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\
|
"convenience_variable (NAME, VALUE) -> None.\n\
|
||||||
Set the value of the convenience variable $NAME." },
|
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}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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>
|
2020-02-22 Tom Tromey <tom@tromey.com>
|
||||||
|
|
||||||
PR tui/17850:
|
PR tui/17850:
|
||||||
|
51
gdb/testsuite/gdb.python/tui-window.exp
Normal file
51
gdb/testsuite/gdb.python/tui-window.exp
Normal 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"
|
37
gdb/testsuite/gdb.python/tui-window.py
Normal file
37
gdb/testsuite/gdb.python/tui-window.py
Normal 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)
|
@ -65,6 +65,11 @@ static tui_layout_split *asm_regs_layout;
|
|||||||
/* See tui-data.h. */
|
/* See tui-data.h. */
|
||||||
std::vector<tui_win_info *> tui_windows;
|
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. */
|
/* See tui-layout.h. */
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -75,10 +80,10 @@ tui_apply_current_layout ()
|
|||||||
|
|
||||||
extract_display_start_addr (&gdbarch, &addr);
|
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 ();
|
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);
|
win_info->make_visible (false);
|
||||||
|
|
||||||
applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
|
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. */
|
/* Now delete any window that was not re-applied. */
|
||||||
tui_win_info *focus = tui_win_with_focus ();
|
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 ())
|
if (!win_info->is_visible ())
|
||||||
{
|
{
|
||||||
@ -107,6 +112,8 @@ tui_apply_current_layout ()
|
|||||||
if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
|
if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
|
||||||
tui_get_begin_asm_address (&gdbarch, &addr);
|
tui_get_begin_asm_address (&gdbarch, &addr);
|
||||||
tui_update_source_windows_with_addr (gdbarch, addr);
|
tui_update_source_windows_with_addr (gdbarch, addr);
|
||||||
|
|
||||||
|
saved_tui_windows.clear ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See tui-layout. */
|
/* See tui-layout. */
|
||||||
@ -395,6 +402,21 @@ initialize_known_windows ()
|
|||||||
|
|
||||||
/* See tui-layout.h. */
|
/* 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>
|
std::unique_ptr<tui_layout_base>
|
||||||
tui_layout_window::clone () const
|
tui_layout_window::clone () const
|
||||||
{
|
{
|
||||||
|
@ -249,4 +249,14 @@ extern void tui_apply_current_layout ();
|
|||||||
extern void tui_adjust_window_height (struct tui_win_info *win,
|
extern void tui_adjust_window_height (struct tui_win_info *win,
|
||||||
int new_height);
|
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 */
|
#endif /* TUI_TUI_LAYOUT_H */
|
||||||
|
Reference in New Issue
Block a user