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