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