public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFC] TUI windows for locals/display/threads/frames/memory
       [not found] <20210306173417.21528-1-ssbssa.ref@yahoo.de>
@ 2021-03-06 17:33 ` Hannes Domani
  2021-03-06 17:33   ` [PATCH 01/22] Initial TUI mouse support Hannes Domani
                     ` (9 more replies)
  0 siblings, 10 replies; 34+ messages in thread
From: Hannes Domani @ 2021-03-06 17:33 UTC (permalink / raw)
  To: gdb-patches

This series adds rudimentary mouse support and uses the python TUI window API
to create these windows:
- locals
  Shows the values of the local variables.
- display
  Shows the values of the automatic display variables.
- threads
  Shows the currently known threads.
- frames
  Shows all stack frames of the current thread.
- memory
  Shows the hexadecimal values of some memory, the start address is
  set with the new 'memory' command.

In the variable windows, a left click shows the children of the value on
the clicked line.
A right click on a value with address sets a convenience variable
with the pointer to this value (either $dv (display variable) or
$lv (local variable)).
And a middle click copies the text representation of the value to the
clipboard.

A left click on the threads/frames window selects this thread/frame.

It's also possible to create custom windows with the output of a gdb
command, e.g.:
(gdb) cccw shared-libs "info sharedlibrary"
This creates a window named "shared-libs", and shows the output of
"info sharedlibrary".

This also creates a few TUI layouts, and short aliases for them, e.g.:
(gdb) lldf
This opens the layout locals-display-frames.

Patch 20 changes the pretty printers in gdb-gdb.py to use children()
instead of to_string() for the output of their childs, so they can be
expanded in the variable windows.


This series still needs a lot of cleanup, in particular:
- Add comments.
- Refactor/rewrite VariableWindow::add_val().
- The display window uses the 'info display' output, since there is no
  python API to access the automatic display variables.
- Patches 14/16/18 may be counted as ugly hacks.

Knowing that I still would like some feedback on its current state.


[PATCH 01/22] Initial TUI mouse support
[PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with
[PATCH 03/22] Forward mouse click to python TUI window
[PATCH 04/22] Prevent flickering when redrawing the TUI python window
[PATCH 05/22] Implement locals TUI window
[PATCH 06/22] Implement display TUI window
[PATCH 07/22] Implement threads TUI window
[PATCH 08/22] Implement frames TUI window
[PATCH 09/22] Implement cccw TUI command
[PATCH 10/22] Add optional styled argument to gdb.execute
[PATCH 11/22] Use styled argument of gdb.execute() for cccw command
[PATCH 12/22] Add optional full_window argument to TuiWindow.write
[PATCH 13/22] Use the full_window argument of TuiWindow.write to
[PATCH 14/22] Add set_tui_auto_display python function
[PATCH 15/22] Disable automatic display while the display window is
[PATCH 16/22] Show raw flag in info display
[PATCH 17/22] Use the raw flag of automatic display to disable pretty
[PATCH 18/22] Update the source location with Frame.select
[PATCH 19/22] Refresh the TUI source window when changing the frame
[PATCH 20/22] Use method children instead of to_string in pretty
[PATCH 21/22] Implement memory TUI window
[PATCH 22/22] Copy variable value to clipboard on middle-click


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 01/22] Initial TUI mouse support
  2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
@ 2021-03-06 17:33   ` Hannes Domani
  2021-03-11 17:32     ` Tom Tromey
  2021-03-06 17:33   ` [PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with mouse Hannes Domani
                     ` (8 subsequent siblings)
  9 siblings, 1 reply; 34+ messages in thread
From: Hannes Domani @ 2021-03-06 17:33 UTC (permalink / raw)
  To: gdb-patches

Implements an overridable tui_win_info::click method whose arguments
are the mouse coordinates inside the specific window, and the mouse
button clicked.

And if the curses implementation supports 5 buttons, the 4th and 5th
buttons are used for scrolling.
---
 gdb/ser-mingw.c    |  5 +++++
 gdb/tui/tui-data.h |  4 ++++
 gdb/tui/tui-io.c   | 32 ++++++++++++++++++++++++++++++++
 gdb/tui/tui.c      |  2 ++
 4 files changed, 43 insertions(+)

diff --git a/gdb/ser-mingw.c b/gdb/ser-mingw.c
index 043bb50b577..2bad51310f6 100644
--- a/gdb/ser-mingw.c
+++ b/gdb/ser-mingw.c
@@ -599,6 +599,11 @@ console_select_thread (void *arg)
 		  break;
 		}
 	    }
+	  else if (record.EventType == MOUSE_EVENT)
+	    {
+	      SetEvent (state->read_event);
+	      break;
+	    }
 
 	  /* Otherwise discard it and wait again.  */
 	  ReadConsoleInput (h, &record, 1, &n_records);
diff --git a/gdb/tui/tui-data.h b/gdb/tui/tui-data.h
index b4d788dd0a4..46ccb72b97c 100644
--- a/gdb/tui/tui-data.h
+++ b/gdb/tui/tui-data.h
@@ -137,6 +137,10 @@ struct tui_win_info
     return true;
   }
 
+  virtual void click (int mouse_x, int mouse_y, int mouse_button)
+  {
+  }
+
   void check_and_display_highlight_if_needed ();
 
   /* Window handle.  */
diff --git a/gdb/tui/tui-io.c b/gdb/tui/tui-io.c
index a2be4d4353e..7787789f0c7 100644
--- a/gdb/tui/tui-io.c
+++ b/gdb/tui/tui-io.c
@@ -33,6 +33,7 @@
 #include "tui/tui-wingeneral.h"
 #include "tui/tui-file.h"
 #include "tui/tui-out.h"
+#include "tui/tui-source.h"
 #include "ui-out.h"
 #include "cli-out.h"
 #include <fcntl.h>
@@ -639,6 +640,7 @@ tui_redisplay_readline (void)
 static void
 tui_prep_terminal (int notused1)
 {
+  mousemask (ALL_MOUSE_EVENTS, NULL);
 }
 
 /* Readline callback to restore the terminal.  It is called once each
@@ -646,6 +648,7 @@ tui_prep_terminal (int notused1)
 static void
 tui_deprep_terminal (void)
 {
+  mousemask (0, NULL);
 }
 
 #ifdef TUI_USE_PIPE_FOR_READLINE
@@ -978,6 +981,35 @@ tui_dispatch_ctrl_char (unsigned int ch)
     case KEY_LEFT:
       win_info->right_scroll (1);
       break;
+    case KEY_MOUSE:
+	{
+	  MEVENT mev;
+	  if (getmouse (&mev) != OK)
+	    break;
+
+	  for (tui_win_info *wi : all_tui_windows ())
+	    if (mev.x > wi->x && mev.x < wi->x + wi->width - 1
+		&& mev.y > wi->y && mev.y < wi->y + wi->height - 1)
+	      {
+		if ((mev.bstate & BUTTON1_CLICKED)
+		    || (mev.bstate & BUTTON2_CLICKED)
+		    || (mev.bstate & BUTTON3_CLICKED))
+		  {
+		    int button = (mev.bstate & BUTTON1_CLICKED) ? 1
+		      : (mev.bstate & BUTTON2_CLICKED) ? 2
+		      : 3;
+		    wi->click (mev.x - wi->x - 1, mev.y - wi->y - 1, button);
+		  }
+#ifdef BUTTON5_PRESSED
+		else if (mev.bstate & BUTTON4_PRESSED)
+		  wi->backward_scroll (3);
+		else if (mev.bstate & BUTTON5_PRESSED)
+		  wi->forward_scroll (3);
+#endif
+		break;
+	      }
+	}
+      break;
     case '\f':
       break;
     default:
diff --git a/gdb/tui/tui.c b/gdb/tui/tui.c
index af92b2a8042..a2654a2e5a4 100644
--- a/gdb/tui/tui.c
+++ b/gdb/tui/tui.c
@@ -508,6 +508,8 @@ tui_disable (void)
   rl_startup_hook = 0;
   rl_already_prompted = 0;
 
+  mousemask (0, NULL);
+
   /* Leave curses and restore previous gdb terminal setting.  */
   endwin ();
 
-- 
2.30.1


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with mouse
  2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
  2021-03-06 17:33   ` [PATCH 01/22] Initial TUI mouse support Hannes Domani
@ 2021-03-06 17:33   ` Hannes Domani
  2021-03-08  9:32     ` Andrew Burgess
  2021-03-11 21:16     ` Tom Tromey
  2021-03-06 17:33   ` [PATCH 03/22] Forward mouse click to python TUI window Hannes Domani
                     ` (7 subsequent siblings)
  9 siblings, 2 replies; 34+ messages in thread
From: Hannes Domani @ 2021-03-06 17:33 UTC (permalink / raw)
  To: gdb-patches

Target area is the first 3 columns of the TUI source window, where the
breakpoint status is shown.

On a left click, this either creates a new breakpoint, or if one already
exists, inverts the enabled state of it.
And a middle click deletes the breakpoint.
---
 gdb/tui/tui-source.c | 71 ++++++++++++++++++++++++++++++++++++++++++++
 gdb/tui/tui-source.h |  2 ++
 2 files changed, 73 insertions(+)

diff --git a/gdb/tui/tui-source.c b/gdb/tui/tui-source.c
index 69f51ceb6f1..c70485f9612 100644
--- a/gdb/tui/tui-source.c
+++ b/gdb/tui/tui-source.c
@@ -29,6 +29,7 @@
 #include "objfiles.h"
 #include "filenames.h"
 #include "source-cache.h"
+#include "linespec.h"
 
 #include "tui/tui.h"
 #include "tui/tui-data.h"
@@ -237,3 +238,73 @@ tui_source_window::show_line_number (int offset) const
   xsnprintf (text, sizeof (text), "%*d  ", m_digits - 1, lineno);
   waddstr (handle.get (), text);
 }
+
+void
+tui_source_window::click (int mouse_x, int mouse_y, int mouse_button)
+{
+  if ((mouse_button == 1 || mouse_button == 2) && mouse_x <= 2)
+    {
+      int line = m_start_line_or_addr.u.line_no + mouse_y;
+      gdb::unique_xmalloc_ptr<char> bp_str;
+      bp_str.reset (xstrprintf ("%s:%d", m_fullname.get (), line));
+
+      struct linespec_sals lsal;
+      lsal.canonical = NULL;
+      try
+	{
+	  lsal.sals = decode_line_with_current_source (bp_str.get (), 0);
+	}
+      catch (const gdb_exception_error &e)
+	{
+	}
+
+      if (lsal.sals.size () == 1)
+	{
+	  symtab_and_line &sal = lsal.sals[0];
+
+	  breakpoint *bp;
+	  bp = iterate_over_breakpoints ([&] (breakpoint *b) -> bool
+	    {
+	      struct bp_location *loc;
+
+	      for (loc = b->loc; loc != NULL; loc = loc->next)
+		{
+		  if (loc->symtab != NULL
+		      && loc->line_number == sal.line
+		      && filename_cmp (m_fullname.get (),
+				       symtab_to_fullname (loc->symtab)) == 0)
+		    return true;
+		}
+
+		return false;
+	    });
+
+	  if (mouse_button == 2)
+	    {
+	      if (bp)
+		delete_breakpoint (bp);
+	    }
+	  else if (mouse_button == 1 && bp)
+	    {
+	      if (bp->enable_state == bp_disabled)
+		enable_breakpoint (bp);
+	      else
+		disable_breakpoint (bp);
+	    }
+	  else if (mouse_button == 1)
+	    {
+	      const char *loc_str = bp_str.get ();
+	      event_location_up location = string_to_event_location_basic
+		(&loc_str, current_language,
+		 symbol_name_match_type::WILD);
+
+	      reinitialize_more_filter ();
+
+	      create_breakpoint (m_gdbarch, location.get (), NULL, -1,
+				 NULL, 0, 0, bp_breakpoint, 0,
+				 AUTO_BOOLEAN_FALSE,
+				 &bkpt_breakpoint_ops, 0, 1, 0, 0);
+	    }
+	}
+    }
+}
diff --git a/gdb/tui/tui-source.h b/gdb/tui/tui-source.h
index 4c69b87e8e3..7a395d3eb9e 100644
--- a/gdb/tui/tui-source.h
+++ b/gdb/tui/tui-source.h
@@ -56,6 +56,8 @@ struct tui_source_window : public tui_source_window_base
   void display_start_addr (struct gdbarch **gdbarch_p,
 			   CORE_ADDR *addr_p) override;
 
+  void click (int mouse_x, int mouse_y, int mouse_button) override;
+
 protected:
 
   void do_scroll_vertical (int num_to_scroll) override;
-- 
2.30.1


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 03/22] Forward mouse click to python TUI window
  2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
  2021-03-06 17:33   ` [PATCH 01/22] Initial TUI mouse support Hannes Domani
  2021-03-06 17:33   ` [PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with mouse Hannes Domani
@ 2021-03-06 17:33   ` Hannes Domani
  2021-03-06 18:09     ` Eli Zaretskii
                       ` (2 more replies)
  2021-03-06 17:33   ` [PATCH 04/22] Prevent flickering when redrawing the TUI python window Hannes Domani
                     ` (6 subsequent siblings)
  9 siblings, 3 replies; 34+ messages in thread
From: Hannes Domani @ 2021-03-06 17:33 UTC (permalink / raw)
  To: gdb-patches

If the TUI window object implements the click method, it is called for each
mouse click event in this window.
---
 gdb/doc/python.texi |  6 ++++++
 gdb/python/py-tui.c | 17 +++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index ba3d2f92a43..97a28d054fa 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -5925,6 +5925,12 @@ contents.  A positive argument should cause the viewport to move down,
 and so the content should appear to move up.
 @end defun
 
+@defun Window.click (@var{x}, @var{y}, @var{button})
+This is called on a mouse click in this window.  @var{x} and @var{y} are
+the mouse coordinates inside the window, and @var{button} specifies which
+mouse button was used.
+@end defun
+
 @node Python Auto-loading
 @subsection Python Auto-loading
 @cindex Python auto-loading
diff --git a/gdb/python/py-tui.c b/gdb/python/py-tui.c
index 72e9c0d5e2b..39753aed468 100644
--- a/gdb/python/py-tui.c
+++ b/gdb/python/py-tui.c
@@ -99,6 +99,8 @@ class tui_py_window : public tui_win_info
       }
   }
 
