From patchwork Sat Jan 4 18:33:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Tromey X-Patchwork-Id: 37187 Received: (qmail 974 invoked by alias); 4 Jan 2020 18:34:27 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 430 invoked by uid 89); 4 Jan 2020 18:34:23 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-21.3 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_HELO_PASS autolearn=ham version=3.3.1 spammy=filling, FOUND, shrink, funny X-HELO: gateway34.websitewelcome.com Received: from gateway34.websitewelcome.com (HELO gateway34.websitewelcome.com) (192.185.148.109) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 04 Jan 2020 18:34:16 +0000 Received: from cm14.websitewelcome.com (cm14.websitewelcome.com [100.42.49.7]) by gateway34.websitewelcome.com (Postfix) with ESMTP id 5ACE59F974 for ; Sat, 4 Jan 2020 12:34:15 -0600 (CST) Received: from box5379.bluehost.com ([162.241.216.53]) by cmsmtp with SMTP id noFbi0exT4kpjnoFbiV4JJ; Sat, 04 Jan 2020 12:34:15 -0600 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=tromey.com; s=default; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=weDXbYGhXK5r99AKgoqfeSjnvl4FsPPSWD3nBnB/XLw=; b=t0WisP/oQPEYnQ7XF+Zz+N9QaX rZWvuKs7Z0kE5JxhM1tcP5bijIetHB3hnHu6wQ0xO4jsONC0ayNM2ZZnScAwWmwbFtHENIA9C2sF5 9KmqjwSseUh9764NY4LCK//vm; Received: from 75-166-123-50.hlrn.qwest.net ([75.166.123.50]:48942 helo=bapiya.Home) by box5379.bluehost.com with esmtpsa (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92) (envelope-from ) id 1inoFb-0026Lh-4a; Sat, 04 Jan 2020 11:34:15 -0700 From: Tom Tromey To: gdb-patches@sourceware.org Cc: Tom Tromey Subject: [PATCH 11/24] Add horizontal splitting to TUI layout Date: Sat, 4 Jan 2020 11:33:57 -0700 Message-Id: <20200104183410.17114-12-tom@tromey.com> In-Reply-To: <20200104183410.17114-1-tom@tromey.com> References: <20200104183410.17114-1-tom@tromey.com> This changes the TUI layout engine to add horizontal splitting. Now, windows can be side-by-side. A horizontal split is defined using the "-horizontal" parameter to "tui new-layout". This also adds the first "winheight" test to the test suite. One open question is whether we want a new "winwidth" command, now that horizontal layouts are possible. This is easily done using the generic layout code. gdb/ChangeLog 2020-01-04 Tom Tromey PR tui/17850: * tui/tui-win.c (tui_gen_win_info::max_width): New method. * tui/tui-layout.h (class tui_layout_base) : Add "height" argument. (class tui_layout_window) : Likewise. (class tui_layout_split) : Add "vertical" argument. : Add "height" argument. : New field. * tui/tui-layout.c (tui_layout_split::clone): Update. (tui_layout_split::get_sizes): Add "height" argument. (tui_layout_split::adjust_size, tui_layout_split::apply): Update. (tui_new_layout_command): Parse "-horizontal". (_initialize_tui_layout): Update help string. (tui_layout_split::specification): Add "-horizontal" when needed. * tui/tui-layout.c (tui_layout_window::get_sizes): Add "height" argument. * tui/tui-data.h (struct tui_gen_win_info) : New methods. gdb/doc/ChangeLog 2020-01-04 Tom Tromey PR tui/17850: * gdb.texinfo (TUI Commands): Document horizontal layouts. gdb/testsuite/ChangeLog 2020-01-04 Tom Tromey PR tui/17850: * gdb.tui/new-layout.exp: Add horizontal layout and winheight tests. Change-Id: I38b35e504f34698578af86686be03c0fefd954ae --- gdb/ChangeLog | 22 ++++ gdb/NEWS | 2 + gdb/doc/ChangeLog | 5 + gdb/doc/gdb.texinfo | 31 +++++- gdb/testsuite/ChangeLog | 6 ++ gdb/testsuite/gdb.tui/new-layout.exp | 21 +++- gdb/tui/tui-data.h | 9 ++ gdb/tui/tui-layout.c | 147 +++++++++++++++++---------- gdb/tui/tui-layout.h | 19 +++- gdb/tui/tui-win.c | 8 ++ 10 files changed, 207 insertions(+), 63 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index b8939693f4e..a936620c0a8 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -7,6 +7,8 @@ that support it (see entry for GDB 9, below), providing faster performance for programs with many symbols. +* TUI windows can now be arranged horizontally. + * New commands tui new-layout NAME WINDOW WEIGHT [WINDOW WEIGHT]... diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 147d0c28c92..e011e992ba7 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -27909,11 +27909,23 @@ List and give the size of all displayed windows. Create a new TUI layout. The new layout will be named @var{name}, and can be accessed using the @code{layout} command (see below). -Each @var{window} parameter is the name of a window to display. The -windows will be displayed from top to bottom in the order listed. The -names of the windows are the same as the ones given to the +Each @var{window} parameter is either the name of a window to display, +or a window description. The windows will be displayed from top to +bottom in the order listed. + +The names of the windows are the same as the ones given to the @code{focus} command (see below); additional, the @code{locator} -window can be specified. +window can be specified. Note that, because it is of fixed height, +the weight assigned to the locator is of no importance. It is +conventional to use @samp{0} here. + +A window description looks a bit like an invocation of @code{tui +new-layout}, and is of the form +@{@r{[}@code{-horizontal}@r{]}@var{window} @var{weight} @r{[}@var{window} @var{weight}@dots{}@r{]}@}. + +This specifies a sub-layout. If @code{-horizontal} is given, the +windows in this description will be arranged side-by-side, rather than +top-to-bottom. Each @var{weight} is an integer. It is the weight of this window relative to all the other windows in the layout. These numbers are @@ -27930,6 +27942,17 @@ and register windows, followed by the locator, and then finally the command window. The non-locator windows all have the same weight, so the terminal will be split into three roughly equal sections. +Here is a more complex example, showing a horizontal layout: + +@example +(gdb) tui new-layout example @{-horizontal src 1 asm 1@} 2 locator 0 cmd 1 +@end example + +This will result in side-by-side source and assembly windows; with the +locator and command window being beneath these, filling the entire +width of the terminal. Because they have weight 2, the source and +assembly windows will be twice the height of the command window. + @item layout @var{name} @kindex layout Changes which TUI windows are displayed. The @var{name} parameter diff --git a/gdb/testsuite/gdb.tui/new-layout.exp b/gdb/testsuite/gdb.tui/new-layout.exp index 406d9b25f08..61f878532e6 100644 --- a/gdb/testsuite/gdb.tui/new-layout.exp +++ b/gdb/testsuite/gdb.tui/new-layout.exp @@ -52,6 +52,11 @@ gdb_test_no_output "tui new-layout example2 {asm 1 locator 0} 1 cmd 1" gdb_test "help layout example2" \ "Apply the \"example2\" layout.*tui new-layout example2 {asm 1 locator 0} 1 cmd 1" +gdb_test_no_output "tui new-layout h {-horizontal asm 1 src 1} 1 locator 0 cmd 1" + +gdb_test "help layout h" \ + "Apply the \"h\" layout.*tui new-layout h {-horizontal asm 1 src 1} 1 locator 0 cmd 1" + if {![Term::enter_tui]} { unsupported "TUI not supported" } @@ -62,4 +67,18 @@ gdb_assert {![string match "No Source Available" $text]} \ Term::command "layout example" Term::check_contents "example layout shows assembly" \ - "No Assembly Available" + "$hex
" + +Term::command "layout h" +Term::check_box "left window box" 0 0 40 15 +Term::check_box "right window box" 39 0 41 15 +Term::check_contents "horizontal display" \ + "$hex
.*21.*return 0" + +Term::command "winheight src - 5" +Term::check_box "left window box after shrink" 0 0 40 10 +Term::check_box "right window box after shrink" 39 0 41 10 + +Term::command "winheight src + 5" +Term::check_box "left window box after grow" 0 0 40 15 +Term::check_box "right window box after grow" 39 0 41 15 diff --git a/gdb/tui/tui-data.h b/gdb/tui/tui-data.h index 66866dbc23f..570b55b1962 100644 --- a/gdb/tui/tui-data.h +++ b/gdb/tui/tui-data.h @@ -82,6 +82,15 @@ public: /* Compute the minimum height of this window. */ virtual int min_height () const = 0; + /* Compute the maximum width of this window. */ + int max_width () const; + + /* Compute the minimum width of this window. */ + int min_width () const + { + return 3; + } + /* Return true if this window can be boxed. */ virtual bool can_box () const { diff --git a/gdb/tui/tui-layout.c b/gdb/tui/tui-layout.c index be6c754d022..f33317beee8 100644 --- a/gdb/tui/tui-layout.c +++ b/gdb/tui/tui-layout.c @@ -355,12 +355,20 @@ tui_layout_window::apply (int x_, int y_, int width_, int height_) /* See tui-layout.h. */ void -tui_layout_window::get_sizes (int *min_height, int *max_height) +tui_layout_window::get_sizes (bool height, int *min_value, int *max_value) { if (m_window == nullptr) m_window = tui_get_window_by_name (m_contents); - *min_height = m_window->min_height (); - *max_height = m_window->max_height (); + if (height) + { + *min_value = m_window->min_height (); + *max_value = m_window->max_height (); + } + else + { + *min_value = m_window->min_width (); + *max_value = m_window->max_width (); + } } /* See tui-layout.h. */ @@ -430,7 +438,7 @@ tui_layout_split::add_window (const char *name, int weight) std::unique_ptr tui_layout_split::clone () const { - tui_layout_split *result = new tui_layout_split (); + tui_layout_split *result = new tui_layout_split (m_vertical); for (const split &item : m_splits) { std::unique_ptr next = item.layout->clone (); @@ -443,16 +451,29 @@ tui_layout_split::clone () const /* See tui-layout.h. */ void -tui_layout_split::get_sizes (int *min_height, int *max_height) +tui_layout_split::get_sizes (bool height, int *min_value, int *max_value) { - *min_height = 0; - *max_height = 0; + *min_value = 0; + *max_value = 0; + bool first_time = true; for (const split &item : m_splits) { int new_min, new_max; - item.layout->get_sizes (&new_min, &new_max); - *min_height += new_min; - *max_height += new_max; + item.layout->get_sizes (height, &new_min, &new_max); + /* For the mismatch case, the first time through we want to set + the min and max to the computed values -- the "first_time" + check here is just a funny way of doing that. */ + if (height == m_vertical || first_time) + { + *min_value += new_min; + *max_value += new_max; + } + else + { + *min_value = std::max (*min_value, new_min); + *max_value = std::min (*max_value, new_max); + } + first_time = false; } } @@ -502,6 +523,8 @@ tui_layout_split::adjust_size (const char *name, int new_height) return HANDLED; if (adjusted == FOUND) { + if (!m_vertical) + return FOUND; found_index = i; break; } @@ -524,7 +547,7 @@ tui_layout_split::adjust_size (const char *name, int new_height) int index = (found_index + 1 + i) % m_splits.size (); int new_min, new_max; - m_splits[index].layout->get_sizes (&new_min, &new_max); + m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max); if (delta < 0) { @@ -571,23 +594,23 @@ tui_layout_split::apply (int x_, int y_, int width_, int height_) width = width_; height = height_; - struct height_info + struct size_info { - int height; - int min_height; - int max_height; + int size; + int min_size; + int max_size; /* True if this window will share a box border with the previous window in the list. */ bool share_box; }; - std::vector info (m_splits.size ()); + std::vector info (m_splits.size ()); - /* Step 1: Find the min and max height of each sub-layout. - Fixed-sized layouts are given their desired height, and then the + /* Step 1: Find the min and max size of each sub-layout. + Fixed-sized layouts are given their desired size, and then the remaining space is distributed among the remaining windows according to the weights given. */ - int available_height = height; + int available_size = m_vertical ? height : width; int last_index = -1; int total_weight = 0; for (int i = 0; i < m_splits.size (); ++i) @@ -597,7 +620,8 @@ tui_layout_split::apply (int x_, int y_, int width_, int height_) /* Always call get_sizes, to ensure that the window is instantiated. This is a bit gross but less gross than adding special cases for this in other places. */ - m_splits[i].layout->get_sizes (&info[i].min_height, &info[i].max_height); + m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size, + &info[i].max_size); if (!m_applied && cmd_win_already_exists @@ -607,15 +631,17 @@ tui_layout_split::apply (int x_, int y_, int width_, int height_) /* If this layout has never been applied, then it means the user just changed the layout. In this situation, it's desirable to keep the size of the command window the - same. Setting the min and max heights this way ensures + same. Setting the min and max sizes this way ensures that the resizing step, below, does the right thing with this window. */ - info[i].min_height = TUI_CMD_WIN->height; - info[i].max_height = TUI_CMD_WIN->height; + info[i].min_size = (m_vertical + ? TUI_CMD_WIN->height + : TUI_CMD_WIN->width); + info[i].max_size = info[i].min_size; } - if (info[i].min_height == info[i].max_height) - available_height -= info[i].min_height; + if (info[i].min_size == info[i].max_size) + available_size -= info[i].min_size; else { last_index = i; @@ -623,54 +649,58 @@ tui_layout_split::apply (int x_, int y_, int width_, int height_) } /* Two adjacent boxed windows will share a border, making a bit - more height available. */ + more size available. */ if (i > 0 && m_splits[i - 1].layout->bottom_boxed_p () && m_splits[i].layout->top_boxed_p ()) info[i].share_box = true; } - /* Step 2: Compute the height of each sub-layout. Fixed-sized items + /* Step 2: Compute the size of each sub-layout. Fixed-sized items are given their fixed size, while others are resized according to their weight. */ - int used_height = 0; + int used_size = 0; for (int i = 0; i < m_splits.size (); ++i) { /* Compute the height and clamp to the allowable range. */ - info[i].height = available_height * m_splits[i].weight / total_weight; - if (info[i].height > info[i].max_height) - info[i].height = info[i].max_height; - if (info[i].height < info[i].min_height) - info[i].height = info[i].min_height; - /* If there is any leftover height, just redistribute it to the + info[i].size = available_size * m_splits[i].weight / total_weight; + if (info[i].size > info[i].max_size) + info[i].size = info[i].max_size; + if (info[i].size < info[i].min_size) + info[i].size = info[i].min_size; + /* If there is any leftover size, just redistribute it to the last resizeable window, by dropping it from the allocated - height. We could try to be fancier here perhaps, by - redistributing this height among all windows, not just the + size. We could try to be fancier here perhaps, by + redistributing this size among all windows, not just the last window. */ - if (info[i].min_height != info[i].max_height) + if (info[i].min_size != info[i].max_size) { - used_height += info[i].height; + used_size += info[i].size; if (info[i].share_box) - --used_height; + --used_size; } } - /* Allocate any leftover height. */ - if (available_height >= used_height && last_index != -1) - info[last_index].height += available_height - used_height; + /* Allocate any leftover size. */ + if (available_size >= used_size && last_index != -1) + info[last_index].size += available_size - used_size; /* Step 3: Resize. */ - int height_accum = 0; + int size_accum = 0; + const int maximum = m_vertical ? height : width; for (int i = 0; i < m_splits.size (); ++i) { /* If we fall off the bottom, just make allocations overlap. GIGO. */ - if (height_accum + info[i].height > height) - height_accum = height - info[i].height; + if (size_accum + info[i].size > maximum) + size_accum = maximum - info[i].size; else if (info[i].share_box) - --height_accum; - m_splits[i].layout->apply (x, y + height_accum, width, info[i].height); - height_accum += info[i].height; + --size_accum; + if (m_vertical) + m_splits[i].layout->apply (x, y + size_accum, width, info[i].size); + else + m_splits[i].layout->apply (x + size_accum, y, info[i].size, height); + size_accum += info[i].size; } m_applied = true; @@ -716,6 +746,9 @@ tui_layout_split::specification (ui_file *output, int depth) if (depth > 0) fputs_unfiltered ("{", output); + if (!m_vertical) + fputs_unfiltered ("-horizontal ", output); + bool first = true; for (auto &item : m_splits) { @@ -839,8 +872,13 @@ tui_new_layout_command (const char *spec, int from_tty) if (new_name[0] == '-') error (_("Layout name cannot start with '-'")); + bool is_vertical = true; + spec = skip_spaces (spec); + if (check_for_argument (&spec, "-horizontal")) + is_vertical = false; + std::vector> splits; - splits.emplace_back (new tui_layout_split); + splits.emplace_back (new tui_layout_split (is_vertical)); std::unordered_set seen_windows; while (true) { @@ -850,8 +888,11 @@ tui_new_layout_command (const char *spec, int from_tty) if (spec[0] == '{') { - splits.emplace_back (new tui_layout_split); - ++spec; + is_vertical = true; + spec = skip_spaces (spec + 1); + if (check_for_argument (&spec, "-horizontal")) + is_vertical = false; + splits.emplace_back (new tui_layout_split (is_vertical)); continue; } @@ -939,12 +980,12 @@ Usage: layout prev | next | LAYOUT-NAME"), add_cmd ("new-layout", class_tui, tui_new_layout_command, _("Create a new TUI layout.\n\ -Usage: tui new-layout NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\ +Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\ Create a new TUI layout. The new layout will be named NAME,\n\ and can be accessed using \"layout NAME\".\n\ The windows will be displayed in the specified order.\n\ A WINDOW can also be of the form:\n\ - { NAME WEIGHT [NAME WEIGHT]... }\n\ + { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\ This form indicates a sub-frame.\n\ Each WEIGHT is an integer, which holds the relative size\n\ to be allocated to the window."), diff --git a/gdb/tui/tui-layout.h b/gdb/tui/tui-layout.h index 969e4dfd231..6607e8d40d8 100644 --- a/gdb/tui/tui-layout.h +++ b/gdb/tui/tui-layout.h @@ -58,8 +58,9 @@ public: /* Change the size and location of this layout. */ virtual void apply (int x, int y, int width, int height) = 0; - /* Return the minimum and maximum height of this layout. */ - virtual void get_sizes (int *min_height, int *max_height) = 0; + /* Return the minimum and maximum height or width of this layout. + HEIGHT is true to fetch height, false to fetch width. */ + virtual void get_sizes (bool height, int *min_value, int *max_value) = 0; /* True if the topmost item in this layout is boxed. */ virtual bool top_boxed_p () const = 0; @@ -142,7 +143,7 @@ public: protected: - void get_sizes (int *min_height, int *max_height) override; + void get_sizes (bool height, int *min_value, int *max_value) override; private: @@ -159,7 +160,12 @@ class tui_layout_split : public tui_layout_base { public: - tui_layout_split () = default; + /* Create a new layout. If VERTICAL is true, then windows in this + layout will be arranged vertically. */ + explicit tui_layout_split (bool vertical = true) + : m_vertical (vertical) + { + } DISABLE_COPY_AND_ASSIGN (tui_layout_split); @@ -191,7 +197,7 @@ public: protected: - void get_sizes (int *min_height, int *max_height) override; + void get_sizes (bool height, int *min_value, int *max_value) override; private: @@ -209,6 +215,9 @@ private: /* The splits. */ std::vector m_splits; + /* True if the windows in this split are arranged vertically. */ + bool m_vertical; + /* True if this layout has already been applied at least once. */ bool m_applied = false; }; diff --git a/gdb/tui/tui-win.c b/gdb/tui/tui-win.c index 4f90c765b53..8206f3e6965 100644 --- a/gdb/tui/tui-win.c +++ b/gdb/tui/tui-win.c @@ -952,6 +952,14 @@ tui_win_info::max_height () const return tui_term_height () - 2; } +/* See tui-data.h. */ + +int +tui_gen_win_info::max_width () const +{ + return tui_term_width () - 2; +} + static void parse_scrolling_args (const char *arg, struct tui_win_info **win_to_scroll,