CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
1342 lines
36 KiB
C
1342 lines
36 KiB
C
/* TUI layout window management.
|
|
|
|
Copyright (C) 1998-2024 Free Software Foundation, Inc.
|
|
|
|
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
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "command.h"
|
|
#include "symtab.h"
|
|
#include "frame.h"
|
|
#include "cli/cli-decode.h"
|
|
#include "cli/cli-utils.h"
|
|
#include <unordered_set>
|
|
|
|
#include "tui/tui.h"
|
|
#include "tui/tui-command.h"
|
|
#include "tui/tui-data.h"
|
|
#include "tui/tui-wingeneral.h"
|
|
#include "tui/tui-status.h"
|
|
#include "tui/tui-regs.h"
|
|
#include "tui/tui-win.h"
|
|
#include "tui/tui-disasm.h"
|
|
#include "tui/tui-layout.h"
|
|
#include "tui/tui-source.h"
|
|
#include "gdb_curses.h"
|
|
#include "gdbsupport/gdb-safe-ctype.h"
|
|
|
|
/* The layouts. */
|
|
static std::vector<std::unique_ptr<tui_layout_split>> layouts;
|
|
|
|
/* The layout that is currently applied. */
|
|
static std::unique_ptr<tui_layout_base> applied_layout;
|
|
|
|
/* The "skeleton" version of the layout that is currently applied. */
|
|
static tui_layout_split *applied_skeleton;
|
|
|
|
/* The two special "regs" layouts. Note that these aren't registered
|
|
as commands and so can never be deleted. */
|
|
static tui_layout_split *src_regs_layout;
|
|
static tui_layout_split *asm_regs_layout;
|
|
|
|
/* See tui-data.h. */
|
|
std::vector<tui_win_info *> tui_windows;
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_apply_current_layout (bool preserve_cmd_win_size_p)
|
|
{
|
|
tui_batch_rendering defer;
|
|
|
|
for (tui_win_info *win_info : tui_windows)
|
|
win_info->make_visible (false);
|
|
|
|
applied_layout->apply (0, 0, tui_term_width (), tui_term_height (),
|
|
preserve_cmd_win_size_p);
|
|
|
|
/* Keep the list of internal windows up-to-date. */
|
|
for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
|
|
if (tui_win_list[win_type] != nullptr
|
|
&& !tui_win_list[win_type]->is_visible ())
|
|
tui_win_list[win_type] = nullptr;
|
|
|
|
/* This should always be made visible by a layout. */
|
|
gdb_assert (tui_cmd_win () != nullptr);
|
|
gdb_assert (tui_cmd_win ()->is_visible ());
|
|
|
|
/* Get the new list of currently visible windows. */
|
|
std::vector<tui_win_info *> new_tui_windows;
|
|
applied_layout->get_windows (&new_tui_windows);
|
|
|
|
/* Now delete any window that was not re-applied. */
|
|
tui_win_info *focus = tui_win_with_focus ();
|
|
for (tui_win_info *win_info : tui_windows)
|
|
{
|
|
if (!win_info->is_visible ())
|
|
{
|
|
if (focus == win_info)
|
|
tui_set_win_focus_to (new_tui_windows[0]);
|
|
delete win_info;
|
|
}
|
|
}
|
|
|
|
/* Replace the global list of active windows. */
|
|
tui_windows = std::move (new_tui_windows);
|
|
}
|
|
|
|
/* See tui-layout. */
|
|
|
|
void
|
|
tui_adjust_window_height (struct tui_win_info *win, int new_height)
|
|
{
|
|
applied_layout->set_height (win->name (), new_height);
|
|
}
|
|
|
|
/* See tui-layout. */
|
|
|
|
void
|
|
tui_adjust_window_width (struct tui_win_info *win, int new_width)
|
|
{
|
|
applied_layout->set_width (win->name (), new_width);
|
|
}
|
|
|
|
/* Set the current layout to LAYOUT. */
|
|
|
|
static void
|
|
tui_set_layout (tui_layout_split *layout)
|
|
{
|
|
std::string old_fingerprint;
|
|
if (applied_layout != nullptr)
|
|
old_fingerprint = applied_layout->layout_fingerprint ();
|
|
|
|
applied_skeleton = layout;
|
|
applied_layout = layout->clone ();
|
|
|
|
std::string new_fingerprint = applied_layout->layout_fingerprint ();
|
|
bool preserve_command_window_size
|
|
= (tui_cmd_win () != nullptr && old_fingerprint == new_fingerprint);
|
|
|
|
tui_apply_current_layout (preserve_command_window_size);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_add_win_to_layout (enum tui_win_type type)
|
|
{
|
|
gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
|
|
|
|
/* If the window already exists, no need to add it. */
|
|
if (tui_win_list[type] != nullptr)
|
|
return;
|
|
|
|
/* If the window we are trying to replace doesn't exist, we're
|
|
done. */
|
|
enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
|
|
if (tui_win_list[other] == nullptr)
|
|
return;
|
|
|
|
const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
|
|
applied_layout->replace_window (tui_win_list[other]->name (), name);
|
|
tui_apply_current_layout (true);
|
|
}
|
|
|
|
/* Find LAYOUT in the "layouts" global and return its index. */
|
|
|
|
static size_t
|
|
find_layout (tui_layout_split *layout)
|
|
{
|
|
for (size_t i = 0; i < layouts.size (); ++i)
|
|
{
|
|
if (layout == layouts[i].get ())
|
|
return i;
|
|
}
|
|
gdb_assert_not_reached ("layout not found!?");
|
|
}
|
|
|
|
/* Function to set the layout. */
|
|
|
|
static void
|
|
tui_apply_layout (const char *args, int from_tty, cmd_list_element *command)
|
|
{
|
|
tui_layout_split *layout = (tui_layout_split *) command->context ();
|
|
|
|
/* Make sure the curses mode is enabled. */
|
|
tui_enable ();
|
|
tui_set_layout (layout);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_next_layout ()
|
|
{
|
|
size_t index = find_layout (applied_skeleton);
|
|
++index;
|
|
if (index == layouts.size ())
|
|
index = 0;
|
|
tui_set_layout (layouts[index].get ());
|
|
}
|
|
|
|
/* Implement the "layout next" command. */
|
|
|
|
static void
|
|
tui_next_layout_command (const char *arg, int from_tty)
|
|
{
|
|
tui_enable ();
|
|
tui_next_layout ();
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_set_initial_layout ()
|
|
{
|
|
tui_set_layout (layouts[0].get ());
|
|
}
|
|
|
|
/* Implement the "layout prev" command. */
|
|
|
|
static void
|
|
tui_prev_layout_command (const char *arg, int from_tty)
|
|
{
|
|
tui_enable ();
|
|
size_t index = find_layout (applied_skeleton);
|
|
if (index == 0)
|
|
index = layouts.size ();
|
|
--index;
|
|
tui_set_layout (layouts[index].get ());
|
|
}
|
|
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_regs_layout ()
|
|
{
|
|
/* If there's already a register window, we're done. */
|
|
if (tui_data_win () != nullptr)
|
|
return;
|
|
|
|
tui_set_layout (tui_disasm_win () != nullptr
|
|
? asm_regs_layout
|
|
: src_regs_layout);
|
|
}
|
|
|
|
/* Implement the "layout regs" command. */
|
|
|
|
static void
|
|
tui_regs_layout_command (const char *arg, int from_tty)
|
|
{
|
|
tui_enable ();
|
|
tui_regs_layout ();
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_remove_some_windows ()
|
|
{
|
|
tui_win_info *focus = tui_win_with_focus ();
|
|
|
|
if (strcmp (focus->name (), CMD_NAME) == 0)
|
|
{
|
|
/* Try leaving the source or disassembly window. If neither
|
|
exists, just do nothing. */
|
|
focus = tui_src_win ();
|
|
if (focus == nullptr)
|
|
focus = tui_disasm_win ();
|
|
if (focus == nullptr)
|
|
return;
|
|
}
|
|
|
|
applied_layout->remove_windows (focus->name ());
|
|
tui_apply_current_layout (true);
|
|
}
|
|
|
|
void
|
|
tui_win_info::resize (int height_, int width_,
|
|
int origin_x_, int origin_y_)
|
|
{
|
|
if (width == width_ && height == height_
|
|
&& x == origin_x_ && y == origin_y_
|
|
&& handle != nullptr)
|
|
return;
|
|
|
|
width = width_;
|
|
height = height_;
|
|
x = origin_x_;
|
|
y = origin_y_;
|
|
|
|
if (handle != nullptr)
|
|
{
|
|
#ifdef HAVE_WRESIZE
|
|
wresize (handle.get (), height, width);
|
|
mvwin (handle.get (), y, x);
|
|
wmove (handle.get (), 0, 0);
|
|
#else
|
|
handle.reset (nullptr);
|
|
#endif
|
|
}
|
|
|
|
if (handle == nullptr)
|
|
make_window ();
|
|
|
|
rerender ();
|
|
}
|
|
|
|
|
|
|
|
/* Helper function to create one of the built-in (non-status)
|
|
windows. */
|
|
|
|
template<enum tui_win_type V, class T>
|
|
static tui_win_info *
|
|
make_standard_window (const char *)
|
|
{
|
|
if (tui_win_list[V] == nullptr)
|
|
tui_win_list[V] = new T ();
|
|
return tui_win_list[V];
|
|
}
|
|
|
|
/* A map holding all the known window types, keyed by name. */
|
|
|
|
static window_types_map known_window_types;
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
known_window_names_range
|
|
all_known_window_names ()
|
|
{
|
|
auto begin = known_window_names_iterator (known_window_types.begin ());
|
|
auto end = known_window_names_iterator (known_window_types.end ());
|
|
return known_window_names_range (begin, end);
|
|
}
|
|
|
|
/* Helper function that returns a TUI window, given its name. */
|
|
|
|
static tui_win_info *
|
|
tui_get_window_by_name (const std::string &name)
|
|
{
|
|
for (tui_win_info *window : tui_windows)
|
|
if (name == window->name ())
|
|
return window;
|
|
|
|
auto iter = known_window_types.find (name);
|
|
if (iter == known_window_types.end ())
|
|
error (_("Unknown window type \"%s\""), name.c_str ());
|
|
|
|
tui_win_info *result = iter->second (name.c_str ());
|
|
if (result == nullptr)
|
|
error (_("Could not create window \"%s\""), name.c_str ());
|
|
return result;
|
|
}
|
|
|
|
/* Initialize the known window types. */
|
|
|
|
static void
|
|
initialize_known_windows ()
|
|
{
|
|
known_window_types.emplace (SRC_NAME,
|
|
make_standard_window<SRC_WIN,
|
|
tui_source_window>);
|
|
known_window_types.emplace (CMD_NAME,
|
|
make_standard_window<CMD_WIN, tui_cmd_window>);
|
|
known_window_types.emplace (DATA_NAME,
|
|
make_standard_window<DATA_WIN,
|
|
tui_data_window>);
|
|
known_window_types.emplace (DISASSEM_NAME,
|
|
make_standard_window<DISASSEM_WIN,
|
|
tui_disasm_window>);
|
|
known_window_types.emplace (STATUS_NAME,
|
|
make_standard_window<STATUS_WIN,
|
|
tui_status_window>);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_register_window (const char *name, window_factory &&factory)
|
|
{
|
|
std::string name_copy = name;
|
|
|
|
if (name_copy == SRC_NAME || name_copy == CMD_NAME || name_copy == DATA_NAME
|
|
|| name_copy == DISASSEM_NAME || name_copy == STATUS_NAME)
|
|
error (_("Window type \"%s\" is built-in"), name);
|
|
|
|
for (const char &c : name_copy)
|
|
{
|
|
if (ISSPACE (c))
|
|
error (_("invalid whitespace character in window name"));
|
|
|
|
if (!ISALNUM (c) && strchr ("-_.", c) == nullptr)
|
|
error (_("invalid character '%c' in window name"), c);
|
|
}
|
|
|
|
if (!ISALPHA (name_copy[0]))
|
|
error (_("window name must start with a letter, not '%c'"), name_copy[0]);
|
|
|
|
/* We already check above for all the builtin window names. If we get
|
|
this far then NAME must be a user defined window. Remove any existing
|
|
factory and replace it with this new version. */
|
|
|
|
auto iter = known_window_types.find (name);
|
|
if (iter != known_window_types.end ())
|
|
known_window_types.erase (iter);
|
|
|
|
known_window_types.emplace (std::move (name_copy),
|
|
std::move (factory));
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
std::unique_ptr<tui_layout_base>
|
|
tui_layout_window::clone () const
|
|
{
|
|
tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
|
|
return std::unique_ptr<tui_layout_base> (result);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_window::apply (int x_, int y_, int width_, int height_,
|
|
bool preserve_cmd_win_size_p)
|
|
{
|
|
x = x_;
|
|
y = y_;
|
|
width = width_;
|
|
height = height_;
|
|
gdb_assert (m_window != nullptr);
|
|
if (width == 0 || height == 0)
|
|
{
|
|
/* The window was dropped, so it's going to be deleted, reset the
|
|
soon to be dangling pointer. */
|
|
m_window = nullptr;
|
|
return;
|
|
}
|
|
m_window->resize (height, width, x, y);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
|
|
{
|
|
TUI_SCOPED_DEBUG_ENTER_EXIT;
|
|
|
|
if (m_window == nullptr)
|
|
m_window = tui_get_window_by_name (m_contents);
|
|
|
|
tui_debug_printf ("window = %s, getting %s",
|
|
m_window->name (), (height ? "height" : "width"));
|
|
|
|
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 ();
|
|
}
|
|
|
|
tui_debug_printf ("min = %d, max = %d", *min_value, *max_value);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
bool
|
|
tui_layout_window::first_edge_has_border_p () const
|
|
{
|
|
gdb_assert (m_window != nullptr);
|
|
return m_window->can_box ();
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
bool
|
|
tui_layout_window::last_edge_has_border_p () const
|
|
{
|
|
gdb_assert (m_window != nullptr);
|
|
return m_window->can_box ();
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_window::replace_window (const char *name, const char *new_window)
|
|
{
|
|
if (m_contents == name)
|
|
{
|
|
m_contents = new_window;
|
|
if (m_window != nullptr)
|
|
{
|
|
m_window->make_visible (false);
|
|
m_window = tui_get_window_by_name (m_contents);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_window::specification (ui_file *output, int depth)
|
|
{
|
|
gdb_puts (get_name (), output);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
std::string
|
|
tui_layout_window::layout_fingerprint () const
|
|
{
|
|
if (strcmp (get_name (), "cmd") == 0)
|
|
return "C";
|
|
else
|
|
return "";
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
|
|
int weight)
|
|
{
|
|
split s = {weight, std::move (layout)};
|
|
m_splits.push_back (std::move (s));
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_split::add_window (const char *name, int weight)
|
|
{
|
|
tui_layout_window *result = new tui_layout_window (name);
|
|
split s = {weight, std::unique_ptr<tui_layout_base> (result)};
|
|
m_splits.push_back (std::move (s));
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
std::unique_ptr<tui_layout_base>
|
|
tui_layout_split::clone () const
|
|
{
|
|
tui_layout_split *result = new tui_layout_split (m_vertical);
|
|
for (const split &item : m_splits)
|
|
{
|
|
std::unique_ptr<tui_layout_base> next = item.layout->clone ();
|
|
split s = {item.weight, std::move (next)};
|
|
result->m_splits.push_back (std::move (s));
|
|
}
|
|
return std::unique_ptr<tui_layout_base> (result);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
|
|
{
|
|
TUI_SCOPED_DEBUG_ENTER_EXIT;
|
|
|
|
*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 (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;
|
|
}
|
|
|
|
tui_debug_printf ("min_value = %d, max_value = %d", *min_value, *max_value);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
bool
|
|
tui_layout_split::first_edge_has_border_p () const
|
|
{
|
|
if (m_splits.empty ())
|
|
return false;
|
|
return m_splits[0].layout->first_edge_has_border_p ();
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
bool
|
|
tui_layout_split::last_edge_has_border_p () const
|
|
{
|
|
if (m_splits.empty ())
|
|
return false;
|
|
return m_splits.back ().layout->last_edge_has_border_p ();
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_split::set_weights_from_sizes ()
|
|
{
|
|
for (int i = 0; i < m_splits.size (); ++i)
|
|
m_splits[i].weight
|
|
= m_vertical ? m_splits[i].layout->height : m_splits[i].layout->width;
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
std::string
|
|
tui_layout_split::tui_debug_weights_to_string () const
|
|
{
|
|
std::string str;
|
|
|
|
for (int i = 0; i < m_splits.size (); ++i)
|
|
{
|
|
if (i > 0)
|
|
str += ", ";
|
|
str += string_printf ("[%d] %d", i, m_splits[i].weight);
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_split::tui_debug_print_size_info
|
|
(const std::vector<tui_layout_split::size_info> &info)
|
|
{
|
|
gdb_assert (debug_tui);
|
|
|
|
tui_debug_printf ("current size info data:");
|
|
for (int i = 0; i < info.size (); ++i)
|
|
tui_debug_printf (" [%d] { size = %d, min = %d, max = %d, share_box = %d }",
|
|
i, info[i].size, info[i].min_size,
|
|
info[i].max_size, info[i].share_box);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
tui_adjust_result
|
|
tui_layout_split::set_size (const char *name, int new_size, bool set_width_p)
|
|
{
|
|
TUI_SCOPED_DEBUG_ENTER_EXIT;
|
|
|
|
tui_debug_printf ("this = %p, name = %s, new_size = %d",
|
|
this, name, new_size);
|
|
|
|
/* Look through the children. If one is a layout holding the named
|
|
window, we're done; or if one actually is the named window,
|
|
update it. */
|
|
int found_index = -1;
|
|
for (int i = 0; i < m_splits.size (); ++i)
|
|
{
|
|
tui_adjust_result adjusted;
|
|
if (set_width_p)
|
|
adjusted = m_splits[i].layout->set_width (name, new_size);
|
|
else
|
|
adjusted = m_splits[i].layout->set_height (name, new_size);
|
|
if (adjusted == HANDLED)
|
|
return HANDLED;
|
|
if (adjusted == FOUND)
|
|
{
|
|
if (set_width_p ? m_vertical : !m_vertical)
|
|
return FOUND;
|
|
found_index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found_index == -1)
|
|
return NOT_FOUND;
|
|
int curr_size = (set_width_p
|
|
? m_splits[found_index].layout->width
|
|
: m_splits[found_index].layout->height);
|
|
if (curr_size == new_size)
|
|
return HANDLED;
|
|
|
|
tui_debug_printf ("found window %s at index %d", name, found_index);
|
|
|
|
set_weights_from_sizes ();
|
|
int delta = m_splits[found_index].weight - new_size;
|
|
m_splits[found_index].weight = new_size;
|
|
|
|
tui_debug_printf ("before delta (%d) distribution, weights: %s",
|
|
delta, tui_debug_weights_to_string ().c_str ());
|
|
|
|
/* Distribute the "delta" over all other windows, while respecting their
|
|
min/max sizes. We grow each window by 1 line at a time continually
|
|
looping over all the windows. However, skip the window that the user
|
|
just resized, obviously we don't want to readjust that window. */
|
|
bool found_window_that_can_grow_p = true;
|
|
for (int i = 0; delta != 0; i = (i + 1) % m_splits.size ())
|
|
{
|
|
int index = (found_index + 1 + i) % m_splits.size ();
|
|
if (index == found_index)
|
|
{
|
|
if (!found_window_that_can_grow_p)
|
|
break;
|
|
found_window_that_can_grow_p = false;
|
|
continue;
|
|
}
|
|
|
|
int new_min, new_max;
|
|
m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
|
|
|
|
if (delta < 0)
|
|
{
|
|
/* The primary window grew, so we are trying to shrink other
|
|
windows. */
|
|
if (m_splits[index].weight > new_min)
|
|
{
|
|
m_splits[index].weight -= 1;
|
|
delta += 1;
|
|
found_window_that_can_grow_p = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The primary window shrank, so we are trying to grow other
|
|
windows. */
|
|
if (m_splits[index].weight < new_max)
|
|
{
|
|
m_splits[index].weight += 1;
|
|
delta -= 1;
|
|
found_window_that_can_grow_p = true;
|
|
}
|
|
}
|
|
|
|
tui_debug_printf ("index = %d, weight now: %d",
|
|
index, m_splits[index].weight);
|
|
}
|
|
|
|
tui_debug_printf ("after delta (%d) distribution, weights: %s",
|
|
delta, tui_debug_weights_to_string ().c_str ());
|
|
|
|
if (delta != 0)
|
|
{
|
|
if (set_width_p)
|
|
warning (_("Invalid window width specified"));
|
|
else
|
|
warning (_("Invalid window height specified"));
|
|
/* Effectively undo any modifications made here. */
|
|
set_weights_from_sizes ();
|
|
}
|
|
else
|
|
{
|
|
/* Simply re-apply the updated layout. We pass false here so that
|
|
the cmd window can be resized. However, we should have already
|
|
resized everything above to be "just right", so the apply call
|
|
here should not end up changing the sizes at all. */
|
|
apply (x, y, width, height, false);
|
|
}
|
|
|
|
return HANDLED;
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_split::apply (int x_, int y_, int width_, int height_,
|
|
bool preserve_cmd_win_size_p)
|
|
{
|
|
TUI_SCOPED_DEBUG_ENTER_EXIT;
|
|
|
|
x = x_;
|
|
y = y_;
|
|
width = width_;
|
|
height = height_;
|
|
|
|
/* In some situations we fix the size of the cmd window. However,
|
|
occasionally this turns out to be a mistake. This struct is used to
|
|
hold the original information about the cmd window, so we can restore
|
|
it if needed. */
|
|
struct old_size_info
|
|
{
|
|
/* Constructor. */
|
|
old_size_info (int index_, int min_size_, int max_size_)
|
|
: index (index_),
|
|
min_size (min_size_),
|
|
max_size (max_size_)
|
|
{ /* Nothing. */ }
|
|
|
|
/* The index in m_splits where the cmd window was found. */
|
|
int index;
|
|
|
|
/* The previous min/max size. */
|
|
int min_size;
|
|
int max_size;
|
|
};
|
|
|
|
/* This is given a value only if we fix the size of the cmd window. */
|
|
std::optional<old_size_info> old_cmd_info;
|
|
|
|
std::vector<size_info> info (m_splits.size ());
|
|
|
|
tui_debug_printf ("weights are: %s",
|
|
tui_debug_weights_to_string ().c_str ());
|
|
|
|
/* 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_size = m_vertical ? height : width;
|
|
int last_index = -1;
|
|
int total_weight = 0;
|
|
int prev = -1;
|
|
for (int i = 0; i < m_splits.size (); ++i)
|
|
{
|
|
bool cmd_win_already_exists = tui_cmd_win () != nullptr;
|
|
|
|
/* 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 (m_vertical, &info[i].min_size,
|
|
&info[i].max_size);
|
|
|
|
if (preserve_cmd_win_size_p
|
|
&& cmd_win_already_exists
|
|
&& m_splits[i].layout->get_name () != nullptr
|
|
&& strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
|
|
{
|
|
/* Save the old cmd window information, in case we need to
|
|
restore it later. */
|
|
old_cmd_info.emplace (i, info[i].min_size, info[i].max_size);
|
|
|
|
/* 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 sizes this way ensures
|
|
that the resizing step, below, does the right thing with
|
|
this window. */
|
|
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_size > info[i].max_size)
|
|
{
|
|
/* There is not enough room for this window, drop it. */
|
|
info[i].min_size = 0;
|
|
info[i].max_size = 0;
|
|
continue;
|
|
}
|
|
|
|
/* Two adjacent boxed windows will share a border. */
|
|
if (prev != -1
|
|
&& m_splits[prev].layout->last_edge_has_border_p ()
|
|
&& m_splits[i].layout->first_edge_has_border_p ())
|
|
info[i].share_box = true;
|
|
|
|
if (info[i].min_size == info[i].max_size)
|
|
{
|
|
available_size -= info[i].min_size;
|
|
if (info[i].share_box)
|
|
{
|
|
/* A shared border makes a bit more size available. */
|
|
++available_size;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_index = i;
|
|
total_weight += m_splits[i].weight;
|
|
}
|
|
|
|
prev = i;
|
|
}
|
|
|
|
/* If last_index is set then we have a window that is not of a fixed
|
|
size. This window will have its size calculated below, which requires
|
|
that the total_weight not be zero (we divide by total_weight, so don't
|
|
want a floating-point exception). */
|
|
gdb_assert (last_index == -1 || total_weight > 0);
|
|
|
|
/* 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_size = 0;
|
|
for (int i = 0; i < m_splits.size (); ++i)
|
|
{
|
|
if (info[i].min_size != info[i].max_size)
|
|
{
|
|
/* Compute the height and clamp to the allowable range. */
|
|
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;
|
|
/* Keep a total of all the size we've used so far (we gain some
|
|
size back if this window can share a border with a preceding
|
|
window). Any unused space will be distributed between all of
|
|
the other windows (while respecting min/max sizes) later in
|
|
this function. */
|
|
used_size += info[i].size;
|
|
if (info[i].share_box)
|
|
{
|
|
/* A shared border makes a bit more size available. */
|
|
--used_size;
|
|
}
|
|
}
|
|
else
|
|
info[i].size = info[i].min_size;
|
|
}
|
|
|
|
if (debug_tui)
|
|
{
|
|
tui_debug_printf ("after initial size calculation");
|
|
tui_debug_printf ("available_size = %d, used_size = %d",
|
|
available_size, used_size);
|
|
tui_debug_printf ("total_weight = %d, last_index = %d",
|
|
total_weight, last_index);
|
|
tui_debug_print_size_info (info);
|
|
}
|
|
|
|
/* If we didn't find any sub-layouts that were of a non-fixed size, but
|
|
we did find the cmd window, then we can consider that a sort-of
|
|
non-fixed size sub-layout.
|
|
|
|
The cmd window might, initially, be of a fixed size (see above), but,
|
|
we are willing to relax this constraint if required to correctly apply
|
|
this layout (see below). */
|
|
if (last_index == -1 && old_cmd_info.has_value ())
|
|
last_index = old_cmd_info->index;
|
|
|
|
/* Allocate any leftover size. */
|
|
if (available_size != used_size && last_index != -1)
|
|
{
|
|
/* Loop over all windows until the amount of used space is equal to
|
|
the amount of available space. There's an escape hatch within
|
|
the loop in case we can't find any sub-layouts to resize. */
|
|
bool found_window_that_can_grow_p = true;
|
|
for (int idx = last_index;
|
|
available_size != used_size;
|
|
idx = (idx + 1) % m_splits.size ())
|
|
{
|
|
/* Every time we get back to last_index, which is where the loop
|
|
started, we check to make sure that we did assign some space
|
|
to a window, bringing used_size closer to available_size.
|
|
|
|
If we didn't, but the cmd window is of a fixed size, then we
|
|
can make the console window non-fixed-size, and continue
|
|
around the loop, hopefully, this will allow the layout to be
|
|
applied correctly.
|
|
|
|
If we still make it around the loop without moving used_size
|
|
closer to available_size, then there's nothing more we can do,
|
|
and we break out of the loop. */
|
|
if (idx == last_index)
|
|
{
|
|
/* If the used_size is greater than the available_size then
|
|
this indicates that the fixed-sized sub-layouts claimed
|
|
more space than is available. This layout is not going to
|
|
work. Our only hope at this point is to make the cmd
|
|
window non-fixed-size (if possible), and hope we can
|
|
shrink this enough to fit the rest of the sub-layouts in.
|
|
|
|
Alternatively, we've made it around the loop without
|
|
adjusting any window's size. This likely means all
|
|
windows have hit their min or max size. Again, our only
|
|
hope is to make the cmd window non-fixed-size, and hope
|
|
this fixes all our problems. */
|
|
if (old_cmd_info.has_value ()
|
|
&& ((available_size < used_size)
|
|
|| !found_window_that_can_grow_p))
|
|
{
|
|
info[old_cmd_info->index].min_size = old_cmd_info->min_size;
|
|
info[old_cmd_info->index].max_size = old_cmd_info->max_size;
|
|
tui_debug_printf
|
|
("restoring index %d (cmd) size limits, min = %d, max = %d",
|
|
old_cmd_info->index, old_cmd_info->min_size,
|
|
old_cmd_info->max_size);
|
|
old_cmd_info.reset ();
|
|
}
|
|
else if (!found_window_that_can_grow_p)
|
|
break;
|
|
found_window_that_can_grow_p = false;
|
|
}
|
|
|
|
if (available_size > used_size
|
|
&& info[idx].size < info[idx].max_size)
|
|
{
|
|
found_window_that_can_grow_p = true;
|
|
info[idx].size += 1;
|
|
used_size += 1;
|
|
}
|
|
else if (available_size < used_size
|
|
&& info[idx].size > info[idx].min_size)
|
|
{
|
|
found_window_that_can_grow_p = true;
|
|
info[idx].size -= 1;
|
|
used_size -= 1;
|
|
}
|
|
}
|
|
|
|
if (debug_tui)
|
|
{
|
|
tui_debug_printf ("after final size calculation");
|
|
tui_debug_printf ("available_size = %d, used_size = %d",
|
|
available_size, used_size);
|
|
tui_debug_printf ("total_weight = %d, last_index = %d",
|
|
total_weight, last_index);
|
|
tui_debug_print_size_info (info);
|
|
}
|
|
}
|
|
|
|
/* Step 3: Resize. */
|
|
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 (size_accum + info[i].size > maximum)
|
|
size_accum = maximum - info[i].size;
|
|
else if (info[i].share_box)
|
|
--size_accum;
|
|
if (m_vertical)
|
|
m_splits[i].layout->apply (x, y + size_accum, width, info[i].size,
|
|
preserve_cmd_win_size_p);
|
|
else
|
|
m_splits[i].layout->apply (x + size_accum, y, info[i].size, height,
|
|
preserve_cmd_win_size_p);
|
|
size_accum += info[i].size;
|
|
}
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_split::remove_windows (const char *name)
|
|
{
|
|
for (int i = 0; i < m_splits.size (); ++i)
|
|
{
|
|
const char *this_name = m_splits[i].layout->get_name ();
|
|
if (this_name == nullptr)
|
|
m_splits[i].layout->remove_windows (name);
|
|
else if (strcmp (this_name, name) == 0
|
|
|| strcmp (this_name, CMD_NAME) == 0
|
|
|| strcmp (this_name, STATUS_NAME) == 0)
|
|
{
|
|
/* Keep. */
|
|
}
|
|
else
|
|
{
|
|
m_splits.erase (m_splits.begin () + i);
|
|
--i;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_split::replace_window (const char *name, const char *new_window)
|
|
{
|
|
for (auto &item : m_splits)
|
|
item.layout->replace_window (name, new_window);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
void
|
|
tui_layout_split::specification (ui_file *output, int depth)
|
|
{
|
|
if (depth > 0)
|
|
gdb_puts ("{", output);
|
|
|
|
if (!m_vertical)
|
|
gdb_puts ("-horizontal ", output);
|
|
|
|
bool first = true;
|
|
for (auto &item : m_splits)
|
|
{
|
|
if (!first)
|
|
gdb_puts (" ", output);
|
|
first = false;
|
|
item.layout->specification (output, depth + 1);
|
|
gdb_printf (output, " %d", item.weight);
|
|
}
|
|
|
|
if (depth > 0)
|
|
gdb_puts ("}", output);
|
|
}
|
|
|
|
/* See tui-layout.h. */
|
|
|
|
std::string
|
|
tui_layout_split::layout_fingerprint () const
|
|
{
|
|
for (auto &item : m_splits)
|
|
{
|
|
std::string fp = item.layout->layout_fingerprint ();
|
|
if (!fp.empty () && m_splits.size () != 1)
|
|
return std::string (m_vertical ? "V" : "H") + fp;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/* Destroy the layout associated with SELF. */
|
|
|
|
static void
|
|
destroy_layout (struct cmd_list_element *self, void *context)
|
|
{
|
|
tui_layout_split *layout = (tui_layout_split *) context;
|
|
size_t index = find_layout (layout);
|
|
layouts.erase (layouts.begin () + index);
|
|
}
|
|
|
|
/* List holding the sub-commands of "layout". */
|
|
|
|
static struct cmd_list_element *layout_list;
|
|
|
|
/* Called to implement 'tui layout'. */
|
|
|
|
static void
|
|
tui_layout_command (const char *args, int from_tty)
|
|
{
|
|
help_list (layout_list, "tui layout ", all_commands, gdb_stdout);
|
|
}
|
|
|
|
/* Add a "layout" command with name NAME that switches to LAYOUT. */
|
|
|
|
static struct cmd_list_element *
|
|
add_layout_command (const char *name, tui_layout_split *layout)
|
|
{
|
|
struct cmd_list_element *cmd;
|
|
|
|
string_file spec;
|
|
layout->specification (&spec, 0);
|
|
|
|
gdb::unique_xmalloc_ptr<char> doc
|
|
= xstrprintf (_("Apply the \"%s\" layout.\n\
|
|
This layout was created using:\n\
|
|
tui new-layout %s %s"),
|
|
name, name, spec.c_str ());
|
|
|
|
cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
|
|
cmd->set_context (layout);
|
|
/* There is no API to set this. */
|
|
cmd->func = tui_apply_layout;
|
|
cmd->destroyer = destroy_layout;
|
|
cmd->doc_allocated = 1;
|
|
doc.release ();
|
|
layouts.emplace_back (layout);
|
|
|
|
return cmd;
|
|
}
|
|
|
|
/* Initialize the standard layouts. */
|
|
|
|
static void
|
|
initialize_layouts ()
|
|
{
|
|
tui_layout_split *layout;
|
|
|
|
layout = new tui_layout_split ();
|
|
layout->add_window (SRC_NAME, 2);
|
|
layout->add_window (STATUS_NAME, 0);
|
|
layout->add_window (CMD_NAME, 1);
|
|
add_layout_command (SRC_NAME, layout);
|
|
|
|
layout = new tui_layout_split ();
|
|
layout->add_window (DISASSEM_NAME, 2);
|
|
layout->add_window (STATUS_NAME, 0);
|
|
layout->add_window (CMD_NAME, 1);
|
|
add_layout_command (DISASSEM_NAME, layout);
|
|
|
|
layout = new tui_layout_split ();
|
|
layout->add_window (SRC_NAME, 1);
|
|
layout->add_window (DISASSEM_NAME, 1);
|
|
layout->add_window (STATUS_NAME, 0);
|
|
layout->add_window (CMD_NAME, 1);
|
|
add_layout_command ("split", layout);
|
|
|
|
layout = new tui_layout_split ();
|
|
layout->add_window (DATA_NAME, 1);
|
|
layout->add_window (SRC_NAME, 1);
|
|
layout->add_window (STATUS_NAME, 0);
|
|
layout->add_window (CMD_NAME, 1);
|
|
layouts.emplace_back (layout);
|
|
src_regs_layout = layout;
|
|
|
|
layout = new tui_layout_split ();
|
|
layout->add_window (DATA_NAME, 1);
|
|
layout->add_window (DISASSEM_NAME, 1);
|
|
layout->add_window (STATUS_NAME, 0);
|
|
layout->add_window (CMD_NAME, 1);
|
|
layouts.emplace_back (layout);
|
|
asm_regs_layout = layout;
|
|
}
|
|
|
|
|
|
|
|
/* A helper function that returns true if NAME is the name of an
|
|
available window. */
|
|
|
|
static bool
|
|
validate_window_name (const std::string &name)
|
|
{
|
|
auto iter = known_window_types.find (name);
|
|
return iter != known_window_types.end ();
|
|
}
|
|
|
|
/* Implementation of the "tui new-layout" command. */
|
|
|
|
static void
|
|
tui_new_layout_command (const char *spec, int from_tty)
|
|
{
|
|
std::string new_name = extract_arg (&spec);
|
|
if (new_name.empty ())
|
|
error (_("No layout name specified"));
|
|
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<std::unique_ptr<tui_layout_split>> splits;
|
|
splits.emplace_back (new tui_layout_split (is_vertical));
|
|
std::unordered_set<std::string> seen_windows;
|
|
while (true)
|
|
{
|
|
spec = skip_spaces (spec);
|
|
if (spec[0] == '\0')
|
|
break;
|
|
|
|
if (spec[0] == '{')
|
|
{
|
|
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;
|
|
}
|
|
|
|
bool is_close = false;
|
|
std::string name;
|
|
if (spec[0] == '}')
|
|
{
|
|
is_close = true;
|
|
++spec;
|
|
if (splits.size () == 1)
|
|
error (_("Extra '}' in layout specification"));
|
|
}
|
|
else
|
|
{
|
|
name = extract_arg (&spec);
|
|
if (name.empty ())
|
|
break;
|
|
if (!validate_window_name (name))
|
|
error (_("Unknown window \"%s\""), name.c_str ());
|
|
if (seen_windows.find (name) != seen_windows.end ())
|
|
error (_("Window \"%s\" seen twice in layout"), name.c_str ());
|
|
}
|
|
|
|
ULONGEST weight = get_ulongest (&spec, '}');
|
|
if ((int) weight != weight)
|
|
error (_("Weight out of range: %s"), pulongest (weight));
|
|
if (is_close)
|
|
{
|
|
std::unique_ptr<tui_layout_split> last_split
|
|
= std::move (splits.back ());
|
|
splits.pop_back ();
|
|
splits.back ()->add_split (std::move (last_split), weight);
|
|
}
|
|
else
|
|
{
|
|
splits.back ()->add_window (name.c_str (), weight);
|
|
seen_windows.insert (name);
|
|
}
|
|
}
|
|
if (splits.size () > 1)
|
|
error (_("Missing '}' in layout specification"));
|
|
if (seen_windows.empty ())
|
|
error (_("New layout does not contain any windows"));
|
|
if (seen_windows.find (CMD_NAME) == seen_windows.end ())
|
|
error (_("New layout does not contain the \"" CMD_NAME "\" window"));
|
|
|
|
gdb::unique_xmalloc_ptr<char> cmd_name
|
|
= make_unique_xstrdup (new_name.c_str ());
|
|
std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
|
|
struct cmd_list_element *cmd
|
|
= add_layout_command (cmd_name.get (), new_layout.get ());
|
|
cmd->name_allocated = 1;
|
|
cmd_name.release ();
|
|
new_layout.release ();
|
|
}
|
|
|
|
/* Function to initialize gdb commands, for tui window layout
|
|
manipulation. */
|
|
|
|
void _initialize_tui_layout ();
|
|
void
|
|
_initialize_tui_layout ()
|
|
{
|
|
struct cmd_list_element *layout_cmd
|
|
= add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
|
|
Change the layout of windows.\n\
|
|
Usage: tui layout prev | next | LAYOUT-NAME"),
|
|
&layout_list, 0, tui_get_cmd_list ());
|
|
add_com_alias ("layout", layout_cmd, class_tui, 0);
|
|
|
|
add_cmd ("next", class_tui, tui_next_layout_command,
|
|
_("Apply the next TUI layout."),
|
|
&layout_list);
|
|
add_cmd ("prev", class_tui, tui_prev_layout_command,
|
|
_("Apply the previous TUI layout."),
|
|
&layout_list);
|
|
add_cmd ("regs", class_tui, tui_regs_layout_command,
|
|
_("Apply the TUI register layout."),
|
|
&layout_list);
|
|
|
|
add_cmd ("new-layout", class_tui, tui_new_layout_command,
|
|
_("Create a new TUI layout.\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\
|
|
{ [-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."),
|
|
tui_get_cmd_list ());
|
|
|
|
initialize_layouts ();
|
|
initialize_known_windows ();
|
|
}
|