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