diff options
Diffstat (limited to 'gdb/tui')
-rw-r--r-- | gdb/tui/tui-layout.c | 360 | ||||
-rw-r--r-- | gdb/tui/tui-layout.h | 146 |
2 files changed, 506 insertions, 0 deletions
diff --git a/gdb/tui/tui-layout.c b/gdb/tui/tui-layout.c index 0a7812a..91f270d 100644 --- a/gdb/tui/tui-layout.c +++ b/gdb/tui/tui-layout.c @@ -543,6 +543,366 @@ show_source_or_disasm_and_command (enum tui_layout_type layout_type) +/* Helper function that returns a TUI window, given its name. */ + +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 (); + return tui_win_list[SRC_WIN]; + } + 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 (); + 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. */ + +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; +} + + + /* Function to initialize gdb commands, for tui window layout manipulation. */ diff --git a/gdb/tui/tui-layout.h b/gdb/tui/tui-layout.h index 23f05f3..d7f0731 100644 --- a/gdb/tui/tui-layout.h +++ b/gdb/tui/tui-layout.h @@ -25,6 +25,152 @@ #include "tui/tui.h" #include "tui/tui-data.h" +/* The basic object in a TUI layout. This represents a single piece + of screen real estate. Subclasses determine the exact + behavior. */ +class tui_layout_base +{ +public: + + DISABLE_COPY_AND_ASSIGN (tui_layout_base); + + /* Clone this object. Ordinarily a layout is cloned before it is + used, so that any necessary modifications do not affect the + "skeleton" layout. */ + virtual std::unique_ptr<tui_layout_base> clone () const = 0; + + /* Change the size and location of this layout. */ + virtual void apply (int x, int y, int width, int height) = 0; + + /* Return the minimum and maximum height of this layout. */ + virtual void get_sizes (int *min_height, int *max_height) = 0; + + /* True if the topmost item in this layout is boxed. */ + virtual bool top_boxed_p () const = 0; + + /* True if the bottommost item in this layout is boxed. */ + virtual bool bottom_boxed_p () const = 0; + + /* Return the name of this layout's window, or nullptr if this + layout does not represent a single window. */ + virtual const char *get_name () const + { + return nullptr; + } + + /* Adjust the size of the window named NAME to NEW_HEIGHT, updating + the sizes of the other windows around it. */ + virtual bool adjust_size (const char *name, int new_height) = 0; + + /* The most recent space allocation. */ + int x = 0; + int y = 0; + int width = 0; + int height = 0; + +protected: + + tui_layout_base () = default; +}; + +/* A TUI layout object that displays a single window. The window is + given by name. */ +class tui_layout_window : public tui_layout_base +{ +public: + + explicit tui_layout_window (const char *name) + : m_contents (name) + { + } + + DISABLE_COPY_AND_ASSIGN (tui_layout_window); + + std::unique_ptr<tui_layout_base> clone () const override; + + void apply (int x, int y, int width, int height) override; + + const char *get_name () const override + { + return m_contents.c_str (); + } + + bool adjust_size (const char *name, int new_height) override + { + return false; + } + + bool top_boxed_p () const override; + + bool bottom_boxed_p () const override; + +protected: + + void get_sizes (int *min_height, int *max_height) override; + +private: + + /* Type of content to display. */ + std::string m_contents; + + /* When a layout is applied, this is updated to point to the window + object. */ + tui_gen_win_info *m_window = nullptr; +}; + +/* A TUI layout that holds other layouts. */ +class tui_layout_split : public tui_layout_base +{ +public: + + tui_layout_split () = default; + + DISABLE_COPY_AND_ASSIGN (tui_layout_split); + + /* Add a new split layout to this layout. WEIGHT is the desired + size, which is relative to the other weights given in this + layout. */ + tui_layout_split *add_split (int weight); + + /* Add a new window to this layout. NAME is the name of the window + to add. WEIGHT is the desired size, which is relative to the + other weights given in this layout. */ + void add_window (const char *name, int weight); + + std::unique_ptr<tui_layout_base> clone () const override; + + void apply (int x, int y, int width, int height) override; + + bool adjust_size (const char *name, int new_height) override; + + bool top_boxed_p () const override; + + bool bottom_boxed_p () const override; + +protected: + + void get_sizes (int *min_height, int *max_height) override; + +private: + + /* Set the weights from the current heights. */ + void set_weights_from_heights (); + + struct split + { + /* The requested weight. */ + int weight; + /* The layout. */ + std::unique_ptr<tui_layout_base> layout; + }; + + /* The splits. */ + std::vector<split> m_splits; + + /* True if this layout has already been applied at least once. */ + bool m_applied = false; +}; + extern void tui_add_win_to_layout (enum tui_win_type); extern void tui_set_layout (enum tui_layout_type); |