Home | History | Annotate | Line # | Download | only in tui
tui-layout.c revision 1.10
      1 /* TUI layout window management.
      2 
      3    Copyright (C) 1998-2023 Free Software Foundation, Inc.
      4 
      5    Contributed by Hewlett-Packard Company.
      6 
      7    This file is part of GDB.
      8 
      9    This program is free software; you can redistribute it and/or modify
     10    it under the terms of the GNU General Public License as published by
     11    the Free Software Foundation; either version 3 of the License, or
     12    (at your option) any later version.
     13 
     14    This program is distributed in the hope that it will be useful,
     15    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17    GNU General Public License for more details.
     18 
     19    You should have received a copy of the GNU General Public License
     20    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     21 
     22 #include "defs.h"
     23 #include "arch-utils.h"
     24 #include "command.h"
     25 #include "symtab.h"
     26 #include "frame.h"
     27 #include "source.h"
     28 #include "cli/cli-cmds.h"
     29 #include "cli/cli-decode.h"
     30 #include "cli/cli-utils.h"
     31 #include <ctype.h>
     32 #include <unordered_map>
     33 #include <unordered_set>
     34 
     35 #include "tui/tui.h"
     36 #include "tui/tui-command.h"
     37 #include "tui/tui-data.h"
     38 #include "tui/tui-wingeneral.h"
     39 #include "tui/tui-stack.h"
     40 #include "tui/tui-regs.h"
     41 #include "tui/tui-win.h"
     42 #include "tui/tui-winsource.h"
     43 #include "tui/tui-disasm.h"
     44 #include "tui/tui-layout.h"
     45 #include "tui/tui-source.h"
     46 #include "gdb_curses.h"
     47 #include "safe-ctype.h"
     48 
     49 static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
     50 
     51 /* The layouts.  */
     52 static std::vector<std::unique_ptr<tui_layout_split>> layouts;
     53 
     54 /* The layout that is currently applied.  */
     55 static std::unique_ptr<tui_layout_base> applied_layout;
     56 
     57 /* The "skeleton" version of the layout that is currently applied.  */
     58 static tui_layout_split *applied_skeleton;
     59 
     60 /* The two special "regs" layouts.  Note that these aren't registered
     61    as commands and so can never be deleted.  */
     62 static tui_layout_split *src_regs_layout;
     63 static tui_layout_split *asm_regs_layout;
     64 
     65 /* See tui-data.h.  */
     66 std::vector<tui_win_info *> tui_windows;
     67 
     68 /* See tui-layout.h.  */
     69 
     70 void
     71 tui_apply_current_layout (bool preserve_cmd_win_size_p)
     72 {
     73   struct gdbarch *gdbarch;
     74   CORE_ADDR addr;
     75 
     76   extract_display_start_addr (&gdbarch, &addr);
     77 
     78   for (tui_win_info *win_info : tui_windows)
     79     win_info->make_visible (false);
     80 
     81   applied_layout->apply (0, 0, tui_term_width (), tui_term_height (),
     82 			 preserve_cmd_win_size_p);
     83 
     84   /* Keep the list of internal windows up-to-date.  */
     85   for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
     86     if (tui_win_list[win_type] != nullptr
     87 	&& !tui_win_list[win_type]->is_visible ())
     88       tui_win_list[win_type] = nullptr;
     89 
     90   /* This should always be made visible by a layout.  */
     91   gdb_assert (TUI_CMD_WIN != nullptr);
     92   gdb_assert (TUI_CMD_WIN->is_visible ());
     93 
     94   /* Get the new list of currently visible windows.  */
     95   std::vector<tui_win_info *> new_tui_windows;
     96   applied_layout->get_windows (&new_tui_windows);
     97 
     98   /* Now delete any window that was not re-applied.  */
     99   tui_win_info *focus = tui_win_with_focus ();
    100   for (tui_win_info *win_info : tui_windows)
    101     {
    102       if (!win_info->is_visible ())
    103 	{
    104 	  if (focus == win_info)
    105 	    tui_set_win_focus_to (new_tui_windows[0]);
    106 	  delete win_info;
    107 	}
    108     }
    109 
    110   /* Replace the global list of active windows.  */
    111   tui_windows = std::move (new_tui_windows);
    112 
    113   if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
    114     tui_get_begin_asm_address (&gdbarch, &addr);
    115   tui_update_source_windows_with_addr (gdbarch, addr);
    116 }
    117 
    118 /* See tui-layout.  */
    119 
    120 void
    121 tui_adjust_window_height (struct tui_win_info *win, int new_height)
    122 {
    123   applied_layout->set_height (win->name (), new_height);
    124 }
    125 
    126 /* See tui-layout.  */
    127 
    128 void
    129 tui_adjust_window_width (struct tui_win_info *win, int new_width)
    130 {
    131   applied_layout->set_width (win->name (), new_width);
    132 }
    133 
    134 /* Set the current layout to LAYOUT.  */
    135 
    136 static void
    137 tui_set_layout (tui_layout_split *layout)
    138 {
    139   std::string old_fingerprint;
    140   if (applied_layout != nullptr)
    141     old_fingerprint = applied_layout->layout_fingerprint ();
    142 
    143   applied_skeleton = layout;
    144   applied_layout = layout->clone ();
    145 
    146   std::string new_fingerprint = applied_layout->layout_fingerprint ();
    147   bool preserve_command_window_size
    148     = (TUI_CMD_WIN != nullptr && old_fingerprint == new_fingerprint);
    149 
    150   tui_apply_current_layout (preserve_command_window_size);
    151 }
    152 
    153 /* See tui-layout.h.  */
    154 
    155 void
    156 tui_add_win_to_layout (enum tui_win_type type)
    157 {
    158   gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
    159 
    160   /* If the window already exists, no need to add it.  */
    161   if (tui_win_list[type] != nullptr)
    162     return;
    163 
    164   /* If the window we are trying to replace doesn't exist, we're
    165      done.  */
    166   enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
    167   if (tui_win_list[other] == nullptr)
    168     return;
    169 
    170   const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
    171   applied_layout->replace_window (tui_win_list[other]->name (), name);
    172   tui_apply_current_layout (true);
    173 }
    174 
    175 /* Find LAYOUT in the "layouts" global and return its index.  */
    176 
    177 static size_t
    178 find_layout (tui_layout_split *layout)
    179 {
    180   for (size_t i = 0; i < layouts.size (); ++i)
    181     {
    182       if (layout == layouts[i].get ())
    183 	return i;
    184     }
    185   gdb_assert_not_reached ("layout not found!?");
    186 }
    187 
    188 /* Function to set the layout. */
    189 
    190 static void
    191 tui_apply_layout (const char *args, int from_tty, cmd_list_element *command)
    192 {
    193   tui_layout_split *layout = (tui_layout_split *) command->context ();
    194 
    195   /* Make sure the curses mode is enabled.  */
    196   tui_enable ();
    197   tui_set_layout (layout);
    198 }
    199 
    200 /* See tui-layout.h.  */
    201 
    202 void
    203 tui_next_layout ()
    204 {
    205   size_t index = find_layout (applied_skeleton);
    206   ++index;
    207   if (index == layouts.size ())
    208     index = 0;
    209   tui_set_layout (layouts[index].get ());
    210 }
    211 
    212 /* Implement the "layout next" command.  */
    213 
    214 static void
    215 tui_next_layout_command (const char *arg, int from_tty)
    216 {
    217   tui_enable ();
    218   tui_next_layout ();
    219 }
    220 
    221 /* See tui-layout.h.  */
    222 
    223 void
    224 tui_set_initial_layout ()
    225 {
    226   tui_set_layout (layouts[0].get ());
    227 }
    228 
    229 /* Implement the "layout prev" command.  */
    230 
    231 static void
    232 tui_prev_layout_command (const char *arg, int from_tty)
    233 {
    234   tui_enable ();
    235   size_t index = find_layout (applied_skeleton);
    236   if (index == 0)
    237     index = layouts.size ();
    238   --index;
    239   tui_set_layout (layouts[index].get ());
    240 }
    241 
    242 
    243 /* See tui-layout.h.  */
    244 
    245 void
    246 tui_regs_layout ()
    247 {
    248   /* If there's already a register window, we're done.  */
    249   if (TUI_DATA_WIN != nullptr)
    250     return;
    251 
    252   tui_set_layout (TUI_DISASM_WIN != nullptr
    253 		  ? asm_regs_layout
    254 		  : src_regs_layout);
    255 }
    256 
    257 /* Implement the "layout regs" command.  */
    258 
    259 static void
    260 tui_regs_layout_command (const char *arg, int from_tty)
    261 {
    262   tui_enable ();
    263   tui_regs_layout ();
    264 }
    265 
    266 /* See tui-layout.h.  */
    267 
    268 void
    269 tui_remove_some_windows ()
    270 {
    271   tui_win_info *focus = tui_win_with_focus ();
    272 
    273   if (strcmp (focus->name (), CMD_NAME) == 0)
    274     {
    275       /* Try leaving the source or disassembly window.  If neither
    276 	 exists, just do nothing.  */
    277       focus = TUI_SRC_WIN;
    278       if (focus == nullptr)
    279 	focus = TUI_DISASM_WIN;
    280       if (focus == nullptr)
    281 	return;
    282     }
    283 
    284   applied_layout->remove_windows (focus->name ());
    285   tui_apply_current_layout (true);
    286 }
    287 
    288 static void
    289 extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
    290 {
    291   if (TUI_SRC_WIN != nullptr)
    292     TUI_SRC_WIN->display_start_addr (gdbarch_p, addr_p);
    293   else if (TUI_DISASM_WIN != nullptr)
    294     TUI_DISASM_WIN->display_start_addr (gdbarch_p, addr_p);
    295   else
    296     {
    297       *gdbarch_p = nullptr;
    298       *addr_p = 0;
    299     }
    300 }
    301 
    302 void
    303 tui_win_info::resize (int height_, int width_,
    304 		      int origin_x_, int origin_y_)
    305 {
    306   if (width == width_ && height == height_
    307       && x == origin_x_ && y == origin_y_
    308       && handle != nullptr)
    309     return;
    310 
    311   width = width_;
    312   height = height_;
    313   x = origin_x_;
    314   y = origin_y_;
    315 
    316   if (handle != nullptr)
    317     {
    318 #ifdef HAVE_WRESIZE
    319       wresize (handle.get (), height, width);
    320       mvwin (handle.get (), y, x);
    321       wmove (handle.get (), 0, 0);
    322 #else
    323       handle.reset (nullptr);
    324 #endif
    325     }
    326 
    327   if (handle == nullptr)
    328     make_window ();
    329 
    330   rerender ();
    331 }
    332 
    333 
    334 
    336 /* Helper function to create one of the built-in (non-locator)
    337    windows.  */
    338 
    339 template<enum tui_win_type V, class T>
    340 static tui_win_info *
    341 make_standard_window (const char *)
    342 {
    343   if (tui_win_list[V] == nullptr)
    344     tui_win_list[V] = new T ();
    345   return tui_win_list[V];
    346 }
    347 
    348 /* A map holding all the known window types, keyed by name.  Note that
    349    this is heap-allocated and "leaked" at gdb exit.  This avoids
    350    ordering issues with destroying elements in the map at shutdown.
    351    In particular, destroying this map can occur after Python has been
    352    shut down, causing crashes if any window destruction requires
    353    running Python code.  */
    354 
    355 static std::unordered_map<std::string, window_factory> *known_window_types;
    356 
    357 /* Helper function that returns a TUI window, given its name.  */
    358 
    359 static tui_win_info *
    360 tui_get_window_by_name (const std::string &name)
    361 {
    362   for (tui_win_info *window : tui_windows)
    363     if (name == window->name ())
    364       return window;
    365 
    366   auto iter = known_window_types->find (name);
    367   if (iter == known_window_types->end ())
    368     error (_("Unknown window type \"%s\""), name.c_str ());
    369 
    370   tui_win_info *result = iter->second (name.c_str ());
    371   if (result == nullptr)
    372     error (_("Could not create window \"%s\""), name.c_str ());
    373   return result;
    374 }
    375 
    376 /* Initialize the known window types.  */
    377 
    378 static void
    379 initialize_known_windows ()
    380 {
    381   known_window_types = new std::unordered_map<std::string, window_factory>;
    382 
    383   known_window_types->emplace (SRC_NAME,
    384 			       make_standard_window<SRC_WIN,
    385 						    tui_source_window>);
    386   known_window_types->emplace (CMD_NAME,
    387 			       make_standard_window<CMD_WIN, tui_cmd_window>);
    388   known_window_types->emplace (DATA_NAME,
    389 			       make_standard_window<DATA_WIN,
    390 						    tui_data_window>);
    391   known_window_types->emplace (DISASSEM_NAME,
    392 			       make_standard_window<DISASSEM_WIN,
    393 						    tui_disasm_window>);
    394   known_window_types->emplace (STATUS_NAME,
    395 			       make_standard_window<STATUS_WIN,
    396 						    tui_locator_window>);
    397 }
    398 
    399 /* See tui-layout.h.  */
    400 
    401 void
    402 tui_register_window (const char *name, window_factory &&factory)
    403 {
    404   std::string name_copy = name;
    405 
    406   if (name_copy == SRC_NAME || name_copy == CMD_NAME || name_copy == DATA_NAME
    407       || name_copy == DISASSEM_NAME || name_copy == STATUS_NAME)
    408     error (_("Window type \"%s\" is built-in"), name);
    409 
    410   for (const char &c : name_copy)
    411     {
    412       if (ISSPACE (c))
    413 	error (_("invalid whitespace character in window name"));
    414 
    415       if (!ISALNUM (c) && strchr ("-_.", c) == nullptr)
    416 	error (_("invalid character '%c' in window name"), c);
    417     }
    418 
    419   if (!ISALPHA (name_copy[0]))
    420     error (_("window name must start with a letter, not '%c'"), name_copy[0]);
    421 
    422   known_window_types->emplace (std::move (name_copy),
    423 			       std::move (factory));
    424 }
    425 
    426 /* See tui-layout.h.  */
    427 
    428 std::unique_ptr<tui_layout_base>
    429 tui_layout_window::clone () const
    430 {
    431   tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
    432   return std::unique_ptr<tui_layout_base> (result);
    433 }
    434 
    435 /* See tui-layout.h.  */
    436 
    437 void
    438 tui_layout_window::apply (int x_, int y_, int width_, int height_,
    439 			  bool preserve_cmd_win_size_p)
    440 {
    441   x = x_;
    442   y = y_;
    443   width = width_;
    444   height = height_;
    445   gdb_assert (m_window != nullptr);
    446   m_window->resize (height, width, x, y);
    447 }
    448 
    449 /* See tui-layout.h.  */
    450 
    451 void
    452 tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
    453 {
    454   TUI_SCOPED_DEBUG_ENTER_EXIT;
    455 
    456   if (m_window == nullptr)
    457     m_window = tui_get_window_by_name (m_contents);
    458 
    459   tui_debug_printf ("window = %s, getting %s",
    460 		    m_window->name (), (height ? "height" : "width"));
    461 
    462   if (height)
    463     {
    464       *min_value = m_window->min_height ();
    465       *max_value = m_window->max_height ();
    466     }
    467   else
    468     {
    469       *min_value = m_window->min_width ();
    470       *max_value = m_window->max_width ();
    471     }
    472 
    473   tui_debug_printf ("min = %d, max = %d", *min_value, *max_value);
    474 }
    475 
    476 /* See tui-layout.h.  */
    477 
    478 bool
    479 tui_layout_window::first_edge_has_border_p () const
    480 {
    481   gdb_assert (m_window != nullptr);
    482   return m_window->can_box ();
    483 }
    484 
    485 /* See tui-layout.h.  */
    486 
    487 bool
    488 tui_layout_window::last_edge_has_border_p () const
    489 {
    490   gdb_assert (m_window != nullptr);
    491   return m_window->can_box ();
    492 }
    493 
    494 /* See tui-layout.h.  */
    495 
    496 void
    497 tui_layout_window::replace_window (const char *name, const char *new_window)
    498 {
    499   if (m_contents == name)
    500     {
    501       m_contents = new_window;
    502       if (m_window != nullptr)
    503 	{
    504 	  m_window->make_visible (false);
    505 	  m_window = tui_get_window_by_name (m_contents);
    506 	}
    507     }
    508 }
    509 
    510 /* See tui-layout.h.  */
    511 
    512 void
    513 tui_layout_window::specification (ui_file *output, int depth)
    514 {
    515   gdb_puts (get_name (), output);
    516 }
    517 
    518 /* See tui-layout.h.  */
    519 
    520 std::string
    521 tui_layout_window::layout_fingerprint () const
    522 {
    523   if (strcmp (get_name (), "cmd") == 0)
    524     return "C";
    525   else
    526     return "";
    527 }
    528 
    529 /* See tui-layout.h.  */
    530 
    531 void
    532 tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
    533 			     int weight)
    534 {
    535   split s = {weight, std::move (layout)};
    536   m_splits.push_back (std::move (s));
    537 }
    538 
    539 /* See tui-layout.h.  */
    540 
    541 void
    542 tui_layout_split::add_window (const char *name, int weight)
    543 {
    544   tui_layout_window *result = new tui_layout_window (name);
    545   split s = {weight, std::unique_ptr<tui_layout_base> (result)};
    546   m_splits.push_back (std::move (s));
    547 }
    548 
    549 /* See tui-layout.h.  */
    550 
    551 std::unique_ptr<tui_layout_base>
    552 tui_layout_split::clone () const
    553 {
    554   tui_layout_split *result = new tui_layout_split (m_vertical);
    555   for (const split &item : m_splits)
    556     {
    557       std::unique_ptr<tui_layout_base> next = item.layout->clone ();
    558       split s = {item.weight, std::move (next)};
    559       result->m_splits.push_back (std::move (s));
    560     }
    561   return std::unique_ptr<tui_layout_base> (result);
    562 }
    563 
    564 /* See tui-layout.h.  */
    565 
    566 void
    567 tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
    568 {
    569   TUI_SCOPED_DEBUG_ENTER_EXIT;
    570 
    571   *min_value = 0;
    572   *max_value = 0;
    573   bool first_time = true;
    574   for (const split &item : m_splits)
    575     {
    576       int new_min, new_max;
    577       item.layout->get_sizes (height, &new_min, &new_max);
    578       /* For the mismatch case, the first time through we want to set
    579 	 the min and max to the computed values -- the "first_time"
    580 	 check here is just a funny way of doing that.  */
    581       if (height == m_vertical || first_time)
    582 	{
    583 	  *min_value += new_min;
    584 	  *max_value += new_max;
    585 	}
    586       else
    587 	{
    588 	  *min_value = std::max (*min_value, new_min);
    589 	  *max_value = std::min (*max_value, new_max);
    590 	}
    591       first_time = false;
    592     }
    593 
    594   tui_debug_printf ("min_value = %d, max_value = %d", *min_value, *max_value);
    595 }
    596 
    597 /* See tui-layout.h.  */
    598 
    599 bool
    600 tui_layout_split::first_edge_has_border_p () const
    601 {
    602   if (m_splits.empty ())
    603     return false;
    604   return m_splits[0].layout->first_edge_has_border_p ();
    605 }
    606 
    607 /* See tui-layout.h.  */
    608 
    609 bool
    610 tui_layout_split::last_edge_has_border_p () const
    611 {
    612   if (m_splits.empty ())
    613     return false;
    614   return m_splits.back ().layout->last_edge_has_border_p ();
    615 }
    616 
    617 /* See tui-layout.h.  */
    618 
    619 void
    620 tui_layout_split::set_weights_from_sizes ()
    621 {
    622   for (int i = 0; i < m_splits.size (); ++i)
    623     m_splits[i].weight
    624       = m_vertical ? m_splits[i].layout->height : m_splits[i].layout->width;
    625 }
    626 
    627 /* See tui-layout.h.  */
    628 
    629 std::string
    630 tui_layout_split::tui_debug_weights_to_string () const
    631 {
    632   std::string str;
    633 
    634   for (int i = 0; i < m_splits.size (); ++i)
    635     {
    636       if (i > 0)
    637        str += ", ";
    638       str += string_printf ("[%d] %d", i, m_splits[i].weight);
    639     }
    640 
    641   return str;
    642 }
    643 
    644 /* See tui-layout.h.  */
    645 
    646 void
    647 tui_layout_split::tui_debug_print_size_info
    648   (const std::vector<tui_layout_split::size_info> &info)
    649 {
    650   gdb_assert (debug_tui);
    651 
    652   tui_debug_printf ("current size info data:");
    653   for (int i = 0; i < info.size (); ++i)
    654     tui_debug_printf ("  [%d] { size = %d, min = %d, max = %d, share_box = %d }",
    655 		      i, info[i].size, info[i].min_size,
    656 		      info[i].max_size, info[i].share_box);
    657 }
    658 
    659 /* See tui-layout.h.  */
    660 
    661 tui_adjust_result
    662 tui_layout_split::set_size (const char *name, int new_size, bool set_width_p)
    663 {
    664   TUI_SCOPED_DEBUG_ENTER_EXIT;
    665 
    666   tui_debug_printf ("this = %p, name = %s, new_size = %d",
    667 		    this, name, new_size);
    668 
    669   /* Look through the children.  If one is a layout holding the named
    670      window, we're done; or if one actually is the named window,
    671      update it.  */
    672   int found_index = -1;
    673   for (int i = 0; i < m_splits.size (); ++i)
    674     {
    675       tui_adjust_result adjusted;
    676       if (set_width_p)
    677 	adjusted = m_splits[i].layout->set_width (name, new_size);
    678       else
    679 	adjusted = m_splits[i].layout->set_height (name, new_size);
    680       if (adjusted == HANDLED)
    681 	return HANDLED;
    682       if (adjusted == FOUND)
    683 	{
    684 	  if (set_width_p ? m_vertical : !m_vertical)
    685 	    return FOUND;
    686 	  found_index = i;
    687 	  break;
    688 	}
    689     }
    690 
    691   if (found_index == -1)
    692     return NOT_FOUND;
    693   int curr_size = (set_width_p
    694 		   ? m_splits[found_index].layout->width
    695 		   : m_splits[found_index].layout->height);
    696   if (curr_size == new_size)
    697     return HANDLED;
    698 
    699   tui_debug_printf ("found window %s at index %d", name, found_index);
    700 
    701   set_weights_from_sizes ();
    702   int delta = m_splits[found_index].weight - new_size;
    703   m_splits[found_index].weight = new_size;
    704 
    705   tui_debug_printf ("before delta (%d) distribution, weights: %s",
    706 		    delta, tui_debug_weights_to_string ().c_str ());
    707 
    708   /* Distribute the "delta" over all other windows, while respecting their
    709      min/max sizes.  We grow each window by 1 line at a time continually
    710      looping over all the windows.  However, skip the window that the user
    711      just resized, obviously we don't want to readjust that window.  */
    712   bool found_window_that_can_grow_p = true;
    713   for (int i = 0; delta != 0; i = (i + 1) % m_splits.size ())
    714     {
    715       int index = (found_index + 1 + i) % m_splits.size ();
    716       if (index == found_index)
    717 	{
    718 	  if (!found_window_that_can_grow_p)
    719 	    break;
    720 	  found_window_that_can_grow_p = false;
    721 	  continue;
    722 	}
    723 
    724       int new_min, new_max;
    725       m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
    726 
    727       if (delta < 0)
    728 	{
    729 	  /* The primary window grew, so we are trying to shrink other
    730 	     windows.  */
    731 	  if (m_splits[index].weight > new_min)
    732 	    {
    733 	      m_splits[index].weight -= 1;
    734 	      delta += 1;
    735 	      found_window_that_can_grow_p = true;
    736 	    }
    737 	}
    738       else
    739 	{
    740 	  /* The primary window shrank, so we are trying to grow other
    741 	     windows.  */
    742 	  if (m_splits[index].weight < new_max)
    743 	    {
    744 	      m_splits[index].weight += 1;
    745 	      delta -= 1;
    746 	      found_window_that_can_grow_p = true;
    747 	    }
    748 	}
    749 
    750       tui_debug_printf ("index = %d, weight now: %d",
    751 			index, m_splits[index].weight);
    752     }
    753 
    754   tui_debug_printf ("after delta (%d) distribution, weights: %s",
    755 		    delta, tui_debug_weights_to_string ().c_str ());
    756 
    757   if (delta != 0)
    758     {
    759       if (set_width_p)
    760 	warning (_("Invalid window width specified"));
    761       else
    762 	warning (_("Invalid window height specified"));
    763       /* Effectively undo any modifications made here.  */
    764       set_weights_from_sizes ();
    765     }
    766   else
    767     {
    768       /* Simply re-apply the updated layout.  We pass false here so that
    769 	 the cmd window can be resized.  However, we should have already
    770 	 resized everything above to be "just right", so the apply call
    771 	 here should not end up changing the sizes at all.  */
    772       apply (x, y, width, height, false);
    773     }
    774 
    775   return HANDLED;
    776 }
    777 
    778 /* See tui-layout.h.  */
    779 
    780 void
    781 tui_layout_split::apply (int x_, int y_, int width_, int height_,
    782 			 bool preserve_cmd_win_size_p)
    783 {
    784   TUI_SCOPED_DEBUG_ENTER_EXIT;
    785 
    786   x = x_;
    787   y = y_;
    788   width = width_;
    789   height = height_;
    790 
    791   /* In some situations we fix the size of the cmd window.  However,
    792      occasionally this turns out to be a mistake.  This struct is used to
    793      hold the original information about the cmd window, so we can restore
    794      it if needed.  */
    795   struct old_size_info
    796   {
    797     /* Constructor.  */
    798     old_size_info (int index_, int min_size_, int max_size_)
    799       : index (index_),
    800 	min_size (min_size_),
    801 	max_size (max_size_)
    802     { /* Nothing.  */ }
    803 
    804     /* The index in m_splits where the cmd window was found.  */
    805     int index;
    806 
    807     /* The previous min/max size.  */
    808     int min_size;
    809     int max_size;
    810   };
    811 
    812   /* This is given a value only if we fix the size of the cmd window.  */
    813   gdb::optional<old_size_info> old_cmd_info;
    814 
    815   std::vector<size_info> info (m_splits.size ());
    816 
    817   tui_debug_printf ("weights are: %s",
    818 		    tui_debug_weights_to_string ().c_str ());
    819 
    820   /* Step 1: Find the min and max size of each sub-layout.
    821      Fixed-sized layouts are given their desired size, and then the
    822      remaining space is distributed among the remaining windows
    823      according to the weights given.  */
    824   int available_size = m_vertical ? height : width;
    825   int last_index = -1;
    826   int total_weight = 0;
    827   for (int i = 0; i < m_splits.size (); ++i)
    828     {
    829       bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
    830 
    831       /* Always call get_sizes, to ensure that the window is
    832 	 instantiated.  This is a bit gross but less gross than adding
    833 	 special cases for this in other places.  */
    834       m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
    835 				     &info[i].max_size);
    836 
    837       if (preserve_cmd_win_size_p
    838 	  && cmd_win_already_exists
    839 	  && m_splits[i].layout->get_name () != nullptr
    840 	  && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
    841 	{
    842 	  /* Save the old cmd window information, in case we need to
    843 	     restore it later.  */
    844           old_cmd_info.emplace (i, info[i].min_size, info[i].max_size);
    845 
    846 	  /* If this layout has never been applied, then it means the
    847 	     user just changed the layout.  In this situation, it's
    848 	     desirable to keep the size of the command window the
    849 	     same.  Setting the min and max sizes this way ensures
    850 	     that the resizing step, below, does the right thing with
    851 	     this window.  */
    852 	  info[i].min_size = (m_vertical
    853 			      ? TUI_CMD_WIN->height
    854 			      : TUI_CMD_WIN->width);
    855 	  info[i].max_size = info[i].min_size;
    856 	}
    857 
    858       if (info[i].min_size == info[i].max_size)
    859 	available_size -= info[i].min_size;
    860       else
    861 	{
    862 	  last_index = i;
    863 	  total_weight += m_splits[i].weight;
    864 	}
    865 
    866       /* Two adjacent boxed windows will share a border, making a bit
    867 	 more size available.  */
    868       if (i > 0
    869 	  && m_splits[i - 1].layout->last_edge_has_border_p ()
    870 	  && m_splits[i].layout->first_edge_has_border_p ())
    871 	info[i].share_box = true;
    872     }
    873 
    874   /* If last_index is set then we have a window that is not of a fixed
    875      size.  This window will have its size calculated below, which requires
    876      that the total_weight not be zero (we divide by total_weight, so don't
    877      want a floating-point exception).  */
    878   gdb_assert (last_index == -1 || total_weight > 0);
    879 
    880   /* Step 2: Compute the size of each sub-layout.  Fixed-sized items
    881      are given their fixed size, while others are resized according to
    882      their weight.  */
    883   int used_size = 0;
    884   for (int i = 0; i < m_splits.size (); ++i)
    885     {
    886       if (info[i].min_size != info[i].max_size)
    887 	{
    888 	  /* Compute the height and clamp to the allowable range.  */
    889 	  info[i].size = available_size * m_splits[i].weight / total_weight;
    890 	  if (info[i].size > info[i].max_size)
    891 	    info[i].size = info[i].max_size;
    892 	  if (info[i].size < info[i].min_size)
    893 	    info[i].size = info[i].min_size;
    894 	  /* Keep a total of all the size we've used so far (we gain some
    895 	     size back if this window can share a border with a preceding
    896 	     window).  Any unused space will be distributed between all of
    897 	     the other windows (while respecting min/max sizes) later in
    898 	     this function.  */
    899 	  used_size += info[i].size;
    900 	  if (info[i].share_box)
    901 	    --used_size;
    902 	}
    903       else
    904 	info[i].size = info[i].min_size;
    905     }
    906 
    907   if (debug_tui)
    908     {
    909       tui_debug_printf ("after initial size calculation");
    910       tui_debug_printf ("available_size = %d, used_size = %d",
    911 			available_size, used_size);
    912       tui_debug_printf ("total_weight = %d, last_index = %d",
    913 			total_weight, last_index);
    914       tui_debug_print_size_info (info);
    915     }
    916 
    917   /* If we didn't find any sub-layouts that were of a non-fixed size, but
    918      we did find the cmd window, then we can consider that a sort-of
    919      non-fixed size sub-layout.
    920 
    921      The cmd window might, initially, be of a fixed size (see above), but,
    922      we are willing to relax this constraint if required to correctly apply
    923      this layout (see below).  */
    924   if (last_index == -1 && old_cmd_info.has_value ())
    925     last_index = old_cmd_info->index;
    926 
    927   /* Allocate any leftover size.  */
    928   if (available_size != used_size && last_index != -1)
    929     {
    930       /* Loop over all windows until the amount of used space is equal to
    931 	 the amount of available space.  There's an escape hatch within
    932 	 the loop in case we can't find any sub-layouts to resize.  */
    933       bool found_window_that_can_grow_p = true;
    934       for (int idx = last_index;
    935 	   available_size != used_size;
    936 	   idx = (idx + 1) % m_splits.size ())
    937 	{
    938 	  /* Every time we get back to last_index, which is where the loop
    939 	     started, we check to make sure that we did assign some space
    940 	     to a window, bringing used_size closer to available_size.
    941 
    942 	     If we didn't, but the cmd window is of a fixed size, then we
    943 	     can make the console window non-fixed-size, and continue
    944 	     around the loop, hopefully, this will allow the layout to be
    945 	     applied correctly.
    946 
    947 	     If we still make it around the loop without moving used_size
    948 	     closer to available_size, then there's nothing more we can do,
    949 	     and we break out of the loop.  */
    950 	  if (idx == last_index)
    951 	    {
    952 	      /* If the used_size is greater than the available_size then
    953 		 this indicates that the fixed-sized sub-layouts claimed
    954 		 more space than is available.  This layout is not going to
    955 		 work.  Our only hope at this point is to make the cmd
    956 		 window non-fixed-size (if possible), and hope we can
    957 		 shrink this enough to fit the rest of the sub-layouts in.
    958 
    959 	         Alternatively, we've made it around the loop without
    960 	         adjusting any window's size.  This likely means all
    961 	         windows have hit their min or max size.  Again, our only
    962 	         hope is to make the cmd window non-fixed-size, and hope
    963 	         this fixes all our problems.  */
    964 	      if (old_cmd_info.has_value ()
    965 		  && ((available_size < used_size)
    966 		      || !found_window_that_can_grow_p))
    967 		{
    968 		  info[old_cmd_info->index].min_size = old_cmd_info->min_size;
    969 		  info[old_cmd_info->index].max_size = old_cmd_info->max_size;
    970 		  tui_debug_printf
    971 		    ("restoring index %d (cmd) size limits, min = %d, max = %d",
    972 		     old_cmd_info->index, old_cmd_info->min_size,
    973 		     old_cmd_info->max_size);
    974 		  old_cmd_info.reset ();
    975 		}
    976 	      else if (!found_window_that_can_grow_p)
    977 		break;
    978 	      found_window_that_can_grow_p = false;
    979 	    }
    980 
    981 	  if (available_size > used_size
    982 	      && info[idx].size < info[idx].max_size)
    983 	    {
    984 	      found_window_that_can_grow_p = true;
    985 	      info[idx].size += 1;
    986 	      used_size += 1;
    987 	    }
    988 	  else if (available_size < used_size
    989 		   && info[idx].size > info[idx].min_size)
    990 	    {
    991 	      found_window_that_can_grow_p = true;
    992 	      info[idx].size -= 1;
    993 	      used_size -= 1;
    994 	    }
    995 	}
    996 
    997       if (debug_tui)
    998 	{
    999 	  tui_debug_printf ("after final size calculation");
   1000 	  tui_debug_printf ("available_size = %d, used_size = %d",
   1001 			    available_size, used_size);
   1002 	  tui_debug_printf ("total_weight = %d, last_index = %d",
   1003 			    total_weight, last_index);
   1004 	  tui_debug_print_size_info (info);
   1005 	}
   1006     }
   1007 
   1008   /* Step 3: Resize.  */
   1009   int size_accum = 0;
   1010   const int maximum = m_vertical ? height : width;
   1011   for (int i = 0; i < m_splits.size (); ++i)
   1012     {
   1013       /* If we fall off the bottom, just make allocations overlap.
   1014 	 GIGO.  */
   1015       if (size_accum + info[i].size > maximum)
   1016 	size_accum = maximum - info[i].size;
   1017       else if (info[i].share_box)
   1018 	--size_accum;
   1019       if (m_vertical)
   1020 	m_splits[i].layout->apply (x, y + size_accum, width, info[i].size,
   1021 				   preserve_cmd_win_size_p);
   1022       else
   1023 	m_splits[i].layout->apply (x + size_accum, y, info[i].size, height,
   1024 				   preserve_cmd_win_size_p);
   1025       size_accum += info[i].size;
   1026     }
   1027 }
   1028 
   1029 /* See tui-layout.h.  */
   1030 
   1031 void
   1032 tui_layout_split::remove_windows (const char *name)
   1033 {
   1034   for (int i = 0; i < m_splits.size (); ++i)
   1035     {
   1036       const char *this_name = m_splits[i].layout->get_name ();
   1037       if (this_name == nullptr)
   1038 	m_splits[i].layout->remove_windows (name);
   1039       else if (strcmp (this_name, name) == 0
   1040 	       || strcmp (this_name, CMD_NAME) == 0
   1041 	       || strcmp (this_name, STATUS_NAME) == 0)
   1042 	{
   1043 	  /* Keep.  */
   1044 	}
   1045       else
   1046 	{
   1047 	  m_splits.erase (m_splits.begin () + i);
   1048 	  --i;
   1049 	}
   1050     }
   1051 }
   1052 
   1053 /* See tui-layout.h.  */
   1054 
   1055 void
   1056 tui_layout_split::replace_window (const char *name, const char *new_window)
   1057 {
   1058   for (auto &item : m_splits)
   1059     item.layout->replace_window (name, new_window);
   1060 }
   1061 
   1062 /* See tui-layout.h.  */
   1063 
   1064 void
   1065 tui_layout_split::specification (ui_file *output, int depth)
   1066 {
   1067   if (depth > 0)
   1068     gdb_puts ("{", output);
   1069 
   1070   if (!m_vertical)
   1071     gdb_puts ("-horizontal ", output);
   1072 
   1073   bool first = true;
   1074   for (auto &item : m_splits)
   1075     {
   1076       if (!first)
   1077 	gdb_puts (" ", output);
   1078       first = false;
   1079       item.layout->specification (output, depth + 1);
   1080       gdb_printf (output, " %d", item.weight);
   1081     }
   1082 
   1083   if (depth > 0)
   1084     gdb_puts ("}", output);
   1085 }
   1086 
   1087 /* See tui-layout.h.  */
   1088 
   1089 std::string
   1090 tui_layout_split::layout_fingerprint () const
   1091 {
   1092   for (auto &item : m_splits)
   1093     {
   1094       std::string fp = item.layout->layout_fingerprint ();
   1095       if (!fp.empty ())
   1096 	return std::string (m_vertical ? "V" : "H") + fp;
   1097     }
   1098 
   1099   return "";
   1100 }
   1101 
   1102 /* Destroy the layout associated with SELF.  */
   1103 
   1104 static void
   1105 destroy_layout (struct cmd_list_element *self, void *context)
   1106 {
   1107   tui_layout_split *layout = (tui_layout_split *) context;
   1108   size_t index = find_layout (layout);
   1109   layouts.erase (layouts.begin () + index);
   1110 }
   1111 
   1112 /* List holding the sub-commands of "layout".  */
   1113 
   1114 static struct cmd_list_element *layout_list;
   1115 
   1116 /* Called to implement 'tui layout'.  */
   1117 
   1118 static void
   1119 tui_layout_command (const char *args, int from_tty)
   1120 {
   1121   help_list (layout_list, "tui layout ", all_commands, gdb_stdout);
   1122 }
   1123 
   1124 /* Add a "layout" command with name NAME that switches to LAYOUT.  */
   1125 
   1126 static struct cmd_list_element *
   1127 add_layout_command (const char *name, tui_layout_split *layout)
   1128 {
   1129   struct cmd_list_element *cmd;
   1130 
   1131   string_file spec;
   1132   layout->specification (&spec, 0);
   1133 
   1134   gdb::unique_xmalloc_ptr<char> doc
   1135     = xstrprintf (_("Apply the \"%s\" layout.\n\
   1136 This layout was created using:\n\
   1137   tui new-layout %s %s"),
   1138 		  name, name, spec.c_str ());
   1139 
   1140   cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
   1141   cmd->set_context (layout);
   1142   /* There is no API to set this.  */
   1143   cmd->func = tui_apply_layout;
   1144   cmd->destroyer = destroy_layout;
   1145   cmd->doc_allocated = 1;
   1146   doc.release ();
   1147   layouts.emplace_back (layout);
   1148 
   1149   return cmd;
   1150 }
   1151 
   1152 /* Initialize the standard layouts.  */
   1153 
   1154 static void
   1155 initialize_layouts ()
   1156 {
   1157   tui_layout_split *layout;
   1158 
   1159   layout = new tui_layout_split ();
   1160   layout->add_window (SRC_NAME, 2);
   1161   layout->add_window (STATUS_NAME, 0);
   1162   layout->add_window (CMD_NAME, 1);
   1163   add_layout_command (SRC_NAME, layout);
   1164 
   1165   layout = new tui_layout_split ();
   1166   layout->add_window (DISASSEM_NAME, 2);
   1167   layout->add_window (STATUS_NAME, 0);
   1168   layout->add_window (CMD_NAME, 1);
   1169   add_layout_command (DISASSEM_NAME, layout);
   1170 
   1171   layout = new tui_layout_split ();
   1172   layout->add_window (SRC_NAME, 1);
   1173   layout->add_window (DISASSEM_NAME, 1);
   1174   layout->add_window (STATUS_NAME, 0);
   1175   layout->add_window (CMD_NAME, 1);
   1176   add_layout_command ("split", layout);
   1177 
   1178   layout = new tui_layout_split ();
   1179   layout->add_window (DATA_NAME, 1);
   1180   layout->add_window (SRC_NAME, 1);
   1181   layout->add_window (STATUS_NAME, 0);
   1182   layout->add_window (CMD_NAME, 1);
   1183   layouts.emplace_back (layout);
   1184   src_regs_layout = layout;
   1185 
   1186   layout = new tui_layout_split ();
   1187   layout->add_window (DATA_NAME, 1);
   1188   layout->add_window (DISASSEM_NAME, 1);
   1189   layout->add_window (STATUS_NAME, 0);
   1190   layout->add_window (CMD_NAME, 1);
   1191   layouts.emplace_back (layout);
   1192   asm_regs_layout = layout;
   1193 }
   1194 
   1195 
   1196 
   1198 /* A helper function that returns true if NAME is the name of an
   1199    available window.  */
   1200 
   1201 static bool
   1202 validate_window_name (const std::string &name)
   1203 {
   1204   auto iter = known_window_types->find (name);
   1205   return iter != known_window_types->end ();
   1206 }
   1207 
   1208 /* Implementation of the "tui new-layout" command.  */
   1209 
   1210 static void
   1211 tui_new_layout_command (const char *spec, int from_tty)
   1212 {
   1213   std::string new_name = extract_arg (&spec);
   1214   if (new_name.empty ())
   1215     error (_("No layout name specified"));
   1216   if (new_name[0] == '-')
   1217     error (_("Layout name cannot start with '-'"));
   1218 
   1219   bool is_vertical = true;
   1220   spec = skip_spaces (spec);
   1221   if (check_for_argument (&spec, "-horizontal"))
   1222     is_vertical = false;
   1223 
   1224   std::vector<std::unique_ptr<tui_layout_split>> splits;
   1225   splits.emplace_back (new tui_layout_split (is_vertical));
   1226   std::unordered_set<std::string> seen_windows;
   1227   while (true)
   1228     {
   1229       spec = skip_spaces (spec);
   1230       if (spec[0] == '\0')
   1231 	break;
   1232 
   1233       if (spec[0] == '{')
   1234 	{
   1235 	  is_vertical = true;
   1236 	  spec = skip_spaces (spec + 1);
   1237 	  if (check_for_argument (&spec, "-horizontal"))
   1238 	    is_vertical = false;
   1239 	  splits.emplace_back (new tui_layout_split (is_vertical));
   1240 	  continue;
   1241 	}
   1242 
   1243       bool is_close = false;
   1244       std::string name;
   1245       if (spec[0] == '}')
   1246 	{
   1247 	  is_close = true;
   1248 	  ++spec;
   1249 	  if (splits.size () == 1)
   1250 	    error (_("Extra '}' in layout specification"));
   1251 	}
   1252       else
   1253 	{
   1254 	  name = extract_arg (&spec);
   1255 	  if (name.empty ())
   1256 	    break;
   1257 	  if (!validate_window_name (name))
   1258 	    error (_("Unknown window \"%s\""), name.c_str ());
   1259 	  if (seen_windows.find (name) != seen_windows.end ())
   1260 	    error (_("Window \"%s\" seen twice in layout"), name.c_str ());
   1261 	}
   1262 
   1263       ULONGEST weight = get_ulongest (&spec, '}');
   1264       if ((int) weight != weight)
   1265 	error (_("Weight out of range: %s"), pulongest (weight));
   1266       if (is_close)
   1267 	{
   1268 	  std::unique_ptr<tui_layout_split> last_split
   1269 	    = std::move (splits.back ());
   1270 	  splits.pop_back ();
   1271 	  splits.back ()->add_split (std::move (last_split), weight);
   1272 	}
   1273       else
   1274 	{
   1275 	  splits.back ()->add_window (name.c_str (), weight);
   1276 	  seen_windows.insert (name);
   1277 	}
   1278     }
   1279   if (splits.size () > 1)
   1280     error (_("Missing '}' in layout specification"));
   1281   if (seen_windows.empty ())
   1282     error (_("New layout does not contain any windows"));
   1283   if (seen_windows.find (CMD_NAME) == seen_windows.end ())
   1284     error (_("New layout does not contain the \"" CMD_NAME "\" window"));
   1285 
   1286   gdb::unique_xmalloc_ptr<char> cmd_name
   1287     = make_unique_xstrdup (new_name.c_str ());
   1288   std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
   1289   struct cmd_list_element *cmd
   1290     = add_layout_command (cmd_name.get (), new_layout.get ());
   1291   cmd->name_allocated = 1;
   1292   cmd_name.release ();
   1293   new_layout.release ();
   1294 }
   1295 
   1296 /* Function to initialize gdb commands, for tui window layout
   1297    manipulation.  */
   1298 
   1299 void _initialize_tui_layout ();
   1300 void
   1301 _initialize_tui_layout ()
   1302 {
   1303   struct cmd_list_element *layout_cmd
   1304     = add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
   1305 Change the layout of windows.\n\
   1306 Usage: tui layout prev | next | LAYOUT-NAME"),
   1307 		      &layout_list, 0, tui_get_cmd_list ());
   1308   add_com_alias ("layout", layout_cmd, class_tui, 0);
   1309 
   1310   add_cmd ("next", class_tui, tui_next_layout_command,
   1311 	   _("Apply the next TUI layout."),
   1312 	   &layout_list);
   1313   add_cmd ("prev", class_tui, tui_prev_layout_command,
   1314 	   _("Apply the previous TUI layout."),
   1315 	   &layout_list);
   1316   add_cmd ("regs", class_tui, tui_regs_layout_command,
   1317 	   _("Apply the TUI register layout."),
   1318 	   &layout_list);
   1319 
   1320   add_cmd ("new-layout", class_tui, tui_new_layout_command,
   1321 	   _("Create a new TUI layout.\n\
   1322 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
   1323 Create a new TUI layout.  The new layout will be named NAME,\n\
   1324 and can be accessed using \"layout NAME\".\n\
   1325 The windows will be displayed in the specified order.\n\
   1326 A WINDOW can also be of the form:\n\
   1327   { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
   1328 This form indicates a sub-frame.\n\
   1329 Each WEIGHT is an integer, which holds the relative size\n\
   1330 to be allocated to the window."),
   1331 	   tui_get_cmd_list ());
   1332 
   1333   initialize_layouts ();
   1334   initialize_known_windows ();
   1335 }
   1336