+  void click (int mouse_x, int mouse_y, int mouse_button) override;
+
   /* Erase and re-box the window.  */
   void erase ()
   {
@@ -226,6 +228,21 @@ tui_py_window::do_scroll_vertical (int num_to_scroll)
     }
 }
 
+void
+tui_py_window::click (int mouse_x, int mouse_y, int mouse_button)
+{
+  gdbpy_enter enter_py (get_current_arch (), current_language);
+
+  if (PyObject_HasAttrString (m_window.get (), "click"))
+    {
+      gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "click",
+					       "iii", mouse_x, mouse_y,
+					       mouse_button));
+      if (result == nullptr)
+	gdbpy_print_stack ();
+    }
+}
+
 void
 tui_py_window::output (const char *text)
 {
-- 
2.30.1


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 04/22] Prevent flickering when redrawing the TUI python window
  2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
                     ` (2 preceding siblings ...)
  2021-03-06 17:33   ` [PATCH 03/22] Forward mouse click to python TUI window Hannes Domani
@ 2021-03-06 17:33   ` Hannes Domani
  2021-03-11 21:21     ` Tom Tromey
  2021-03-06 17:34   ` [PATCH 05/22] Implement locals TUI window Hannes Domani
                     ` (5 subsequent siblings)
  9 siblings, 1 reply; 34+ messages in thread
From: Hannes Domani @ 2021-03-06 17:33 UTC (permalink / raw)
  To: gdb-patches

tui_win_info::refresh_window first redraws the background window, then
tui_wrefresh draws the python text on top of it, which flickers.

By using wnoutrefresh for the background window, the actual drawing on
the screen is only done once, without flickering.
---
 gdb/python/py-tui.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/gdb/python/py-tui.c b/gdb/python/py-tui.c
index 39753aed468..02dcd6949df 100644
--- a/gdb/python/py-tui.c
+++ b/gdb/python/py-tui.c
@@ -91,12 +91,14 @@ class tui_py_window : public tui_win_info
 
   void refresh_window () override
   {
-    tui_win_info::refresh_window ();
     if (m_inner_window != nullptr)
       {
+	wnoutrefresh (handle.get ());
 	touchwin (m_inner_window.get ());
 	tui_wrefresh (m_inner_window.get ());
       }
+    else
+      tui_win_info::refresh_window ();
   }
 
   void click (int mouse_x, int mouse_y, int mouse_button) override;
-- 
2.30.1


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 05/22] Implement locals TUI window
  2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
                     ` (3 preceding siblings ...)
  2021-03-06 17:33   ` [PATCH 04/22] Prevent flickering when redrawing the TUI python window Hannes Domani
