Home | History | Annotate | Line # | Download | only in tui
tui-layout.c revision 1.9
      1 /* TUI layout window management.
      2 
      3    Copyright (C) 1998-2020 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 
     48 static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
     49 
     50 /* The layouts.  */
     51 static std::vector<std::unique_ptr<tui_layout_split>> layouts;
     52 
     53 /* The layout that is currently applied.  */
     54 static std::unique_ptr<tui_layout_base> applied_layout;
     55 
     56 /* The "skeleton" version of the layout that is currently applied.  */
     57 static tui_layout_split *applied_skeleton;
     58 
     59 /* The two special "regs" layouts.  Note that these aren't registered
     60    as commands and so can never be deleted.  */
     61 static tui_layout_split *src_regs_layout;
     62 static tui_layout_split *asm_regs_layout;
     63 
     64 /* See tui-data.h.  */
     65 std::vector<tui_win_info *> tui_windows;
     66 
     67 /* When applying a layout, this is the list of all windows that were
     68    in the previous layout.  This is used to re-use windows when
     69    changing a layout.  */
     70 static std::vector<tui_win_info *> saved_tui_windows;
     71 
     72 /* See tui-layout.h.  */
     73 
     74 void
     75 tui_apply_current_layout ()
     76 {
     77   struct gdbarch *gdbarch;
     78   CORE_ADDR addr;
     79 
     80   extract_display_start_addr (&gdbarch, &addr);
     81 
     82   saved_tui_windows = std::move (tui_windows);
     83   tui_windows.clear ();
     84 
     85   for (tui_win_info *win_info : saved_tui_windows)
     86     win_info->make_visible (false);
     87 
     88   applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
     89 
     90   /* Keep the list of internal windows up-to-date.  */
     91   for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
     92     if (tui_win_list[win_type] != nullptr
     93 	&& !tui_win_list[win_type]->is_visible ())
     94       tui_win_list[win_type] = nullptr;
     95 
     96   /* This should always be made visible by a layout.  */
     97   gdb_assert (TUI_CMD_WIN->is_visible ());
     98 
     99   /* Now delete any window that was not re-applied.  */
    100   tui_win_info *focus = tui_win_with_focus ();
    101   for (tui_win_info *win_info : saved_tui_windows)
    102     {
    103       if (!win_info->is_visible ())
    104 	{
    105 	  if (focus == win_info)
    106 	    tui_set_win_focus_to (tui_windows[0]);
    107 	  delete win_info;
    108 	}
    109     }
    110 
    111   if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
    112     tui_get_begin_asm_address (&gdbarch, &addr);
    113   tui_update_source_windows_with_addr (gdbarch, addr);
    114 
    115   saved_tui_windows.clear ();
    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->adjust_size (win->name (), new_height);
    124 }
    125 
    126 /* Set the current layout to LAYOUT.  */
    127 
    128 static void
    129 tui_set_layout (tui_layout_split *layout)
    130 {
    131   applied_skeleton = layout;
    132   applied_layout = layout->clone ();
    133   tui_apply_current_layout ();
    134 }
    135 
    136 /* See tui-layout.h.  */
    137 
    138 void
    139 tui_add_win_to_layout (enum tui_win_type type)
    140 {
    141   gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
    142 
    143   /* If the window already exists, no need to add it.  */
    144   if (tui_win_list[type] != nullptr)
    145     return;
    146 
    147   /* If the window we are trying to replace doesn't exist, we're
    148      done.  */
    149   enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
    150   if (tui_win_list[other] == nullptr)
    151     return;
    152 
    153   const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
    154   applied_layout->replace_window (tui_win_list[other]->name (), name);
    155   tui_apply_current_layout ();
    156 }
    157 
    158 /* Find LAYOUT in the "layouts" global and return its index.  */
    159 
    160 static size_t
    161 find_layout (tui_layout_split *layout)
    162 {
    163   for (size_t i = 0; i < layouts.size (); ++i)
    164     {
    165       if (layout == layouts[i].get ())
    166 	return i;
    167     }
    168   gdb_assert_not_reached (_("layout not found!?"));
    169 }
    170 
    171 /* Function to set the layout. */
    172 
    173 static void
    174 tui_apply_layout (struct cmd_list_element *command,
    175 		  const char *args, int from_tty)
    176 {
    177   tui_layout_split *layout
    178     = (tui_layout_split *) get_cmd_context (command);
    179 
    180   /* Make sure the curses mode is enabled.  */
    181   tui_enable ();
    182   tui_set_layout (layout);
    183 }
    184 
    185 /* See tui-layout.h.  */
    186 
    187 void
    188 tui_next_layout ()
    189 {
    190   size_t index = find_layout (applied_skeleton);
    191   ++index;
    192   if (index == layouts.size ())
    193     index = 0;
    194   tui_set_layout (layouts[index].get ());
    195 }
    196 
    197 /* Implement the "layout next" command.  */
    198 
    199 static void
    200 tui_next_layout_command (const char *arg, int from_tty)
    201 {
    202   tui_enable ();
    203   tui_next_layout ();
    204 }
    205 
    206 /* See tui-layout.h.  */
    207 
    208 void
    209 tui_set_initial_layout ()
    210 {
    211   tui_set_layout (layouts[0].get ());
    212 }
    213 
    214 /* Implement the "layout prev" command.  */
    215 
    216 static void
    217 tui_prev_layout_command (const char *arg, int from_tty)
    218 {
    219   tui_enable ();
    220   size_t index = find_layout (applied_skeleton);
    221   if (index == 0)
    222     index = layouts.size ();
    223   --index;
    224   tui_set_layout (layouts[index].get ());
    225 }
    226 
    227 
    228 /* See tui-layout.h.  */
    229 
    230 void
    231 tui_regs_layout ()
    232 {
    233   /* If there's already a register window, we're done.  */
    234   if (TUI_DATA_WIN != nullptr)
    235     return;
    236 
    237   tui_set_layout (TUI_DISASM_WIN != nullptr
    238 		  ? asm_regs_layout
    239 		  : src_regs_layout);
    240 }
    241 
    242 /* Implement the "layout regs" command.  */
    243 
    244 static void
    245 tui_regs_layout_command (const char *arg, int from_tty)
    246 {
    247   tui_enable ();
    248   tui_regs_layout ();
    249 }
    250 
    251 /* See tui-layout.h.  */
    252 
    253 void
    254 tui_remove_some_windows ()
    255 {
    256   tui_win_info *focus = tui_win_with_focus ();
    257 
    258   if (strcmp (focus->name (), CMD_NAME) == 0)
    259     {
    260       /* Try leaving the source or disassembly window.  If neither
    261 	 exists, just do nothing.  */
    262       focus = TUI_SRC_WIN;
    263       if (focus == nullptr)
    264 	focus = TUI_DISASM_WIN;
    265       if (focus == nullptr)
    266 	return;
    267     }
    268 
    269   applied_layout->remove_windows (focus->name ());
    270   tui_apply_current_layout ();
    271 }
    272 
    273 static void
    274 extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
    275 {
    276   if (TUI_SRC_WIN != nullptr)
    277     TUI_SRC_WIN->display_start_addr (gdbarch_p, addr_p);
    278   else if (TUI_DISASM_WIN != nullptr)
    279     TUI_DISASM_WIN->display_start_addr (gdbarch_p, addr_p);
    280   else
    281     {
    282       *gdbarch_p = nullptr;
    283       *addr_p = 0;
    284     }
    285 }
    286 
    287 void
    288 tui_win_info::resize (int height_, int width_,
    289 		      int origin_x_, int origin_y_)
    290 {
    291   if (width == width_ && height == height_
    292       && x == origin_x_ && y == origin_y_
    293       && handle != nullptr)
    294     return;
    295 
    296   width = width_;
    297   height = height_;
    298   x = origin_x_;
    299   y = origin_y_;
    300 
    301   if (handle != nullptr)
    302     {
    303 #ifdef HAVE_WRESIZE
    304       wresize (handle.get (), height, width);
    305       mvwin (handle.get (), y, x);
    306       wmove (handle.get (), 0, 0);
    307 #else
    308       handle.reset (nullptr);
    309 #endif
    310     }
    311 
    312   if (handle == nullptr)
    313     make_window ();
    314 
    315   rerender ();
    316 }
    317 
    318 
    319 
    321 /* Helper function to create one of the built-in (non-locator)
    322    windows.  */
    323 
    324 template<enum tui_win_type V, class T>
    325 static tui_win_info *
    326 make_standard_window (const char *)
    327 {
    328   if (tui_win_list[V] == nullptr)
    329     tui_win_list[V] = new T ();
    330   return tui_win_list[V];
    331 }
    332 
    333 /* Helper function to wrap tui_locator_win_info_ptr for
    334    tui_get_window_by_name.  */
    335 
    336 static tui_win_info *
    337 get_locator_window (const char *)
    338 {
    339   return tui_locator_win_info_ptr ();
    340 }
    341 
    342 /* A map holding all the known window types, keyed by name.  Note that
    343    this is heap-allocated and "leaked" at gdb exit.  This avoids
    344    ordering issues with destroying elements in the map at shutdown.
    345    In particular, destroying this map can occur after Python has been
    346    shut down, causing crashes if any window destruction requires
    347    running Python code.  */
    348 
    349 static std::unordered_map<std::string, window_factory> *known_window_types;
    350 
    351 /* Helper function that returns a TUI window, given its name.  */
    352 
    353 static tui_win_info *
    354 tui_get_window_by_name (const std::string &name)
    355 {
    356   for (tui_win_info *window : saved_tui_windows)
    357     if (name == window->name ())
    358       return window;
    359 
    360   auto iter = known_window_types->find (name);
    361   if (iter == known_window_types->end ())
    362     error (_("Unknown window type \"%s\""), name.c_str ());
    363 
    364   tui_win_info *result = iter->second (name.c_str ());
    365   if (result == nullptr)
    366     error (_("Could not create window \"%s\""), name.c_str ());
    367   return result;
    368 }
    369 
    370 /* Initialize the known window types.  */
    371 
    372 static void
    373 initialize_known_windows ()
    374 {
    375   known_window_types = new std::unordered_map<std::string, window_factory>;
    376 
    377   known_window_types->emplace (SRC_NAME,
    378 			       make_standard_window<SRC_WIN,
    379 						    tui_source_window>);
    380   known_window_types->emplace (CMD_NAME,
    381 			       make_standard_window<CMD_WIN, tui_cmd_window>);
    382   known_window_types->emplace (DATA_NAME,
    383 			       make_standard_window<DATA_WIN,
    384 						    tui_data_window>);
    385   known_window_types->emplace (DISASSEM_NAME,
    386 			       make_standard_window<DISASSEM_WIN,
    387 						    tui_disasm_window>);
    388   known_window_types->emplace (STATUS_NAME, get_locator_window);
    389 }
    390 
    391 /* See tui-layout.h.  */
    392 
    393 void
    394 tui_register_window (const char *name, window_factory &&factory)
    395 {
    396   std::string name_copy = name;
    397 
    398   if (name_copy == SRC_NAME || name_copy == CMD_NAME || name_copy == DATA_NAME
    399       || name_copy == DISASSEM_NAME || name_copy == STATUS_NAME)
    400     error (_("Window type \"%s\" is built-in"), name);
    401 
    402   known_window_types->emplace (std::move (name_copy),
    403 			       std::move (factory));
    404 }
    405 
    406 /* See tui-layout.h.  */
    407 
    408 std::unique_ptr<tui_layout_base>
    409 tui_layout_window::clone () const
    410 {
    411   tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
    412   return std::unique_ptr<tui_layout_base> (result);
    413 }
    414 
    415 /* See tui-layout.h.  */
    416 
    417 void
    418 tui_layout_window::apply (int x_, int y_, int width_, int height_)
    419 {
    420   x = x_;
    421   y = y_;
    422   width = width_;
    423   height = height_;
    424   gdb_assert (m_window != nullptr);
    425   m_window->resize (height, width, x, y);
    426   tui_windows.push_back (m_window);
    427 }
    428 
    429 /* See tui-layout.h.  */
    430 
    431 void
    432 tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
    433 {
    434   if (m_window == nullptr)
    435     m_window = tui_get_window_by_name (m_contents);
    436   if (height)
    437     {
    438       *min_value = m_window->min_height ();
    439       *max_value = m_window->max_height ();
    440     }
    441   else
    442     {
    443       *min_value = m_window->min_width ();
    444       *max_value = m_window->max_width ();
    445     }
    446 }
    447 
    448 /* See tui-layout.h.  */
    449 
    450 bool
    451 tui_layout_window::top_boxed_p () const
    452 {
    453   gdb_assert (m_window != nullptr);
    454   return m_window->can_box ();
    455 }
    456 
    457 /* See tui-layout.h.  */
    458 
    459 bool
    460 tui_layout_window::bottom_boxed_p () const
    461 {
    462   gdb_assert (m_window != nullptr);
    463   return m_window->can_box ();
    464 }
    465 
    466 /* See tui-layout.h.  */
    467 
    468 void
    469 tui_layout_window::replace_window (const char *name, const char *new_window)
    470 {
    471   if (m_contents == name)
    472     {
    473       m_contents = new_window;
    474       if (m_window != nullptr)
    475 	{
    476 	  m_window->make_visible (false);
    477 	  m_window = tui_get_window_by_name (m_contents);
    478 	}
    479     }
    480 }
    481 
    482 /* See tui-layout.h.  */
    483 
    484 void
    485 tui_layout_window::specification (ui_file *output, int depth)
    486 {
    487   fputs_unfiltered (get_name (), output);
    488 }
    489 
    490 /* See tui-layout.h.  */
    491 
    492 void
    493 tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
    494 			     int weight)
    495 {
    496   split s = {weight, std::move (layout)};
    497   m_splits.push_back (std::move (s));
    498 }
    499 
    500 /* See tui-layout.h.  */
    501 
    502 void
    503 tui_layout_split::add_window (const char *name, int weight)
    504 {
    505   tui_layout_window *result = new tui_layout_window (name);
    506   split s = {weight, std::unique_ptr<tui_layout_base> (result)};
    507   m_splits.push_back (std::move (s));
    508 }
    509 
    510 /* See tui-layout.h.  */
    511 
    512 std::unique_ptr<tui_layout_base>
    513 tui_layout_split::clone () const
    514 {
    515   tui_layout_split *result = new tui_layout_split (m_vertical);
    516   for (const split &item : m_splits)
    517     {
    518       std::unique_ptr<tui_layout_base> next = item.layout->clone ();
    519       split s = {item.weight, std::move (next)};
    520       result->m_splits.push_back (std::move (s));
    521     }
    522   return std::unique_ptr<tui_layout_base> (result);
    523 }
    524 
    525 /* See tui-layout.h.  */
    526 
    527 void
    528 tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
    529 {
    530   *min_value = 0;
    531   *max_value = 0;
    532   bool first_time = true;
    533   for (const split &item : m_splits)
    534     {
    535       int new_min, new_max;
    536       item.layout->get_sizes (height, &new_min, &new_max);
    537       /* For the mismatch case, the first time through we want to set
    538 	 the min and max to the computed values -- the "first_time"
    539 	 check here is just a funny way of doing that.  */
    540       if (height == m_vertical || first_time)
    541 	{
    542 	  *min_value += new_min;
    543 	  *max_value += new_max;
    544 	}
    545       else
    546 	{
    547 	  *min_value = std::max (*min_value, new_min);
    548 	  *max_value = std::min (*max_value, new_max);
    549 	}
    550       first_time = false;
    551     }
    552 }
    553 
    554 /* See tui-layout.h.  */
    555 
    556 bool
    557 tui_layout_split::top_boxed_p () const
    558 {
    559   if (m_splits.empty ())
    560     return false;
    561   return m_splits[0].layout->top_boxed_p ();
    562 }
    563 
    564 /* See tui-layout.h.  */
    565 
    566 bool
    567 tui_layout_split::bottom_boxed_p () const
    568 {
    569   if (m_splits.empty ())
    570     return false;
    571   return m_splits.back ().layout->top_boxed_p ();
    572 }
    573 
    574 /* See tui-layout.h.  */
    575 
    576 void
    577 tui_layout_split::set_weights_from_heights ()
    578 {
    579   for (int i = 0; i < m_splits.size (); ++i)
    580     m_splits[i].weight = m_splits[i].layout->height;
    581 }
    582 
    583 /* See tui-layout.h.  */
    584 
    585 tui_adjust_result
    586 tui_layout_split::adjust_size (const char *name, int new_height)
    587 {
    588   /* Look through the children.  If one is a layout holding the named
    589      window, we're done; or if one actually is the named window,
    590      update it.  */
    591   int found_index = -1;
    592   for (int i = 0; i < m_splits.size (); ++i)
    593     {
    594       tui_adjust_result adjusted
    595 	= m_splits[i].layout->adjust_size (name, new_height);
    596       if (adjusted == HANDLED)
    597 	return HANDLED;
    598       if (adjusted == FOUND)
    599 	{
    600 	  if (!m_vertical)
    601 	    return FOUND;
    602 	  found_index = i;
    603 	  break;
    604 	}
    605     }
    606 
    607   if (found_index == -1)
    608     return NOT_FOUND;
    609   if (m_splits[found_index].layout->height == new_height)
    610     return HANDLED;
    611 
    612   set_weights_from_heights ();
    613   int delta = m_splits[found_index].weight - new_height;
    614   m_splits[found_index].weight = new_height;
    615 
    616   /* Distribute the "delta" over the next window; but if the next
    617      window cannot hold it all, keep going until we either find a
    618      window that does, or until we loop all the way around.  */
    619   for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i)
    620     {
    621       int index = (found_index + 1 + i) % m_splits.size ();
    622 
    623       int new_min, new_max;
    624       m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
    625 
    626       if (delta < 0)
    627 	{
    628 	  /* The primary window grew, so we are trying to shrink other
    629 	     windows.  */
    630 	  int available = m_splits[index].weight - new_min;
    631 	  int shrink_by = std::min (available, -delta);
    632 	  m_splits[index].weight -= shrink_by;
    633 	  delta += shrink_by;
    634 	}
    635       else
    636 	{
    637 	  /* The primary window shrank, so we are trying to grow other
    638 	     windows.  */
    639 	  int available = new_max - m_splits[index].weight;
    640 	  int grow_by = std::min (available, delta);
    641 	  m_splits[index].weight += grow_by;
    642 	  delta -= grow_by;
    643 	}
    644     }
    645 
    646   if (delta != 0)
    647     {
    648       warning (_("Invalid window height specified"));
    649       /* Effectively undo any modifications made here.  */
    650       set_weights_from_heights ();
    651     }
    652   else
    653     {
    654       /* Simply re-apply the updated layout.  */
    655       apply (x, y, width, height);
    656     }
    657 
    658   return HANDLED;
    659 }
    660 
    661 /* See tui-layout.h.  */
    662 
    663 void
    664 tui_layout_split::apply (int x_, int y_, int width_, int height_)
    665 {
    666   x = x_;
    667   y = y_;
    668   width = width_;
    669   height = height_;
    670 
    671   struct size_info
    672   {
    673     int size;
    674     int min_size;
    675     int max_size;
    676     /* True if this window will share a box border with the previous
    677        window in the list.  */
    678     bool share_box;
    679   };
    680 
    681   std::vector<size_info> info (m_splits.size ());
    682 
    683   /* Step 1: Find the min and max size of each sub-layout.
    684      Fixed-sized layouts are given their desired size, and then the
    685      remaining space is distributed among the remaining windows
    686      according to the weights given.  */
    687   int available_size = m_vertical ? height : width;
    688   int last_index = -1;
    689   int total_weight = 0;
    690   for (int i = 0; i < m_splits.size (); ++i)
    691     {
    692       bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
    693 
    694       /* Always call get_sizes, to ensure that the window is
    695 	 instantiated.  This is a bit gross but less gross than adding
    696 	 special cases for this in other places.  */
    697       m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
    698 				     &info[i].max_size);
    699 
    700       if (!m_applied
    701 	  && cmd_win_already_exists
    702 	  && m_splits[i].layout->get_name () != nullptr
    703 	  && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
    704 	{
    705 	  /* If this layout has never been applied, then it means the
    706 	     user just changed the layout.  In this situation, it's
    707 	     desirable to keep the size of the command window the
    708 	     same.  Setting the min and max sizes this way ensures
    709 	     that the resizing step, below, does the right thing with
    710 	     this window.  */
    711 	  info[i].min_size = (m_vertical
    712 			      ? TUI_CMD_WIN->height
    713 			      : TUI_CMD_WIN->width);
    714 	  info[i].max_size = info[i].min_size;
    715 	}
    716 
    717       if (info[i].min_size == info[i].max_size)
    718 	available_size -= info[i].min_size;
    719       else
    720 	{
    721 	  last_index = i;
    722 	  total_weight += m_splits[i].weight;
    723 	}
    724 
    725       /* Two adjacent boxed windows will share a border, making a bit
    726 	 more size available.  */
    727       if (i > 0
    728 	  && m_splits[i - 1].layout->bottom_boxed_p ()
    729 	  && m_splits[i].layout->top_boxed_p ())
    730 	info[i].share_box = true;
    731     }
    732 
    733   /* Step 2: Compute the size of each sub-layout.  Fixed-sized items
    734      are given their fixed size, while others are resized according to
    735      their weight.  */
    736   int used_size = 0;
    737   for (int i = 0; i < m_splits.size (); ++i)
    738     {
    739       /* Compute the height and clamp to the allowable range.  */
    740       info[i].size = available_size * m_splits[i].weight / total_weight;
    741       if (info[i].size > info[i].max_size)
    742 	info[i].size = info[i].max_size;
    743       if (info[i].size < info[i].min_size)
    744 	info[i].size = info[i].min_size;
    745       /* If there is any leftover size, just redistribute it to the
    746 	 last resizeable window, by dropping it from the allocated
    747 	 size.  We could try to be fancier here perhaps, by
    748 	 redistributing this size among all windows, not just the
    749 	 last window.  */
    750       if (info[i].min_size != info[i].max_size)
    751 	{
    752 	  used_size += info[i].size;
    753 	  if (info[i].share_box)
    754 	    --used_size;
    755 	}
    756     }
    757 
    758   /* Allocate any leftover size.  */
    759   if (available_size >= used_size && last_index != -1)
    760     info[last_index].size += available_size - used_size;
    761 
    762   /* Step 3: Resize.  */
    763   int size_accum = 0;
    764   const int maximum = m_vertical ? height : width;
    765   for (int i = 0; i < m_splits.size (); ++i)
    766     {
    767       /* If we fall off the bottom, just make allocations overlap.
    768 	 GIGO.  */
    769       if (size_accum + info[i].size > maximum)
    770 	size_accum = maximum - info[i].size;
    771       else if (info[i].share_box)
    772 	--size_accum;
    773       if (m_vertical)
    774 	m_splits[i].layout->apply (x, y + size_accum, width, info[i].size);
    775       else
    776 	m_splits[i].layout->apply (x + size_accum, y, info[i].size, height);
    777       size_accum += info[i].size;
    778     }
    779 
    780   m_applied = true;
    781 }
    782 
    783 /* See tui-layout.h.  */
    784 
    785 void
    786 tui_layout_split::remove_windows (const char *name)
    787 {
    788   for (int i = 0; i < m_splits.size (); ++i)
    789     {
    790       const char *this_name = m_splits[i].layout->get_name ();
    791       if (this_name == nullptr)
    792 	m_splits[i].layout->remove_windows (name);
    793       else if (strcmp (this_name, name) == 0
    794 	       || strcmp (this_name, CMD_NAME) == 0
    795 	       || strcmp (this_name, STATUS_NAME) == 0)
    796 	{
    797 	  /* Keep.  */
    798 	}
    799       else
    800 	{
    801 	  m_splits.erase (m_splits.begin () + i);
    802 	  --i;
    803 	}
    804     }
    805 }
    806 
    807 /* See tui-layout.h.  */
    808 
    809 void
    810 tui_layout_split::replace_window (const char *name, const char *new_window)
    811 {
    812   for (auto &item : m_splits)
    813     item.layout->replace_window (name, new_window);
    814 }
    815 
    816 /* See tui-layout.h.  */
    817 
    818 void
    819 tui_layout_split::specification (ui_file *output, int depth)
    820 {
    821   if (depth > 0)
    822     fputs_unfiltered ("{", output);
    823 
    824   if (!m_vertical)
    825     fputs_unfiltered ("-horizontal ", output);
    826 
    827   bool first = true;
    828   for (auto &item : m_splits)
    829     {
    830       if (!first)
    831 	fputs_unfiltered (" ", output);
    832       first = false;
    833       item.layout->specification (output, depth + 1);
    834       fprintf_unfiltered (output, " %d", item.weight);
    835     }
    836 
    837   if (depth > 0)
    838     fputs_unfiltered ("}", output);
    839 }
    840 
    841 /* Destroy the layout associated with SELF.  */
    842 
    843 static void
    844 destroy_layout (struct cmd_list_element *self, void *context)
    845 {
    846   tui_layout_split *layout = (tui_layout_split *) context;
    847   size_t index = find_layout (layout);
    848   layouts.erase (layouts.begin () + index);
    849 }
    850 
    851 /* List holding the sub-commands of "layout".  */
    852 
    853 static struct cmd_list_element *layout_list;
    854 
    855 /* Add a "layout" command with name NAME that switches to LAYOUT.  */
    856 
    857 static struct cmd_list_element *
    858 add_layout_command (const char *name, tui_layout_split *layout)
    859 {
    860   struct cmd_list_element *cmd;
    861 
    862   string_file spec;
    863   layout->specification (&spec, 0);
    864 
    865   gdb::unique_xmalloc_ptr<char> doc
    866     (xstrprintf (_("Apply the \"%s\" layout.\n\
    867 This layout was created using:\n\
    868   tui new-layout %s %s"),
    869 		 name, name, spec.c_str ()));
    870 
    871   cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
    872   set_cmd_context (cmd, layout);
    873   /* There is no API to set this.  */
    874   cmd->func = tui_apply_layout;
    875   cmd->destroyer = destroy_layout;
    876   cmd->doc_allocated = 1;
    877   doc.release ();
    878   layouts.emplace_back (layout);
    879 
    880   return cmd;
    881 }
    882 
    883 /* Initialize the standard layouts.  */
    884 
    885 static void
    886 initialize_layouts ()
    887 {
    888   tui_layout_split *layout;
    889 
    890   layout = new tui_layout_split ();
    891   layout->add_window (SRC_NAME, 2);
    892   layout->add_window (STATUS_NAME, 0);
    893   layout->add_window (CMD_NAME, 1);
    894   add_layout_command (SRC_NAME, layout);
    895 
    896   layout = new tui_layout_split ();
    897   layout->add_window (DISASSEM_NAME, 2);
    898   layout->add_window (STATUS_NAME, 0);
    899   layout->add_window (CMD_NAME, 1);
    900   add_layout_command (DISASSEM_NAME, layout);
    901 
    902   layout = new tui_layout_split ();
    903   layout->add_window (SRC_NAME, 1);
    904   layout->add_window (DISASSEM_NAME, 1);
    905   layout->add_window (STATUS_NAME, 0);
    906   layout->add_window (CMD_NAME, 1);
    907   add_layout_command ("split", layout);
    908 
    909   layout = new tui_layout_split ();
    910   layout->add_window (DATA_NAME, 1);
    911   layout->add_window (SRC_NAME, 1);
    912   layout->add_window (STATUS_NAME, 0);
    913   layout->add_window (CMD_NAME, 1);
    914   layouts.emplace_back (layout);
    915   src_regs_layout = layout;
    916 
    917   layout = new tui_layout_split ();
    918   layout->add_window (DATA_NAME, 1);
    919   layout->add_window (DISASSEM_NAME, 1);
    920   layout->add_window (STATUS_NAME, 0);
    921   layout->add_window (CMD_NAME, 1);
    922   layouts.emplace_back (layout);
    923   asm_regs_layout = layout;
    924 }
    925 
    926 
    927 
    929 /* A helper function that returns true if NAME is the name of an
    930    available window.  */
    931 
    932 static bool
    933 validate_window_name (const std::string &name)
    934 {
    935   auto iter = known_window_types->find (name);
    936   return iter != known_window_types->end ();
    937 }
    938 
    939 /* Implementation of the "tui new-layout" command.  */
    940 
    941 static void
    942 tui_new_layout_command (const char *spec, int from_tty)
    943 {
    944   std::string new_name = extract_arg (&spec);
    945   if (new_name.empty ())
    946     error (_("No layout name specified"));
    947   if (new_name[0] == '-')
    948     error (_("Layout name cannot start with '-'"));
    949 
    950   bool is_vertical = true;
    951   spec = skip_spaces (spec);
    952   if (check_for_argument (&spec, "-horizontal"))
    953     is_vertical = false;
    954 
    955   std::vector<std::unique_ptr<tui_layout_split>> splits;
    956   splits.emplace_back (new tui_layout_split (is_vertical));
    957   std::unordered_set<std::string> seen_windows;
    958   while (true)
    959     {
    960       spec = skip_spaces (spec);
    961       if (spec[0] == '\0')
    962 	break;
    963 
    964       if (spec[0] == '{')
    965 	{
    966 	  is_vertical = true;
    967 	  spec = skip_spaces (spec + 1);
    968 	  if (check_for_argument (&spec, "-horizontal"))
    969 	    is_vertical = false;
    970 	  splits.emplace_back (new tui_layout_split (is_vertical));
    971 	  continue;
    972 	}
    973 
    974       bool is_close = false;
    975       std::string name;
    976       if (spec[0] == '}')
    977 	{
    978 	  is_close = true;
    979 	  ++spec;
    980 	  if (splits.size () == 1)
    981 	    error (_("Extra '}' in layout specification"));
    982 	}
    983       else
    984 	{
    985 	  name = extract_arg (&spec);
    986 	  if (name.empty ())
    987 	    break;
    988 	  if (!validate_window_name (name))
    989 	    error (_("Unknown window \"%s\""), name.c_str ());
    990 	  if (seen_windows.find (name) != seen_windows.end ())
    991 	    error (_("Window \"%s\" seen twice in layout"), name.c_str ());
    992 	}
    993 
    994       ULONGEST weight = get_ulongest (&spec, '}');
    995       if ((int) weight != weight)
    996 	error (_("Weight out of range: %s"), pulongest (weight));
    997       if (is_close)
    998 	{
    999 	  std::unique_ptr<tui_layout_split> last_split
   1000 	    = std::move (splits.back ());
   1001 	  splits.pop_back ();
   1002 	  splits.back ()->add_split (std::move (last_split), weight);
   1003 	}
   1004       else
   1005 	{
   1006 	  splits.back ()->add_window (name.c_str (), weight);
   1007 	  seen_windows.insert (name);
   1008 	}
   1009     }
   1010   if (splits.size () > 1)
   1011     error (_("Missing '}' in layout specification"));
   1012   if (seen_windows.empty ())
   1013     error (_("New layout does not contain any windows"));
   1014   if (seen_windows.find (CMD_NAME) == seen_windows.end ())
   1015     error (_("New layout does not contain the \"" CMD_NAME "\" window"));
   1016 
   1017   gdb::unique_xmalloc_ptr<char> cmd_name
   1018     = make_unique_xstrdup (new_name.c_str ());
   1019   std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
   1020   struct cmd_list_element *cmd
   1021     = add_layout_command (cmd_name.get (), new_layout.get ());
   1022   cmd->name_allocated = 1;
   1023   cmd_name.release ();
   1024   new_layout.release ();
   1025 }
   1026 
   1027 /* Function to initialize gdb commands, for tui window layout
   1028    manipulation.  */
   1029 
   1030 void _initialize_tui_layout ();
   1031 void
   1032 _initialize_tui_layout ()
   1033 {
   1034   add_basic_prefix_cmd ("layout", class_tui, _("\
   1035 Change the layout of windows.\n\
   1036 Usage: layout prev | next | LAYOUT-NAME"),
   1037 			&layout_list, "layout ", 0, &cmdlist);
   1038 
   1039   add_cmd ("next", class_tui, tui_next_layout_command,
   1040 	   _("Apply the next TUI layout."),
   1041 	   &layout_list);
   1042   add_cmd ("prev", class_tui, tui_prev_layout_command,
   1043 	   _("Apply the previous TUI layout."),
   1044 	   &layout_list);
   1045   add_cmd ("regs", class_tui, tui_regs_layout_command,
   1046 	   _("Apply the TUI register layout."),
   1047 	   &layout_list);
   1048 
   1049   add_cmd ("new-layout", class_tui, tui_new_layout_command,
   1050 	   _("Create a new TUI layout.\n\
   1051 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
   1052 Create a new TUI layout.  The new layout will be named NAME,\n\
   1053 and can be accessed using \"layout NAME\".\n\
   1054 The windows will be displayed in the specified order.\n\
   1055 A WINDOW can also be of the form:\n\
   1056   { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
   1057 This form indicates a sub-frame.\n\
   1058 Each WEIGHT is an integer, which holds the relative size\n\
   1059 to be allocated to the window."),
   1060 	   tui_get_cmd_list ());
   1061 
   1062   initialize_layouts ();
   1063   initialize_known_windows ();
   1064 }
   1065