/* TUI layout window management.
- Copyright (C) 1998-2019 Free Software Foundation, Inc.
+ Copyright (C) 1998-2020 Free Software Foundation, Inc.
Contributed by Hewlett-Packard Company.
#include "symtab.h"
#include "frame.h"
#include "source.h"
+#include "cli/cli-cmds.h"
+#include "cli/cli-decode.h"
+#include "cli/cli-utils.h"
#include <ctype.h>
+#include <unordered_map>
+#include <unordered_set>
#include "tui/tui.h"
+#include "tui/tui-command.h"
#include "tui/tui-data.h"
-#include "tui/tui-windata.h"
#include "tui/tui-wingeneral.h"
#include "tui/tui-stack.h"
#include "tui/tui-regs.h"
#include "tui/tui-winsource.h"
#include "tui/tui-disasm.h"
#include "tui/tui-layout.h"
+#include "tui/tui-source.h"
#include "gdb_curses.h"
-/*******************************
-** Static Local Decls
-********************************/
-static void show_layout (enum tui_layout_type);
-static tui_gen_win_info *init_and_make_win (tui_gen_win_info *,
- enum tui_win_type,
- int, int, int, int,
- enum tui_box);
-static void show_source_or_disasm_and_command (enum tui_layout_type);
-static struct tui_win_info *make_source_or_disasm_window (enum tui_win_type,
- int, int);
-static struct tui_win_info *make_command_window (int, int);
-static struct tui_win_info *make_source_window (int, int);
-static struct tui_win_info *make_disasm_window (int, int);
-static void make_data_window (struct tui_win_info **, int, int);
-static void show_source_command (void);
-static void show_disasm_command (void);
-static void show_source_disasm_command (void);
-static void show_data (enum tui_layout_type);
-static enum tui_layout_type next_layout (void);
-static enum tui_layout_type prev_layout (void);
-static void tui_layout_command (const char *, int);
static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
+/* The layouts. */
+static std::vector<std::unique_ptr<tui_layout_split>> layouts;
-/***************************************
-** DEFINITIONS
-***************************************/
+/* The layout that is currently applied. */
+static std::unique_ptr<tui_layout_base> applied_layout;
-#define LAYOUT_USAGE "Usage: layout prev | next | <layout_name> \n"
+/* The "skeleton" version of the layout that is currently applied. */
+static tui_layout_split *applied_skeleton;
-/* Show the screen layout defined. */
-static void
-show_layout (enum tui_layout_type layout)
-{
- enum tui_layout_type cur_layout = tui_current_layout ();
+/* 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;
- if (layout != cur_layout)
- {
- /* Since the new layout may cause changes in window size, we
- should free the content and reallocate on next display of
- source/asm. */
- tui_clear_source_windows ();
- if (layout == SRC_DATA_COMMAND
- || layout == DISASSEM_DATA_COMMAND)
- {
- show_data (layout);
- tui_refresh_all (tui_win_list);
- }
- else
- {
- /* First make the current layout be invisible. */
- tui_make_all_invisible ();
- tui_make_invisible (tui_locator_win_info_ptr ());
+/* See tui-data.h. */
+std::vector<tui_win_info *> tui_windows;
- switch (layout)
- {
- /* Now show the new layout. */
- case SRC_COMMAND:
- show_source_command ();
- tui_add_to_source_windows (TUI_SRC_WIN);
- break;
- case DISASSEM_COMMAND:
- show_disasm_command ();
- tui_add_to_source_windows (TUI_DISASM_WIN);
- break;
- case SRC_DISASSEM_COMMAND:
- show_source_disasm_command ();
- tui_add_to_source_windows (TUI_SRC_WIN);
- tui_add_to_source_windows (TUI_DISASM_WIN);
- break;
- default:
- break;
- }
- }
- }
-}
+/* When applying a layout, this is the list of all windows that were
+ in the previous layout. This is used to re-use windows when
+ changing a layout. */
+static std::vector<tui_win_info *> saved_tui_windows;
+/* See tui-layout.h. */
-/* Function to set the layout to SRC_COMMAND, DISASSEM_COMMAND,
- SRC_DISASSEM_COMMAND, SRC_DATA_COMMAND, or DISASSEM_DATA_COMMAND. */
-enum tui_status
-tui_set_layout (enum tui_layout_type layout_type)
+void
+tui_apply_current_layout ()
{
- enum tui_status status = TUI_SUCCESS;
+ struct gdbarch *gdbarch;
+ CORE_ADDR addr;
- if (layout_type != UNDEFINED_LAYOUT)
- {
- enum tui_layout_type cur_layout = tui_current_layout (),
- new_layout = UNDEFINED_LAYOUT;
- int regs_populate = FALSE;
- struct gdbarch *gdbarch;
- CORE_ADDR addr;
- struct tui_win_info *win_with_focus = tui_win_with_focus ();
- struct tui_layout_def *layout_def = tui_layout_def ();
+ extract_display_start_addr (&gdbarch, &addr);
- extract_display_start_addr (&gdbarch, &addr);
+ saved_tui_windows = std::move (tui_windows);
+ tui_windows.clear ();
- new_layout = layout_type;
+ for (tui_win_info *win_info : saved_tui_windows)
+ win_info->make_visible (false);
- regs_populate = (new_layout == SRC_DATA_COMMAND
- || new_layout == DISASSEM_DATA_COMMAND);
- if (new_layout != cur_layout)
- {
- show_layout (new_layout);
+ applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
- /* Now determine where focus should be. */
- if (win_with_focus != TUI_CMD_WIN)
- {
- switch (new_layout)
- {
- case SRC_COMMAND:
- tui_set_win_focus_to (TUI_SRC_WIN);
- layout_def->display_mode = SRC_WIN;
- break;
- case DISASSEM_COMMAND:
- /* The previous layout was not showing code.
- This can happen if there is no source
- available:
-
- 1. if the source file is in another dir OR
- 2. if target was compiled without -g
- We still want to show the assembly though! */
-
- tui_get_begin_asm_address (&gdbarch, &addr);
- tui_set_win_focus_to (TUI_DISASM_WIN);
- layout_def->display_mode = DISASSEM_WIN;
- break;
- case SRC_DISASSEM_COMMAND:
- /* The previous layout was not showing code.
- This can happen if there is no source
- available:
-
- 1. if the source file is in another dir OR
- 2. if target was compiled without -g
- We still want to show the assembly though! */
-
- tui_get_begin_asm_address (&gdbarch, &addr);
- if (win_with_focus == TUI_SRC_WIN)
- tui_set_win_focus_to (TUI_SRC_WIN);
- else
- tui_set_win_focus_to (TUI_DISASM_WIN);
- break;
- case SRC_DATA_COMMAND:
- if (win_with_focus != TUI_DATA_WIN)
- tui_set_win_focus_to (TUI_SRC_WIN);
- else
- tui_set_win_focus_to (TUI_DATA_WIN);
- layout_def->display_mode = SRC_WIN;
- break;
- case DISASSEM_DATA_COMMAND:
- /* The previous layout was not showing code.
- This can happen if there is no source
- available:
-
- 1. if the source file is in another dir OR
- 2. if target was compiled without -g
- We still want to show the assembly though! */
-
- tui_get_begin_asm_address (&gdbarch, &addr);
- if (win_with_focus != TUI_DATA_WIN)
- tui_set_win_focus_to (TUI_DISASM_WIN);
- else
- tui_set_win_focus_to (TUI_DATA_WIN);
- layout_def->display_mode = DISASSEM_WIN;
- break;
- default:
- break;
- }
- }
- /*
- * Now update the window content.
- */
- if (!regs_populate
- && (new_layout == SRC_DATA_COMMAND
- || new_layout == DISASSEM_DATA_COMMAND))
- tui_display_all_data ();
-
- tui_update_source_windows_with_addr (gdbarch, addr);
-
- if (regs_populate)
- tui_show_registers (TUI_DATA_WIN->current_group);
+ /* 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->is_visible ());
+
+ /* Now delete any window that was not re-applied. */
+ tui_win_info *focus = tui_win_with_focus ();
+ for (tui_win_info *win_info : saved_tui_windows)
+ {
+ if (!win_info->is_visible ())
+ {
+ if (focus == win_info)
+ tui_set_win_focus_to (tui_windows[0]);
+ delete win_info;
}
}
- else
- status = TUI_FAILURE;
- return status;
+ if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
+ tui_get_begin_asm_address (&gdbarch, &addr);
+ tui_update_source_windows_with_addr (gdbarch, addr);
+
+ saved_tui_windows.clear ();
}
-/* 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. */
+/* See tui-layout. */
+
void
-tui_add_win_to_layout (enum tui_win_type type)
+tui_adjust_window_height (struct tui_win_info *win, int new_height)
{
- enum tui_layout_type cur_layout = tui_current_layout ();
+ applied_layout->adjust_size (win->name (), new_height);
+}
- switch (type)
- {
- case SRC_WIN:
- if (cur_layout != SRC_COMMAND
- && cur_layout != SRC_DISASSEM_COMMAND
- && cur_layout != SRC_DATA_COMMAND)
- {
- tui_clear_source_windows_detail ();
- if (cur_layout == DISASSEM_DATA_COMMAND)
- show_layout (SRC_DATA_COMMAND);
- else
- show_layout (SRC_COMMAND);
- }
- break;
- case DISASSEM_WIN:
- if (cur_layout != DISASSEM_COMMAND
- && cur_layout != SRC_DISASSEM_COMMAND
- && cur_layout != DISASSEM_DATA_COMMAND)
- {
- tui_clear_source_windows_detail ();
- if (cur_layout == SRC_DATA_COMMAND)
- show_layout (DISASSEM_DATA_COMMAND);
- else
- show_layout (DISASSEM_COMMAND);
- }
- break;
- case DATA_WIN:
- if (cur_layout != SRC_DATA_COMMAND
- && cur_layout != DISASSEM_DATA_COMMAND)
- {
- if (cur_layout == DISASSEM_COMMAND)
- show_layout (DISASSEM_DATA_COMMAND);
- else
- show_layout (SRC_DATA_COMMAND);
- }
- break;
- default:
- break;
- }
+/* Set the current layout to LAYOUT. */
+
+static void
+tui_set_layout (tui_layout_split *layout)
+{
+ applied_skeleton = layout;
+ applied_layout = layout->clone ();
+ tui_apply_current_layout ();
}
+/* See tui-layout.h. */
-/* Answer the height of a window. If it hasn't been created yet,
- answer what the height of a window would be based upon its type and
- the layout. */
-int
-tui_default_win_height (enum tui_win_type type,
- enum tui_layout_type layout)
+void
+tui_add_win_to_layout (enum tui_win_type type)
{
- int h;
+ gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
- if (tui_win_list[type] != NULL)
- h = tui_win_list[type]->height;
- else
+ /* 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 ();
+}
+
+/* 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)
{
- switch (layout)
- {
- case SRC_COMMAND:
- case DISASSEM_COMMAND:
- if (TUI_CMD_WIN == NULL)
- h = tui_term_height () / 2;
- else
- h = tui_term_height () - TUI_CMD_WIN->height;
- break;
- case SRC_DISASSEM_COMMAND:
- case SRC_DATA_COMMAND:
- case DISASSEM_DATA_COMMAND:
- if (TUI_CMD_WIN == NULL)
- h = tui_term_height () / 3;
- else
- h = (tui_term_height () - TUI_CMD_WIN->height) / 2;
- break;
- default:
- h = 0;
- break;
- }
+ if (layout == layouts[i].get ())
+ return i;
}
-
- return h;
+ gdb_assert_not_reached (_("layout not found!?"));
}
+/* Function to set the layout. */
-/* Answer the height of a window. If it hasn't been created yet,
- answer what the height of a window would be based upon its type and
- the layout. */
-int
-tui_default_win_viewport_height (enum tui_win_type type,
- enum tui_layout_type layout)
+static void
+tui_apply_layout (struct cmd_list_element *command,
+ const char *args, int from_tty)
{
- int h;
+ tui_layout_split *layout
+ = (tui_layout_split *) get_cmd_context (command);
- h = tui_default_win_height (type, layout);
+ /* Make sure the curses mode is enabled. */
+ tui_enable ();
+ tui_set_layout (layout);
+}
- if (tui_win_list[type] == TUI_CMD_WIN)
- h -= 1;
- else
- h -= 2;
+/* See tui-layout.h. */
- return 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 ());
}
-/* Complete possible layout names. TEXT is the complete text entered so
- far, WORD is the word currently being completed. */
+/* Implement the "layout next" command. */
static void
-layout_completer (struct cmd_list_element *ignore,
- completion_tracker &tracker,
- const char *text, const char *word)
+tui_next_layout_command (const char *arg, int from_tty)
{
- static const char *layout_names [] =
- { "src", "asm", "split", "regs", "next", "prev", NULL };
-
- complete_on_enum (tracker, layout_names, text, word);
+ tui_enable ();
+ tui_next_layout ();
}
-/* Function to initialize gdb commands, for tui window layout
- manipulation. */
+/* See tui-layout.h. */
void
-_initialize_tui_layout (void)
+tui_set_initial_layout ()
{
- struct cmd_list_element *cmd;
+ tui_set_layout (layouts[0].get ());
+}
- cmd = add_com ("layout", class_tui, tui_layout_command, _("\
-Change the layout of windows.\n\
-Usage: layout prev | next | LAYOUT-NAME\n\
-Layout names are:\n\
- src : Displays source and command windows.\n\
- asm : Displays disassembly and command windows.\n\
- split : Displays source, disassembly and command windows.\n\
- regs : Displays register window. If existing layout\n\
- is source/command or assembly/command, the \n\
- register window is displayed. If the\n\
- source/assembly/command (split) is displayed, \n\
- the register window is displayed with \n\
- the window that has current logical focus."));
- set_cmd_completer (cmd, layout_completer);
+/* 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 ());
}
-/*************************
-** STATIC LOCAL FUNCTIONS
-**************************/
+/* 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);
+}
-/* Function to set the layout to SRC, ASM, SPLIT, NEXT, PREV, DATA, or
- REGS. */
-enum tui_status
-tui_set_layout_by_name (const char *layout_name)
+/* Implement the "layout regs" command. */
+
+static void
+tui_regs_layout_command (const char *arg, int from_tty)
{
- enum tui_status status = TUI_SUCCESS;
+ tui_enable ();
+ tui_regs_layout ();
+}
- if (layout_name != NULL)
- {
- int i;
- enum tui_layout_type new_layout = UNDEFINED_LAYOUT;
- enum tui_layout_type cur_layout = tui_current_layout ();
+/* See tui-layout.h. */
- std::string copy = layout_name;
- for (i = 0; i < copy.size (); i++)
- copy[i] = toupper (copy[i]);
- const char *buf_ptr = copy.c_str ();
+void
+tui_remove_some_windows ()
+{
+ tui_win_info *focus = tui_win_with_focus ();
- /* First check for ambiguous input. */
- if (strlen (buf_ptr) <= 1 && *buf_ptr == 'S')
- {
- warning (_("Ambiguous command input."));
- status = TUI_FAILURE;
- }
- else
- {
- if (subset_compare (buf_ptr, "SRC"))
- new_layout = SRC_COMMAND;
- else if (subset_compare (buf_ptr, "ASM"))
- new_layout = DISASSEM_COMMAND;
- else if (subset_compare (buf_ptr, "SPLIT"))
- new_layout = SRC_DISASSEM_COMMAND;
- else if (subset_compare (buf_ptr, "REGS"))
- {
- if (cur_layout == SRC_COMMAND
- || cur_layout == SRC_DATA_COMMAND)
- new_layout = SRC_DATA_COMMAND;
- else
- new_layout = DISASSEM_DATA_COMMAND;
- }
- else if (subset_compare (buf_ptr, "NEXT"))
- new_layout = next_layout ();
- else if (subset_compare (buf_ptr, "PREV"))
- new_layout = prev_layout ();
- else
- status = TUI_FAILURE;
-
- if (status == TUI_SUCCESS)
- {
- /* Make sure the curses mode is enabled. */
- tui_enable ();
- tui_set_layout (new_layout);
- }
- }
+ if (strcmp (focus->name (), "cmd") == 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;
}
- else
- status = TUI_FAILURE;
- return status;
+ applied_layout->remove_windows (focus->name ());
+ tui_apply_current_layout ();
}
-
static void
extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
{
- enum tui_layout_type cur_layout = tui_current_layout ();
- struct gdbarch *gdbarch = get_current_arch ();
- CORE_ADDR addr;
- CORE_ADDR pc;
- struct symtab_and_line cursal = get_current_source_symtab_and_line ();
+ if (TUI_SRC_WIN != nullptr)
+ TUI_SRC_WIN->display_start_addr (gdbarch_p, addr_p);
+ else if (TUI_DISASM_WIN != nullptr)
+ TUI_DISASM_WIN->display_start_addr (gdbarch_p, addr_p);
+ else
+ {
+ *gdbarch_p = nullptr;
+ *addr_p = 0;
+ }
+}
- switch (cur_layout)
+void
+tui_gen_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)
{
- case SRC_COMMAND:
- case SRC_DATA_COMMAND:
- gdbarch = TUI_SRC_WIN->gdbarch;
- find_line_pc (cursal.symtab,
- TUI_SRC_WIN->start_line_or_addr.u.line_no,
- &pc);
- addr = pc;
- break;
- case DISASSEM_COMMAND:
- case SRC_DISASSEM_COMMAND:
- case DISASSEM_DATA_COMMAND:
- gdbarch = TUI_DISASM_WIN->gdbarch;
- addr = TUI_DISASM_WIN->start_line_or_addr.u.addr;
- break;
- default:
- addr = 0;
- break;
+#ifdef HAVE_WRESIZE
+ wresize (handle.get (), height, width);
+ mvwin (handle.get (), y, x);
+ wmove (handle.get (), 0, 0);
+#else
+ handle.reset (nullptr);
+#endif
}
- *gdbarch_p = gdbarch;
- *addr_p = addr;
+ if (handle == nullptr)
+ make_window ();
+
+ rerender ();
+}
+
+\f
+
+/* Helper function to create one of the built-in (non-locator)
+ windows. */
+
+template<enum tui_win_type V, class T>
+static tui_gen_win_info *
+make_standard_window (const char *)
+{
+ if (tui_win_list[V] == nullptr)
+ tui_win_list[V] = new T ();
+ return tui_win_list[V];
+}
+
+/* Helper function to wrap tui_locator_win_info_ptr for
+ tui_get_window_by_name. */
+
+static tui_gen_win_info *
+get_locator_window (const char *)
+{
+ return tui_locator_win_info_ptr ();
+}
+
+/* A map holding all the known window types, keyed by name. Note that
+ this is heap-allocated and "leaked" at gdb exit. This avoids
+ ordering issues with destroying elements in the map at shutdown.
+ In particular, destroying this map can occur after Python has been
+ shut down, causing crashes if any window destruction requires
+ running Python code. */
+
+static std::unordered_map<std::string, window_factory> *known_window_types;
+
+/* Helper function that returns a TUI window, given its name. */
+
+static tui_gen_win_info *
+tui_get_window_by_name (const std::string &name)
+{
+ for (tui_win_info *window : saved_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_gen_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
-tui_layout_command (const char *arg, int from_tty)
+initialize_known_windows ()
{
- /* Switch to the selected layout. */
- if (tui_set_layout_by_name (arg) != TUI_SUCCESS)
- warning (_("Invalid layout specified.\n%s"), LAYOUT_USAGE);
+ known_window_types = new std::unordered_map<std::string, window_factory>;
+
+ known_window_types->emplace ("src",
+ make_standard_window<SRC_WIN,
+ tui_source_window>);
+ known_window_types->emplace ("cmd",
+ make_standard_window<CMD_WIN, tui_cmd_window>);
+ known_window_types->emplace ("regs",
+ make_standard_window<DATA_WIN,
+ tui_data_window>);
+ known_window_types->emplace ("asm",
+ make_standard_window<DISASSEM_WIN,
+ tui_disasm_window>);
+ known_window_types->emplace ("status", get_locator_window);
}
-/* Answer the previous layout to cycle to. */
-static enum tui_layout_type
-next_layout (void)
+/* See tui-layout.h. */
+
+void
+tui_register_window (const char *name, window_factory &&factory)
{
- int new_layout;
+ std::string name_copy = name;
+
+ if (name_copy == "src" || name_copy == "cmd" || name_copy == "regs"
+ || name_copy == "asm" || name_copy == "status")
+ error (_("Window type \"%s\" is built-in"), name);
+
+ known_window_types->emplace (std::move (name_copy),
+ std::move (factory));
+}
+
+/* See tui-layout.h. */
+
+std::unique_ptr<tui_layout_base>
+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_)
+{
+ x = x_;
+ y = y_;
+ width = width_;
+ height = height_;
+ gdb_assert (m_window != nullptr);
+ m_window->resize (height, width, x, y);
+ if (dynamic_cast<tui_win_info *> (m_window) != nullptr)
+ tui_windows.push_back ((tui_win_info *) m_window);
+}
- new_layout = tui_current_layout ();
- if (new_layout == UNDEFINED_LAYOUT)
- new_layout = SRC_COMMAND;
+/* See tui-layout.h. */
+
+void
+tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
+{
+ if (m_window == nullptr)
+ m_window = tui_get_window_by_name (m_contents);
+ if (height)
+ {
+ *min_value = m_window->min_height ();
+ *max_value = m_window->max_height ();
+ }
else
{
- new_layout++;
- if (new_layout == UNDEFINED_LAYOUT)
- new_layout = SRC_COMMAND;
+ *min_value = m_window->min_width ();
+ *max_value = m_window->max_width ();
}
+}
+
+/* See tui-layout.h. */
- return (enum tui_layout_type) new_layout;
+bool
+tui_layout_window::top_boxed_p () const
+{
+ gdb_assert (m_window != nullptr);
+ return m_window->can_box ();
}
+/* See tui-layout.h. */
-/* Answer the next layout to cycle to. */
-static enum tui_layout_type
-prev_layout (void)
+bool
+tui_layout_window::bottom_boxed_p () const
{
- int new_layout;
+ gdb_assert (m_window != nullptr);
+ return m_window->can_box ();
+}
- new_layout = tui_current_layout ();
- if (new_layout == SRC_COMMAND)
- new_layout = DISASSEM_DATA_COMMAND;
- else
+/* See tui-layout.h. */
+
+void
+tui_layout_window::replace_window (const char *name, const char *new_window)
+{
+ if (m_contents == name)
{
- new_layout--;
- if (new_layout == UNDEFINED_LAYOUT)
- new_layout = DISASSEM_DATA_COMMAND;
+ m_contents = new_window;
+ if (m_window != nullptr)
+ {
+ m_window->make_visible (false);
+ m_window = tui_get_window_by_name (m_contents);
+ }
}
-
- return (enum tui_layout_type) new_layout;
}
+/* See tui-layout.h. */
+
+void
+tui_layout_window::specification (ui_file *output, int depth)
+{
+ fputs_unfiltered (get_name (), output);
+}
+/* See tui-layout.h. */
-static struct tui_win_info *
-make_command_window (int height, int origin_y)
+void
+tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
+ int weight)
{
- struct tui_win_info *result
- = (struct tui_win_info *) init_and_make_win (NULL,
- CMD_WIN,
- height,
- tui_term_width (),
- 0,
- origin_y,
- DONT_BOX_WINDOW);
- return result;
+ split s = {weight, std::move (layout)};
+ m_splits.push_back (std::move (s));
}
+/* See tui-layout.h. */
-/* make_source_window().
- */
-static struct tui_win_info *
-make_source_window (int height, int origin_y)
+void
+tui_layout_split::add_window (const char *name, int weight)
{
- return make_source_or_disasm_window (SRC_WIN, height, origin_y);
-} /* make_source_window */
+ 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. */
-/* make_disasm_window().
- */
-static struct tui_win_info *
-make_disasm_window (int height, int origin_y)
+std::unique_ptr<tui_layout_base>
+tui_layout_split::clone () const
{
- return make_source_or_disasm_window (DISASSEM_WIN, height, origin_y);
-} /* make_disasm_window */
+ 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. */
-static void
-make_data_window (struct tui_win_info **win_info_ptr,
- int height, int origin_y)
+void
+tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
{
- *win_info_ptr
- = (struct tui_win_info *) init_and_make_win (*win_info_ptr,
- DATA_WIN,
- height,
- tui_term_width (),
- 0,
- origin_y,
- BOX_WINDOW);
+ *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;
+ }
}
+/* See tui-layout.h. */
+bool
+tui_layout_split::top_boxed_p () const
+{
+ if (m_splits.empty ())
+ return false;
+ return m_splits[0].layout->top_boxed_p ();
+}
-/* Show the Source/Command layout. */
-static void
-show_source_command (void)
+/* See tui-layout.h. */
+
+bool
+tui_layout_split::bottom_boxed_p () const
{
- show_source_or_disasm_and_command (SRC_COMMAND);
+ if (m_splits.empty ())
+ return false;
+ return m_splits.back ().layout->top_boxed_p ();
}
+/* See tui-layout.h. */
-/* Show the Dissassem/Command layout. */
-static void
-show_disasm_command (void)
+void
+tui_layout_split::set_weights_from_heights ()
{
- show_source_or_disasm_and_command (DISASSEM_COMMAND);
+ for (int i = 0; i < m_splits.size (); ++i)
+ m_splits[i].weight = m_splits[i].layout->height;
}
+/* See tui-layout.h. */
-/* Show the Source/Disassem/Command layout. */
-static void
-show_source_disasm_command (void)
+tui_adjust_result
+tui_layout_split::adjust_size (const char *name, int new_height)
{
- if (tui_current_layout () != SRC_DISASSEM_COMMAND)
+ /* 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)
{
- int cmd_height, src_height, asm_height;
+ tui_adjust_result adjusted
+ = m_splits[i].layout->adjust_size (name, new_height);
+ if (adjusted == HANDLED)
+ return HANDLED;
+ if (adjusted == FOUND)
+ {
+ if (!m_vertical)
+ return FOUND;
+ found_index = i;
+ break;
+ }
+ }
- if (TUI_CMD_WIN != NULL)
- cmd_height = TUI_CMD_WIN->height;
- else
- cmd_height = tui_term_height () / 3;
+ if (found_index == -1)
+ return NOT_FOUND;
+ if (m_splits[found_index].layout->height == new_height)
+ return HANDLED;
+
+ set_weights_from_heights ();
+ int delta = m_splits[found_index].weight - new_height;
+ m_splits[found_index].weight = new_height;
+
+ /* Distribute the "delta" over the next window; but if the next
+ window cannot hold it all, keep going until we either find a
+ window that does, or until we loop all the way around. */
+ for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i)
+ {
+ int index = (found_index + 1 + i) % m_splits.size ();
- src_height = (tui_term_height () - cmd_height) / 2;
- asm_height = tui_term_height () - (src_height + cmd_height);
+ int new_min, new_max;
+ m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
- if (TUI_SRC_WIN == NULL)
- tui_win_list[SRC_WIN] = make_source_window (src_height, 0);
+ if (delta < 0)
+ {
+ /* The primary window grew, so we are trying to shrink other
+ windows. */
+ int available = m_splits[index].weight - new_min;
+ int shrink_by = std::min (available, -delta);
+ m_splits[index].weight -= shrink_by;
+ delta += shrink_by;
+ }
else
{
- TUI_SRC_WIN->reset (TUI_SRC_WIN->type,
- src_height,
- TUI_SRC_WIN->width,
- TUI_SRC_WIN->execution_info->width,
- 0);
- TUI_SRC_WIN->execution_info->reset (EXEC_INFO_WIN,
- src_height,
- 3,
- 0,
- 0);
- tui_make_visible (TUI_SRC_WIN);
- tui_make_visible (TUI_SRC_WIN->execution_info);
- TUI_SRC_WIN->m_has_locator = false;
+ /* The primary window shrank, so we are trying to grow other
+ windows. */
+ int available = new_max - m_splits[index].weight;
+ int grow_by = std::min (available, delta);
+ m_splits[index].weight += grow_by;
+ delta -= grow_by;
}
+ }
- struct tui_locator_window *locator = tui_locator_win_info_ptr ();
- gdb_assert (locator != nullptr);
+ if (delta != 0)
+ {
+ warning (_("Invalid window height specified"));
+ /* Effectively undo any modifications made here. */
+ set_weights_from_heights ();
+ }
+ else
+ {
+ /* Simply re-apply the updated layout. */
+ apply (x, y, width, height);
+ }
- tui_show_source_content (TUI_SRC_WIN);
- if (TUI_DISASM_WIN == NULL)
+ return HANDLED;
+}
+
+/* See tui-layout.h. */
+
+void
+tui_layout_split::apply (int x_, int y_, int width_, int height_)
+{
+ x = x_;
+ y = y_;
+ width = width_;
+ height = height_;
+
+ struct size_info
+ {
+ int size;
+ int min_size;
+ int max_size;
+ /* True if this window will share a box border with the previous
+ window in the list. */
+ bool share_box;
+ };
+
+ std::vector<size_info> info (m_splits.size ());
+
+ /* 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;
+ 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 (!m_applied
+ && cmd_win_already_exists
+ && m_splits[i].layout->get_name () != nullptr
+ && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
{
- tui_win_list[DISASSEM_WIN]
- = make_disasm_window (asm_height, src_height - 1);
- init_and_make_win (locator,
- LOCATOR_WIN,
- 2 /* 1 */ ,
- tui_term_width (),
- 0,
- (src_height + asm_height) - 1,
- DONT_BOX_WINDOW);
+ /* 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)
+ available_size -= info[i].min_size;
else
{
- locator->reset (LOCATOR_WIN,
- 2 /* 1 */ ,
- tui_term_width (),
- 0,
- (src_height + asm_height) - 1);
- TUI_DISASM_WIN->m_has_locator = true;
- TUI_DISASM_WIN->reset (TUI_DISASM_WIN->type,
- asm_height,
- TUI_DISASM_WIN->width,
- TUI_DISASM_WIN->execution_info->width,
- src_height - 1);
- TUI_DISASM_WIN->execution_info->reset (EXEC_INFO_WIN,
- asm_height,
- 3,
- 0,
- src_height - 1);
- tui_make_visible (TUI_DISASM_WIN);
- tui_make_visible (TUI_DISASM_WIN->execution_info);
+ last_index = i;
+ total_weight += m_splits[i].weight;
}
- TUI_SRC_WIN->m_has_locator = false;
- TUI_DISASM_WIN->m_has_locator = true;
- tui_make_visible (locator);
- tui_show_locator_content ();
- tui_show_source_content (TUI_DISASM_WIN);
-
- if (TUI_CMD_WIN == NULL)
- tui_win_list[CMD_WIN]
- = make_command_window (cmd_height, tui_term_height () - cmd_height);
- else
+
+ /* 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 ())
+ info[i].share_box = true;
+ }
+
+ /* 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)
+ {
+ /* 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;
+ /* If there is any leftover size, just redistribute it to the
+ last resizeable window, by dropping it from the allocated
+ size. We could try to be fancier here perhaps, by
+ redistributing this size among all windows, not just the
+ last window. */
+ if (info[i].min_size != info[i].max_size)
{
- TUI_CMD_WIN->reset (TUI_CMD_WIN->type,
- TUI_CMD_WIN->height,
- TUI_CMD_WIN->width,
- 0,
- TUI_CMD_WIN->origin.y);
- tui_make_visible (TUI_CMD_WIN);
+ used_size += info[i].size;
+ if (info[i].share_box)
+ --used_size;
}
- TUI_CMD_WIN->refresh_window ();
- tui_set_current_layout_to (SRC_DISASSEM_COMMAND);
}
-}
+ /* Allocate any leftover size. */
+ if (available_size >= used_size && last_index != -1)
+ info[last_index].size += available_size - used_size;
-/* Show the Source/Data/Command or the Dissassembly/Data/Command
- layout. */
-static void
-show_data (enum tui_layout_type new_layout)
-{
- int total_height = (tui_term_height () - TUI_CMD_WIN->height);
- int src_height, data_height;
- enum tui_win_type win_type;
-
- struct tui_locator_window *locator = tui_locator_win_info_ptr ();
- gdb_assert (locator != nullptr);
-
- data_height = total_height / 2;
- src_height = total_height - data_height;
- tui_make_all_invisible ();
- tui_make_invisible (locator);
- make_data_window (&tui_win_list[DATA_WIN], data_height, 0);
- if (new_layout == SRC_DATA_COMMAND)
- win_type = SRC_WIN;
- else
- win_type = DISASSEM_WIN;
-
- tui_source_window_base *base;
- if (tui_win_list[win_type] == NULL)
+ /* Step 3: Resize. */
+ int size_accum = 0;
+ const int maximum = m_vertical ? height : width;
+ for (int i = 0; i < m_splits.size (); ++i)
{
- if (win_type == SRC_WIN)
- tui_win_list[win_type]
- = make_source_window (src_height, data_height - 1);
+ /* 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);
else
- tui_win_list[win_type]
- = make_disasm_window (src_height, data_height - 1);
- init_and_make_win (locator,
- LOCATOR_WIN,
- 2 /* 1 */ ,
- tui_term_width (),
- 0,
- total_height - 1,
- DONT_BOX_WINDOW);
- base = (tui_source_window_base *) tui_win_list[win_type];
+ m_splits[i].layout->apply (x + size_accum, y, info[i].size, height);
+ size_accum += info[i].size;
}
- else
+
+ m_applied = true;
+}
+
+/* See tui-layout.h. */
+
+void
+tui_layout_split::remove_windows (const char *name)
+{
+ for (int i = 0; i < m_splits.size (); ++i)
{
- base = (tui_source_window_base *) tui_win_list[win_type];
- tui_win_list[win_type]->reset (tui_win_list[win_type]->type,
- src_height,
- tui_win_list[win_type]->width,
- base->execution_info->width,
- data_height - 1);
- base->execution_info->reset (EXEC_INFO_WIN,
- src_height,
- 3,
- 0,
- data_height - 1);
- tui_make_visible (tui_win_list[win_type]);
- tui_make_visible (base->execution_info);
- locator->reset (LOCATOR_WIN,
- 2 /* 1 */ ,
- tui_term_width (),
- 0,
- total_height - 1);
+ 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") == 0)
+ {
+ /* Keep. */
+ }
+ m_splits.erase (m_splits.begin () + i);
+ --i;
+ }
}
- base->m_has_locator = true;
- tui_make_visible (locator);
- tui_show_locator_content ();
- tui_add_to_source_windows
- ((tui_source_window_base *) tui_win_list[win_type]);
- tui_set_current_layout_to (new_layout);
}
+/* See tui-layout.h. */
+
void
-tui_gen_win_info::reset (enum tui_win_type win_type,
- int height_, int width_,
- int origin_x_, int origin_y_)
+tui_layout_split::replace_window (const char *name, const char *new_window)
{
- int h = height_;
+ for (auto &item : m_splits)
+ item.layout->replace_window (name, new_window);
+}
- gdb_assert (type == win_type);
+/* See tui-layout.h. */
- width = width_;
- height = h;
- if (h > 1)
+void
+tui_layout_split::specification (ui_file *output, int depth)
+{
+ if (depth > 0)
+ fputs_unfiltered ("{", output);
+
+ if (!m_vertical)
+ fputs_unfiltered ("-horizontal ", output);
+
+ bool first = true;
+ for (auto &item : m_splits)
{
- viewport_height = h - 1;
- if (type != CMD_WIN)
- viewport_height--;
+ if (!first)
+ fputs_unfiltered (" ", output);
+ first = false;
+ item.layout->specification (output, depth + 1);
+ fprintf_unfiltered (output, " %d", item.weight);
}
- else
- viewport_height = 1;
- origin.x = origin_x_;
- origin.y = origin_y_;
+
+ if (depth > 0)
+ fputs_unfiltered ("}", output);
}
-/* init_and_make_win().
- */
-static tui_gen_win_info *
-init_and_make_win (tui_gen_win_info *win_info,
- enum tui_win_type win_type,
- int height, int width,
- int origin_x, int origin_y,
- enum tui_box box_it)
+/* Destroy the layout associated with SELF. */
+
+static void
+destroy_layout (struct cmd_list_element *self, void *context)
{
- if (win_info == NULL)
- {
- switch (win_type)
- {
- case SRC_WIN:
- win_info = new tui_source_window ();
- break;
+ tui_layout_split *layout = (tui_layout_split *) context;
+ size_t index = find_layout (layout);
+ layouts.erase (layouts.begin () + index);
+}
- case DISASSEM_WIN:
- win_info = new tui_disasm_window ();
- break;
+/* List holding the sub-commands of "layout". */
- case DATA_WIN:
- win_info = new tui_data_window ();
- break;
+static struct cmd_list_element *layout_list;
- case CMD_WIN:
- win_info = new tui_cmd_window ();
- break;
+/* Add a "layout" command with name NAME that switches to LAYOUT. */
- case EXEC_INFO_WIN:
- win_info = new tui_exec_info_window ();
- break;
+static struct cmd_list_element *
+add_layout_command (const char *name, tui_layout_split *layout)
+{
+ struct cmd_list_element *cmd;
- default:
- gdb_assert_not_reached (_("unhandled window type"));
- }
- }
+ 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);
+ set_cmd_context (cmd, 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;
+}
- win_info->reset (win_type, height, width, origin_x, origin_y);
- tui_make_window (win_info, box_it);
-
- return win_info;
-}
-
-
-static struct tui_win_info *
-make_source_or_disasm_window (enum tui_win_type type,
- int height, int origin_y)
-{
- struct tui_exec_info_window *execution_info
- = (tui_exec_info_window *) init_and_make_win (nullptr,
- EXEC_INFO_WIN,
- height,
- 3,
- 0,
- origin_y,
- DONT_BOX_WINDOW);
-
- /* Now create the source window. */
- struct tui_source_window_base *result
- = ((struct tui_source_window_base *)
- init_and_make_win (NULL,
- type,
- height,
- tui_term_width () - execution_info->width,
- execution_info->width,
- origin_y,
- BOX_WINDOW));
- result->execution_info = execution_info;
- return result;
+/* Initialize the standard layouts. */
+
+static void
+initialize_layouts ()
+{
+ tui_layout_split *layout;
+
+ layout = new tui_layout_split ();
+ layout->add_window ("src", 2);
+ layout->add_window ("status", 0);
+ layout->add_window ("cmd", 1);
+ add_layout_command ("src", layout);
+
+ layout = new tui_layout_split ();
+ layout->add_window ("asm", 2);
+ layout->add_window ("status", 0);
+ layout->add_window ("cmd", 1);
+ add_layout_command ("asm", layout);
+
+ layout = new tui_layout_split ();
+ layout->add_window ("src", 1);
+ layout->add_window ("asm", 1);
+ layout->add_window ("status", 0);
+ layout->add_window ("cmd", 1);
+ add_layout_command ("split", layout);
+
+ layout = new tui_layout_split ();
+ layout->add_window ("regs", 1);
+ layout->add_window ("src", 1);
+ layout->add_window ("status", 0);
+ layout->add_window ("cmd", 1);
+ layouts.emplace_back (layout);
+ src_regs_layout = layout;
+
+ layout = new tui_layout_split ();
+ layout->add_window ("regs", 1);
+ layout->add_window ("asm", 1);
+ layout->add_window ("status", 0);
+ layout->add_window ("cmd", 1);
+ layouts.emplace_back (layout);
+ asm_regs_layout = layout;
+}
+
+\f
+
+/* 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. */
-/* Show the Source/Command or the Disassem layout. */
static void
-show_source_or_disasm_and_command (enum tui_layout_type layout_type)
+tui_new_layout_command (const char *spec, int from_tty)
{
- if (tui_current_layout () != layout_type)
+ 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)
{
- struct tui_win_info **win_info_ptr;
- int src_height, cmd_height;
- struct tui_locator_window *locator = tui_locator_win_info_ptr ();
- gdb_assert (locator != nullptr);
-
- if (TUI_CMD_WIN != NULL)
- cmd_height = TUI_CMD_WIN->height;
- else
- cmd_height = tui_term_height () / 3;
- src_height = tui_term_height () - cmd_height;
+ spec = skip_spaces (spec);
+ if (spec[0] == '\0')
+ break;
- if (layout_type == SRC_COMMAND)
- win_info_ptr = &tui_win_list[SRC_WIN];
- else
- win_info_ptr = &tui_win_list[DISASSEM_WIN];
+ 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;
+ }
- tui_source_window_base *base;
- if ((*win_info_ptr) == NULL)
+ bool is_close = false;
+ std::string name;
+ if (spec[0] == '}')
{
- if (layout_type == SRC_COMMAND)
- *win_info_ptr = make_source_window (src_height - 1, 0);
- else
- *win_info_ptr = make_disasm_window (src_height - 1, 0);
- init_and_make_win (locator,
- LOCATOR_WIN,
- 2 /* 1 */ ,
- tui_term_width (),
- 0,
- src_height - 1,
- DONT_BOX_WINDOW);
- base = (tui_source_window_base *) *win_info_ptr;
+ is_close = true;
+ ++spec;
+ if (splits.size () == 1)
+ error (_("Extra '}' in layout specification"));
}
else
{
- base = (tui_source_window_base *) *win_info_ptr;
- locator->reset (LOCATOR_WIN,
- 2 /* 1 */ ,
- tui_term_width (),
- 0,
- src_height - 1);
- base->m_has_locator = true;
- (*win_info_ptr)->reset ((*win_info_ptr)->type,
- src_height - 1,
- (*win_info_ptr)->width,
- base->execution_info->width,
- 0);
- base->execution_info->reset (EXEC_INFO_WIN,
- src_height - 1,
- 3,
- 0,
- 0);
- tui_make_visible (*win_info_ptr);
- tui_make_visible (base->execution_info);
+ 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 ());
}
- base->m_has_locator = true;
- tui_make_visible (locator);
- tui_show_locator_content ();
- tui_show_source_content (base);
-
- if (TUI_CMD_WIN == NULL)
+ ULONGEST weight = get_ulongest (&spec, '}');
+ if ((int) weight != weight)
+ error (_("Weight out of range: %s"), pulongest (weight));
+ if (is_close)
{
- tui_win_list[CMD_WIN] = make_command_window (cmd_height,
- src_height);
- TUI_CMD_WIN->refresh_window ();
+ 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
{
- TUI_CMD_WIN->reset (TUI_CMD_WIN->type,
- TUI_CMD_WIN->height,
- TUI_CMD_WIN->width,
- TUI_CMD_WIN->origin.x,
- TUI_CMD_WIN->origin.y);
- tui_make_visible (TUI_CMD_WIN);
+ splits.back ()->add_window (name.c_str (), weight);
+ seen_windows.insert (name);
}
- tui_set_current_layout_to (layout_type);
}
+ 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") == seen_windows.end ())
+ error (_("New layout does not contain the \"cmd\" 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 ()
+{
+ add_basic_prefix_cmd ("layout", class_tui, _("\
+Change the layout of windows.\n\
+Usage: layout prev | next | LAYOUT-NAME"),
+ &layout_list, "layout ", 0, &cmdlist);
+
+ 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 ();
}