* tuiWin.c, tuiWin.h, tui.c, tui.h, tuiCommand.c: Add FSF copyright.
tuiCommand.h, tuiIO.c, tuiIO.h, tuiData.h, tuiData.c: Likewise.
tuiDataWin.c, tuiDataWin.h, tuiDisassem.c, tuiDisassem.h: Likewise.
tuiGeneralWin.c, tuiGeneralWin.h, tuiLayout.c, tuiLayout.h: Likewise.
tuiRegs.c, tuiRegs.h, tuiSource.c, tuiSource.h: Likewise.
tuiSouceWin.c, tuiSourceWin.h, tuiStack.c, tuiStack.h: Likewise.
2001-07-14 19:01:25 +00:00
|
|
|
/* TUI layout window management.
|
2004-01-23 23:25:17 +00:00
|
|
|
|
2023-01-01 16:49:04 +04:00
|
|
|
Copyright (C) 1998-2023 Free Software Foundation, Inc.
|
2004-01-23 23:25:17 +00:00
|
|
|
|
* tuiWin.c, tuiWin.h, tui.c, tui.h, tuiCommand.c: Add FSF copyright.
tuiCommand.h, tuiIO.c, tuiIO.h, tuiData.h, tuiData.c: Likewise.
tuiDataWin.c, tuiDataWin.h, tuiDisassem.c, tuiDisassem.h: Likewise.
tuiGeneralWin.c, tuiGeneralWin.h, tuiLayout.c, tuiLayout.h: Likewise.
tuiRegs.c, tuiRegs.h, tuiSource.c, tuiSource.h: Likewise.
tuiSouceWin.c, tuiSourceWin.h, tuiStack.c, tuiStack.h: Likewise.
2001-07-14 19:01:25 +00:00
|
|
|
Contributed by Hewlett-Packard Company.
|
|
|
|
|
|
|
|
This file is part of GDB.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
2007-08-23 18:08:50 +00:00
|
|
|
the Free Software Foundation; either version 3 of the License, or
|
* tuiWin.c, tuiWin.h, tui.c, tui.h, tuiCommand.c: Add FSF copyright.
tuiCommand.h, tuiIO.c, tuiIO.h, tuiData.h, tuiData.c: Likewise.
tuiDataWin.c, tuiDataWin.h, tuiDisassem.c, tuiDisassem.h: Likewise.
tuiGeneralWin.c, tuiGeneralWin.h, tuiLayout.c, tuiLayout.h: Likewise.
tuiRegs.c, tuiRegs.h, tuiSource.c, tuiSource.h: Likewise.
tuiSouceWin.c, tuiSourceWin.h, tuiStack.c, tuiStack.h: Likewise.
2001-07-14 19:01:25 +00:00
|
|
|
(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
|
2007-08-23 18:08:50 +00:00
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
* tuiWin.c, tuiWin.h, tui.c, tui.h, tuiCommand.c: Add FSF copyright.
tuiCommand.h, tuiIO.c, tuiIO.h, tuiData.h, tuiData.c: Likewise.
tuiDataWin.c, tuiDataWin.h, tuiDisassem.c, tuiDisassem.h: Likewise.
tuiGeneralWin.c, tuiGeneralWin.h, tuiLayout.c, tuiLayout.h: Likewise.
tuiRegs.c, tuiRegs.h, tuiSource.c, tuiSource.h: Likewise.
tuiSouceWin.c, tuiSourceWin.h, tuiStack.c, tuiStack.h: Likewise.
2001-07-14 19:01:25 +00:00
|
|
|
|
2019-01-27 12:51:36 -07:00
|
|
|
#ifndef TUI_TUI_LAYOUT_H
|
|
|
|
#define TUI_TUI_LAYOUT_H
|
1999-04-16 01:35:26 +00:00
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
#include "ui-file.h"
|
|
|
|
|
2004-01-23 23:25:17 +00:00
|
|
|
#include "tui/tui.h"
|
|
|
|
#include "tui/tui-data.h"
|
gdb/tui: improve errors from tui focus command
This commit improves (I think) the errors from the tui focus command.
There are a number of errors that can be triggered by the focus
command, they include:
(1) Window name "NAME" is ambiguous
(2) Unrecognized window name "NAME"
(3) Window "NAME" cannot be focused
Error (1) is triggered when the user gives a partial window name, and
the name matches multiple windows in the current layout.
It is worth noting that the ambiguity must be within the current
layout; if the partial name matches one window in the current layout,
and one or more windows not in the current layout, then this is not
ambiguous, and focus will shift to the matching window in the current
layout.
This error was not previous being tested, but in this commit I make
use of the Python API to trigger and test this error.
Error (3) is simple enough, and was already being tested. This is
triggered by something like 'focus status'. The named window needs to
be present in the current layout, and non-focusable in order to
trigger the error.
Error (2) is what I'd like to improve in this commit. This error
triggers if the name the user gives doesn't match any window in the
current layout. Even if GDB does know about the window, but the
window isn't in the current layout, then GDB will say it doesn't
recognize the window name.
In this commit I propose to to split this error into three different
errors. These will be:
(a) Unrecognized window name "NAME"
(b) No windows matching "NAME" in the current layout
(c) Window "NAME" is not in the current layout
Error (a) is the same as before, but will now only trigger if GDB
doesn't know about window NAME at all. If the window is known, but
not in the current layout then one of the other errors will trigger.
Error (b) will trigger if NAME is ambiguous for multiple windows that
are not in the current layout. If NAME identifies a single window in
the current layout then that window will continue to be selected, just
as it currently is. Only in the case where NAME doesn't identify a
window in the current layout do we then check all the other known
windows, if NAME matches multiple of these, then (b) is triggered.
Finally, error (c) is used when NAME uniquely identifies a single
window that is not in the current layout.
The hope with these new errors is that the user will have a better
understanding of what went wrong. Instead of GDB claiming to not know
about a window, the mention of the current layout will hint to the
user that they should first switch layouts.
There are tests included for all the new errors.
2022-12-22 16:26:37 +00:00
|
|
|
#include "gdbsupport/iterator-range.h"
|
|
|
|
|
|
|
|
#include <unordered_map>
|
2004-01-23 23:25:17 +00:00
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* Values that can be returned when handling a request to adjust a
|
|
|
|
window's size. */
|
|
|
|
enum tui_adjust_result
|
|
|
|
{
|
|
|
|
/* Requested window was not found here. */
|
|
|
|
NOT_FOUND,
|
|
|
|
/* Window was found but not handled. */
|
|
|
|
FOUND,
|
|
|
|
/* Window was found and handled. */
|
|
|
|
HANDLED
|
|
|
|
};
|
|
|
|
|
2019-10-07 18:03:02 -06:00
|
|
|
/* The basic object in a TUI layout. This represents a single piece
|
|
|
|
of screen real estate. Subclasses determine the exact
|
|
|
|
behavior. */
|
|
|
|
class tui_layout_base
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
DISABLE_COPY_AND_ASSIGN (tui_layout_base);
|
|
|
|
|
Add virtual destructor to tui_layout_base
I stumbled on some ASan failures when using the TUI, when tearing down a
TUI layout. The simplest way to trigger it is to run:
$ ./gdb --data-directory=data-directory -batch -ex "layout next"
The ASan report is:
=================================================================
==2829136==ERROR: AddressSanitizer: new-delete-type-mismatch on 0x608000009a20 in thread T0:
object passed to delete has wrong type:
size of the allocated type: 88 bytes;
size of the deallocated type: 24 bytes.
#0 0x7f470fe2507e in operator delete(void*, unsigned long) /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cc:177
#1 0x55f88c75700d in std::default_delete<tui_layout_base>::operator()(tui_layout_base*) const /usr/include/c++/9.2.0/bits/unique_ptr.h:81
#2 0x55f88c756328 in std::unique_ptr<tui_layout_base, std::default_delete<tui_layout_base> >::~unique_ptr() /usr/include/c++/9.2.0/bits/unique_ptr.h:284
#3 0x7f470ee536a6 in __run_exit_handlers (/usr/lib/libc.so.6+0x3e6a6)
#4 0x7f470ee5385d in __GI_exit (/usr/lib/libc.so.6+0x3e85d)
#5 0x55f88c69f2ac in quit_force(int*, int) /home/simark/src/binutils-gdb/gdb/top.c:1766
#6 0x55f88becc29a in captured_main_1 /home/simark/src/binutils-gdb/gdb/main.c:1183
#7 0x55f88becc814 in captured_main /home/simark/src/binutils-gdb/gdb/main.c:1192
#8 0x55f88becc8a9 in gdb_main(captured_main_args*) /home/simark/src/binutils-gdb/gdb/main.c:1217
#9 0x55f88b3159cd in main /home/simark/src/binutils-gdb/gdb/gdb.c:32
#10 0x7f470ee3c152 in __libc_start_main (/usr/lib/libc.so.6+0x27152)
#11 0x55f88b31579d in _start (/home/simark/build/binutils-gdb/gdb/gdb+0x11fb79d)
0x608000009a20 is located 0 bytes inside of 88-byte region [0x608000009a20,0x608000009a78)
allocated by thread T0 here:
#0 0x7f470fe238f8 in operator new(unsigned long) /build/gcc/src/gcc/libsanitizer/asan/asan_new_delete.cc:104
#1 0x55f88c750906 in tui_layout_split::clone() const /home/simark/src/binutils-gdb/gdb/tui/tui-layout.c:515
#2 0x55f88c74e60e in show_layout /home/simark/src/binutils-gdb/gdb/tui/tui-layout.c:90
#3 0x55f88c74e7db in tui_set_layout(tui_layout_type) /home/simark/src/binutils-gdb/gdb/tui/tui-layout.c:116
#4 0x55f88c782f4f in tui_enable() /home/simark/src/binutils-gdb/gdb/tui/tui.c:481
#5 0x55f88c74eeb2 in tui_layout_command /home/simark/src/binutils-gdb/gdb/tui/tui-layout.c:286
#6 0x55f88b6f969b in do_const_cfunc /home/simark/src/binutils-gdb/gdb/cli/cli-decode.c:107
#7 0x55f88b701859 in cmd_func(cmd_list_element*, char const*, int) /home/simark/src/binutils-gdb/gdb/cli/cli-decode.c:1952
#8 0x55f88c69b455 in execute_command(char const*, int) /home/simark/src/binutils-gdb/gdb/top.c:652
#9 0x55f88bec9026 in catch_command_errors /home/simark/src/binutils-gdb/gdb/main.c:400
#10 0x55f88becc1f2 in captured_main_1 /home/simark/src/binutils-gdb/gdb/main.c:1167
#11 0x55f88becc814 in captured_main /home/simark/src/binutils-gdb/gdb/main.c:1192
#12 0x55f88becc8a9 in gdb_main(captured_main_args*) /home/simark/src/binutils-gdb/gdb/main.c:1217
#13 0x55f88b3159cd in main /home/simark/src/binutils-gdb/gdb/gdb.c:32
#14 0x7f470ee3c152 in __libc_start_main (/usr/lib/libc.so.6+0x27152)
The problem is that the tui_layout_base is missing a virtual destructor.
We allocate a derived object (tui_layout_split), but delete it through a
tui_layout_base pointer. Since the tui_layout_base destructor is not
virtual, the derived (tui_layout_split) destructor is not called, only
the base destructor.
That code is not in gdb-9-branch, so I don't think this patch is
relevant for the stable branch.
Note that this is caught as a diagnostic with clang:
In file included from /home/simark/src/binutils-gdb/gdb/tui/tui-layout.c:22:
In file included from /home/simark/src/binutils-gdb/gdb/defs.h:28:
In file included from /home/simark/src/binutils-gdb/gdb/gdbsupport/common-defs.h:133:
In file included from /home/simark/src/binutils-gdb/gdb/gdbsupport/common-exceptions.h:25:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../include/c++/9.2.0/memory:80:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/unique_ptr.h:81:2: error: delete called on 'tui_layout_base' that is abstract but has non-virtual destructor [-Werror,-Wdelete-abstract-non-virtual-dtor]
delete __ptr;
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/unique_ptr.h:284:4: note: in instantiation of member function 'std::default_delete<tui_layout_base>::operator()' requested here
get_deleter()(std::move(__ptr));
^
/home/simark/src/binutils-gdb/gdb/tui/tui-layout.c:54:41: note: in instantiation of member function 'std::unique_ptr<tui_layout_base, std::default_delete<tui_layout_base> >::~unique_ptr' requested here
static std::unique_ptr<tui_layout_base> applied_layout;
^
1 error generated.
GCC has the similar -Wdelete-non-virtual-dtor, enabled by -Wall, but it
doesn't show up because warnings are inhibited for system headers, where
std::unique_ptr is defined. There is a bug about it here:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58876
gdb/ChangeLog:
* tui/tui-layout.h (class tui_layout_base): Add virtual
destructor.
2019-12-17 15:01:15 -05:00
|
|
|
virtual ~tui_layout_base () = default;
|
|
|
|
|
2019-10-07 18:03:02 -06:00
|
|
|
/* Clone this object. Ordinarily a layout is cloned before it is
|
|
|
|
used, so that any necessary modifications do not affect the
|
|
|
|
"skeleton" layout. */
|
|
|
|
virtual std::unique_ptr<tui_layout_base> clone () const = 0;
|
|
|
|
|
gdb/tui: support placing the cmd window into a horizontal layout
This commit allows the user to place the cmd window within horizontal
tui layouts. Consider this set of steps, carried out in an 80 columns
by 24 lines terminal, using current master gdb:
(gdb) tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
(gdb) tui layout hsrc
What you end up with is a full width cmd window with the status bar
beneath. Where's the src window gone? We then try:
(gdb) info win
Name Lines Columns Focus
src 23 3 (has focus)
cmd 23 80
status 1 80
(gdb)
Something weird has gone on, gdb has overlapped the cmd window with
the src window. If we trigger the src window to redraw is content,
for example, 'list main', then we see corruption in the cmd window as
the src window overwrites it.
So, what's going on?
The problem is some code in tui_layout_split::apply, in tui-layout.c.
Within 'Step 1', there is a loop that calculates the min/max window
sizes for all windows within a tui_layout_split. However, there's a
special case for the cmd window.
This special case is trying to have the cmd window retain its current
size when a layout is re-applied, or a new layout is applied. This
makes sense, consider moving from the 'src' layout to the 'asm'
layout, this looks something like this (status window removed):
.-------. .-------.
| src | | asm |
|-------| ====> |-------|
| cmd | | cmd |
'-------' '-------'
If the user has gone to the effort of adjusting the cmd window size,
then, the thinking goes, we shouldn't reset the cmd window size when
switching layouts like this.
The problem though, is that when we do a switch more like this:
.-----------. .-----------.
| src | | | |
|-----------| ====> | asm | cmd |
| cmd | | | |
'-----------' '-----------'
Now retaining the cmd window width makes no sense; the new layout has
a completely different placement for the cmd window, instead of sizing
by height, we're now sizing by width. The existing code doesn't
understand this though, and tried to retain the full width for the cmd
window.
To solve this problem, I propose we introduce the idea of a layout
"fingerprint". The fingerprint tries to capture, in an abstract way,
where the cmd window lives within the layout.
Only when two layouts have the same fingerprint will we attempt to
retain the cmd window size.
The fingerprint for a layout is represented as a string, the string is
a series of 'V' or 'H' characters, ending with a single 'C'
character. The series of 'V' and 'H' characters represent the
vertical or horizontal layouts that must be passed through to find the
cmd window.
Here are a few examples:
# This layout is equivalent to the builtin 'src' layout.
# Fingerprint: VC
tui new-layout example1 src 2 status 0 cmd 1
# This layout is equivalent to the builtin 'split' layout.
# Fingerprint: VC
tui new-layout example2 src 1 asm 1 status 0 cmd 1
# This is the same layout that was given at the top.
# Fingerprint: VHC
tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
And so, when switching between example1 and example2, gdb knows that
the cmd window is, basically, in the same sort of position within the
layout, and will retain the cmd window size.
In contrast, when switching to the hsrc layout, gdb understands that
the position of the cmd window is different, and does not try to
retain the cmd window size.
2022-02-01 14:02:19 +00:00
|
|
|
/* Change the size and location of this layout. When
|
|
|
|
PRESERVE_CMD_WIN_SIZE_P is true the current size of the TUI_CMD_WIN
|
|
|
|
is preserved, otherwise, the TUI_CMD_WIN will resize just like any
|
|
|
|
other window. */
|
|
|
|
virtual void apply (int x, int y, int width, int height,
|
|
|
|
bool preserve_cmd_win_size_p) = 0;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* 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;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
gdb/tui: add left_boxed_p and right_boxed_p member functions
When I initially saw this code in tui_layout_split::apply, I assumed
that this must be a bug:
/* Two adjacent boxed windows will share a border, making a bit
more size available. */
if (i > 0
&& m_splits[i - 1].layout->bottom_boxed_p ()
&& m_splits[i].layout->top_boxed_p ())
...
After all, the apply might be laying out a horizontal layout, right?
So checking bottom_boxed_p and top_boxed_p is clearly wrong.
Well, it turns on, that due to the implementations of these things,
bottom_boxed_p is equivalent to an imagined right_boxed_p, and
top_boxed_p is equivalent to an imagined left_boxed_p.
In this commit I've renamed both top_boxed_p and bottom_boxed_p to
first_edge_has_border_p and last_edge_has_border_p respectively, and
extended the comments in tui_layout_base to mention that these methods
handle both horizontal and vertical layouts.
Now, hopefully, the code shouldn't look like it only applies for
vertical layouts.
There should be no user visible changes after this commit.
2022-01-31 12:13:38 +00:00
|
|
|
/* True if the topmost (for vertical layouts), or the leftmost (for
|
|
|
|
horizontal layouts) item in this layout is boxed. */
|
|
|
|
virtual bool first_edge_has_border_p () const = 0;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
gdb/tui: add left_boxed_p and right_boxed_p member functions
When I initially saw this code in tui_layout_split::apply, I assumed
that this must be a bug:
/* Two adjacent boxed windows will share a border, making a bit
more size available. */
if (i > 0
&& m_splits[i - 1].layout->bottom_boxed_p ()
&& m_splits[i].layout->top_boxed_p ())
...
After all, the apply might be laying out a horizontal layout, right?
So checking bottom_boxed_p and top_boxed_p is clearly wrong.
Well, it turns on, that due to the implementations of these things,
bottom_boxed_p is equivalent to an imagined right_boxed_p, and
top_boxed_p is equivalent to an imagined left_boxed_p.
In this commit I've renamed both top_boxed_p and bottom_boxed_p to
first_edge_has_border_p and last_edge_has_border_p respectively, and
extended the comments in tui_layout_base to mention that these methods
handle both horizontal and vertical layouts.
Now, hopefully, the code shouldn't look like it only applies for
vertical layouts.
There should be no user visible changes after this commit.
2022-01-31 12:13:38 +00:00
|
|
|
/* True if the bottommost (for vertical layouts), or the rightmost (for
|
|
|
|
horizontal layouts) item in this layout is boxed. */
|
|
|
|
virtual bool last_edge_has_border_p () const = 0;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
|
|
|
/* Return the name of this layout's window, or nullptr if this
|
|
|
|
layout does not represent a single window. */
|
|
|
|
virtual const char *get_name () const
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-01-24 17:49:12 +00:00
|
|
|
/* Set the height of the window named NAME to NEW_HEIGHT, updating
|
2019-10-07 18:03:02 -06:00
|
|
|
the sizes of the other windows around it. */
|
2022-01-24 17:49:12 +00:00
|
|
|
virtual tui_adjust_result set_height (const char *name, int new_height) = 0;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
gdb/tui: add new 'tui window width' command and 'winwidth' alias
This commit adds a new command 'tui window width', and an alias
'winwidth'. This command is equivalent to the old 'winheight'
command (which was recently renamed 'tui window height').
Even though I recently moved the old tui commands under the tui
namespace, and I would strongly encourage all new tui commands to be
added as 'tui ....' only (users can create their own top-level aliases
if they want), I'm breaking that suggestion here, and adding a
'winwidth' alias.
Given that we already have 'winheight' and have done for years, it
just didn't seem right to no have the matching 'winwidth'.
You might notice in the test that the window resizing doesn't quite
work right. I setup a horizontal layout, then grow and shrink the
windows. At the end of the test the windows should be back to their
original size...
... they are not. This isn't my fault, honest! GDB's window resizing
is a little ... temperamental, and is prone to getting things slightly
wrong during resizes, off by 1 type things. This is true for height
resizing, as well as the new width resizing.
Later patches in this series will rework the resizing algorithm, which
should improve things in this area. For now, I'm happy that the width
resizing is as good as the height resizing, given the existing quirks.
For the docs side I include a paragraph that explains how multiple
windows are required before the width can be adjusted. For
completeness, I've added the same paragraph to the winheight
description. With the predefined layouts this extra paragraph is not
really needed for winheight, as there are always multiple windows on
the screen. However, with custom layouts, this might not be true, so
adding the paragraph seems like a good idea.
As for the changes in gdb itself, I've mostly just taken the existing
height adjustment code, changed the name to make it generic 'size'
adjustment, and added a boolean flag to indicate if we are adjusting
the width or the height.
2022-01-24 22:02:59 +00:00
|
|
|
/* Set the width of the window named NAME to NEW_WIDTH, updating
|
|
|
|
the sizes of the other windows around it. */
|
|
|
|
virtual tui_adjust_result set_width (const char *name, int new_width) = 0;
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* Remove some windows from the layout, leaving the command window
|
|
|
|
and the window being passed in here. */
|
|
|
|
virtual void remove_windows (const char *name) = 0;
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* Replace the window named NAME in the layout with the window named
|
|
|
|
NEW_WINDOW. */
|
|
|
|
virtual void replace_window (const char *name, const char *new_window) = 0;
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* Append the specification to this window to OUTPUT. DEPTH is the
|
|
|
|
depth of this layout in the hierarchy (zero-based). */
|
|
|
|
virtual void specification (ui_file *output, int depth) = 0;
|
2020-02-22 11:48:26 -07:00
|
|
|
|
gdb/tui: support placing the cmd window into a horizontal layout
This commit allows the user to place the cmd window within horizontal
tui layouts. Consider this set of steps, carried out in an 80 columns
by 24 lines terminal, using current master gdb:
(gdb) tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
(gdb) tui layout hsrc
What you end up with is a full width cmd window with the status bar
beneath. Where's the src window gone? We then try:
(gdb) info win
Name Lines Columns Focus
src 23 3 (has focus)
cmd 23 80
status 1 80
(gdb)
Something weird has gone on, gdb has overlapped the cmd window with
the src window. If we trigger the src window to redraw is content,
for example, 'list main', then we see corruption in the cmd window as
the src window overwrites it.
So, what's going on?
The problem is some code in tui_layout_split::apply, in tui-layout.c.
Within 'Step 1', there is a loop that calculates the min/max window
sizes for all windows within a tui_layout_split. However, there's a
special case for the cmd window.
This special case is trying to have the cmd window retain its current
size when a layout is re-applied, or a new layout is applied. This
makes sense, consider moving from the 'src' layout to the 'asm'
layout, this looks something like this (status window removed):
.-------. .-------.
| src | | asm |
|-------| ====> |-------|
| cmd | | cmd |
'-------' '-------'
If the user has gone to the effort of adjusting the cmd window size,
then, the thinking goes, we shouldn't reset the cmd window size when
switching layouts like this.
The problem though, is that when we do a switch more like this:
.-----------. .-----------.
| src | | | |
|-----------| ====> | asm | cmd |
| cmd | | | |
'-----------' '-----------'
Now retaining the cmd window width makes no sense; the new layout has
a completely different placement for the cmd window, instead of sizing
by height, we're now sizing by width. The existing code doesn't
understand this though, and tried to retain the full width for the cmd
window.
To solve this problem, I propose we introduce the idea of a layout
"fingerprint". The fingerprint tries to capture, in an abstract way,
where the cmd window lives within the layout.
Only when two layouts have the same fingerprint will we attempt to
retain the cmd window size.
The fingerprint for a layout is represented as a string, the string is
a series of 'V' or 'H' characters, ending with a single 'C'
character. The series of 'V' and 'H' characters represent the
vertical or horizontal layouts that must be passed through to find the
cmd window.
Here are a few examples:
# This layout is equivalent to the builtin 'src' layout.
# Fingerprint: VC
tui new-layout example1 src 2 status 0 cmd 1
# This layout is equivalent to the builtin 'split' layout.
# Fingerprint: VC
tui new-layout example2 src 1 asm 1 status 0 cmd 1
# This is the same layout that was given at the top.
# Fingerprint: VHC
tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
And so, when switching between example1 and example2, gdb knows that
the cmd window is, basically, in the same sort of position within the
layout, and will retain the cmd window size.
In contrast, when switching to the hsrc layout, gdb understands that
the position of the cmd window is different, and does not try to
retain the cmd window size.
2022-02-01 14:02:19 +00:00
|
|
|
/* Return a FINGERPRINT string containing an abstract representation of
|
|
|
|
the location of the cmd window in this layout.
|
|
|
|
|
|
|
|
When called on a complete, top-level layout, the fingerprint will be a
|
|
|
|
non-empty string made of 'V' and 'H' characters, followed by a single
|
|
|
|
'C' character. Each 'V' and 'H' represents a vertical or horizontal
|
|
|
|
layout that must be passed through in order to find the cmd
|
2023-05-31 07:39:31 +02:00
|
|
|
window. A vertical or horizontal layout of just one window does not add
|
|
|
|
a 'V' or 'H' character.
|
gdb/tui: support placing the cmd window into a horizontal layout
This commit allows the user to place the cmd window within horizontal
tui layouts. Consider this set of steps, carried out in an 80 columns
by 24 lines terminal, using current master gdb:
(gdb) tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
(gdb) tui layout hsrc
What you end up with is a full width cmd window with the status bar
beneath. Where's the src window gone? We then try:
(gdb) info win
Name Lines Columns Focus
src 23 3 (has focus)
cmd 23 80
status 1 80
(gdb)
Something weird has gone on, gdb has overlapped the cmd window with
the src window. If we trigger the src window to redraw is content,
for example, 'list main', then we see corruption in the cmd window as
the src window overwrites it.
So, what's going on?
The problem is some code in tui_layout_split::apply, in tui-layout.c.
Within 'Step 1', there is a loop that calculates the min/max window
sizes for all windows within a tui_layout_split. However, there's a
special case for the cmd window.
This special case is trying to have the cmd window retain its current
size when a layout is re-applied, or a new layout is applied. This
makes sense, consider moving from the 'src' layout to the 'asm'
layout, this looks something like this (status window removed):
.-------. .-------.
| src | | asm |
|-------| ====> |-------|
| cmd | | cmd |
'-------' '-------'
If the user has gone to the effort of adjusting the cmd window size,
then, the thinking goes, we shouldn't reset the cmd window size when
switching layouts like this.
The problem though, is that when we do a switch more like this:
.-----------. .-----------.
| src | | | |
|-----------| ====> | asm | cmd |
| cmd | | | |
'-----------' '-----------'
Now retaining the cmd window width makes no sense; the new layout has
a completely different placement for the cmd window, instead of sizing
by height, we're now sizing by width. The existing code doesn't
understand this though, and tried to retain the full width for the cmd
window.
To solve this problem, I propose we introduce the idea of a layout
"fingerprint". The fingerprint tries to capture, in an abstract way,
where the cmd window lives within the layout.
Only when two layouts have the same fingerprint will we attempt to
retain the cmd window size.
The fingerprint for a layout is represented as a string, the string is
a series of 'V' or 'H' characters, ending with a single 'C'
character. The series of 'V' and 'H' characters represent the
vertical or horizontal layouts that must be passed through to find the
cmd window.
Here are a few examples:
# This layout is equivalent to the builtin 'src' layout.
# Fingerprint: VC
tui new-layout example1 src 2 status 0 cmd 1
# This layout is equivalent to the builtin 'split' layout.
# Fingerprint: VC
tui new-layout example2 src 1 asm 1 status 0 cmd 1
# This is the same layout that was given at the top.
# Fingerprint: VHC
tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
And so, when switching between example1 and example2, gdb knows that
the cmd window is, basically, in the same sort of position within the
layout, and will retain the cmd window size.
In contrast, when switching to the hsrc layout, gdb understands that
the position of the cmd window is different, and does not try to
retain the cmd window size.
2022-02-01 14:02:19 +00:00
|
|
|
|
|
|
|
Of course, layouts are built recursively, so, when called on a partial
|
|
|
|
layout, if this object represents a single window, then either the
|
|
|
|
empty string is returned (for non-cmd windows), or a string
|
|
|
|
containing a single 'C' is returned.
|
|
|
|
|
|
|
|
For object representing layouts, if the layout contains the cmd
|
2023-05-31 07:39:31 +02:00
|
|
|
window then we will get back a valid fingerprint string (may contain 'V'
|
gdb/tui: support placing the cmd window into a horizontal layout
This commit allows the user to place the cmd window within horizontal
tui layouts. Consider this set of steps, carried out in an 80 columns
by 24 lines terminal, using current master gdb:
(gdb) tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
(gdb) tui layout hsrc
What you end up with is a full width cmd window with the status bar
beneath. Where's the src window gone? We then try:
(gdb) info win
Name Lines Columns Focus
src 23 3 (has focus)
cmd 23 80
status 1 80
(gdb)
Something weird has gone on, gdb has overlapped the cmd window with
the src window. If we trigger the src window to redraw is content,
for example, 'list main', then we see corruption in the cmd window as
the src window overwrites it.
So, what's going on?
The problem is some code in tui_layout_split::apply, in tui-layout.c.
Within 'Step 1', there is a loop that calculates the min/max window
sizes for all windows within a tui_layout_split. However, there's a
special case for the cmd window.
This special case is trying to have the cmd window retain its current
size when a layout is re-applied, or a new layout is applied. This
makes sense, consider moving from the 'src' layout to the 'asm'
layout, this looks something like this (status window removed):
.-------. .-------.
| src | | asm |
|-------| ====> |-------|
| cmd | | cmd |
'-------' '-------'
If the user has gone to the effort of adjusting the cmd window size,
then, the thinking goes, we shouldn't reset the cmd window size when
switching layouts like this.
The problem though, is that when we do a switch more like this:
.-----------. .-----------.
| src | | | |
|-----------| ====> | asm | cmd |
| cmd | | | |
'-----------' '-----------'
Now retaining the cmd window width makes no sense; the new layout has
a completely different placement for the cmd window, instead of sizing
by height, we're now sizing by width. The existing code doesn't
understand this though, and tried to retain the full width for the cmd
window.
To solve this problem, I propose we introduce the idea of a layout
"fingerprint". The fingerprint tries to capture, in an abstract way,
where the cmd window lives within the layout.
Only when two layouts have the same fingerprint will we attempt to
retain the cmd window size.
The fingerprint for a layout is represented as a string, the string is
a series of 'V' or 'H' characters, ending with a single 'C'
character. The series of 'V' and 'H' characters represent the
vertical or horizontal layouts that must be passed through to find the
cmd window.
Here are a few examples:
# This layout is equivalent to the builtin 'src' layout.
# Fingerprint: VC
tui new-layout example1 src 2 status 0 cmd 1
# This layout is equivalent to the builtin 'split' layout.
# Fingerprint: VC
tui new-layout example2 src 1 asm 1 status 0 cmd 1
# This is the same layout that was given at the top.
# Fingerprint: VHC
tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
And so, when switching between example1 and example2, gdb knows that
the cmd window is, basically, in the same sort of position within the
layout, and will retain the cmd window size.
In contrast, when switching to the hsrc layout, gdb understands that
the position of the cmd window is different, and does not try to
retain the cmd window size.
2022-02-01 14:02:19 +00:00
|
|
|
and 'H', ends with 'C'), or, if this layout doesn't contain the cmd
|
|
|
|
window, an empty string is returned. */
|
|
|
|
virtual std::string layout_fingerprint () const = 0;
|
|
|
|
|
gdb/tui: don't add windows to global list from tui_layout:window::apply
This commit was inspired by this mailing list patch:
https://sourceware.org/pipermail/gdb-patches/2021-January/174713.html
Currently, calling tui_layout_window::apply will add the window from
the layout object to the global tui_windows list.
Unfortunately, when the user runs the 'winheight' command, this calls
tui_adjust_window_height, which calls the tui_layout_base::adjust_size
function, which can then call tui_layout_base::apply. The consequence
of this is that when the user does 'winheight' duplicate copies of a
window can be added to the global tui_windows list.
The original patch fixed this by changing the apply function to only
update the global list some of the time.
This patch takes a different approach. The apply function no longer
updates the global tui_windows list. Instead a new virtual function
is added to tui_layout_base which is used to gather all the currently
applied windows into a vector. Finally tui_apply_current_layout is
updated to make use of this new function to update the tui_windows
list.
The benefits I see in this approach are, (a) the apply function now no
longer touches global state, this solves the immediate problem,
and (b) now that tui_windows is updated directly in the function
tui_apply_current_layout, we can drop the saved_tui_windows global.
gdb/ChangeLog:
* tui-layout.c (saved_tui_windows): Delete.
(tui_apply_current_layout): Don't make use of saved_tui_windows,
call new get_windows member function instead.
(tui_get_window_by_name): Check in tui_windows.
(tui_layout_window::apply): Don't add to tui_windows.
* tui-layout.h (tui_layout_base::get_windows): New member function.
(tui_layout_window::get_windows): Likewise.
(tui_layout_split::get_windows): Likewise.
gdb/testsuite/ChangeLog:
* gdb.tui/winheight.exp: Add more tests.
2021-01-25 15:46:58 +00:00
|
|
|
/* Add all windows to the WINDOWS vector. */
|
|
|
|
virtual void get_windows (std::vector<tui_win_info *> *windows) = 0;
|
|
|
|
|
2019-10-07 18:03:02 -06:00
|
|
|
/* The most recent space allocation. */
|
|
|
|
int x = 0;
|
|
|
|
int y = 0;
|
|
|
|
int width = 0;
|
|
|
|
int height = 0;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
|
|
tui_layout_base () = default;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* A TUI layout object that displays a single window. The window is
|
|
|
|
given by name. */
|
|
|
|
class tui_layout_window : public tui_layout_base
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
explicit tui_layout_window (const char *name)
|
|
|
|
: m_contents (name)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DISABLE_COPY_AND_ASSIGN (tui_layout_window);
|
|
|
|
|
|
|
|
std::unique_ptr<tui_layout_base> clone () const override;
|
|
|
|
|
gdb/tui: support placing the cmd window into a horizontal layout
This commit allows the user to place the cmd window within horizontal
tui layouts. Consider this set of steps, carried out in an 80 columns
by 24 lines terminal, using current master gdb:
(gdb) tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
(gdb) tui layout hsrc
What you end up with is a full width cmd window with the status bar
beneath. Where's the src window gone? We then try:
(gdb) info win
Name Lines Columns Focus
src 23 3 (has focus)
cmd 23 80
status 1 80
(gdb)
Something weird has gone on, gdb has overlapped the cmd window with
the src window. If we trigger the src window to redraw is content,
for example, 'list main', then we see corruption in the cmd window as
the src window overwrites it.
So, what's going on?
The problem is some code in tui_layout_split::apply, in tui-layout.c.
Within 'Step 1', there is a loop that calculates the min/max window
sizes for all windows within a tui_layout_split. However, there's a
special case for the cmd window.
This special case is trying to have the cmd window retain its current
size when a layout is re-applied, or a new layout is applied. This
makes sense, consider moving from the 'src' layout to the 'asm'
layout, this looks something like this (status window removed):
.-------. .-------.
| src | | asm |
|-------| ====> |-------|
| cmd | | cmd |
'-------' '-------'
If the user has gone to the effort of adjusting the cmd window size,
then, the thinking goes, we shouldn't reset the cmd window size when
switching layouts like this.
The problem though, is that when we do a switch more like this:
.-----------. .-----------.
| src | | | |
|-----------| ====> | asm | cmd |
| cmd | | | |
'-----------' '-----------'
Now retaining the cmd window width makes no sense; the new layout has
a completely different placement for the cmd window, instead of sizing
by height, we're now sizing by width. The existing code doesn't
understand this though, and tried to retain the full width for the cmd
window.
To solve this problem, I propose we introduce the idea of a layout
"fingerprint". The fingerprint tries to capture, in an abstract way,
where the cmd window lives within the layout.
Only when two layouts have the same fingerprint will we attempt to
retain the cmd window size.
The fingerprint for a layout is represented as a string, the string is
a series of 'V' or 'H' characters, ending with a single 'C'
character. The series of 'V' and 'H' characters represent the
vertical or horizontal layouts that must be passed through to find the
cmd window.
Here are a few examples:
# This layout is equivalent to the builtin 'src' layout.
# Fingerprint: VC
tui new-layout example1 src 2 status 0 cmd 1
# This layout is equivalent to the builtin 'split' layout.
# Fingerprint: VC
tui new-layout example2 src 1 asm 1 status 0 cmd 1
# This is the same layout that was given at the top.
# Fingerprint: VHC
tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
And so, when switching between example1 and example2, gdb knows that
the cmd window is, basically, in the same sort of position within the
layout, and will retain the cmd window size.
In contrast, when switching to the hsrc layout, gdb understands that
the position of the cmd window is different, and does not try to
retain the cmd window size.
2022-02-01 14:02:19 +00:00
|
|
|
void apply (int x, int y, int width, int height,
|
|
|
|
bool preserve_cmd_win_size_p) override;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
|
|
|
const char *get_name () const override
|
|
|
|
{
|
|
|
|
return m_contents.c_str ();
|
|
|
|
}
|
|
|
|
|
2022-01-24 17:49:12 +00:00
|
|
|
tui_adjust_result set_height (const char *name, int new_height) override
|
2019-10-07 18:03:02 -06:00
|
|
|
{
|
2020-02-22 11:48:26 -07:00
|
|
|
return m_contents == name ? FOUND : NOT_FOUND;
|
2019-10-07 18:03:02 -06:00
|
|
|
}
|
|
|
|
|
gdb/tui: add new 'tui window width' command and 'winwidth' alias
This commit adds a new command 'tui window width', and an alias
'winwidth'. This command is equivalent to the old 'winheight'
command (which was recently renamed 'tui window height').
Even though I recently moved the old tui commands under the tui
namespace, and I would strongly encourage all new tui commands to be
added as 'tui ....' only (users can create their own top-level aliases
if they want), I'm breaking that suggestion here, and adding a
'winwidth' alias.
Given that we already have 'winheight' and have done for years, it
just didn't seem right to no have the matching 'winwidth'.
You might notice in the test that the window resizing doesn't quite
work right. I setup a horizontal layout, then grow and shrink the
windows. At the end of the test the windows should be back to their
original size...
... they are not. This isn't my fault, honest! GDB's window resizing
is a little ... temperamental, and is prone to getting things slightly
wrong during resizes, off by 1 type things. This is true for height
resizing, as well as the new width resizing.
Later patches in this series will rework the resizing algorithm, which
should improve things in this area. For now, I'm happy that the width
resizing is as good as the height resizing, given the existing quirks.
For the docs side I include a paragraph that explains how multiple
windows are required before the width can be adjusted. For
completeness, I've added the same paragraph to the winheight
description. With the predefined layouts this extra paragraph is not
really needed for winheight, as there are always multiple windows on
the screen. However, with custom layouts, this might not be true, so
adding the paragraph seems like a good idea.
As for the changes in gdb itself, I've mostly just taken the existing
height adjustment code, changed the name to make it generic 'size'
adjustment, and added a boolean flag to indicate if we are adjusting
the width or the height.
2022-01-24 22:02:59 +00:00
|
|
|
tui_adjust_result set_width (const char *name, int new_width) override
|
|
|
|
{
|
|
|
|
return m_contents == name ? FOUND : NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
gdb/tui: add left_boxed_p and right_boxed_p member functions
When I initially saw this code in tui_layout_split::apply, I assumed
that this must be a bug:
/* Two adjacent boxed windows will share a border, making a bit
more size available. */
if (i > 0
&& m_splits[i - 1].layout->bottom_boxed_p ()
&& m_splits[i].layout->top_boxed_p ())
...
After all, the apply might be laying out a horizontal layout, right?
So checking bottom_boxed_p and top_boxed_p is clearly wrong.
Well, it turns on, that due to the implementations of these things,
bottom_boxed_p is equivalent to an imagined right_boxed_p, and
top_boxed_p is equivalent to an imagined left_boxed_p.
In this commit I've renamed both top_boxed_p and bottom_boxed_p to
first_edge_has_border_p and last_edge_has_border_p respectively, and
extended the comments in tui_layout_base to mention that these methods
handle both horizontal and vertical layouts.
Now, hopefully, the code shouldn't look like it only applies for
vertical layouts.
There should be no user visible changes after this commit.
2022-01-31 12:13:38 +00:00
|
|
|
bool first_edge_has_border_p () const override;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
gdb/tui: add left_boxed_p and right_boxed_p member functions
When I initially saw this code in tui_layout_split::apply, I assumed
that this must be a bug:
/* Two adjacent boxed windows will share a border, making a bit
more size available. */
if (i > 0
&& m_splits[i - 1].layout->bottom_boxed_p ()
&& m_splits[i].layout->top_boxed_p ())
...
After all, the apply might be laying out a horizontal layout, right?
So checking bottom_boxed_p and top_boxed_p is clearly wrong.
Well, it turns on, that due to the implementations of these things,
bottom_boxed_p is equivalent to an imagined right_boxed_p, and
top_boxed_p is equivalent to an imagined left_boxed_p.
In this commit I've renamed both top_boxed_p and bottom_boxed_p to
first_edge_has_border_p and last_edge_has_border_p respectively, and
extended the comments in tui_layout_base to mention that these methods
handle both horizontal and vertical layouts.
Now, hopefully, the code shouldn't look like it only applies for
vertical layouts.
There should be no user visible changes after this commit.
2022-01-31 12:13:38 +00:00
|
|
|
bool last_edge_has_border_p () const override;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
void remove_windows (const char *name) override
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
void replace_window (const char *name, const char *new_window) override;
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
void specification (ui_file *output, int depth) override;
|
2020-02-22 11:48:26 -07:00
|
|
|
|
gdb/tui: support placing the cmd window into a horizontal layout
This commit allows the user to place the cmd window within horizontal
tui layouts. Consider this set of steps, carried out in an 80 columns
by 24 lines terminal, using current master gdb:
(gdb) tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
(gdb) tui layout hsrc
What you end up with is a full width cmd window with the status bar
beneath. Where's the src window gone? We then try:
(gdb) info win
Name Lines Columns Focus
src 23 3 (has focus)
cmd 23 80
status 1 80
(gdb)
Something weird has gone on, gdb has overlapped the cmd window with
the src window. If we trigger the src window to redraw is content,
for example, 'list main', then we see corruption in the cmd window as
the src window overwrites it.
So, what's going on?
The problem is some code in tui_layout_split::apply, in tui-layout.c.
Within 'Step 1', there is a loop that calculates the min/max window
sizes for all windows within a tui_layout_split. However, there's a
special case for the cmd window.
This special case is trying to have the cmd window retain its current
size when a layout is re-applied, or a new layout is applied. This
makes sense, consider moving from the 'src' layout to the 'asm'
layout, this looks something like this (status window removed):
.-------. .-------.
| src | | asm |
|-------| ====> |-------|
| cmd | | cmd |
'-------' '-------'
If the user has gone to the effort of adjusting the cmd window size,
then, the thinking goes, we shouldn't reset the cmd window size when
switching layouts like this.
The problem though, is that when we do a switch more like this:
.-----------. .-----------.
| src | | | |
|-----------| ====> | asm | cmd |
| cmd | | | |
'-----------' '-----------'
Now retaining the cmd window width makes no sense; the new layout has
a completely different placement for the cmd window, instead of sizing
by height, we're now sizing by width. The existing code doesn't
understand this though, and tried to retain the full width for the cmd
window.
To solve this problem, I propose we introduce the idea of a layout
"fingerprint". The fingerprint tries to capture, in an abstract way,
where the cmd window lives within the layout.
Only when two layouts have the same fingerprint will we attempt to
retain the cmd window size.
The fingerprint for a layout is represented as a string, the string is
a series of 'V' or 'H' characters, ending with a single 'C'
character. The series of 'V' and 'H' characters represent the
vertical or horizontal layouts that must be passed through to find the
cmd window.
Here are a few examples:
# This layout is equivalent to the builtin 'src' layout.
# Fingerprint: VC
tui new-layout example1 src 2 status 0 cmd 1
# This layout is equivalent to the builtin 'split' layout.
# Fingerprint: VC
tui new-layout example2 src 1 asm 1 status 0 cmd 1
# This is the same layout that was given at the top.
# Fingerprint: VHC
tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
And so, when switching between example1 and example2, gdb knows that
the cmd window is, basically, in the same sort of position within the
layout, and will retain the cmd window size.
In contrast, when switching to the hsrc layout, gdb understands that
the position of the cmd window is different, and does not try to
retain the cmd window size.
2022-02-01 14:02:19 +00:00
|
|
|
std::string layout_fingerprint () const override;
|
|
|
|
|
gdb/tui: don't add windows to global list from tui_layout:window::apply
This commit was inspired by this mailing list patch:
https://sourceware.org/pipermail/gdb-patches/2021-January/174713.html
Currently, calling tui_layout_window::apply will add the window from
the layout object to the global tui_windows list.
Unfortunately, when the user runs the 'winheight' command, this calls
tui_adjust_window_height, which calls the tui_layout_base::adjust_size
function, which can then call tui_layout_base::apply. The consequence
of this is that when the user does 'winheight' duplicate copies of a
window can be added to the global tui_windows list.
The original patch fixed this by changing the apply function to only
update the global list some of the time.
This patch takes a different approach. The apply function no longer
updates the global tui_windows list. Instead a new virtual function
is added to tui_layout_base which is used to gather all the currently
applied windows into a vector. Finally tui_apply_current_layout is
updated to make use of this new function to update the tui_windows
list.
The benefits I see in this approach are, (a) the apply function now no
longer touches global state, this solves the immediate problem,
and (b) now that tui_windows is updated directly in the function
tui_apply_current_layout, we can drop the saved_tui_windows global.
gdb/ChangeLog:
* tui-layout.c (saved_tui_windows): Delete.
(tui_apply_current_layout): Don't make use of saved_tui_windows,
call new get_windows member function instead.
(tui_get_window_by_name): Check in tui_windows.
(tui_layout_window::apply): Don't add to tui_windows.
* tui-layout.h (tui_layout_base::get_windows): New member function.
(tui_layout_window::get_windows): Likewise.
(tui_layout_split::get_windows): Likewise.
gdb/testsuite/ChangeLog:
* gdb.tui/winheight.exp: Add more tests.
2021-01-25 15:46:58 +00:00
|
|
|
/* See tui_layout_base::get_windows. */
|
|
|
|
void get_windows (std::vector<tui_win_info *> *windows) override
|
|
|
|
{
|
|
|
|
windows->push_back (m_window);
|
|
|
|
}
|
|
|
|
|
2019-10-07 18:03:02 -06:00
|
|
|
protected:
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
void get_sizes (bool height, int *min_value, int *max_value) override;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
/* Type of content to display. */
|
|
|
|
std::string m_contents;
|
|
|
|
|
|
|
|
/* When a layout is applied, this is updated to point to the window
|
|
|
|
object. */
|
2020-07-01 21:21:12 -06:00
|
|
|
tui_win_info *m_window = nullptr;
|
2019-10-07 18:03:02 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
/* A TUI layout that holds other layouts. */
|
|
|
|
class tui_layout_split : public tui_layout_base
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
}
|
2019-10-07 18:03:02 -06:00
|
|
|
|
|
|
|
DISABLE_COPY_AND_ASSIGN (tui_layout_split);
|
|
|
|
|
|
|
|
/* Add a new split layout to this layout. WEIGHT is the desired
|
|
|
|
size, which is relative to the other weights given in this
|
|
|
|
layout. */
|
2020-02-22 11:48:26 -07:00
|
|
|
void add_split (std::unique_ptr<tui_layout_split> &&layout, int weight);
|
2019-10-07 18:03:02 -06:00
|
|
|
|
|
|
|
/* Add a new window to this layout. NAME is the name of the window
|
|
|
|
to add. WEIGHT is the desired size, which is relative to the
|
|
|
|
other weights given in this layout. */
|
|
|
|
void add_window (const char *name, int weight);
|
|
|
|
|
|
|
|
std::unique_ptr<tui_layout_base> clone () const override;
|
|
|
|
|
gdb/tui: support placing the cmd window into a horizontal layout
This commit allows the user to place the cmd window within horizontal
tui layouts. Consider this set of steps, carried out in an 80 columns
by 24 lines terminal, using current master gdb:
(gdb) tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
(gdb) tui layout hsrc
What you end up with is a full width cmd window with the status bar
beneath. Where's the src window gone? We then try:
(gdb) info win
Name Lines Columns Focus
src 23 3 (has focus)
cmd 23 80
status 1 80
(gdb)
Something weird has gone on, gdb has overlapped the cmd window with
the src window. If we trigger the src window to redraw is content,
for example, 'list main', then we see corruption in the cmd window as
the src window overwrites it.
So, what's going on?
The problem is some code in tui_layout_split::apply, in tui-layout.c.
Within 'Step 1', there is a loop that calculates the min/max window
sizes for all windows within a tui_layout_split. However, there's a
special case for the cmd window.
This special case is trying to have the cmd window retain its current
size when a layout is re-applied, or a new layout is applied. This
makes sense, consider moving from the 'src' layout to the 'asm'
layout, this looks something like this (status window removed):
.-------. .-------.
| src | | asm |
|-------| ====> |-------|
| cmd | | cmd |
'-------' '-------'
If the user has gone to the effort of adjusting the cmd window size,
then, the thinking goes, we shouldn't reset the cmd window size when
switching layouts like this.
The problem though, is that when we do a switch more like this:
.-----------. .-----------.
| src | | | |
|-----------| ====> | asm | cmd |
| cmd | | | |
'-----------' '-----------'
Now retaining the cmd window width makes no sense; the new layout has
a completely different placement for the cmd window, instead of sizing
by height, we're now sizing by width. The existing code doesn't
understand this though, and tried to retain the full width for the cmd
window.
To solve this problem, I propose we introduce the idea of a layout
"fingerprint". The fingerprint tries to capture, in an abstract way,
where the cmd window lives within the layout.
Only when two layouts have the same fingerprint will we attempt to
retain the cmd window size.
The fingerprint for a layout is represented as a string, the string is
a series of 'V' or 'H' characters, ending with a single 'C'
character. The series of 'V' and 'H' characters represent the
vertical or horizontal layouts that must be passed through to find the
cmd window.
Here are a few examples:
# This layout is equivalent to the builtin 'src' layout.
# Fingerprint: VC
tui new-layout example1 src 2 status 0 cmd 1
# This layout is equivalent to the builtin 'split' layout.
# Fingerprint: VC
tui new-layout example2 src 1 asm 1 status 0 cmd 1
# This is the same layout that was given at the top.
# Fingerprint: VHC
tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
And so, when switching between example1 and example2, gdb knows that
the cmd window is, basically, in the same sort of position within the
layout, and will retain the cmd window size.
In contrast, when switching to the hsrc layout, gdb understands that
the position of the cmd window is different, and does not try to
retain the cmd window size.
2022-02-01 14:02:19 +00:00
|
|
|
void apply (int x, int y, int width, int height,
|
|
|
|
bool preserve_cmd_win_size_p) override;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
gdb/tui: add new 'tui window width' command and 'winwidth' alias
This commit adds a new command 'tui window width', and an alias
'winwidth'. This command is equivalent to the old 'winheight'
command (which was recently renamed 'tui window height').
Even though I recently moved the old tui commands under the tui
namespace, and I would strongly encourage all new tui commands to be
added as 'tui ....' only (users can create their own top-level aliases
if they want), I'm breaking that suggestion here, and adding a
'winwidth' alias.
Given that we already have 'winheight' and have done for years, it
just didn't seem right to no have the matching 'winwidth'.
You might notice in the test that the window resizing doesn't quite
work right. I setup a horizontal layout, then grow and shrink the
windows. At the end of the test the windows should be back to their
original size...
... they are not. This isn't my fault, honest! GDB's window resizing
is a little ... temperamental, and is prone to getting things slightly
wrong during resizes, off by 1 type things. This is true for height
resizing, as well as the new width resizing.
Later patches in this series will rework the resizing algorithm, which
should improve things in this area. For now, I'm happy that the width
resizing is as good as the height resizing, given the existing quirks.
For the docs side I include a paragraph that explains how multiple
windows are required before the width can be adjusted. For
completeness, I've added the same paragraph to the winheight
description. With the predefined layouts this extra paragraph is not
really needed for winheight, as there are always multiple windows on
the screen. However, with custom layouts, this might not be true, so
adding the paragraph seems like a good idea.
As for the changes in gdb itself, I've mostly just taken the existing
height adjustment code, changed the name to make it generic 'size'
adjustment, and added a boolean flag to indicate if we are adjusting
the width or the height.
2022-01-24 22:02:59 +00:00
|
|
|
tui_adjust_result set_height (const char *name, int new_height) override
|
|
|
|
{
|
|
|
|
/* Pass false as the final argument to indicate change of height. */
|
|
|
|
return set_size (name, new_height, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
tui_adjust_result set_width (const char *name, int new_width) override
|
|
|
|
{
|
|
|
|
/* Pass true as the final argument to indicate change of width. */
|
|
|
|
return set_size (name, new_width, true);
|
|
|
|
}
|
2019-10-07 18:03:02 -06:00
|
|
|
|
gdb/tui: add left_boxed_p and right_boxed_p member functions
When I initially saw this code in tui_layout_split::apply, I assumed
that this must be a bug:
/* Two adjacent boxed windows will share a border, making a bit
more size available. */
if (i > 0
&& m_splits[i - 1].layout->bottom_boxed_p ()
&& m_splits[i].layout->top_boxed_p ())
...
After all, the apply might be laying out a horizontal layout, right?
So checking bottom_boxed_p and top_boxed_p is clearly wrong.
Well, it turns on, that due to the implementations of these things,
bottom_boxed_p is equivalent to an imagined right_boxed_p, and
top_boxed_p is equivalent to an imagined left_boxed_p.
In this commit I've renamed both top_boxed_p and bottom_boxed_p to
first_edge_has_border_p and last_edge_has_border_p respectively, and
extended the comments in tui_layout_base to mention that these methods
handle both horizontal and vertical layouts.
Now, hopefully, the code shouldn't look like it only applies for
vertical layouts.
There should be no user visible changes after this commit.
2022-01-31 12:13:38 +00:00
|
|
|
bool first_edge_has_border_p () const override;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
gdb/tui: add left_boxed_p and right_boxed_p member functions
When I initially saw this code in tui_layout_split::apply, I assumed
that this must be a bug:
/* Two adjacent boxed windows will share a border, making a bit
more size available. */
if (i > 0
&& m_splits[i - 1].layout->bottom_boxed_p ()
&& m_splits[i].layout->top_boxed_p ())
...
After all, the apply might be laying out a horizontal layout, right?
So checking bottom_boxed_p and top_boxed_p is clearly wrong.
Well, it turns on, that due to the implementations of these things,
bottom_boxed_p is equivalent to an imagined right_boxed_p, and
top_boxed_p is equivalent to an imagined left_boxed_p.
In this commit I've renamed both top_boxed_p and bottom_boxed_p to
first_edge_has_border_p and last_edge_has_border_p respectively, and
extended the comments in tui_layout_base to mention that these methods
handle both horizontal and vertical layouts.
Now, hopefully, the code shouldn't look like it only applies for
vertical layouts.
There should be no user visible changes after this commit.
2022-01-31 12:13:38 +00:00
|
|
|
bool last_edge_has_border_p () const override;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
void remove_windows (const char *name) override;
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
void replace_window (const char *name, const char *new_window) override;
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
void specification (ui_file *output, int depth) override;
|
2020-02-22 11:48:26 -07:00
|
|
|
|
gdb/tui: support placing the cmd window into a horizontal layout
This commit allows the user to place the cmd window within horizontal
tui layouts. Consider this set of steps, carried out in an 80 columns
by 24 lines terminal, using current master gdb:
(gdb) tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
(gdb) tui layout hsrc
What you end up with is a full width cmd window with the status bar
beneath. Where's the src window gone? We then try:
(gdb) info win
Name Lines Columns Focus
src 23 3 (has focus)
cmd 23 80
status 1 80
(gdb)
Something weird has gone on, gdb has overlapped the cmd window with
the src window. If we trigger the src window to redraw is content,
for example, 'list main', then we see corruption in the cmd window as
the src window overwrites it.
So, what's going on?
The problem is some code in tui_layout_split::apply, in tui-layout.c.
Within 'Step 1', there is a loop that calculates the min/max window
sizes for all windows within a tui_layout_split. However, there's a
special case for the cmd window.
This special case is trying to have the cmd window retain its current
size when a layout is re-applied, or a new layout is applied. This
makes sense, consider moving from the 'src' layout to the 'asm'
layout, this looks something like this (status window removed):
.-------. .-------.
| src | | asm |
|-------| ====> |-------|
| cmd | | cmd |
'-------' '-------'
If the user has gone to the effort of adjusting the cmd window size,
then, the thinking goes, we shouldn't reset the cmd window size when
switching layouts like this.
The problem though, is that when we do a switch more like this:
.-----------. .-----------.
| src | | | |
|-----------| ====> | asm | cmd |
| cmd | | | |
'-----------' '-----------'
Now retaining the cmd window width makes no sense; the new layout has
a completely different placement for the cmd window, instead of sizing
by height, we're now sizing by width. The existing code doesn't
understand this though, and tried to retain the full width for the cmd
window.
To solve this problem, I propose we introduce the idea of a layout
"fingerprint". The fingerprint tries to capture, in an abstract way,
where the cmd window lives within the layout.
Only when two layouts have the same fingerprint will we attempt to
retain the cmd window size.
The fingerprint for a layout is represented as a string, the string is
a series of 'V' or 'H' characters, ending with a single 'C'
character. The series of 'V' and 'H' characters represent the
vertical or horizontal layouts that must be passed through to find the
cmd window.
Here are a few examples:
# This layout is equivalent to the builtin 'src' layout.
# Fingerprint: VC
tui new-layout example1 src 2 status 0 cmd 1
# This layout is equivalent to the builtin 'split' layout.
# Fingerprint: VC
tui new-layout example2 src 1 asm 1 status 0 cmd 1
# This is the same layout that was given at the top.
# Fingerprint: VHC
tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
And so, when switching between example1 and example2, gdb knows that
the cmd window is, basically, in the same sort of position within the
layout, and will retain the cmd window size.
In contrast, when switching to the hsrc layout, gdb understands that
the position of the cmd window is different, and does not try to
retain the cmd window size.
2022-02-01 14:02:19 +00:00
|
|
|
std::string layout_fingerprint () const override;
|
|
|
|
|
gdb/tui: don't add windows to global list from tui_layout:window::apply
This commit was inspired by this mailing list patch:
https://sourceware.org/pipermail/gdb-patches/2021-January/174713.html
Currently, calling tui_layout_window::apply will add the window from
the layout object to the global tui_windows list.
Unfortunately, when the user runs the 'winheight' command, this calls
tui_adjust_window_height, which calls the tui_layout_base::adjust_size
function, which can then call tui_layout_base::apply. The consequence
of this is that when the user does 'winheight' duplicate copies of a
window can be added to the global tui_windows list.
The original patch fixed this by changing the apply function to only
update the global list some of the time.
This patch takes a different approach. The apply function no longer
updates the global tui_windows list. Instead a new virtual function
is added to tui_layout_base which is used to gather all the currently
applied windows into a vector. Finally tui_apply_current_layout is
updated to make use of this new function to update the tui_windows
list.
The benefits I see in this approach are, (a) the apply function now no
longer touches global state, this solves the immediate problem,
and (b) now that tui_windows is updated directly in the function
tui_apply_current_layout, we can drop the saved_tui_windows global.
gdb/ChangeLog:
* tui-layout.c (saved_tui_windows): Delete.
(tui_apply_current_layout): Don't make use of saved_tui_windows,
call new get_windows member function instead.
(tui_get_window_by_name): Check in tui_windows.
(tui_layout_window::apply): Don't add to tui_windows.
* tui-layout.h (tui_layout_base::get_windows): New member function.
(tui_layout_window::get_windows): Likewise.
(tui_layout_split::get_windows): Likewise.
gdb/testsuite/ChangeLog:
* gdb.tui/winheight.exp: Add more tests.
2021-01-25 15:46:58 +00:00
|
|
|
/* See tui_layout_base::get_windows. */
|
|
|
|
void get_windows (std::vector<tui_win_info *> *windows) override
|
|
|
|
{
|
|
|
|
for (auto &item : m_splits)
|
|
|
|
item.layout->get_windows (windows);
|
|
|
|
}
|
|
|
|
|
2019-10-07 18:03:02 -06:00
|
|
|
protected:
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
void get_sizes (bool height, int *min_value, int *max_value) override;
|
2019-10-07 18:03:02 -06:00
|
|
|
|
|
|
|
private:
|
|
|
|
|
gdb/tui: add new 'tui window width' command and 'winwidth' alias
This commit adds a new command 'tui window width', and an alias
'winwidth'. This command is equivalent to the old 'winheight'
command (which was recently renamed 'tui window height').
Even though I recently moved the old tui commands under the tui
namespace, and I would strongly encourage all new tui commands to be
added as 'tui ....' only (users can create their own top-level aliases
if they want), I'm breaking that suggestion here, and adding a
'winwidth' alias.
Given that we already have 'winheight' and have done for years, it
just didn't seem right to no have the matching 'winwidth'.
You might notice in the test that the window resizing doesn't quite
work right. I setup a horizontal layout, then grow and shrink the
windows. At the end of the test the windows should be back to their
original size...
... they are not. This isn't my fault, honest! GDB's window resizing
is a little ... temperamental, and is prone to getting things slightly
wrong during resizes, off by 1 type things. This is true for height
resizing, as well as the new width resizing.
Later patches in this series will rework the resizing algorithm, which
should improve things in this area. For now, I'm happy that the width
resizing is as good as the height resizing, given the existing quirks.
For the docs side I include a paragraph that explains how multiple
windows are required before the width can be adjusted. For
completeness, I've added the same paragraph to the winheight
description. With the predefined layouts this extra paragraph is not
really needed for winheight, as there are always multiple windows on
the screen. However, with custom layouts, this might not be true, so
adding the paragraph seems like a good idea.
As for the changes in gdb itself, I've mostly just taken the existing
height adjustment code, changed the name to make it generic 'size'
adjustment, and added a boolean flag to indicate if we are adjusting
the width or the height.
2022-01-24 22:02:59 +00:00
|
|
|
/* Used to implement set_height and set_width member functions. When
|
|
|
|
SET_WIDTH_P is true, set the width, otherwise, set the height of the
|
|
|
|
window named NAME to NEW_SIZE, updating the sizes of the other windows
|
|
|
|
around it as needed. The result indicates if the window NAME was
|
|
|
|
found and had its size adjusted, was found but was not adjusted, or
|
|
|
|
was not found at all. */
|
|
|
|
tui_adjust_result set_size (const char *name, int new_size,
|
|
|
|
bool set_width_p);
|
|
|
|
|
gdb/tui: rename tui_layout_split:set_weights_from_heights
In a following commit I'm going to add the ability to change the width
of a tui window (when in a horizontal layout). As a result, some of
the places where we currently hard-code references to height need to
be changed to handle either height, or width, based on whether we are
in a vertical, or horizontal layout.
This commit renames set_weights_from_heights to
set_weights_from_sizes, and makes the function use either the height,
or width as appropriate.
Currently, the only place that we call this function is from the
tui_layout_split::set_height function, in a part of the code we will
only reach for vertical layouts, so the new code is not actually being
used, but, this small change will help make later patches smaller, so
I'm proposing this as a stand alone change.
There should be no user visible changes after this commit.
2022-01-28 12:00:31 +00:00
|
|
|
/* Set the weights from the current heights (when m_vertical is true) or
|
|
|
|
widths (when m_vertical is false). */
|
|
|
|
void set_weights_from_sizes ();
|
2019-10-07 18:03:02 -06:00
|
|
|
|
gdb/tui: add a tui debugging flag
This commit adds 'set debug tui on|off' and 'show debug tui'. This
commit adds the control variable, and the printing macros in
tui/tui.h. I've then added some uses of these in tui.c and
tui-layout.c.
To help produce more useful debug output in tui-layout.c, I've added
some helper member functions in the class tui_layout_split, and also
moved the size_info struct out of tui_layout_split::apply into the
tui_layout_split class.
If tui debug is not turned on, then there should be no user visible
changes after this commit.
One thing to note is that, due to the way that the tui terminal is
often cleared, the only way I've found this useful is when I do:
(gdb) tui enable
(gdb) set logging file /path/to/file
(gdb) set logging debugredirect on
(gdb) set logging enable on
Additionally, gdb has some quirks when it comes to setting up logging
redirect and switching interpreters. Thus, the above only really
works if the logging is enabled after the tui is enabled, and disabled
again before the tui is disabled.
Enabling logging and switching interpreters can cause undefined
results, including crashes. This is an existing bug in gdb[1], and
has nothing directly to do with tui debug, but it is worth mentioning
here I think.
[1] https://sourceware.org/bugzilla/show_bug.cgi?id=28948
2022-01-26 18:52:56 +00:00
|
|
|
/* Structure used when resizing, or applying a layout. An instance of
|
|
|
|
this structure is created for each sub-layout. */
|
|
|
|
struct size_info
|
|
|
|
{
|
|
|
|
/* The calculated size for this sub-layout. */
|
|
|
|
int size;
|
|
|
|
|
|
|
|
/* The minimum and maximum sizes for this sub-layout, obtained by
|
|
|
|
calling the get_sizes member function. */
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Used for debug, prints the contents of INFO using tui_debug_printf.
|
|
|
|
Only call this when the global debug_tui is true. */
|
|
|
|
static void tui_debug_print_size_info (const std::vector<size_info> &info);
|
|
|
|
|
|
|
|
/* Used for debug, returns a string describing the current weight of each
|
|
|
|
sub-layout. */
|
|
|
|
std::string tui_debug_weights_to_string () const;
|
|
|
|
|
2019-10-07 18:03:02 -06:00
|
|
|
struct split
|
|
|
|
{
|
|
|
|
/* The requested weight. */
|
|
|
|
int weight;
|
|
|
|
/* The layout. */
|
|
|
|
std::unique_ptr<tui_layout_base> layout;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* The splits. */
|
|
|
|
std::vector<split> m_splits;
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* True if the windows in this split are arranged vertically. */
|
|
|
|
bool m_vertical;
|
2019-10-07 18:03:02 -06:00
|
|
|
};
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* Add the specified window to the layout in a logical way. This
|
|
|
|
means setting up the most logical layout given the window to be
|
|
|
|
added. Only the source or disassembly window can be added this
|
|
|
|
way. */
|
2004-01-23 23:25:17 +00:00
|
|
|
extern void tui_add_win_to_layout (enum tui_win_type);
|
2020-02-22 11:48:26 -07:00
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* Set the initial layout. */
|
|
|
|
extern void tui_set_initial_layout ();
|
1999-04-16 01:35:26 +00:00
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* Switch to the next layout. */
|
|
|
|
extern void tui_next_layout ();
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* Show the register window. Like "layout regs". */
|
|
|
|
extern void tui_regs_layout ();
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* Remove some windows from the layout, leaving only the focused
|
|
|
|
window and the command window; if no window has the focus, then
|
|
|
|
some other window is chosen to remain. */
|
|
|
|
extern void tui_remove_some_windows ();
|
|
|
|
|
gdb/tui: support placing the cmd window into a horizontal layout
This commit allows the user to place the cmd window within horizontal
tui layouts. Consider this set of steps, carried out in an 80 columns
by 24 lines terminal, using current master gdb:
(gdb) tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
(gdb) tui layout hsrc
What you end up with is a full width cmd window with the status bar
beneath. Where's the src window gone? We then try:
(gdb) info win
Name Lines Columns Focus
src 23 3 (has focus)
cmd 23 80
status 1 80
(gdb)
Something weird has gone on, gdb has overlapped the cmd window with
the src window. If we trigger the src window to redraw is content,
for example, 'list main', then we see corruption in the cmd window as
the src window overwrites it.
So, what's going on?
The problem is some code in tui_layout_split::apply, in tui-layout.c.
Within 'Step 1', there is a loop that calculates the min/max window
sizes for all windows within a tui_layout_split. However, there's a
special case for the cmd window.
This special case is trying to have the cmd window retain its current
size when a layout is re-applied, or a new layout is applied. This
makes sense, consider moving from the 'src' layout to the 'asm'
layout, this looks something like this (status window removed):
.-------. .-------.
| src | | asm |
|-------| ====> |-------|
| cmd | | cmd |
'-------' '-------'
If the user has gone to the effort of adjusting the cmd window size,
then, the thinking goes, we shouldn't reset the cmd window size when
switching layouts like this.
The problem though, is that when we do a switch more like this:
.-----------. .-----------.
| src | | | |
|-----------| ====> | asm | cmd |
| cmd | | | |
'-----------' '-----------'
Now retaining the cmd window width makes no sense; the new layout has
a completely different placement for the cmd window, instead of sizing
by height, we're now sizing by width. The existing code doesn't
understand this though, and tried to retain the full width for the cmd
window.
To solve this problem, I propose we introduce the idea of a layout
"fingerprint". The fingerprint tries to capture, in an abstract way,
where the cmd window lives within the layout.
Only when two layouts have the same fingerprint will we attempt to
retain the cmd window size.
The fingerprint for a layout is represented as a string, the string is
a series of 'V' or 'H' characters, ending with a single 'C'
character. The series of 'V' and 'H' characters represent the
vertical or horizontal layouts that must be passed through to find the
cmd window.
Here are a few examples:
# This layout is equivalent to the builtin 'src' layout.
# Fingerprint: VC
tui new-layout example1 src 2 status 0 cmd 1
# This layout is equivalent to the builtin 'split' layout.
# Fingerprint: VC
tui new-layout example2 src 1 asm 1 status 0 cmd 1
# This is the same layout that was given at the top.
# Fingerprint: VHC
tui new-layout hsrc { -horizontal src 1 cmd 1 } 1 status 1
And so, when switching between example1 and example2, gdb knows that
the cmd window is, basically, in the same sort of position within the
layout, and will retain the cmd window size.
In contrast, when switching to the hsrc layout, gdb understands that
the position of the cmd window is different, and does not try to
retain the cmd window size.
2022-02-01 14:02:19 +00:00
|
|
|
/* Apply the current layout. When PRESERVE_CMD_WIN_SIZE_P is true the
|
|
|
|
current size of the TUI_CMD_WIN is preserved, otherwise, the TUI_CMD_WIN
|
|
|
|
will resize just like any other window. */
|
|
|
|
extern void tui_apply_current_layout (bool);
|
2019-10-09 16:35:41 -06:00
|
|
|
|
2019-10-26 16:37:32 -06:00
|
|
|
/* Adjust the window height of WIN to NEW_HEIGHT. */
|
|
|
|
extern void tui_adjust_window_height (struct tui_win_info *win,
|
|
|
|
int new_height);
|
|
|
|
|
gdb/tui: add new 'tui window width' command and 'winwidth' alias
This commit adds a new command 'tui window width', and an alias
'winwidth'. This command is equivalent to the old 'winheight'
command (which was recently renamed 'tui window height').
Even though I recently moved the old tui commands under the tui
namespace, and I would strongly encourage all new tui commands to be
added as 'tui ....' only (users can create their own top-level aliases
if they want), I'm breaking that suggestion here, and adding a
'winwidth' alias.
Given that we already have 'winheight' and have done for years, it
just didn't seem right to no have the matching 'winwidth'.
You might notice in the test that the window resizing doesn't quite
work right. I setup a horizontal layout, then grow and shrink the
windows. At the end of the test the windows should be back to their
original size...
... they are not. This isn't my fault, honest! GDB's window resizing
is a little ... temperamental, and is prone to getting things slightly
wrong during resizes, off by 1 type things. This is true for height
resizing, as well as the new width resizing.
Later patches in this series will rework the resizing algorithm, which
should improve things in this area. For now, I'm happy that the width
resizing is as good as the height resizing, given the existing quirks.
For the docs side I include a paragraph that explains how multiple
windows are required before the width can be adjusted. For
completeness, I've added the same paragraph to the winheight
description. With the predefined layouts this extra paragraph is not
really needed for winheight, as there are always multiple windows on
the screen. However, with custom layouts, this might not be true, so
adding the paragraph seems like a good idea.
As for the changes in gdb itself, I've mostly just taken the existing
height adjustment code, changed the name to make it generic 'size'
adjustment, and added a boolean flag to indicate if we are adjusting
the width or the height.
2022-01-24 22:02:59 +00:00
|
|
|
/* Adjust the window width of WIN to NEW_WIDTH. */
|
|
|
|
extern void tui_adjust_window_width (struct tui_win_info *win,
|
|
|
|
int new_width);
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* The type of a function that is used to create a TUI window. */
|
|
|
|
|
2020-07-01 21:21:12 -06:00
|
|
|
typedef std::function<tui_win_info * (const char *name)> window_factory;
|
2020-02-22 11:48:26 -07:00
|
|
|
|
gdb/tui: improve errors from tui focus command
This commit improves (I think) the errors from the tui focus command.
There are a number of errors that can be triggered by the focus
command, they include:
(1) Window name "NAME" is ambiguous
(2) Unrecognized window name "NAME"
(3) Window "NAME" cannot be focused
Error (1) is triggered when the user gives a partial window name, and
the name matches multiple windows in the current layout.
It is worth noting that the ambiguity must be within the current
layout; if the partial name matches one window in the current layout,
and one or more windows not in the current layout, then this is not
ambiguous, and focus will shift to the matching window in the current
layout.
This error was not previous being tested, but in this commit I make
use of the Python API to trigger and test this error.
Error (3) is simple enough, and was already being tested. This is
triggered by something like 'focus status'. The named window needs to
be present in the current layout, and non-focusable in order to
trigger the error.
Error (2) is what I'd like to improve in this commit. This error
triggers if the name the user gives doesn't match any window in the
current layout. Even if GDB does know about the window, but the
window isn't in the current layout, then GDB will say it doesn't
recognize the window name.
In this commit I propose to to split this error into three different
errors. These will be:
(a) Unrecognized window name "NAME"
(b) No windows matching "NAME" in the current layout
(c) Window "NAME" is not in the current layout
Error (a) is the same as before, but will now only trigger if GDB
doesn't know about window NAME at all. If the window is known, but
not in the current layout then one of the other errors will trigger.
Error (b) will trigger if NAME is ambiguous for multiple windows that
are not in the current layout. If NAME identifies a single window in
the current layout then that window will continue to be selected, just
as it currently is. Only in the case where NAME doesn't identify a
window in the current layout do we then check all the other known
windows, if NAME matches multiple of these, then (b) is triggered.
Finally, error (c) is used when NAME uniquely identifies a single
window that is not in the current layout.
The hope with these new errors is that the user will have a better
understanding of what went wrong. Instead of GDB claiming to not know
about a window, the mention of the current layout will hint to the
user that they should first switch layouts.
There are tests included for all the new errors.
2022-12-22 16:26:37 +00:00
|
|
|
/* The type for a data structure that maps a window name to that window's
|
|
|
|
factory function. */
|
|
|
|
typedef std::unordered_map<std::string, window_factory> window_types_map;
|
|
|
|
|
2020-02-22 11:48:26 -07:00
|
|
|
/* Register a new TUI window type. NAME is the name of the window
|
|
|
|
type. FACTORY is a function that can be called to instantiate the
|
|
|
|
window. */
|
|
|
|
|
|
|
|
extern void tui_register_window (const char *name, window_factory &&factory);
|
|
|
|
|
gdb/tui: improve errors from tui focus command
This commit improves (I think) the errors from the tui focus command.
There are a number of errors that can be triggered by the focus
command, they include:
(1) Window name "NAME" is ambiguous
(2) Unrecognized window name "NAME"
(3) Window "NAME" cannot be focused
Error (1) is triggered when the user gives a partial window name, and
the name matches multiple windows in the current layout.
It is worth noting that the ambiguity must be within the current
layout; if the partial name matches one window in the current layout,
and one or more windows not in the current layout, then this is not
ambiguous, and focus will shift to the matching window in the current
layout.
This error was not previous being tested, but in this commit I make
use of the Python API to trigger and test this error.
Error (3) is simple enough, and was already being tested. This is
triggered by something like 'focus status'. The named window needs to
be present in the current layout, and non-focusable in order to
trigger the error.
Error (2) is what I'd like to improve in this commit. This error
triggers if the name the user gives doesn't match any window in the
current layout. Even if GDB does know about the window, but the
window isn't in the current layout, then GDB will say it doesn't
recognize the window name.
In this commit I propose to to split this error into three different
errors. These will be:
(a) Unrecognized window name "NAME"
(b) No windows matching "NAME" in the current layout
(c) Window "NAME" is not in the current layout
Error (a) is the same as before, but will now only trigger if GDB
doesn't know about window NAME at all. If the window is known, but
not in the current layout then one of the other errors will trigger.
Error (b) will trigger if NAME is ambiguous for multiple windows that
are not in the current layout. If NAME identifies a single window in
the current layout then that window will continue to be selected, just
as it currently is. Only in the case where NAME doesn't identify a
window in the current layout do we then check all the other known
windows, if NAME matches multiple of these, then (b) is triggered.
Finally, error (c) is used when NAME uniquely identifies a single
window that is not in the current layout.
The hope with these new errors is that the user will have a better
understanding of what went wrong. Instead of GDB claiming to not know
about a window, the mention of the current layout will hint to the
user that they should first switch layouts.
There are tests included for all the new errors.
2022-12-22 16:26:37 +00:00
|
|
|
/* An iterator class that exposes just the window names from the
|
|
|
|
known_window_types map in tui-layout.c. This is just a wrapper around
|
|
|
|
an iterator of the underlying known_window_types map, but this just
|
|
|
|
exposes the window names. */
|
|
|
|
|
|
|
|
struct known_window_names_iterator
|
|
|
|
{
|
|
|
|
using known_window_types_iterator = window_types_map::iterator;
|
|
|
|
|
|
|
|
known_window_names_iterator (known_window_types_iterator &&iter)
|
|
|
|
: m_iter (std::move (iter))
|
|
|
|
{ /* Nothing. */ }
|
|
|
|
|
|
|
|
known_window_names_iterator &operator++ ()
|
|
|
|
{
|
|
|
|
++m_iter;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string &operator* () const
|
|
|
|
{ return (*m_iter).first; }
|
|
|
|
|
|
|
|
bool operator!= (const known_window_names_iterator &other) const
|
|
|
|
{ return m_iter != other.m_iter; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
/* The underlying iterator. */
|
|
|
|
known_window_types_iterator m_iter;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* A range adapter that makes it possible to iterate over the names of all
|
|
|
|
known tui windows. */
|
|
|
|
|
|
|
|
using known_window_names_range
|
|
|
|
= iterator_range<known_window_names_iterator>;
|
|
|
|
|
|
|
|
/* Return a range that can be used to walk over the name of all known tui
|
|
|
|
windows in a range-for loop. */
|
|
|
|
|
|
|
|
extern known_window_names_range all_known_window_names ();
|
|
|
|
|
2019-01-27 12:51:36 -07:00
|
|
|
#endif /* TUI_TUI_LAYOUT_H */
|