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