@ 2021-03-06 17:34   ` Hannes Domani
  2021-03-08  9:51     ` Andrew Burgess
                       ` (2 more replies)
  2021-03-06 17:34   ` [PATCH 06/22] Implement display " Hannes Domani
                     ` (4 subsequent siblings)
  9 siblings, 3 replies; 34+ messages in thread
From: Hannes Domani @ 2021-03-06 17:34 UTC (permalink / raw)
  To: gdb-patches

PR tui/17849
---
 gdb/data-directory/Makefile.in            |   1 +
 gdb/python/lib/gdb/command/tui_windows.py | 562 ++++++++++++++++++++++
 2 files changed, 563 insertions(+)
 create mode 100644 gdb/python/lib/gdb/command/tui_windows.py

diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
index 8b65790cdd9..b8383ea7808 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -82,6 +82,7 @@ PYTHON_FILE_LIST = \
 	gdb/command/frame_filters.py \
 	gdb/command/pretty_printers.py \
 	gdb/command/prompt.py \
+	gdb/command/tui_windows.py \
 	gdb/command/type_printers.py \
 	gdb/command/unwinders.py \
 	gdb/command/xmethods.py \
diff --git a/gdb/python/lib/gdb/command/tui_windows.py b/gdb/python/lib/gdb/command/tui_windows.py
new file mode 100644
index 00000000000..cda43743cf1
--- /dev/null
+++ b/gdb/python/lib/gdb/command/tui_windows.py
@@ -0,0 +1,562 @@
+# Additional TUI windows.
+# Copyright 2021 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/>.
+
+import gdb
+import sys
+import re
+
+PY3 = sys.version_info[0] == 3
+
+custom_windows = {}
+
+
+col_esc_seq_re = re.compile('(\033\[[0-9;]*m)')
+def escaped_substr(s, n, c):
+    col_esc_seq = False
+    init_col_esc_seq = None
+    last_col_esc_seq = None
+    sub = ""
+    for p in col_esc_seq_re.split(s):
+        t = ""
+        if n > 0:
+            if col_esc_seq:
+                init_col_esc_seq = p
+            else:
+                l = len(p)
+                if l <= n:
+                    n -= l
+                else:
+                    if init_col_esc_seq:
+                        sub += init_col_esc_seq
+                    t = p[n:]
+                    n = 0
+        else:
+            t = p
+        if c > 0:
+            if col_esc_seq:
+                sub += t
+            else:
+                l = len(t)
+                if l <= c:
+                    sub += t
+                    c -= l
+                else:
+                    sub += t[:c]
+                    c = 0
+        elif col_esc_seq:
+            last_col_esc_seq = p
+        col_esc_seq = not col_esc_seq
+    if c > 0:
+        sub += ' ' * c
+    if last_col_esc_seq:
+        sub += last_col_esc_seq
+    return sub
+
+class TextWindow(object):
+    def __init__(self, win, title):
+        self.win = win
+        self.line_ofs = 0
+        self.col_ofs = 0
+        self.lines = []
+        win.title = title
+        global custom_windows
+        custom_windows[title] = self
+
+    def close(self):
+        global custom_windows
+        del custom_windows[self.win.title]
+
+    def render(self):
+        self.line_ofs = 0
+        self.col_ofs = 0
+
+    def hscroll(self, num):
+        prev_col_ofs = self.col_ofs
+        self.col_ofs = self.col_ofs + num
+        if self.col_ofs < 0:
+            self.col_ofs = 0
+        if self.col_ofs != prev_col_ofs:
+            self.redraw()
+
+    def vscroll(self, num):
+        prev_line_ofs = self.line_ofs
+        self.line_ofs = self.line_ofs + num
+        l = len(self.lines)
+        if self.line_ofs >= l:
+            self.line_ofs = l - 1
+        if self.line_ofs < 0:
+            self.line_ofs = 0
+        if self.line_ofs != prev_line_ofs:
+            self.redraw()
+
+    def redraw(self):
+        l = len(self.lines)
+        if self.line_ofs > 0 and self.line_ofs + self.win.height > l:
+            self.line_ofs = l - self.win.height
+            if self.line_ofs < 0:
+                self.line_ofs = 0
+        start = self.line_ofs
+        stop = self.line_ofs + self.win.height
+        if stop > l:
+            stop = l
+        self.win.erase()
+        if stop > start:
+            self.win.write("".join([escaped_substr(l, self.col_ofs, self.win.width) for l in self.lines[start:stop]]))
+
+
+def is_string_instance(s):
+    if PY3:
+        return isinstance(s, str)
+    else:
+        return isinstance(s, basestring)
+
+def val_cmp_color(prev, cur):
+    if prev is None:
+        return ("\033[1;32m", "\033[0m", "", "")
+    elif prev != cur:
+        return ("", "", "\033[1;31m", "\033[0m")
+    return ("", "", "", "")
+
+def octal_escape(s):
+    return "".join(c if ord(c) < 128 else "\\%03o" % ord(c) for c in s)
+
+def value_string(valstr, hint):
+    if hint == "string":
+        is_lazy = type(valstr).__name__ == "LazyString"
+        if is_lazy:
+            l = valstr.length
+        elif not is_string_instance(valstr):
+            l = 1
+        else:
+            l = len(valstr)
+        if l == 0:
+            if is_lazy and str(valstr.type) == "wchar_t *":
+                valstr = "L\"\""
+            else:
+                valstr = "\"\""
+        else:
+            valstr = gdb.Value(valstr).format_string(symbols=False, address=False)
+    if isinstance(valstr, gdb.Value):
+        valstr = valstr.format_string(symbols=False, address=False)
+    elif not type(valstr) is str:
+        valstr = str(valstr)
+    return valstr
+
+def is_typedef_of(valtype, name):
+    if valtype.name == name:
+        return True
+    while valtype.code == gdb.TYPE_CODE_TYPEDEF:
+        valtype = valtype.target()
+        if valtype.name == name:
+            return True
+    return False
+
+
+class VariableWindow(TextWindow):
+    def __init__(self, win, title, convenience_name):
+        super(VariableWindow, self).__init__(win, title)
+        self.prev_vals = {}
+        self.line_names = []
+        self.convenience_name = convenience_name
+
+    def click(self, x, y, button):
+        line = y + self.line_ofs
+        if button == 1 and line < len(self.line_names):
+            name = self.line_names[line]
+            if name:
+                prev = self.prev_vals[name]
+                if prev is not None and prev[0] is not None:
+                    prev[0] = not prev[0]
+                    self.refill(True)
+        elif button == 3 and line < len(self.line_names):
+            name = self.line_names[line]
+            if name:
+                prev = self.prev_vals[name]
+                if prev is not None and prev[3] is not None:
+                    gdb.set_convenience_variable(self.convenience_name, prev[3])
+                    self.refill(True)
+
+    def refill(self, keep_prev=False):
+        if not self.win.is_valid():
+            return
+        self.lines = []
+        self.line_names = []
+        cur_vals = {}
+        for v in self.variables():
+            name = v.symbol()
+            value = v.value()
+            sym_not_init = False
+            num = 0
+            fmt = None
+            error = None
+            if hasattr(v, "undeclared"):
+                sym_not_init = v.undeclared()
+            if hasattr(v, "number"):
+                num = v.number()
+            if hasattr(v, "format"):
+                fmt = v.format()
+            if hasattr(v, "error"):
+                error = v.error()
+            self.add_val(name, name, value, 0, num, cur_vals, keep_prev, False, sym_not_init, fmt, True, error)
+        self.prev_vals = cur_vals
+        self.redraw()
+
+    def add_val(self, n, fn, v, inset, num, cur_vals, keep_prev, def_expand, sym_not_init, fmt, dyn_type, error):
+        n2 = fn
+        if inset == 0:
+            if num == 0:
+                count = 1
+                while n2 in cur_vals:
+                    count += 1
+                    n2 = "%s:%d" % (n, count)
+            else:
+                n2 = "%d:%s" % (num, n)
+
+        cur_entry = [None, False, None, None]
+        cur_vals[n2] = cur_entry
+        expand = None
+        prev_val = None
+        if n2 in self.prev_vals:
+            pv = self.prev_vals[n2]
+            expand = pv[0]
+            if keep_prev:
+                prev_val = pv[2]
+            else:
+                prev_val = pv[1]
+            cur_entry[0] = expand
+            cur_entry[2] = prev_val
+
+        spaces = "  " * inset
+        if num > 0:
+            numstr = "%d: " % num
+        else:
+            numstr = ""
+
+        if v is None:
+            if error is None:
+                var_col_s, var_col_e = "\033[1;30m", "\033[0m"
+                if sym_not_init:
+                    var_col_s, var_col_e = "\033[33m", "\033[0m"
+                self.lines.append(spaces + numstr + "  " + var_col_s + n + var_col_e)
+            else:
+                var_col_s, var_col_e = "", ""
+                if sym_not_init:
+                    var_col_s, var_col_e = "\033[33m", "\033[0m"
+                self.lines.append(spaces + numstr + "  " + var_col_s + n + var_col_e + " = \033[1;30m<" + error + ">\033[0m")
+            self.line_names.append(n2)
+            return
+
+        if is_string_instance(v):
+            (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color(prev_val, v)
+            if sym_not_init:
+                var_col_s, var_col_e = "\033[33m", "\033[0m"
+            self.lines.append(spaces + numstr + "  " + var_col_s + n + var_col_e + " = " + val_col_s + v + val_col_e)
+            self.line_names.append(n2)
+            cur_entry[1] = v
+            return
+
+        if isinstance(v, gdb.Value):
+            if v.type.code in [gdb.TYPE_CODE_REF, gdb.TYPE_CODE_RVALUE_REF]:
+                v = v.referenced_value()
+
+            is_optimized_out = False
+            try:
+                is_optimized_out = v.is_optimized_out
+            except:
+                self.add_val(n, fn, None, inset, num, cur_vals, keep_prev, False, sym_not_init, fmt, False, str(sys.exc_info()[1]))
+                return
+
+            if is_optimized_out and v.type.strip_typedefs().code not in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]:
+                self.add_val(n, fn, None, inset, num, cur_vals, keep_prev, False, sym_not_init, fmt, False, "optimized out")
+                return
+
+            v_addr = v.address
+        else:
+            v = gdb.Value(v)
+
+        cv_str = ""
+        if v_addr is not None:
+            v_addr = v_addr.dereference().reference_value()
+            cur_entry[3] = v_addr
+            cv = gdb.convenience_variable(self.convenience_name)
+            if cv is not None and cv.address == v_addr.address and cv.type == v_addr.type:
+                cv_str = " = \033[1;36m$" + self.convenience_name + "\033[0m"
+
+        try:
+            pp = gdb.default_visualizer(v)
+        except:
+            pp = None
+        if pp:
+            valstr = None
+            try:
+                if expand is None and hasattr(pp, "children"):
+                    expand = def_expand
+                    cur_entry[0] = expand
+                expand_str = "  "
+                if expand is not None:
+                    if expand:
+                        expand_str = "- "
+                    else:
+                        expand_str = "+ "
+                numstr += expand_str
+
+                if hasattr(pp, "to_string"):
+                    valstr = pp.to_string()
+                hint = None
+                if hasattr(pp, "display_hint"):
+                    hint = pp.display_hint()
+                if valstr is not None:
+                    valstr = value_string(valstr, hint)
+                    valstr = octal_escape(valstr)
+                    sp = valstr.split("\n")
+                    (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color(prev_val, valstr)
+                    if sym_not_init:
+                        var_col_s, var_col_e = "\033[33m", "\033[0m"
+                    self.lines.append(spaces + numstr + var_col_s + n + var_col_e + " = " + val_col_s + sp.pop(0) + val_col_e + cv_str)
+                    self.line_names.append(n2)
+                    for extra in sp:
+                        self.lines.append(spaces + "    " + extra)
+                        self.line_names.append(None)
+                    cur_entry[1] = valstr
+                else:
+                    (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color(prev_val, False)
+                    if sym_not_init:
+                        var_col_s, var_col_e = "\033[33m", "\033[0m"
+                    self.lines.append(spaces + numstr + var_col_s + n + var_col_e + cv_str)
+                    self.line_names.append(n2)
+
+                if expand:
+                    childs = None
+                    if hasattr(pp, "children"):
+                        childs = pp.children()
+                    if childs:
+                        if hint == "map":
+                            count = 0
+                            key_prev = None
+                            for c in childs:
+                                (nc, vc) = c
+                                count += 1
+                                fnc = ":".join([n2, nc])
+                                if (count % 2) == 1:
+                                    key_prev = None
+                                    if is_string_instance(vc):
+                                        key_prev = vc
+                                    else:
+                                        vc_check = vc
+                                        if vc_check.type.code in [gdb.TYPE_CODE_REF, gdb.TYPE_CODE_RVALUE_REF]:
+                                            vc_check = vc_check.referenced_value()
+                                        key_pp = gdb.default_visualizer(vc_check)
+                                        if key_pp:
+                                            if hasattr(key_pp, "to_string") and not hasattr(key_pp, "children"):
+                                                vc_check = key_pp.to_string()
+                                                if vc_check is not None:
+                                                    hint = None
+                                                    if hasattr(key_pp, "display_hint"):
+                                                        hint = key_pp.display_hint()
+                                                    key_prev = value_string(vc_check, hint)
+                                        else:
+                                            t_check = vc_check.type.strip_typedefs()
+                                            if t_check.code in [gdb.TYPE_CODE_ENUM, gdb.TYPE_CODE_INT, gdb.TYPE_CODE_FLT,
+                                                    gdb.TYPE_CODE_BOOL, gdb.TYPE_CODE_COMPLEX, gdb.TYPE_CODE_DECFLOAT]:
+                                                if fmt:
+                                                    key_prev = vc_check.format_string(symbols=False, raw=True, format=fmt)
+                                                else:
+                                                    key_prev = vc_check.format_string(symbols=False, raw=True)
+                                    if key_prev is not None and "\n" in key_prev:
+                                        key_prev = None
+                                    if key_prev is None:
+                                        self.add_val("key", fnc, vc, inset + 1, 0, cur_vals, keep_prev, False, False, fmt, True, None)
+                                else:
+                                    if key_prev is not None:
+                                        self.add_val("[" + str(key_prev) + "]", fnc, vc, inset + 1, 0, cur_vals, keep_prev, False, False, fmt, True, None)
+                                    else:
+                                        self.add_val("value", fnc, vc, inset + 1, 0, cur_vals, keep_prev, False, False, fmt, True, None)
+                        else:
+                            for c in childs:
+                                (nc, vc) = c
+                                fnc = ":".join([n2, nc])
+                                self.add_val(nc, fnc, vc, inset + 1, 0, cur_vals, keep_prev, False, False, fmt, True, None)
+            except:
+                self.add_val(n, fn, None, inset, num, cur_vals, keep_prev, False, sym_not_init, fmt, False, str(sys.exc_info()[1]))
+            return
+
+        t = v.type.strip_typedefs()
+
+        is_string = False
+        if t.code in [gdb.TYPE_CODE_ARRAY, gdb.TYPE_CODE_PTR]:
+            target_type = t.target().strip_typedefs()
+            if target_type.code == gdb.TYPE_CODE_INT and (target_type.name == "char" or is_typedef_of(t.target(), "wchar_t")):
+                is_string = True
+        is_array = t.code in [gdb.TYPE_CODE_ARRAY, gdb.TYPE_CODE_RANGE]
+        is_ptr = t.code == gdb.TYPE_CODE_PTR and not target_type.code in [gdb.TYPE_CODE_FUNC, gdb.TYPE_CODE_METHOD]
+        name_add = ""
+        if dyn_type and v.type.code == gdb.TYPE_CODE_STRUCT:
+            try:
+                if v.type != v.dynamic_type:
+                    v = v.cast(v.dynamic_type)
+                    name_add = " <" + v.type.name + ">"
+            except:
+                pass
+        expand_str = "  "
+        if is_ptr or is_array or t.code in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]:
+            if expand is None:
+                expand = def_expand
+                cur_entry[0] = expand
+            if expand is not None:
+                if expand:
+                    expand_str = "- "
+                else:
+                    expand_str = "+ "
+        numstr += expand_str
+
+        if is_string or t.code in [gdb.TYPE_CODE_PTR, gdb.TYPE_CODE_ENUM, gdb.TYPE_CODE_FUNC, gdb.TYPE_CODE_INT,
+                gdb.TYPE_CODE_FLT, gdb.TYPE_CODE_STRING, gdb.TYPE_CODE_METHOD, gdb.TYPE_CODE_METHODPTR,
+                gdb.TYPE_CODE_MEMBERPTR, gdb.TYPE_CODE_CHAR, gdb.TYPE_CODE_BOOL, gdb.TYPE_CODE_COMPLEX, gdb.TYPE_CODE_DECFLOAT]:
+            try:
+                if fmt and not is_string:
+                    valstr = v.format_string(symbols=False, raw=True, address=True, format=fmt)
+                else:
+                    valstr = v.format_string(symbols=False, raw=True, address=True)
+                valstr = octal_escape(valstr)
+                (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color(prev_val, valstr)
+                if sym_not_init:
+                    var_col_s, var_col_e = "\033[33m", "\033[0m"
+                self.lines.append(spaces + numstr + var_col_s + n + var_col_e + " = " + val_col_s + valstr + val_col_e + cv_str)
+                self.line_names.append(n2)
+                cur_entry[1] = valstr
+            except:
+                self.add_val(n, fn, None, inset, num, cur_vals, keep_prev, False, sym_not_init, fmt, False, str(sys.exc_info()[1]))
+                return
+        else:
+            (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color(prev_val, False)
+            if sym_not_init:
+                var_col_s, var_col_e = "\033[33m", "\033[0m"
+            self.lines.append(spaces + numstr + var_col_s + n + var_col_e + name_add + cv_str)
+            self.line_names.append(n2)
+        if t.code == gdb.TYPE_CODE_ENUM:
+            return
+
+        if not expand:
+            return
+
+        if is_array:
+            (low, high) = t.range()
+            for i in range(low, high + 1):
+                nc = "[%d]" % i
+                fnc = ":".join([n2, nc])
+                self.add_val(nc, fnc, v[i], inset + 1, 0, cur_vals, keep_prev, False, False, fmt, True, None)
+            return
+
+        if is_ptr:
+            nc = "[0]"
+            fnc = ":".join([n2, nc])
+            try:
+                v = v.dereference()
+            except:
+                v = None
+            self.add_val(nc, fnc, v, inset + 1, 0, cur_vals, keep_prev, target_type.code != gdb.TYPE_CODE_PTR, False, fmt, True, None)
+            return
+
+        fields = None
+        if t.code in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]:
+            try:
+                fields = v.type.fields()
+            except:
+                pass
+        if fields:
+            num = 1
+            for f in fields:
+                if not hasattr(f, "bitpos"):
+                    continue
+                try:
+                    n = f.name
+                except:
+                    n = None
+                if not n:
+                    n = "<anonymous>"
+                elif f.is_base_class:
+                    n = "<" + n + ">"
+                vf = v[f]
+                fnc = ":".join([n2, n, "%d" % num])
+                num += 1
+                self.add_val(n, fnc, vf, inset + 1, 0, cur_vals, keep_prev, False, False, fmt, False, None)
+
+class VarNameValue(object):
+    def __init__(self, sym, val, undecl, num, fmt, err):
+        self.sym = sym
+        self.val = val
+        self.undecl = undecl
+        self.num = num
+        self.fmt = fmt
+        self.err = err
+
+    def symbol(self):
+        return self.sym
+
+    def value(self):
+        return self.val
+
+    def undeclared(self):
+        return self.undecl
+
+    def number(self):
+        return self.num
+
+    def format(self):
+        return self.fmt
+
+    def error(self):
+        return self.err
+
+class LocalsWindow(VariableWindow):
+    def __init__(self, win):
+        super(LocalsWindow, self).__init__(win, "locals", "lv")
+
+    def variables(self):
+        thread = gdb.selected_thread()
+        thread_valid = thread and thread.is_valid()
+        if thread_valid:
+            frame = gdb.selected_frame()
+            cur_line = frame.find_sal().line
+            try:
+                block = frame.block()
+            except:
+                block = None
+            while block:
+                if not block.is_global:
+                    for symbol in block:
+                        if symbol.is_argument or symbol.is_variable:
+                            sym_not_init = symbol.is_variable and symbol.line > 0 and cur_line <= symbol.line
+                            yield VarNameValue(symbol.name, symbol.value(frame), sym_not_init, 0, None, None)
+                if block.function:
+                    break
+                block = block.superblock
+
+
+gdb.register_window_type("locals", LocalsWindow)
+
+
+def var_change_handler(event=None):
+    global custom_windows
+    for key, value in custom_windows.items():
+        value.refill()
+
+gdb.events.before_prompt.connect(var_change_handler)
+
+
+gdb.execute("tui new-layout locals {-horizontal src 2 locals 1} 2 status 0 cmd 1")
+
+gdb.execute("alias ll = layout locals")
-- 
2.30.1


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 06/22] Implement display TUI window
  2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
                     ` (4 preceding siblings ...)
  2021-03-06 17:34   ` [PATCH 05/22] Implement locals TUI window Hannes Domani
@ 2021-03-06 17:34   ` Hannes Domani
  2021-03-11 21:37     ` Tom Tromey
  2021-03-06 17:34   ` [PATCH 07/22] Implement threads " Hannes Domani
                     ` (3 subsequent siblings)
  9 siblings, 1 reply; 34+ messages in thread
From: Hannes Domani @ 2021-03-06 17:34 UTC (permalink / raw)
  To: gdb-patches

---
 gdb/python/lib/gdb/command/tui_windows.py | 53 +++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/gdb/python/lib/gdb/command/tui_windows.py b/gdb/python/lib/gdb/command/tui_windows.py
index cda43743cf1..a495b19d208 100644
--- a/gdb/python/lib/gdb/command/tui_windows.py
+++ b/gdb/python/lib/gdb/command/tui_windows.py
@@ -545,8 +545,57 @@ class LocalsWindow(VariableWindow):
                     break
                 block = block.superblock
 
+class DisplayWindow(VariableWindow):
+    def __init__(self, win):
+        super(DisplayWindow, self).__init__(win, "display", "dv")
+        self.disp_re = re.compile('^(?P<num>\d+): +(?P<enabled>[yn]) +(?P<fmt>\/\w+ +)?(?P<expr>.*)')
+
+    def variables(self):
+        thread = gdb.selected_thread()
+        thread_valid = thread and thread.is_valid()
+        cant_eval_str = " (cannot be evaluated in the current context)"
+        display = gdb.execute("info display", to_string=True).split("\n")
+        formats = "xduotacfszi"
+        for d in display:
+            m = self.disp_re.search(d)
+            if m:
+                num = int(m.group("num"))
+                numstr = "%d:   " % num
+                expr = m.group("expr").strip()
+                cant_eval = expr.endswith(cant_eval_str)
+                if cant_eval:
+                    expr = expr[:-len(cant_eval_str)].strip()
+                v = None
+                sym_not_init = False
+                fmt = None
+                error = None
+                if m.group("enabled") != "y":
+                    sym_not_init = True
+                elif thread_valid and not cant_eval:
+                    format_flags = m.group("fmt")
+                    if format_flags:
+                        for f in format_flags:
+                            if f in formats:
+                                fmt = f
+                    try:
+                        v = gdb.parse_and_eval(expr)
+                        if v is not None:
+                            if fmt == "i":
+                                dis = gdb.selected_inferior().architecture().disassemble(int(v))
+                                if len(dis):
+                                    v = dis[0]["asm"]
+                                fmt = None
+                            elif fmt == "s":
+                                v = v.cast(gdb.lookup_type("char").pointer())
+                                fmt = None
+                    except:
+                        v = None
+                        error = str(sys.exc_info()[1])
+                yield VarNameValue(expr, v, sym_not_init, num, fmt, error)
+
 
 gdb.register_window_type("locals", LocalsWindow)
+gdb.register_window_type("display", DisplayWindow)
 
 
 def var_change_handler(event=None):
@@ -558,5 +607,9 @@ gdb.events.before_prompt.connect(var_change_handler)
 
 
 gdb.execute("tui new-layout locals {-horizontal src 2 locals 1} 2 status 0 cmd 1")
+gdb.execute("tui new-layout display {-horizontal src 2 display 1} 2 status 0 cmd 1")
+gdb.execute("tui new-layout locals-display {-horizontal src 2 {locals 1 display 1} 1} 2 status 0 cmd 1")
 
 gdb.execute("alias ll = layout locals")
+gdb.execute("alias ld = layout display")
+gdb.execute("alias lld = layout locals-display")
-- 
2.30.1


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 07/22] Implement threads TUI window
  2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
                     ` (5 preceding siblings ...)
  2021-03-06 17:34   ` [PATCH 06/22] Implement display " Hannes Domani
@ 2021-03-06 17:34   ` Hannes Domani
  2021-03-06 17:34   ` [PATCH 08/22] Implement frames " Hannes Domani
                     ` (2 subsequent siblings)
  9 siblings, 0 replies; 34+ messages in thread
