#include "gdb_curses.h"
static void show_layout (enum tui_layout_type);
-static void show_source_or_disasm_and_command (enum tui_layout_type);
-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 pre-defined layouts. */
+static tui_layout_split *standard_layouts[UNDEFINED_LAYOUT];
+
+/* The layout that is currently applied. */
+static std::unique_ptr<tui_layout_base> applied_layout;
+
static enum tui_layout_type current_layout = UNDEFINED_LAYOUT;
/* Accessor for the current layout. */
return current_layout;
}
+/* See tui-layout.h. */
+
+void
+tui_apply_current_layout ()
+{
+ applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
+}
+
+/* See tui-layout. */
+
+void
+tui_adjust_window_height (struct tui_win_info *win, int new_height)
+{
+ applied_layout->adjust_size (win->name (), new_height);
+}
/* Show the screen layout defined. */
static void
if (layout != cur_layout)
{
tui_make_all_invisible ();
- switch (layout)
- {
- case SRC_DATA_COMMAND:
- case DISASSEM_DATA_COMMAND:
- show_data (layout);
- break;
- /* Now show the new layout. */
- case SRC_COMMAND:
- show_source_command ();
- break;
- case DISASSEM_COMMAND:
- show_disasm_command ();
- break;
- case SRC_DISASSEM_COMMAND:
- show_source_disasm_command ();
- break;
- default:
- break;
- }
-
+ applied_layout = standard_layouts[layout]->clone ();
+ tui_apply_current_layout ();
current_layout = layout;
tui_delete_invisible_windows ();
}
return (enum tui_layout_type) new_layout;
}
-/* Show the Source/Command layout. */
-static void
-show_source_command (void)
-{
- show_source_or_disasm_and_command (SRC_COMMAND);
-}
-
-
-/* Show the Dissassem/Command layout. */
-static void
-show_disasm_command (void)
-{
- show_source_or_disasm_and_command (DISASSEM_COMMAND);
-}
-
-
-/* Show the Source/Disassem/Command layout. */
-static void
-show_source_disasm_command (void)
-{
- int cmd_height, src_height, asm_height;
-
- 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) / 2;
- asm_height = tui_term_height () - (src_height + cmd_height);
-
- if (TUI_SRC_WIN == NULL)
- tui_win_list[SRC_WIN] = new tui_source_window ();
- TUI_SRC_WIN->resize (src_height,
- tui_term_width (),
- 0,
- 0);
-
- struct tui_locator_window *locator = tui_locator_win_info_ptr ();
- gdb_assert (locator != nullptr);
-
- if (TUI_DISASM_WIN == NULL)
- tui_win_list[DISASSEM_WIN] = new tui_disasm_window ();
- TUI_DISASM_WIN->resize (asm_height,
- tui_term_width (),
- 0,
- src_height - 1);
- locator->resize (1, tui_term_width (),
- 0, (src_height + asm_height) - 1);
-
- if (TUI_CMD_WIN == NULL)
- tui_win_list[CMD_WIN] = new tui_cmd_window ();
- TUI_CMD_WIN->resize (cmd_height,
- tui_term_width (),
- 0,
- tui_term_height () - cmd_height);
-}
-
-
-/* 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;
- if (tui_win_list[DATA_WIN] == nullptr)
- tui_win_list[DATA_WIN] = new tui_data_window ();
- tui_win_list[DATA_WIN]->resize (data_height, tui_term_width (), 0, 0);
-
- if (new_layout == SRC_DATA_COMMAND)
- win_type = SRC_WIN;
- else
- win_type = DISASSEM_WIN;
-
- if (tui_win_list[win_type] == NULL)
- {
- if (win_type == SRC_WIN)
- tui_win_list[win_type] = new tui_source_window ();
- else
- tui_win_list[win_type] = new tui_disasm_window ();
- }
-
- tui_win_list[win_type]->resize (src_height,
- tui_term_width (),
- 0,
- data_height - 1);
- locator->resize (1, tui_term_width (),
- 0, total_height - 1);
- TUI_CMD_WIN->resize (TUI_CMD_WIN->height, tui_term_width (),
- 0, total_height);
-}
-
void
tui_gen_win_info::resize (int height_, int width_,
int origin_x_, int origin_y_)
{
if (width == width_ && height == height_
- && origin.x == origin_x_ && origin.y == origin_y_
+ && x == origin_x_ && y == origin_y_
&& handle != nullptr)
return;
viewport_height = height - 2;
else
viewport_height = 1;
- origin.x = origin_x_;
- origin.y = origin_y_;
+ x = origin_x_;
+ y = origin_y_;
if (handle != nullptr)
{
#ifdef HAVE_WRESIZE
wresize (handle.get (), height, width);
- mvwin (handle.get (), origin.y, origin.x);
+ mvwin (handle.get (), y, x);
wmove (handle.get (), 0, 0);
#else
handle.reset (nullptr);
rerender ();
}
-/* Show the Source/Command or the Disassem layout. */
-static void
-show_source_or_disasm_and_command (enum tui_layout_type layout_type)
-{
- struct tui_source_window_base *win_info;
- int src_height, cmd_height;
- struct tui_locator_window *locator = tui_locator_win_info_ptr ();
- gdb_assert (locator != nullptr);
+\f
- 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;
+/* Helper function that returns a TUI window, given its name. */
- if (layout_type == SRC_COMMAND)
+static tui_gen_win_info *
+tui_get_window_by_name (const std::string &name)
+{
+ if (name == "src")
{
if (tui_win_list[SRC_WIN] == nullptr)
tui_win_list[SRC_WIN] = new tui_source_window ();
- win_info = TUI_SRC_WIN;
+ return tui_win_list[SRC_WIN];
}
- else
+ else if (name == "cmd")
+ {
+ if (tui_win_list[CMD_WIN] == nullptr)
+ tui_win_list[CMD_WIN] = new tui_cmd_window ();
+ return tui_win_list[CMD_WIN];
+ }
+ else if (name == "regs")
+ {
+ if (tui_win_list[DATA_WIN] == nullptr)
+ tui_win_list[DATA_WIN] = new tui_data_window ();
+ return tui_win_list[DATA_WIN];
+ }
+ else if (name == "asm")
{
if (tui_win_list[DISASSEM_WIN] == nullptr)
tui_win_list[DISASSEM_WIN] = new tui_disasm_window ();
- win_info = TUI_DISASM_WIN;
+ return tui_win_list[DISASSEM_WIN];
+ }
+ else
+ {
+ gdb_assert (name == "locator");
+ return tui_locator_win_info_ptr ();
+ }
+}
+
+/* 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);
+}
+
+/* See tui-layout.h. */
+
+void
+tui_layout_window::get_sizes (int *min_height, int *max_height)
+{
+ if (m_window == nullptr)
+ m_window = tui_get_window_by_name (m_contents);
+ *min_height = m_window->min_height ();
+ *max_height = m_window->max_height ();
+}
+
+/* See tui-layout.h. */
+
+bool
+tui_layout_window::top_boxed_p () const
+{
+ gdb_assert (m_window != nullptr);
+ return m_window->can_box ();
+}
+
+/* See tui-layout.h. */
+
+bool
+tui_layout_window::bottom_boxed_p () const
+{
+ gdb_assert (m_window != nullptr);
+ return m_window->can_box ();
+}
+
+/* See tui-layout.h. */
+
+tui_layout_split *
+tui_layout_split::add_split (int weight)
+{
+ tui_layout_split *result = new tui_layout_split ();
+ split s = {weight, std::unique_ptr<tui_layout_base> (result)};
+ m_splits.push_back (std::move (s));
+ return result;
+}
+
+/* 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 ();
+ 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 (int *min_height, int *max_height)
+{
+ *min_height = 0;
+ *max_height = 0;
+ for (const split &item : m_splits)
+ {
+ int new_min, new_max;
+ item.layout->get_sizes (&new_min, &new_max);
+ *min_height += new_min;
+ *max_height += new_max;
+ }
+}
+
+/* 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 ();
+}
+
+/* See tui-layout.h. */
- locator->resize (1, tui_term_width (),
- 0, src_height - 1);
- win_info->resize (src_height - 1,
- tui_term_width (),
- 0,
- 0);
-
- if (TUI_CMD_WIN == NULL)
- tui_win_list[CMD_WIN] = new tui_cmd_window ();
- TUI_CMD_WIN->resize (cmd_height,
- tui_term_width (),
- 0,
- src_height);
+bool
+tui_layout_split::bottom_boxed_p () const
+{
+ if (m_splits.empty ())
+ return false;
+ return m_splits.back ().layout->top_boxed_p ();
+}
+
+/* See tui-layout.h. */
+
+void
+tui_layout_split::set_weights_from_heights ()
+{
+ for (int i = 0; i < m_splits.size (); ++i)
+ m_splits[i].weight = m_splits[i].layout->height;
+}
+
+/* See tui-layout.h. */
+
+bool
+tui_layout_split::adjust_size (const char *name, int new_height)
+{
+ /* 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)
+ {
+ if (m_splits[i].layout->adjust_size (name, new_height))
+ return true;
+ const char *win_name = m_splits[i].layout->get_name ();
+ if (win_name != nullptr && strcmp (name, win_name) == 0)
+ {
+ found_index = i;
+ break;
+ }
+ }
+
+ if (found_index == -1)
+ return false;
+ if (m_splits[found_index].layout->height == new_height)
+ return true;
+
+ 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 ();
+
+ int new_min, new_max;
+ m_splits[index].layout->get_sizes (&new_min, &new_max);
+
+ 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
+ {
+ /* 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;
+ }
+ }
+
+ 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);
+ }
+
+ return true;
+}
+
+/* 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 height_info
+ {
+ int height;
+ int min_height;
+ int max_height;
+ /* True if this window will share a box border with the previous
+ window in the list. */
+ bool share_box;
+ };
+
+ std::vector<height_info> info (m_splits.size ());
+
+ /* Step 1: Find the min and max height of each sub-layout.
+ Fixed-sized layouts are given their desired height, and then the
+ remaining space is distributed among the remaining windows
+ according to the weights given. */
+ int available_height = height;
+ 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 (&info[i].min_height, &info[i].max_height);
+
+ if (!m_applied
+ && cmd_win_already_exists
+ && m_splits[i].layout->get_name () != nullptr
+ && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
+ {
+ /* If this layout has never been applied, then it means the
+ user just changed the layout. In this situation, it's
+ desirable to keep the size of the command window the
+ same. Setting the min and max heights this way ensures
+ that the resizing step, below, does the right thing with
+ this window. */
+ info[i].min_height = TUI_CMD_WIN->height;
+ info[i].max_height = TUI_CMD_WIN->height;
+ }
+
+ if (info[i].min_height == info[i].max_height)
+ available_height -= info[i].min_height;
+ else
+ {
+ last_index = i;
+ total_weight += m_splits[i].weight;
+ }
+
+ /* Two adjacent boxed windows will share a border, making a bit
+ more height available. */
+ if (i > 0
+ && m_splits[i - 1].layout->bottom_boxed_p ()
+ && m_splits[i].layout->top_boxed_p ())
+ info[i].share_box = true;
+ }
+
+ /* Step 2: Compute the height of each sub-layout. Fixed-sized items
+ are given their fixed size, while others are resized according to
+ their weight. */
+ int used_height = 0;
+ for (int i = 0; i < m_splits.size (); ++i)
+ {
+ /* Compute the height and clamp to the allowable range. */
+ info[i].height = available_height * m_splits[i].weight / total_weight;
+ if (info[i].height > info[i].max_height)
+ info[i].height = info[i].max_height;
+ if (info[i].height < info[i].min_height)
+ info[i].height = info[i].min_height;
+ /* If there is any leftover height, just redistribute it to the
+ last resizeable window, by dropping it from the allocated
+ height. We could try to be fancier here perhaps, by
+ redistributing this height among all windows, not just the
+ last window. */
+ if (info[i].min_height != info[i].max_height)
+ {
+ used_height += info[i].height;
+ if (info[i].share_box)
+ --used_height;
+ }
+ }
+
+ /* Allocate any leftover height. */
+ if (available_height >= used_height && last_index != -1)
+ info[last_index].height += available_height - used_height;
+
+ /* Step 3: Resize. */
+ int height_accum = 0;
+ for (int i = 0; i < m_splits.size (); ++i)
+ {
+ /* If we fall off the bottom, just make allocations overlap.
+ GIGO. */
+ if (height_accum + info[i].height > height)
+ height_accum = height - info[i].height;
+ else if (info[i].share_box)
+ --height_accum;
+ m_splits[i].layout->apply (x, y + height_accum, width, info[i].height);
+ height_accum += info[i].height;
+ }
+
+ m_applied = true;
+}
+
+static void
+initialize_layouts ()
+{
+ standard_layouts[SRC_COMMAND] = new tui_layout_split ();
+ standard_layouts[SRC_COMMAND]->add_window ("src", 2);
+ standard_layouts[SRC_COMMAND]->add_window ("locator", 0);
+ standard_layouts[SRC_COMMAND]->add_window ("cmd", 1);
+
+ standard_layouts[DISASSEM_COMMAND] = new tui_layout_split ();
+ standard_layouts[DISASSEM_COMMAND]->add_window ("asm", 2);
+ standard_layouts[DISASSEM_COMMAND]->add_window ("locator", 0);
+ standard_layouts[DISASSEM_COMMAND]->add_window ("cmd", 1);
+
+ standard_layouts[SRC_DATA_COMMAND] = new tui_layout_split ();
+ standard_layouts[SRC_DATA_COMMAND]->add_window ("regs", 1);
+ standard_layouts[SRC_DATA_COMMAND]->add_window ("src", 1);
+ standard_layouts[SRC_DATA_COMMAND]->add_window ("locator", 0);
+ standard_layouts[SRC_DATA_COMMAND]->add_window ("cmd", 1);
+
+ standard_layouts[DISASSEM_DATA_COMMAND] = new tui_layout_split ();
+ standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("regs", 1);
+ standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("asm", 1);
+ standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("locator", 0);
+ standard_layouts[DISASSEM_DATA_COMMAND]->add_window ("cmd", 1);
+
+ standard_layouts[SRC_DISASSEM_COMMAND] = new tui_layout_split ();
+ standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("src", 1);
+ standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("asm", 1);
+ standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("locator", 0);
+ standard_layouts[SRC_DISASSEM_COMMAND]->add_window ("cmd", 1);
}
\f
the register window is displayed with \n\
the window that has current logical focus."));
set_cmd_completer (cmd, layout_completer);
+
+ initialize_layouts ();
}