From: Hannes Domani @ 2021-03-06 17:34 UTC (permalink / raw)
  To: gdb-patches

---
 gdb/python/lib/gdb/command/tui_windows.py | 66 +++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/gdb/python/lib/gdb/command/tui_windows.py b/gdb/python/lib/gdb/command/tui_windows.py
index a495b19d208..c0699958782 100644
--- a/gdb/python/lib/gdb/command/tui_windows.py
+++ b/gdb/python/lib/gdb/command/tui_windows.py
@@ -594,8 +594,72 @@ class DisplayWindow(VariableWindow):
                 yield VarNameValue(expr, v, sym_not_init, num, fmt, error)
 
 
+template_re = None
+def filter_templates(n):
+    global template_re
+    if template_re is None:
+        template_re = re.compile(r'(?<!\boperator)(<|>)')
+    level = 0
+    rest_arr = []
+    for s in template_re.split(n):
+        if s == '<':
+            level += 1
+        elif s == '>':
+            if level > 0:
+                level -= 1
+        elif level == 0:
+            rest_arr.append(s)
+    return ''.join(rest_arr)
+
+
+class ThreadsWindow(TextWindow):
+    def __init__(self, win):
+        super(ThreadsWindow, self).__init__(win, "threads")
+
+    def refill(self):
+        if not self.win.is_valid():
+            return
+        self.lines = []
+        self.threads = []
+        inferior = gdb.selected_inferior()
+        if inferior and inferior.is_valid():
+            sel_thread = gdb.selected_thread()
+            if sel_thread and sel_thread.is_valid():
+                sel_frame = gdb.selected_frame()
+                for thread in reversed(inferior.threads()):
+                    thread.switch()
+                    frame = gdb.newest_frame()
+                    name = frame.name()
+                    if not name:
+                        name = format(frame.pc(), "#x")
+                    else:
+                        name = filter_templates(name)
+                    num_str = "%d" % thread.num
+                    if thread.ptid[1] > 0:
+                        thread_id = thread.ptid[1]
+                    else:
+                        thread_id = thread.ptid[2]
+                    id_str = "[%d]" % thread_id
+                    name_col_s, name_col_e = "", ""
+                    if thread.ptid == sel_thread.ptid:
+                        name_col_s, name_col_e = "\033[1;37m", "\033[0m"
+                    self.lines.append(num_str + ": " + name_col_s + id_str + " " + name + name_col_e)
+                    self.threads.append(thread)
+                sel_thread.switch()
+                sel_frame.select()
+        self.redraw()
+
+    def click(self, x, y, button):
+        line = y + self.line_ofs
+        if button == 1 and line < len(self.threads):
+            self.threads[line].switch()
+            gdb.selected_frame().select()
+            var_change_handler()
+
+
 gdb.register_window_type("locals", LocalsWindow)
 gdb.register_window_type("display", DisplayWindow)
+gdb.register_window_type("threads", ThreadsWindow)
 
 
 def var_change_handler(event=None):
@@ -609,7 +673,9 @@ gdb.events.before_prompt.connect(var_change_handler)
 gdb.execute("tui new-layout locals {-horizontal src 2 locals 1} 2 status 0 cmd 1")
 gdb.execute("tui new-layout display {-horizontal src 2 display 1} 2 status 0 cmd 1")
 gdb.execute("tui new-layout locals-display {-horizontal src 2 {locals 1 display 1} 1} 2 status 0 cmd 1")
+gdb.execute("tui new-layout threads {-horizontal src 2 threads 1} 2 status 0 cmd 1")
 
 gdb.execute("alias ll = layout locals")
 gdb.execute("alias ld = layout display")
 gdb.execute("alias lld = layout locals-display")
+gdb.execute("alias lt = layout threads")
-- 
2.30.1


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 08/22] Implement frames TUI window
  2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
                     ` (6 preceding siblings ...)
  2021-03-06 17:34   ` [PATCH 07/22] Implement threads " Hannes Domani
@ 2021-03-06 17:34   ` Hannes Domani
  2021-03-11 21:40     ` Tom Tromey
  2021-03-06 17:34   ` [PATCH 09/22] Implement cccw TUI command Hannes Domani
  2021-03-11 21:57   ` [RFC] TUI windows for locals/display/threads/frames/memory Tom Tromey
  9 siblings, 1 reply; 34+ messages in thread
From: Hannes Domani @ 2021-03-06 17:34 UTC (permalink / raw)
  To: gdb-patches

---
 gdb/python/lib/gdb/command/tui_windows.py | 53 +++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/gdb/python/lib/gdb/command/tui_windows.py b/gdb/python/lib/gdb/command/tui_windows.py
index c0699958782..3572d2e3312 100644
--- a/gdb/python/lib/gdb/command/tui_windows.py
+++ b/gdb/python/lib/gdb/command/tui_windows.py
@@ -657,9 +657,48 @@ class ThreadsWindow(TextWindow):
             var_change_handler()
 
 
+class FramesWindow(TextWindow):
+    def __init__(self, win):
+        super(FramesWindow, self).__init__(win, "frames")
+
+    def refill(self):
+        if not self.win.is_valid():
+            return
+        self.lines = []
+        self.frames = []
+        thread = gdb.selected_thread()
+        thread_valid = thread and thread.is_valid()
+        if thread_valid:
+            frame = gdb.newest_frame()
+            selected = gdb.selected_frame()
+            num = 0
+            while frame:
+                name = frame.name()
+                if not name:
+                    name = format(frame.pc(), "#x")
+                else:
+                    name = filter_templates(name)
+                num_str = "#%-2d " % num
+                name_col_s, name_col_e = "", ""
+                if frame == selected:
+                    name_col_s, name_col_e = "\033[1;37m", "\033[0m"
+                self.lines.append(num_str + name_col_s + name + name_col_e)
+                self.frames.append(frame)
+                frame = frame.older()
+                num += 1
+        self.redraw()
+
+    def click(self, x, y, button):
+        line = y + self.line_ofs
+        if button == 1 and line < len(self.frames):
+            self.frames[line].select()
+            var_change_handler()
+
+
 gdb.register_window_type("locals", LocalsWindow)
 gdb.register_window_type("display", DisplayWindow)
 gdb.register_window_type("threads", ThreadsWindow)
+gdb.register_window_type("frames", FramesWindow)
 
 
 def var_change_handler(event=None):
@@ -674,8 +713,22 @@ gdb.execute("tui new-layout locals {-horizontal src 2 locals 1} 2 status 0 cmd 1
 gdb.execute("tui new-layout display {-horizontal src 2 display 1} 2 status 0 cmd 1")
 gdb.execute("tui new-layout locals-display {-horizontal src 2 {locals 1 display 1} 1} 2 status 0 cmd 1")
 gdb.execute("tui new-layout threads {-horizontal src 2 threads 1} 2 status 0 cmd 1")
+gdb.execute("tui new-layout frames {-horizontal src 2 frames 1} 2 status 0 cmd 1")
+gdb.execute("tui new-layout threads-frames {-horizontal src 2 {threads 1 frames 1} 1} 2 status 0 cmd 1")
+gdb.execute("tui new-layout locals-frames {-horizontal src 2 {locals 2 frames 1} 1} 2 status 0 cmd 1")
+gdb.execute("tui new-layout display-frames {-horizontal src 2 {display 2 frames 1} 1} 2 status 0 cmd 1")
+gdb.execute("tui new-layout locals-display-frames {-horizontal src 2 {locals 2 display 2 frames 1} 1} 3 status 0 cmd 1")
+gdb.execute("tui new-layout threads-locals-frames-display {-horizontal src 3 {threads 1 locals 2} 1 {frames 1 display 2} 1} 3 status 0 cmd 1")
+gdb.execute("tui new-layout all {-horizontal {{-horizontal asm 1 regs 1} 1 src 2} 3 {threads 1 locals 2} 1 {frames 1 display 2} 1} 3 status 0 cmd 1")
 
 gdb.execute("alias ll = layout locals")
 gdb.execute("alias ld = layout display")
 gdb.execute("alias lld = layout locals-display")
 gdb.execute("alias lt = layout threads")
+gdb.execute("alias lf = layout frames")
+gdb.execute("alias ltf = layout threads-frames")
+gdb.execute("alias llf = layout locals-frames")
+gdb.execute("alias ldf = layout display-frames")
+gdb.execute("alias lldf = layout locals-display-frames")
+gdb.execute("alias ltlfd = layout threads-locals-frames-display")
+gdb.execute("alias la = layout all")
-- 
2.30.1


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 09/22] Implement cccw TUI command
  2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
                     ` (7 preceding siblings ...)
  2021-03-06 17:34   ` [PATCH 08/22] Implement frames " Hannes Domani
@ 2021-03-06 17:34   ` Hannes Domani
  2021-03-08 10:24     ` Christian Biesinger
  2021-03-11 21:57   ` [RFC] TUI windows for locals/display/threads/frames/memory Tom Tromey
  9 siblings, 1 reply; 34+ messages in thread
From: Hannes Domani @ 2021-03-06 17:34 UTC (permalink / raw)
  To: gdb-patches

This makes it possible to create a TUI window that always shows
the output of some gdb command, e.g.:
(gdb) cccw shared-libs "info sharedlibrary"
This creates a window named "shared-libs", and shows the output of
"info sharedlibrary".
---
 gdb/python/lib/gdb/command/tui_windows.py | 42 +++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/gdb/python/lib/gdb/command/tui_windows.py b/gdb/python/lib/gdb/command/tui_windows.py
index 3572d2e3312..3177e601341 100644
--- a/gdb/python/lib/gdb/command/tui_windows.py
+++ b/gdb/python/lib/gdb/command/tui_windows.py
@@ -701,6 +701,48 @@ gdb.register_window_type("threads", ThreadsWindow)
 gdb.register_window_type("frames", FramesWindow)
 
 
+class CustomCommandWindow(TextWindow):
+    def __init__(self, win, title, command):
+        super(CustomCommandWindow, self).__init__(win, title)
+        self.commands = command.splitlines()
+
+    def refill(self):
+        if not self.win.is_valid():
+            return
+        self.lines = []
+        for c in self.commands:
+            try:
+                self.lines.extend([octal_escape(l) for l in gdb.execute(c, to_string=True).split("\n")])
+                if (self.lines and not self.lines[-1]):
+                    del self.lines[-1]
+            except:
+                self.lines.append(str(sys.exc_info()[1]))
+        self.redraw()
+
+class CustomCommandWindowFactory:
+    def __init__(self, title, command):
+        self.title = title
+        self.command = command
+
+    def __call__(self, win):
+        return CustomCommandWindow(win, self.title, self.command)
+
+class CreateCustomCommandWindow(gdb.Command):
+    """Create custom command window."""
+
+    def __init__(self):
+        super(CreateCustomCommandWindow, self).__init__("cccw", gdb.COMMAND_TUI)
+
+    def invoke(self, arg, from_tty):
+        self.dont_repeat()
+        argv = gdb.string_to_argv(arg)
+        if len(argv) != 2:
+            raise gdb.GdbError("This expects the 2 arguments title and command.")
+        gdb.register_window_type(argv[0], CustomCommandWindowFactory(argv[0], argv[1]))
+
+CreateCustomCommandWindow()
+
+
 def var_change_handler(event=None):
     global custom_windows
     for key, value in custom_windows.items():
-- 
2.30.1


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 03/22] Forward mouse click to python TUI window
  2021-03-06 17:33   ` [PATCH 03/22] Forward mouse click to python TUI window Hannes Domani
@ 2021-03-06 18:09     ` Eli Zaretskii
  2021-03-08  9:36     ` Andrew Burgess
  2021-03-11 21:20     ` Tom Tromey
  2 siblings, 0 replies; 34+ messages in thread
From: Eli Zaretskii @ 2021-03-06 18:09 UTC (permalink / raw)
  To: Hannes Domani; +Cc: gdb-patches

> Date: Sat,  6 Mar 2021 18:33:58 +0100
> From: Hannes Domani via Gdb-patches <gdb-patches@sourceware.org>
> 
> If the TUI window object implements the click method, it is called for each
> mouse click event in this window.
> ---
>  gdb/doc/python.texi |  6 ++++++
>  gdb/python/py-tui.c | 17 +++++++++++++++++
>  2 files changed, 23 insertions(+)

The documentation part is OK, thanks.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with mouse
  2021-03-06 17:33   ` [PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with mouse Hannes Domani
@ 2021-03-08  9:32     ` Andrew Burgess
  2021-03-08 12:00       ` Hannes Domani
  2021-03-11 21:16     ` Tom Tromey
  1 sibling, 1 reply; 34+ messages in thread
From: Andrew Burgess @ 2021-03-08  9:32 UTC (permalink / raw)
  To: Hannes Domani; +Cc: gdb-patches

* Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> [2021-03-06 18:33:57 +0100]:

> Target area is the first 3 columns of the TUI source window, where the
> breakpoint status is shown.
> 
> On a left click, this either creates a new breakpoint, or if one already
> exists, inverts the enabled state of it.
> And a middle click deletes the breakpoint.

Thanks for working on this feature, this looks really exciting.

The same breakpoint markers are used for the source and assembler
windows, both of which inherit from the tui_source_window_base class.

It would be nice if this breakpoint/mouse toggling support was
supported for both of these windows, possibly by moving the click
detection up a level in the class hierarchy?

The documentation for the TUI is not very extensive, but the
source/assembler windows, and their breakpoint markers are discussed
in the 'TUI Overview' section.  I think this change should be
documented there, as well as mentioned in the NEWS file - this is
certainly a note worthy new feature.

Finally, I think we should consider testing.  I don't know if it is
possible to send ctrl sequences to the console to emulate mouse
clicks, but if not then I would suggest this: add a new command
'maintenance tui click X Y BUTTON', which injects a button click into
GDB, X/Y being the click coordinate within the whole GDB terminal, and
BUTTON being the button number.

Using this it should be possible to test the functionality in this
patch.

I have a few style nits I've outlined below too.

> ---
>  gdb/tui/tui-source.c | 71 ++++++++++++++++++++++++++++++++++++++++++++
>  gdb/tui/tui-source.h |  2 ++
>  2 files changed, 73 insertions(+)
> 
> diff --git a/gdb/tui/tui-source.c b/gdb/tui/tui-source.c
> index 69f51ceb6f1..c70485f9612 100644
> --- a/gdb/tui/tui-source.c
> +++ b/gdb/tui/tui-source.c
> @@ -29,6 +29,7 @@
>  #include "objfiles.h"
>  #include "filenames.h"
>  #include "source-cache.h"
> +#include "linespec.h"
>  
>  #include "tui/tui.h"
>  #include "tui/tui-data.h"
> @@ -237,3 +238,73 @@ tui_source_window::show_line_number (int offset) const
>    xsnprintf (text, sizeof (text), "%*d  ", m_digits - 1, lineno);
>    waddstr (handle.get (), text);
>  }
> +
> +void
> +tui_source_window::click (int mouse_x, int mouse_y, int mouse_button)
> +{

This function needs a header comment.  This will be something like:

  /* See ..... */

to point to the header file where the function is declared.

> +  if ((mouse_button == 1 || mouse_button == 2) && mouse_x <= 2)
> +    {
> +      int line = m_start_line_or_addr.u.line_no + mouse_y;
> +      gdb::unique_xmalloc_ptr<char> bp_str;
> +      bp_str.reset (xstrprintf ("%s:%d", m_fullname.get (), line));
> +
> +      struct linespec_sals lsal;
> +      lsal.canonical = NULL;

GDB prefers nullptr to NULL.  However, you could probably add a new
patch to give `canonical` a default value of `nullptr` in the
linespec.h file, this might be nicer than having to initialise it
here.

The NULL -> nullptr thing applies through out this patch.

> +      try
> +	{
> +	  lsal.sals = decode_line_with_current_source (bp_str.get (), 0);
> +	}
> +      catch (const gdb_exception_error &e)
> +	{
> +	}
> +
> +      if (lsal.sals.size () == 1)
> +	{
> +	  symtab_and_line &sal = lsal.sals[0];
> +
> +	  breakpoint *bp;
> +	  bp = iterate_over_breakpoints ([&] (breakpoint *b) -> bool
> +	    {
> +	      struct bp_location *loc;
> +
> +	      for (loc = b->loc; loc != NULL; loc = loc->next)
> +		{
> +		  if (loc->symtab != NULL
> +		      && loc->line_number == sal.line
> +		      && filename_cmp (m_fullname.get (),
> +				       symtab_to_fullname (loc->symtab)) == 0)
> +		    return true;
> +		}
> +
> +		return false;
> +	    });
> +
> +	  if (mouse_button == 2)
> +	    {
> +	      if (bp)

In GDB we always explicitly compare to nullptr, i.e. 'if (bp !=
nullptr)`.  This applies below too.

> +		delete_breakpoint (bp);
> +	    }
> +	  else if (mouse_button == 1 && bp)
> +	    {
> +	      if (bp->enable_state == bp_disabled)
> +		enable_breakpoint (bp);
> +	      else
> +		disable_breakpoint (bp);
> +	    }
> +	  else if (mouse_button == 1)
> +	    {
> +	      const char *loc_str = bp_str.get ();
> +	      event_location_up location = string_to_event_location_basic
> +		(&loc_str, current_language,
> +		 symbol_name_match_type::WILD);
> +
> +	      reinitialize_more_filter ();
> +
> +	      create_breakpoint (m_gdbarch, location.get (), NULL, -1,
> +				 NULL, 0, 0, bp_breakpoint, 0,
> +				 AUTO_BOOLEAN_FALSE,
> +				 &bkpt_breakpoint_ops, 0, 1, 0, 0);
> +	    }
> +	}
> +    }
> +}
> diff --git a/gdb/tui/tui-source.h b/gdb/tui/tui-source.h
> index 4c69b87e8e3..7a395d3eb9e 100644
> --- a/gdb/tui/tui-source.h
> +++ b/gdb/tui/tui-source.h
> @@ -56,6 +56,8 @@ struct tui_source_window : public tui_source_window_base
>    void display_start_addr (struct gdbarch **gdbarch_p,
>  			   CORE_ADDR *addr_p) override;
>  
> +  void click (int mouse_x, int mouse_y, int mouse_button) override;

This is probably where the click function should be documented to
explain what this override does.

Thanks,
Andrew

> +
>  protected:
>  
>    void do_scroll_vertical (int num_to_scroll) override;
> -- 
> 2.30.1
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 03/22] Forward mouse click to python TUI window
  2021-03-06 17:33   ` [PATCH 03/22] Forward mouse click to python TUI window Hannes Domani
  2021-03-06 18:09     ` Eli Zaretskii
@ 2021-03-08  9:36     ` Andrew Burgess
  2021-03-11 21:19       ` Tom Tromey
  2021-03-11 21:20     ` Tom Tromey
  2 siblings, 1 reply; 34+ messages in thread
From: Andrew Burgess @ 2021-03-08  9:36 UTC (permalink / raw)
  To: Hannes Domani; +Cc: gdb-patches

* Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> [2021-03-06 18:33:58 +0100]:

> If the TUI window object implements the click method, it is called for each
> mouse click event in this window.
> ---
>  gdb/doc/python.texi |  6 ++++++
>  gdb/python/py-tui.c | 17 +++++++++++++++++
>  2 files changed, 23 insertions(+)
> 
> diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
> index ba3d2f92a43..97a28d054fa 100644
> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -5925,6 +5925,12 @@ contents.  A positive argument should cause the viewport to move down,
>  and so the content should appear to move up.
>  @end defun
>  
> +@defun Window.click (@var{x}, @var{y}, @var{button})
> +This is called on a mouse click in this window.  @var{x} and @var{y} are
> +the mouse coordinates inside the window, and @var{button} specifies which
> +mouse button was used.
> +@end defun
> +
>  @node Python Auto-loading
>  @subsection Python Auto-loading
>  @cindex Python auto-loading
> diff --git a/gdb/python/py-tui.c b/gdb/python/py-tui.c
> index 72e9c0d5e2b..39753aed468 100644
> --- a/gdb/python/py-tui.c
> +++ b/gdb/python/py-tui.c
> @@ -99,6 +99,8 @@ class tui_py_window : public tui_win_info
>        }
>    }
>  
> +  void click (int mouse_x, int mouse_y, int mouse_button) override;
> +

Unfortunately py-tui.c is pretty hit & miss w.r.t. comments, but this
function really should be commented.

>    /* Erase and re-box the window.  */
>    void erase ()
>    {
> @@ -226,6 +228,21 @@ tui_py_window::do_scroll_vertical (int num_to_scroll)
>      }
>  }
>  
> +void
> +tui_py_window::click (int mouse_x, int mouse_y, int mouse_button)
> +{

And this should have a comment that redirects to the class
declaration.


Thanks,
Andrew

> +  gdbpy_enter enter_py (get_current_arch (), current_language);
> +
> +  if (PyObject_HasAttrString (m_window.get (), "click"))
> +    {
> +      gdbpy_ref<> result (PyObject_CallMethod (m_window.get (), "click",
> +					       "iii", mouse_x, mouse_y,
> +					       mouse_button));
> +      if (result == nullptr)
> +	gdbpy_print_stack ();
> +    }
> +}
> +
>  void
>  tui_py_window::output (const char *text)
>  {
> -- 
> 2.30.1
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 05/22] Implement locals TUI window
  2021-03-06 17:34   ` [PATCH 05/22] Implement locals TUI window Hannes Domani
@ 2021-03-08  9:51     ` Andrew Burgess
  2021-03-11 21:26       ` Tom Tromey
  2021-03-11 21:33     ` Tom Tromey
  2021-03-11 22:00     ` Tom Tromey
  2 siblings, 1 reply; 34+ messages in thread
From: Andrew Burgess @ 2021-03-08  9:51 UTC (permalink / raw)
  To: Hannes Domani; +Cc: gdb-patches

* Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> [2021-03-06 18:34:00 +0100]:

I feel there should be more of a commit message here.

> PR tui/17849

I was going to say the PR number should be attached to the ChangeLog
entry, and it was only then that I realised none of the patches have
had a ChangeLog entry in the commit message.

> ---
>  gdb/data-directory/Makefile.in            |   1 +
>  gdb/python/lib/gdb/command/tui_windows.py | 562 ++++++++++++++++++++++

I'll make some general observations here.  My two observations on
coding style, from a quick scan of the code would be:

  - Comments, and
  - Line length.

I think as in GDB, every function should be commented.  Looking at the
other Python code in this directory the preference seems to be for
Pythons doc-string style, which makes sense.  So you should probably
go with that.

I'm not sure we need to limit ourselves to 80 characters as in GDB,
but I think some of your lines in this file are too long.  My
preference would be target 80 wherever possible, but set a hard limit
of, maybe 100.  However, this is just my opinion, not a general GDB
policy, so others might disagree.

In terms of content, I think you need to add a documentation entry for
this feature, as well as a NEWS entry.

I also think we need some tests written.

Finally, I notice that there's a click handler.  I haven't tried to
figure out what its doing, once you written a commit message & docs
that will be obvious, but I wonder if we should consider adding a
command driven approach to access the same functionality?

So you might add a command 'tui locals .....' that allows the user to
achieve the same thing as a mouse click, even if (potentially) less
efficiently?  Like I say, I made no attempt to figure out what the
click is offering at this point, I just think this is something to
consider.

Thanks,
Andrew

>  2 files changed, 563 insertions(+)
>  create mode 100644 gdb/python/lib/gdb/command/tui_windows.py
> 
> diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
> index 8b65790cdd9..b8383ea7808 100644
> --- a/gdb/data-directory/Makefile.in
> +++ b/gdb/data-directory/Makefile.in
> @@ -82,6 +82,7 @@ PYTHON_FILE_LIST = \
>  	gdb/command/frame_filters.py \
>  	gdb/command/pretty_printers.py \
>  	gdb/command/prompt.py \
> +	gdb/command/tui_windows.py \
>  	gdb/command/type_printers.py \
>  	gdb/command/unwinders.py \
>  	gdb/command/xmethods.py \
> diff --git a/gdb/python/lib/gdb/command/tui_windows.py b/gdb/python/lib/gdb/command/tui_windows.py
> new file mode 100644
> index 00000000000..cda43743cf1
> --- /dev/null
> +++ b/gdb/python/lib/gdb/command/tui_windows.py
> @@ -0,0 +1,562 @@
> +# Additional TUI windows.
> +# Copyright 2021 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/>.
> +
> +import gdb
> +import sys
> +import re
> +
> +PY3 = sys.version_info[0] == 3
> +
> +custom_windows = {}
> +
> +
> +col_esc_seq_re = re.compile('(\033\[[0-9;]*m)')
> +def escaped_substr(s, n, c):
> +    col_esc_seq = False
> +    init_col_esc_seq = None
> +    last_col_esc_seq = None
> +    sub = ""
> +    for p in col_esc_seq_re.split(s):
> +        t = ""
> +        if n > 0:
> +            if col_esc_seq:
> +                init_col_esc_seq = p
> +            else:
> +                l = len(p)
> +                if l <= n:
> +                    n -= l
> +                else:
> +                    if init_col_esc_seq:
> +                        sub += init_col_esc_seq
> +                    t = p[n:]
> +                    n = 0
> +        else:
> +            t = p
> +        if c > 0:
> +            if col_esc_seq:
> +                sub += t
> +            else:
> +                l = len(t)
> +                if l <= c:
> +                    sub += t
> +                    c -= l
> +                else:
> +                    sub += t[:c]
> +                    c = 0
> +        elif col_esc_seq:
> +            last_col_esc_seq = p
> +        col_esc_seq = not col_esc_seq
> +    if c > 0:
> +        sub += ' ' * c
> +    if last_col_esc_seq:
> +        sub += last_col_esc_seq
> +    return sub
> +
> +class TextWindow(object):
> +    def __init__(self, win, title):
> +        self.win = win
> +        self.line_ofs = 0
> +        self.col_ofs = 0
> +        self.lines = []
> +        win.title = title
> +        global custom_windows
> +        custom_windows[title] = self
> +
> +    def close(self):
> +        global custom_windows
> +        del custom_windows[self.win.title]
> +
> +    def render(self):
> +        self.line_ofs = 0
> +        self.col_ofs = 0
> +
> +    def hscroll(self, num):
> +        prev_col_ofs = self.col_ofs
> +        self.col_ofs = self.col_ofs + num
> +        if self.col_ofs < 0:
> +            self.col_ofs = 0
> +        if self.col_ofs != prev_col_ofs:
> +            self.redraw()
> +
> +    def vscroll(self, num):
> +        prev_line_ofs = self.line_ofs
> +        self.line_ofs = self.line_ofs + num
> +        l = len(self.lines)
> +        if self.line_ofs >= l:
> +            self.line_ofs = l - 1
> +        if self.line_ofs < 0:
> +            self.line_ofs = 0
> +        if self.line_ofs != prev_line_ofs:
> +            self.redraw()
> +
> +    def redraw(self):
> +        l = len(self.lines)
> +        if self.line_ofs > 0 and self.line_ofs + self.win.height > l:
> +            self.line_ofs = l - self.win.height
> +            if self.line_ofs < 0:
> +                self.line_ofs = 0
> +        start = self.line_ofs
> +        stop = self.line_ofs + self.win.height
> +        if stop > l:
> +            stop = l
> +        self.win.erase()
> +        if stop > start:
> +            self.win.write("".join([escaped_substr(l, self.col_ofs, self.win.width) for l in self.lines[start:stop]]))
> +
> +
> +def is_string_instance(s):
> +    if PY3:
> +        return isinstance(s, str)
> +    else:
> +        return isinstance(s, basestring)
> +
> +def val_cmp_color(prev, cur):
> +    if prev is None:
> +        return ("\033[1;32m", "\033[0m", "", "")
> +    elif prev != cur:
> +        return ("", "", "\033[1;31m", "\033[0m")
> +    return ("", "", "", "")
> +
> +def octal_escape(s):
> +    return "".join(c if ord(c) < 128 else "\\%03o" % ord(c) for c in s)
> +
> +def value_string(valstr, hint):
> +    if hint == "string":
> +        is_lazy = type(valstr).__name__ == "LazyString"
> +        if is_lazy:
> +            l = valstr.length
> +        elif not is_string_instance(valstr):
> +            l = 1
> +        else:
> +            l = len(valstr)
> +        if l == 0:
> +            if is_lazy and str(valstr.type) == "wchar_t *":
> +                valstr = "L\"\""
> +            else:
> +                valstr = "\"\""
> +        else:
> +            valstr = gdb.Value(valstr).format_string(symbols=False, address=False)
> +    if isinstance(valstr, gdb.Value):
> +        valstr = valstr.format_string(symbols=False, address=False)
> +    elif not type(valstr) is str:
> +        valstr = str(valstr)
> +    return valstr
> +
> +def is_typedef_of(valtype, name):
> +    if valtype.name == name:
> +        return True
> +    while valtype.code == gdb.TYPE_CODE_TYPEDEF:
> +        valtype = valtype.target()
> +        if valtype.name == name:
> +            return True
> +    return False
> +
> +
> +class VariableWindow(TextWindow):
> +    def __init__(self, win, title, convenience_name):
> +        super(VariableWindow, self).__init__(win, title)
> +        self.prev_vals = {}
> +        self.line_names = []
> +        self.convenience_name = convenience_name
> +
> +    def click(self, x, y, button):
> +        line = y + self.line_ofs
> +        if button == 1 and line < len(self.line_names):
> +            name = self.line_names[line]
> +            if name:
> +                prev = self.prev_vals[name]
> +                if prev is not None and prev[0] is not None:
> +                    prev[0] = not prev[0]
> +                    self.refill(True)
> +        elif button == 3 and line < len(self.line_names):
> +            name = self.line_names[line]
> +            if name:
> +                prev = self.prev_vals[name]
> +                if prev is not None and prev[3] is not None:
> +                    gdb.set_convenience_variable(self.convenience_name, prev[3])
> +                    self.refill(True)
> +
> +    def refill(self, keep_prev=False):
> +        if not self.win.is_valid():
> +            return
> +        self.lines = []
> +        self.line_names = []
> +        cur_vals = {}
> +        for v in self.variables():
> +            name = v.symbol()
> +            value = v.value()
> +            sym_not_init = False
> +            num = 0
> +            fmt = None
> +            error = None
> +            if hasattr(v, "undeclared"):
> +                sym_not_init = v.undeclared()
> +            if hasattr(v, "number"):
> +                num = v.number()
> +            if hasattr(v, "format"):
> +                fmt = v.format()
> +            if hasattr(v, "error"):
> +                error = v.error()
> +            self.add_val(name, name, value, 0, num, cur_vals, keep_prev, False, sym_not_init, fmt, True, error)
> +        self.prev_vals = cur_vals
> +        self.redraw()
> +
> +    def add_val(self, n, fn, v, inset, num, cur_vals, keep_prev, def_expand, sym_not_init, fmt, dyn_type, error):
> +        n2 = fn
> +        if inset == 0:
> +            if num == 0:
> +                count = 1
> +                while n2 in cur_vals:
> +                    count += 1
> +                    n2 = "%s:%d" % (n, count)
> +            else:
> +                n2 = "%d:%s" % (num, n)
> +
> +        cur_entry = [None, False, None, None]
> +        cur_vals[n2] = cur_entry
> +        expand = None
> +        prev_val = None
> +        if n2 in self.prev_vals:
> +            pv = self.prev_vals[n2]
> +            expand = pv[0]
> +            if keep_prev:
> +                prev_val = pv[2]
> +            else:
> +                prev_val = pv[1]
> +            cur_entry[0] = expand
> +            cur_entry[2] = prev_val
> +
> +        spaces = "  " * inset
> +        if num > 0:
> +            numstr = "%d: " % num
> +        else:
> +            numstr = ""
> +
> +        if v is None:
> +            if error is None:
> +                var_col_s, var_col_e = "\033[1;30m", "\033[0m"
> +                if sym_not_init:
> +                    var_col_s, var_col_e = "\033[33m", "\033[0m"
> +                self.lines.append(spaces + numstr + "  " + var_col_s + n + var_col_e)
> +            else:
> +                var_col_s, var_col_e = "", ""
> +                if sym_not_init:
> +                    var_col_s, var_col_e = "\033[33m", "\033[0m"
> +                self.lines.append(spaces + numstr + "  " + var_col_s + n + var_col_e + " = \033[1;30m<" + error + ">\033[0m")
> +            self.line_names.append(n2)
> +            return
> +
> +        if is_string_instance(v):
> +            (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color(prev_val, v)
> +            if sym_not_init:
> +                var_col_s, var_col_e = "\033[33m", "\033[0m"
> +            self.lines.append(spaces + numstr + "  " + var_col_s + n + var_col_e + " = " + val_col_s + v + val_col_e)
> +            self.line_names.append(n2)
> +            cur_entry[1] = v
> +            return
> +
> +        if isinstance(v, gdb.Value):
> +            if v.type.code in [gdb.TYPE_CODE_REF, gdb.TYPE_CODE_RVALUE_REF]:
> +                v = v.referenced_value()
> +
> +            is_optimized_out = False
> +            try:
> +                is_optimized_out = v.is_optimized_out
> +            except:
> +                self.add_val(n, fn, None, inset, num, cur_vals, keep_prev, False, sym_not_init, fmt, False, str(sys.exc_info()[1]))
> +                return
> +
> +            if is_optimized_out and v.type.strip_typedefs().code not in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]:
> +                self.add_val(n, fn, None, inset, num, cur_vals, keep_prev, False, sym_not_init, fmt, False, "optimized out")
> +                return
> +
> +            v_addr = v.address
> +        else:
> +            v = gdb.Value(v)
> +
> +        cv_str = ""
> +        if v_addr is not None:
> +            v_addr = v_addr.dereference().reference_value()
> +            cur_entry[3] = v_addr
> +            cv = gdb.convenience_variable(self.convenience_name)
> +            if cv is not None and cv.address == v_addr.address and cv.type == v_addr.type:
> +                cv_str = " = \033[1;36m$" + self.convenience_name + "\033[0m"
> +
> +        try:
> +            pp = gdb.default_visualizer(v)
> +        except:
> +            pp = None
> +        if pp:
> +            valstr = None
> +            try:
> +                if expand is None and hasattr(pp, "children"):
> +                    expand = def_expand
> +                    cur_entry[0] = expand
> +                expand_str = "  "
> +                if expand is not None:
> +                    if expand:
> +                        expand_str = "- "
> +                    else:
> +                        expand_str = "+ "
> +                numstr += expand_str
> +
> +                if hasattr(pp, "to_string"):
> +                    valstr = pp.to_string()
> +                hint = None
> +                if hasattr(pp, "display_hint"):
> +                    hint = pp.display_hint()
> +                if valstr is not None:
> +                    valstr = value_string(valstr, hint)
> +                    valstr = octal_escape(valstr)
> +                    sp = valstr.split("\n")
> +                    (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color(prev_val, valstr)
> +                    if sym_not_init:
> +                        var_col_s, var_col_e = "\033[33m", "\033[0m"
> +                    self.lines.append(spaces + numstr + var_col_s + n + var_col_e + " = " + val_col_s + sp.pop(0) + val_col_e + cv_str)
> +                    self.line_names.append(n2)
> +                    for extra in sp:
> +                        self.lines.append(spaces + "    " + extra)
> +                        self.line_names.append(None)
> +                    cur_entry[1] = valstr
> +                else:
> +                    (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color(prev_val, False)
> +                    if sym_not_init:
> +                        var_col_s, var_col_e = "\033[33m", "\033[0m"
> +                    self.lines.append(spaces + numstr + var_col_s + n + var_col_e + cv_str)
> +                    self.line_names.append(n2)
> +
> +                if expand:
> +                    childs = None
> +                    if hasattr(pp, "children"):
> +                        childs = pp.children()
> +                    if childs:
> +                        if hint == "map":
> +                            count = 0
> +                            key_prev = None
> +                            for c in childs:
> +                                (nc, vc) = c
> +                                count += 1
> +                                fnc = ":".join([n2, nc])
> +                                if (count % 2) == 1:
> +                                    key_prev = None
> +                                    if is_string_instance(vc):
> +                                        key_prev = vc
> +                                    else:
> +                                        vc_check = vc
> +                                        if vc_check.type.code in [gdb.TYPE_CODE_REF, gdb.TYPE_CODE_RVALUE_REF]:
> +                                            vc_check = vc_check.referenced_value()
> +                                        key_pp = gdb.default_visualizer(vc_check)
> +                                        if key_pp:
> +                                            if hasattr(key_pp, "to_string") and not hasattr(key_pp, "children"):
> +                                                vc_check = key_pp.to_string()
> +                                                if vc_check is not None:
> +                                                    hint = None
> +                                                    if hasattr(key_pp, "display_hint"):
> +                                                        hint = key_pp.display_hint()
> +                                                    key_prev = value_string(vc_check, hint)
> +                                        else:
> +                                            t_check = vc_check.type.strip_typedefs()
> +                                            if t_check.code in [gdb.TYPE_CODE_ENUM, gdb.TYPE_CODE_INT, gdb.TYPE_CODE_FLT,
> +                                                    gdb.TYPE_CODE_BOOL, gdb.TYPE_CODE_COMPLEX, gdb.TYPE_CODE_DECFLOAT]:
> +                                                if fmt:
> +                                                    key_prev = vc_check.format_string(symbols=False, raw=True, format=fmt)
> +                                                else:
> +                                                    key_prev = vc_check.format_string(symbols=False, raw=True)
> +                                    if key_prev is not None and "\n" in key_prev:
> +                                        key_prev = None
> +                                    if key_prev is None:
> +                                        self.add_val("key", fnc, vc, inset + 1, 0, cur_vals, keep_prev, False, False, fmt, True, None)
> +                                else:
> +                                    if key_prev is not None:
> +                                        self.add_val("[" + str(key_prev) + "]", fnc, vc, inset + 1, 0, cur_vals, keep_prev, False, False, fmt, True, None)
> +                                    else:
> +                                        self.add_val("value", fnc, vc, inset + 1, 0, cur_vals, keep_prev, False, False, fmt, True, None)
> +                        else:
> +                            for c in childs:
> +                                (nc, vc) = c
> +                                fnc = ":".join([n2, nc])
> +                                self.add_val(nc, fnc, vc, inset + 1, 0, cur_vals, keep_prev, False, False, fmt, True, None)
> +            except:
> +                self.add_val(n, fn, None, inset, num, cur_vals, keep_prev, False, sym_not_init, fmt, False, str(sys.exc_info()[1]))
> +            return
> +
> +        t = v.type.strip_typedefs()
> +
> +        is_string = False
> +        if t.code in [gdb.TYPE_CODE_ARRAY, gdb.TYPE_CODE_PTR]:
> +            target_type = t.target().strip_typedefs()
> +            if target_type.code == gdb.TYPE_CODE_INT and (target_type.name == "char" or is_typedef_of(t.target(), "wchar_t")):
> +                is_string = True
> +        is_array = t.code in [gdb.TYPE_CODE_ARRAY, gdb.TYPE_CODE_RANGE]
> +        is_ptr = t.code == gdb.TYPE_CODE_PTR and not target_type.code in [gdb.TYPE_CODE_FUNC, gdb.TYPE_CODE_METHOD]
> +        name_add = ""
> +        if dyn_type and v.type.code == gdb.TYPE_CODE_STRUCT:
> +            try:
> +                if v.type != v.dynamic_type:
> +                    v = v.cast(v.dynamic_type)
> +                    name_add = " <" + v.type.name + ">"
> +            except:
> +                pass
> +        expand_str = "  "
> +        if is_ptr or is_array or t.code in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]:
> +            if expand is None:
> +                expand = def_expand
> +                cur_entry[0] = expand
> +            if expand is not None:
> +                if expand:
> +                    expand_str = "- "
> +                else:
> +                    expand_str = "+ "
> +        numstr += expand_str
> +
> +        if is_string or t.code in [gdb.TYPE_CODE_PTR, gdb.TYPE_CODE_ENUM, gdb.TYPE_CODE_FUNC, gdb.TYPE_CODE_INT,
> +                gdb.TYPE_CODE_FLT, gdb.TYPE_CODE_STRING, gdb.TYPE_CODE_METHOD, gdb.TYPE_CODE_METHODPTR,
> +                gdb.TYPE_CODE_MEMBERPTR, gdb.TYPE_CODE_CHAR, gdb.TYPE_CODE_BOOL, gdb.TYPE_CODE_COMPLEX, gdb.TYPE_CODE_DECFLOAT]:
> +            try:
> +                if fmt and not is_string:
> +                    valstr = v.format_string(symbols=False, raw=True, address=True, format=fmt)
> +                else:
> +                    valstr = v.format_string(symbols=False, raw=True, address=True)
> +                valstr = octal_escape(valstr)
> +                (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color(prev_val, valstr)
> +                if sym_not_init:
> +                    var_col_s, var_col_e = "\033[33m", "\033[0m"
> +                self.lines.append(spaces + numstr + var_col_s + n + var_col_e + " = " + val_col_s + valstr + val_col_e + cv_str)
> +                self.line_names.append(n2)
> +                cur_entry[1] = valstr
> +            except:
> +                self.add_val(n, fn, None, inset, num, cur_vals, keep_prev, False, sym_not_init, fmt, False, str(sys.exc_info()[1]))
> +                return
> +        else:
> +            (var_col_s, var_col_e, val_col_s, val_col_e) = val_cmp_color(prev_val, False)
> +            if sym_not_init:
> +                var_col_s, var_col_e = "\033[33m", "\033[0m"
> +            self.lines.append(spaces + numstr + var_col_s + n + var_col_e + name_add + cv_str)
> +            self.line_names.append(n2)
> +        if t.code == gdb.TYPE_CODE_ENUM:
> +            return
> +
> +        if not expand:
> +            return
> +
> +        if is_array:
> +            (low, high) = t.range()
> +            for i in range(low, high + 1):
> +                nc = "[%d]" % i
> +                fnc = ":".join([n2, nc])
> +                self.add_val(nc, fnc, v[i], inset + 1, 0, cur_vals, keep_prev, False, False, fmt, True, None)
> +            return
> +
> +        if is_ptr:
> +            nc = "[0]"
> +            fnc = ":".join([n2, nc])
> +            try:
> +                v = v.dereference()
> +            except:
> +                v = None
> +            self.add_val(nc, fnc, v, inset + 1, 0, cur_vals, keep_prev, target_type.code != gdb.TYPE_CODE_PTR, False, fmt, True, None)
> +            return
> +
> +        fields = None
> +        if t.code in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]:
> +            try:
> +                fields = v.type.fields()
> +            except:
> +                pass
> +        if fields:
> +            num = 1
> +            for f in fields:
> +                if not hasattr(f, "bitpos"):
> +                    continue
> +                try:
> +                    n = f.name
> +                except:
> +                    n = None
> +                if not n:
> +                    n = "<anonymous>"
> +                elif f.is_base_class:
> +                    n = "<" + n + ">"
> +                vf = v[f]
> +                fnc = ":".join([n2, n, "%d" % num])
> +                num += 1
> +                self.add_val(n, fnc, vf, inset + 1, 0, cur_vals, keep_prev, False, False, fmt, False, None)
> +
> +class VarNameValue(object):
> +    def __init__(self, sym, val, undecl, num, fmt, err):
> +        self.sym = sym
> +        self.val = val
> +        self.undecl = undecl
> +        self.num = num
> +        self.fmt = fmt
> +        self.err = err
> +
> +    def symbol(self):
> +        return self.sym
> +
> +    def value(self):
> +        return self.val
> +
> +    def undeclared(self):
> +        return self.undecl
> +
> +    def number(self):
> +        return self.num
> +
> +    def format(self):
> +        return self.fmt
> +
> +    def error(self):
> +        return self.err
> +
> +class LocalsWindow(VariableWindow):
> +    def __init__(self, win):
> +        super(LocalsWindow, self).__init__(win, "locals", "lv")
> +
> +    def variables(self):
> +        thread = gdb.selected_thread()
> +        thread_valid = thread and thread.is_valid()
> +        if thread_valid:
> +            frame = gdb.selected_frame()
> +            cur_line = frame.find_sal().line
> +            try:
> +                block = frame.block()
> +            except:
> +                block = None
> +            while block:
> +                if not block.is_global:
> +                    for symbol in block:
> +                        if symbol.is_argument or symbol.is_variable:
> +                            sym_not_init = symbol.is_variable and symbol.line > 0 and cur_line <= symbol.line
> +                            yield VarNameValue(symbol.name, symbol.value(frame), sym_not_init, 0, None, None)
> +                if block.function:
> +                    break
> +                block = block.superblock
> +
> +
> +gdb.register_window_type("locals", LocalsWindow)
> +
> +
> +def var_change_handler(event=None):
> +    global custom_windows
> +    for key, value in custom_windows.items():
> +        value.refill()
> +
> +gdb.events.before_prompt.connect(var_change_handler)
> +
> +
> +gdb.execute("tui new-layout locals {-horizontal src 2 locals 1} 2 status 0 cmd 1")
> +
> +gdb.execute("alias ll = layout locals")
> -- 
> 2.30.1
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 09/22] Implement cccw TUI command
  2021-03-06 17:34   ` [PATCH 09/22] Implement cccw TUI command Hannes Domani
@ 2021-03-08 10:24     ` Christian Biesinger
  2021-03-11 21:42       ` Tom Tromey
  0 siblings, 1 reply; 34+ messages in thread
From: Christian Biesinger @ 2021-03-08 10:24 UTC (permalink / raw)
  To: Hannes Domani; +Cc: gdb-patches

On Sat, Mar 6, 2021 at 6:35 PM Hannes Domani via Gdb-patches
<gdb-patches@sourceware.org> wrote:
>
> This makes it possible to create a TUI window that always shows
> the output of some gdb command, e.g.:
> (gdb) cccw shared-libs "info sharedlibrary"
> This creates a window named "shared-libs", and shows the output of
> "info sharedlibrary".

This is fairly unintuitive command name. Maybe something like "tui
window create watch-command" or something like that?

Christian

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with mouse
  2021-03-08  9:32     ` Andrew Burgess
@ 2021-03-08 12:00       ` Hannes Domani
  2021-03-11 21:17         ` Tom Tromey
  0 siblings, 1 reply; 34+ messages in thread
From: Hannes Domani @ 2021-03-08 12:00 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

 Am Montag, 8. März 2021, 10:32:09 MEZ hat Andrew Burgess <andrew.burgess@embecosm.com> Folgendes geschrieben:

> * Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> [2021-03-06 18:33:57 +0100]:
>
> > Target area is the first 3 columns of the TUI source window, where the
> > breakpoint status is shown.
> >
> > On a left click, this either creates a new breakpoint, or if one already
> > exists, inverts the enabled state of it.
> > And a middle click deletes the breakpoint.
>
> Thanks for working on this feature, this looks really exciting.
>
> The same breakpoint markers are used for the source and assembler
> windows, both of which inherit from the tui_source_window_base class.
>
> It would be nice if this breakpoint/mouse toggling support was
> supported for both of these windows, possibly by moving the click
> detection up a level in the class hierarchy?

I agree, I just personally didn't have a need for this, usually when I have
the assembler window active, it's in addition with the source window.

So I will try to do this as well.


> The documentation for the TUI is not very extensive, but the
> source/assembler windows, and their breakpoint markers are discussed
> in the 'TUI Overview' section.  I think this change should be
> documented there, as well as mentioned in the NEWS file - this is
> certainly a note worthy new feature.

As I wrote in the series header mail [1], there is still a lot of cleanup
necessary.
And the first point I wrote there was "Add comments", I should have included
documentation (+NEWS) and commit messages.

[1] https://sourceware.org/pipermail/gdb-patches/2021-March/176816.html


> Finally, I think we should consider testing.  I don't know if it is
> possible to send ctrl sequences to the console to emulate mouse
> clicks, but if not then I would suggest this: add a new command
> 'maintenance tui click X Y BUTTON', which injects a button click into
> GDB, X/Y being the click coordinate within the whole GDB terminal, and
> BUTTON being the button number.
>
> Using this it should be possible to test the functionality in this
> patch.

This may be possible on Linux, but these kind of tests will not work on
Windows, where I do all my development.

I can run (most) tests in gdb.base and gdb.python, but e.g. the ones testing
colorized output are not working for me.


 I'm grateful you looked it over, but at this point I was more hoping for
comments on the usability of the new TUI windows and the overall design,
not yet for detailed code comments.


Hannes

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 01/22] Initial TUI mouse support
  2021-03-06 17:33   ` [PATCH 01/22] Initial TUI mouse support Hannes Domani
@ 2021-03-11 17:32     ` Tom Tromey
  2021-03-11 17:48       ` Hannes Domani
  0 siblings, 1 reply; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 17:32 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches

>>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:

Hannes> Implements an overridable tui_win_info::click method whose arguments
Hannes> are the mouse coordinates inside the specific window, and the mouse
Hannes> button clicked.

Thank you.  This is a very welcome development.

I won't comment on tests or comments in the code.

Hannes> +#ifdef BUTTON5_PRESSED
Hannes> +		else if (mev.bstate & BUTTON4_PRESSED)
Hannes> +		  wi->backward_scroll (3);
Hannes> +		else if (mev.bstate & BUTTON5_PRESSED)
Hannes> +		  wi->forward_scroll (3);
Hannes> +#endif

One question I have here is that, right now, the mouse wheel already
works to scroll for me.

I assume this won't change.  But I wonder why new code is needed.
Maybe it's the case that if you enable mouse events, curses turns off
automatic translation of scroll wheel to scrolling?

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 01/22] Initial TUI mouse support
  2021-03-11 17:32     ` Tom Tromey
@ 2021-03-11 17:48       ` Hannes Domani
  2021-03-12 16:35         ` Tom Tromey
  0 siblings, 1 reply; 34+ messages in thread
From: Hannes Domani @ 2021-03-11 17:48 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches, Tom Tromey

 Am Donnerstag, 11. März 2021, 18:32:32 MEZ hat Tom Tromey <tom@tromey.com> Folgendes geschrieben:

> >>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:
>
> Hannes> Implements an overridable tui_win_info::click method whose arguments
> Hannes> are the mouse coordinates inside the specific window, and the mouse
> Hannes> button clicked.
>
> Thank you.  This is a very welcome development.
>
> I won't comment on tests or comments in the code.
>
> Hannes> +#ifdef BUTTON5_PRESSED
> Hannes> +        else if (mev.bstate & BUTTON4_PRESSED)
> Hannes> +          wi->backward_scroll (3);
> Hannes> +        else if (mev.bstate & BUTTON5_PRESSED)
> Hannes> +          wi->forward_scroll (3);
> Hannes> +#endif
>
> One question I have here is that, right now, the mouse wheel already
> works to scroll for me.
>
> I assume this won't change.  But I wonder why new code is needed.
> Maybe it's the case that if you enable mouse events, curses turns off
> automatic translation of scroll wheel to scrolling?

I was not aware of that, and have never seen this behavior.
How does that work, how is the mouse wheel currently translated to
window scrolling?


Hannes

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with mouse
  2021-03-06 17:33   ` [PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with mouse Hannes Domani
  2021-03-08  9:32     ` Andrew Burgess
@ 2021-03-11 21:16     ` Tom Tromey
  1 sibling, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:16 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches

>>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:

Hannes> +	      reinitialize_more_filter ();

This seems peculiar but hopefully it is just cut-and-paste.
If this is needed for some obscure reason, it would be good to hear
about it, so maybe we can eventually remove it.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with mouse
  2021-03-08 12:00       ` Hannes Domani
@ 2021-03-11 21:17         ` Tom Tromey
  0 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:17 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches

>>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:

Hannes> I can run (most) tests in gdb.base and gdb.python, but e.g. the
Hannes> ones testing colorized output are not working for me.

Quite a while ago, Pedro suggested that the styling control should be an
auto-boolean, so we could force it "on".  Maybe this would enable these
tests for you?  It's been on my list of things to implement for quite a
while.  (And apologies if I'm misremembered that somehow..)

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 03/22] Forward mouse click to python TUI window
  2021-03-08  9:36     ` Andrew Burgess
@ 2021-03-11 21:19       ` Tom Tromey
  0 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:19 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Hannes Domani, gdb-patches

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

>> +  void click (int mouse_x, int mouse_y, int mouse_button) override;

Andrew> Unfortunately py-tui.c is pretty hit & miss w.r.t. comments, but this
Andrew> function really should be commented.

FWIW I've normally considered "override" as sufficient to mean "see the
comment in the superclass"; though I am not very adamant about this.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 03/22] Forward mouse click to python TUI window
  2021-03-06 17:33   ` [PATCH 03/22] Forward mouse click to python TUI window Hannes Domani
  2021-03-06 18:09     ` Eli Zaretskii
  2021-03-08  9:36     ` Andrew Burgess
@ 2021-03-11 21:20     ` Tom Tromey
  2 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:20 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches

Hannes> +@defun Window.click (@var{x}, @var{y}, @var{button})
Hannes> +This is called on a mouse click in this window.  @var{x} and @var{y} are
Hannes> +the mouse coordinates inside the window, and @var{button} specifies which
Hannes> +mouse button was used.

It would be good to document if these are 0- or 1-based.  Also it maybe
worth noting that if the window is scrolled, the Python code must handle
that explicit.  Assuming that is the case.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 04/22] Prevent flickering when redrawing the TUI python window
  2021-03-06 17:33   ` [PATCH 04/22] Prevent flickering when redrawing the TUI python window Hannes Domani
@ 2021-03-11 21:21     ` Tom Tromey
  0 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:21 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches

>>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:

Hannes> tui_win_info::refresh_window first redraws the background window, then
Hannes> tui_wrefresh draws the python text on top of it, which flickers.

Hannes> By using wnoutrefresh for the background window, the actual drawing on
Hannes> the screen is only done once, without flickering.

This seems fine if it works.

I tried to make all the curses buffering stuff work better, but it seems
like an area of the TUI calling out for another, more principled
refactoring.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 05/22] Implement locals TUI window
  2021-03-08  9:51     ` Andrew Burgess
@ 2021-03-11 21:26       ` Tom Tromey
  0 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:26 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Hannes Domani, gdb-patches

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Andrew> I'm not sure we need to limit ourselves to 80 characters as in GDB,
Andrew> but I think some of your lines in this file are too long.  My
Andrew> preference would be target 80 wherever possible, but set a hard limit
Andrew> of, maybe 100.  However, this is just my opinion, not a general GDB
Andrew> policy, so others might disagree.

For the Python code in particular, I've been planning to propose we
switch to the 'black' code formatter, and just require running (some
specific version of) this on every .py change.  Then we wouldn't have to
review Python style at all.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 05/22] Implement locals TUI window
  2021-03-06 17:34   ` [PATCH 05/22] Implement locals TUI window Hannes Domani
  2021-03-08  9:51     ` Andrew Burgess
@ 2021-03-11 21:33     ` Tom Tromey
  2021-03-11 22:00     ` Tom Tromey
  2 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:33 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches

>>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:

Hannes> PR tui/17849

I didn't read this in detail, or try it, but I'm definitely supportive
of the concept.

I had some trouble with the structure of the code.  I guess I'd prefer
some of the longer functions be split up.

I'm fine with new windows being written in Python.

Hannes> +col_esc_seq_re = re.compile('(\033\[[0-9;]*m)')

Ouch but I see the need for this kind of thing.

Hannes> +gdb.events.before_prompt.connect(var_change_handler)

I wonder if this should be a bit more fine-grained; but also whether it
can be.

In many situations there's no need to do anything.  Like "print blah" ->
nothing changed.

OTOH I'm not sure if Python can detect that a variable's value changed,
say by the user setting it.  So maybe constantly refreshing is the best
we can currently do?

Hannes> +gdb.execute("alias ll = layout locals")

I saw a note, I think from Andrew, elsewhere in the thread about short
names.

I'm also more in favor of long names, and let users write the short ones
as aliases if they want.  I'm +1 on new useful layouts though.

I know some other short names have gone in, but I wish I had said
something about those at the time.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 06/22] Implement display TUI window
  2021-03-06 17:34   ` [PATCH 06/22] Implement display " Hannes Domani
@ 2021-03-11 21:37     ` Tom Tromey
  0 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:37 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches

>>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:

Hannes> +        cant_eval_str = " (cannot be evaluated in the current context)"

One extra thing we could maybe get out of this experience is a list of
future API needs.

Like I wonder if we could just add a Python hook so that "display" calls
into Python code to do the actual displaying.  Then the hook could be
set whenever this TUI window exists.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 08/22] Implement frames TUI window
  2021-03-06 17:34   ` [PATCH 08/22] Implement frames " Hannes Domani
@ 2021-03-11 21:40     ` Tom Tromey
  2021-03-11 21:50       ` Hannes Domani
  0 siblings, 1 reply; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:40 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches

>>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:

Hannes> +class FramesWindow(TextWindow):

I have a UI question for this and the previous.

Do you want separate threads and frames?  Or integrated?
Also, should inferiors be included?

Like one way would be something like:

    > Inferior
      > Thread
      v Thread
        frame
        frame
        frame
      > Thread
    > Inferior
      > Thread

Where the ">" / "v" show things that could be expanded.

You could go even further and put local variables under the frames.

If you've been trying this out in your own debugging, it would be great
to hear about your experiences, what you tried that didn't work (if
there was anything), etc.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 09/22] Implement cccw TUI command
  2021-03-08 10:24     ` Christian Biesinger
@ 2021-03-11 21:42       ` Tom Tromey
  0 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:42 UTC (permalink / raw)
  To: Christian Biesinger via Gdb-patches

>>>>> "Christian" == Christian Biesinger via Gdb-patches <gdb-patches@sourceware.org> writes:

Christian> On Sat, Mar 6, 2021 at 6:35 PM Hannes Domani via Gdb-patches
Christian> <gdb-patches@sourceware.org> wrote:
>> 
>> This makes it possible to create a TUI window that always shows
>> the output of some gdb command, e.g.:
>> (gdb) cccw shared-libs "info sharedlibrary"
>> This creates a window named "shared-libs", and shows the output of
>> "info sharedlibrary".

Christian> This is fairly unintuitive command name. Maybe something like "tui
Christian> window create watch-command" or something like that?

Oops, I had attributed this statement to Andrew elsewhere.
Sorry about that.  I misremembered and was too lazy to look :(

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 08/22] Implement frames TUI window
  2021-03-11 21:40     ` Tom Tromey
@ 2021-03-11 21:50       ` Hannes Domani
  0 siblings, 0 replies; 34+ messages in thread
From: Hannes Domani @ 2021-03-11 21:50 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches, Tom Tromey

 Am Donnerstag, 11. März 2021, 22:40:56 MEZ hat Tom Tromey <tom@tromey.com> Folgendes geschrieben:

> >>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:
>
> Hannes> +class FramesWindow(TextWindow):
>
> I have a UI question for this and the previous.
>
> Do you want separate threads and frames?  Or integrated?
> Also, should inferiors be included?
>
> Like one way would be something like:
>
>     > Inferior
>       > Thread
>       v Thread
>         frame
>         frame
>         frame
>       > Thread
>     > Inferior
>       > Thread
>
>
> Where the ">" / "v" show things that could be expanded.
>
> You could go even further and put local variables under the frames.
>
> If you've been trying this out in your own debugging, it would be great
> to hear about your experiences, what you tried that didn't work (if
> there was anything), etc.

I didn't even think about the possibility to have the threads and frames
together, I just did it the way I was used to from other debuggers, each has
its own window.

In the same vein I would create an extra window for inferiors.
I didn't (yet) because they don't really work on Windows, so I had no need
for it.


Hannes

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [RFC] TUI windows for locals/display/threads/frames/memory
  2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
                     ` (8 preceding siblings ...)
  2021-03-06 17:34   ` [PATCH 09/22] Implement cccw TUI command Hannes Domani
@ 2021-03-11 21:57   ` Tom Tromey
  9 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 21:57 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches

>>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:

Hannes> This series still needs a lot of cleanup, in particular:
Hannes> - Add comments.
Hannes> - Refactor/rewrite VariableWindow::add_val().
Hannes> - The display window uses the 'info display' output, since there is no
Hannes>   python API to access the automatic display variables.
Hannes> - Patches 14/16/18 may be counted as ugly hacks.

Hannes> Knowing that I still would like some feedback on its current state.

I skimmed through it and pointed out things that I thought were
unusually weird.  I didn't try to really dig into all the details
though.

This is pretty much everything I've dreamed of for the TUI in one
series.  It's very cool!  So let me encourage you to keep working on it.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 05/22] Implement locals TUI window
  2021-03-06 17:34   ` [PATCH 05/22] Implement locals TUI window Hannes Domani
  2021-03-08  9:51     ` Andrew Burgess
  2021-03-11 21:33     ` Tom Tromey
@ 2021-03-11 22:00     ` Tom Tromey
  2 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-11 22:00 UTC (permalink / raw)
  To: Hannes Domani via Gdb-patches

>>>>> "Hannes" == Hannes Domani via Gdb-patches <gdb-patches@sourceware.org> writes:

Hannes> +            pp = gdb.default_visualizer(v)

BTW I wanted to say that I think this is a nice approach.

Hannes> +        except:
Hannes> +            pp = None
Hannes> +        if pp:
[...]
Hannes> +        t = v.type.strip_typedefs()
Hannes> +
Hannes> +        is_string = False
Hannes> +        if t.code in [gdb.TYPE_CODE_ARRAY, gdb.TYPE_CODE_PTR]:

I wonder if wrapping a lot of this in a pseudo-pretty-printer would
simplify things.  It could yield the children for appropriate types and
otherwise just set the string value.  That is, if default_visualizer
returns None, instantiate one of the pseudo-printers for use instead.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 01/22] Initial TUI mouse support
  2021-03-11 17:48       ` Hannes Domani
@ 2021-03-12 16:35         ` Tom Tromey
  2021-03-12 16:43           ` Hannes Domani
  0 siblings, 1 reply; 34+ messages in thread
From: Tom Tromey @ 2021-03-12 16:35 UTC (permalink / raw)
  To: Hannes Domani; +Cc: Hannes Domani via Gdb-patches, Tom Tromey

>> One question I have here is that, right now, the mouse wheel already
>> works to scroll for me.
>> 
>> I assume this won't change.  But I wonder why new code is needed.
>> Maybe it's the case that if you enable mouse events, curses turns off
>> automatic translation of scroll wheel to scrolling?

Hannes> I was not aware of that, and have never seen this behavior.
Hannes> How does that work, how is the mouse wheel currently translated to
Hannes> window scrolling?

I debugged today, and tui_dispatch_ctrl_char gets a KEY_UP key when I
use the scroll wheel.  So, I think it's just a feature built in to
ncurses.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 01/22] Initial TUI mouse support
  2021-03-12 16:35         ` Tom Tromey
@ 2021-03-12 16:43           ` Hannes Domani
  2021-03-12 17:02             ` Tom Tromey
  0 siblings, 1 reply; 34+ messages in thread
From: Hannes Domani @ 2021-03-12 16:43 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Hannes Domani via Gdb-patches

 Am Freitag, 12. März 2021, 17:35:15 MEZ hat Tom Tromey <tom@tromey.com> Folgendes geschrieben:

> >> One question I have here is that, right now, the mouse wheel already
> >> works to scroll for me.
> >>
> >> I assume this won't change.  But I wonder why new code is needed.
> >> Maybe it's the case that if you enable mouse events, curses turns off
> >> automatic translation of scroll wheel to scrolling?
>
> Hannes> I was not aware of that, and have never seen this behavior.
> Hannes> How does that work, how is the mouse wheel currently translated to
> Hannes> window scrolling?
>
> I debugged today, and tui_dispatch_ctrl_char gets a KEY_UP key when I
> use the scroll wheel.  So, I think it's just a feature built in to
> ncurses.

I see, so this always scrolls the window with focus.
With this patch, it scrolls the window where the mouse is, so I don't have to
change the focus back and forth.


Hannes

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 01/22] Initial TUI mouse support
  2021-03-12 16:43           ` Hannes Domani
@ 2021-03-12 17:02             ` Tom Tromey
  0 siblings, 0 replies; 34+ messages in thread
From: Tom Tromey @ 2021-03-12 17:02 UTC (permalink / raw)
  To: Hannes Domani; +Cc: Tom Tromey, Hannes Domani via Gdb-patches

>>>>> "Hannes" == Hannes Domani <ssbssa@yahoo.de> writes:

Hannes> I see, so this always scrolls the window with focus.
Hannes> With this patch, it scrolls the window where the mouse is, so I don't have to
Hannes> change the focus back and forth.

FWIW that does seem like an improvement to me.

Tom

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2021-03-12 17:03 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20210306173417.21528-1-ssbssa.ref@yahoo.de>
2021-03-06 17:33 ` [RFC] TUI windows for locals/display/threads/frames/memory Hannes Domani
2021-03-06 17:33   ` [PATCH 01/22] Initial TUI mouse support Hannes Domani
2021-03-11 17:32     ` Tom Tromey
2021-03-11 17:48       ` Hannes Domani
2021-03-12 16:35         ` Tom Tromey
2021-03-12 16:43           ` Hannes Domani
2021-03-12 17:02             ` Tom Tromey
2021-03-06 17:33   ` [PATCH 02/22] Create/disable/enable/delete breakpoints in TUI with mouse Hannes Domani
2021-03-08  9:32     ` Andrew Burgess
2021-03-08 12:00       ` Hannes Domani
2021-03-11 21:17         ` Tom Tromey
2021-03-11 21:16     ` Tom Tromey
2021-03-06 17:33   ` [PATCH 03/22] Forward mouse click to python TUI window Hannes Domani
2021-03-06 18:09     ` Eli Zaretskii
2021-03-08  9:36     ` Andrew Burgess
2021-03-11 21:19       ` Tom Tromey
2021-03-11 21:20     ` Tom Tromey
2021-03-06 17:33   ` [PATCH 04/22] Prevent flickering when redrawing the TUI python window Hannes Domani
2021-03-11 21:21     ` Tom Tromey
2021-03-06 17:34   ` [PATCH 05/22] Implement locals TUI window Hannes Domani
2021-03-08  9:51     ` Andrew Burgess
2021-03-11 21:26       ` Tom Tromey
2021-03-11 21:33     ` Tom Tromey
2021-03-11 22:00     ` Tom Tromey
2021-03-06 17:34   ` [PATCH 06/22] Implement display " Hannes Domani
2021-03-11 21:37     ` Tom Tromey
2021-03-06 17:34   ` [PATCH 07/22] Implement threads " Hannes Domani
2021-03-06 17:34   ` [PATCH 08/22] Implement frames " Hannes Domani
2021-03-11 21:40     ` Tom Tromey
2021-03-11 21:50       ` Hannes Domani
2021-03-06 17:34   ` [PATCH 09/22] Implement cccw TUI command Hannes Domani
2021-03-08 10:24     ` Christian Biesinger
2021-03-11 21:42       ` Tom Tromey
2021-03-11 21:57   ` [RFC] TUI windows for locals/display/threads/frames/memory Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).