1 /* TUI layout window management.
3 Copyright (C) 1998-2020 Free Software Foundation, Inc.
5 Contributed by Hewlett-Packard Company.
7 This file is part of GDB.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include "arch-utils.h"
28 #include "cli/cli-cmds.h"
29 #include "cli/cli-decode.h"
30 #include "cli/cli-utils.h"
32 #include <unordered_set>
35 #include "tui/tui-command.h"
36 #include "tui/tui-data.h"
37 #include "tui/tui-wingeneral.h"
38 #include "tui/tui-stack.h"
39 #include "tui/tui-regs.h"
40 #include "tui/tui-win.h"
41 #include "tui/tui-winsource.h"
42 #include "tui/tui-disasm.h"
43 #include "tui/tui-layout.h"
44 #include "tui/tui-source.h"
45 #include "gdb_curses.h"
47 static void tui_layout_command (const char *, int);
48 static void extract_display_start_addr (struct gdbarch
**, CORE_ADDR
*);
51 static std::vector
<std::unique_ptr
<tui_layout_split
>> layouts
;
53 /* The layout that is currently applied. */
54 static std::unique_ptr
<tui_layout_base
> applied_layout
;
56 /* The "skeleton" version of the layout that is currently applied. */
57 static tui_layout_split
*applied_skeleton
;
59 /* The two special "regs" layouts. Note that these aren't registered
60 as commands and so can never be deleted. */
61 static tui_layout_split
*src_regs_layout
;
62 static tui_layout_split
*asm_regs_layout
;
64 /* See tui-layout.h. */
67 tui_apply_current_layout ()
69 applied_layout
->apply (0, 0, tui_term_width (), tui_term_height ());
75 tui_adjust_window_height (struct tui_win_info
*win
, int new_height
)
77 applied_layout
->adjust_size (win
->name (), new_height
);
80 /* Set the current layout to LAYOUT. */
83 tui_set_layout (tui_layout_split
*layout
)
85 struct gdbarch
*gdbarch
;
88 extract_display_start_addr (&gdbarch
, &addr
);
89 tui_make_all_invisible ();
90 applied_skeleton
= layout
;
91 applied_layout
= layout
->clone ();
92 tui_apply_current_layout ();
93 tui_delete_invisible_windows ();
95 if (gdbarch
== nullptr && TUI_DISASM_WIN
!= nullptr)
96 tui_get_begin_asm_address (&gdbarch
, &addr
);
97 tui_update_source_windows_with_addr (gdbarch
, addr
);
100 /* See tui-layout.h. */
103 tui_add_win_to_layout (enum tui_win_type type
)
105 gdb_assert (type
== SRC_WIN
|| type
== DISASSEM_WIN
);
107 /* If the window already exists, no need to add it. */
108 if (tui_win_list
[type
] != nullptr)
111 /* If the window we are trying to replace doesn't exist, we're
113 enum tui_win_type other
= type
== SRC_WIN
? DISASSEM_WIN
: SRC_WIN
;
114 if (tui_win_list
[other
] == nullptr)
117 const char *name
= type
== SRC_WIN
? SRC_NAME
: DISASSEM_NAME
;
118 applied_layout
->replace_window (tui_win_list
[other
]->name (), name
);
119 tui_apply_current_layout ();
120 tui_delete_invisible_windows ();
123 /* Find LAYOUT in the "layouts" global and return its index. */
126 find_layout (tui_layout_split
*layout
)
128 for (size_t i
= 0; i
< layouts
.size (); ++i
)
130 if (layout
== layouts
[i
].get ())
133 gdb_assert_not_reached (_("layout not found!?"));
136 /* Function to set the layout. */
139 tui_apply_layout (struct cmd_list_element
*command
,
140 const char *args
, int from_tty
)
142 tui_layout_split
*layout
143 = (tui_layout_split
*) get_cmd_context (command
);
145 /* Make sure the curses mode is enabled. */
147 tui_set_layout (layout
);
150 /* See tui-layout.h. */
155 size_t index
= find_layout (applied_skeleton
);
157 if (index
== layouts
.size ())
159 tui_set_layout (layouts
[index
].get ());
162 /* Implement the "layout next" command. */
165 tui_next_layout_command (const char *arg
, int from_tty
)
171 /* See tui-layout.h. */
174 tui_set_initial_layout ()
176 tui_set_layout (layouts
[0].get ());
179 /* Implement the "layout prev" command. */
182 tui_prev_layout_command (const char *arg
, int from_tty
)
185 size_t index
= find_layout (applied_skeleton
);
187 index
= layouts
.size ();
189 tui_set_layout (layouts
[index
].get ());
193 /* See tui-layout.h. */
198 /* If there's already a register window, we're done. */
199 if (TUI_DATA_WIN
!= nullptr)
202 tui_set_layout (TUI_DISASM_WIN
!= nullptr
207 /* Implement the "layout regs" command. */
210 tui_regs_layout_command (const char *arg
, int from_tty
)
216 /* See tui-layout.h. */
219 tui_remove_some_windows ()
221 tui_win_info
*focus
= tui_win_with_focus ();
223 if (strcmp (focus
->name (), "cmd") == 0)
225 /* Try leaving the source or disassembly window. If neither
226 exists, just do nothing. */
228 if (focus
== nullptr)
229 focus
= TUI_DISASM_WIN
;
230 if (focus
== nullptr)
234 applied_layout
->remove_windows (focus
->name ());
235 tui_apply_current_layout ();
239 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
241 struct gdbarch
*gdbarch
= nullptr;
244 struct symtab_and_line cursal
= get_current_source_symtab_and_line ();
246 if (TUI_SRC_WIN
!= nullptr)
248 gdbarch
= TUI_SRC_WIN
->gdbarch
;
249 find_line_pc (cursal
.symtab
,
250 TUI_SRC_WIN
->start_line_or_addr
.u
.line_no
,
254 else if (TUI_DISASM_WIN
!= nullptr)
256 gdbarch
= TUI_DISASM_WIN
->gdbarch
;
257 addr
= TUI_DISASM_WIN
->start_line_or_addr
.u
.addr
;
260 *gdbarch_p
= gdbarch
;
265 tui_gen_win_info::resize (int height_
, int width_
,
266 int origin_x_
, int origin_y_
)
268 if (width
== width_
&& height
== height_
269 && x
== origin_x_
&& y
== origin_y_
270 && handle
!= nullptr)
278 if (handle
!= nullptr)
281 wresize (handle
.get (), height
, width
);
282 mvwin (handle
.get (), y
, x
);
283 wmove (handle
.get (), 0, 0);
285 handle
.reset (nullptr);
289 if (handle
== nullptr)
297 /* Helper function that returns a TUI window, given its name. */
299 static tui_gen_win_info
*
300 tui_get_window_by_name (const std::string
&name
)
304 if (tui_win_list
[SRC_WIN
] == nullptr)
305 tui_win_list
[SRC_WIN
] = new tui_source_window ();
306 return tui_win_list
[SRC_WIN
];
308 else if (name
== "cmd")
310 if (tui_win_list
[CMD_WIN
] == nullptr)
311 tui_win_list
[CMD_WIN
] = new tui_cmd_window ();
312 return tui_win_list
[CMD_WIN
];
314 else if (name
== "regs")
316 if (tui_win_list
[DATA_WIN
] == nullptr)
317 tui_win_list
[DATA_WIN
] = new tui_data_window ();
318 return tui_win_list
[DATA_WIN
];
320 else if (name
== "asm")
322 if (tui_win_list
[DISASSEM_WIN
] == nullptr)
323 tui_win_list
[DISASSEM_WIN
] = new tui_disasm_window ();
324 return tui_win_list
[DISASSEM_WIN
];
328 gdb_assert (name
== "status");
329 return tui_locator_win_info_ptr ();
333 /* See tui-layout.h. */
335 std::unique_ptr
<tui_layout_base
>
336 tui_layout_window::clone () const
338 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
339 return std::unique_ptr
<tui_layout_base
> (result
);
342 /* See tui-layout.h. */
345 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
351 gdb_assert (m_window
!= nullptr);
352 m_window
->resize (height
, width
, x
, y
);
355 /* See tui-layout.h. */
358 tui_layout_window::get_sizes (bool height
, int *min_value
, int *max_value
)
360 if (m_window
== nullptr)
361 m_window
= tui_get_window_by_name (m_contents
);
364 *min_value
= m_window
->min_height ();
365 *max_value
= m_window
->max_height ();
369 *min_value
= m_window
->min_width ();
370 *max_value
= m_window
->max_width ();
374 /* See tui-layout.h. */
377 tui_layout_window::top_boxed_p () const
379 gdb_assert (m_window
!= nullptr);
380 return m_window
->can_box ();
383 /* See tui-layout.h. */
386 tui_layout_window::bottom_boxed_p () const
388 gdb_assert (m_window
!= nullptr);
389 return m_window
->can_box ();
392 /* See tui-layout.h. */
395 tui_layout_window::replace_window (const char *name
, const char *new_window
)
397 if (m_contents
== name
)
399 m_contents
= new_window
;
400 if (m_window
!= nullptr)
402 m_window
->make_visible (false);
403 m_window
= tui_get_window_by_name (m_contents
);
408 /* See tui-layout.h. */
411 tui_layout_window::specification (ui_file
*output
, int depth
)
413 fputs_unfiltered (get_name (), output
);
416 /* See tui-layout.h. */
419 tui_layout_split::add_split (std::unique_ptr
<tui_layout_split
> &&layout
,
422 split s
= {weight
, std::move (layout
)};
423 m_splits
.push_back (std::move (s
));
426 /* See tui-layout.h. */
429 tui_layout_split::add_window (const char *name
, int weight
)
431 tui_layout_window
*result
= new tui_layout_window (name
);
432 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
433 m_splits
.push_back (std::move (s
));
436 /* See tui-layout.h. */
438 std::unique_ptr
<tui_layout_base
>
439 tui_layout_split::clone () const
441 tui_layout_split
*result
= new tui_layout_split (m_vertical
);
442 for (const split
&item
: m_splits
)
444 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
445 split s
= {item
.weight
, std::move (next
)};
446 result
->m_splits
.push_back (std::move (s
));
448 return std::unique_ptr
<tui_layout_base
> (result
);
451 /* See tui-layout.h. */
454 tui_layout_split::get_sizes (bool height
, int *min_value
, int *max_value
)
458 bool first_time
= true;
459 for (const split
&item
: m_splits
)
461 int new_min
, new_max
;
462 item
.layout
->get_sizes (height
, &new_min
, &new_max
);
463 /* For the mismatch case, the first time through we want to set
464 the min and max to the computed values -- the "first_time"
465 check here is just a funny way of doing that. */
466 if (height
== m_vertical
|| first_time
)
468 *min_value
+= new_min
;
469 *max_value
+= new_max
;
473 *min_value
= std::max (*min_value
, new_min
);
474 *max_value
= std::min (*max_value
, new_max
);
480 /* See tui-layout.h. */
483 tui_layout_split::top_boxed_p () const
485 if (m_splits
.empty ())
487 return m_splits
[0].layout
->top_boxed_p ();
490 /* See tui-layout.h. */
493 tui_layout_split::bottom_boxed_p () const
495 if (m_splits
.empty ())
497 return m_splits
.back ().layout
->top_boxed_p ();
500 /* See tui-layout.h. */
503 tui_layout_split::set_weights_from_heights ()
505 for (int i
= 0; i
< m_splits
.size (); ++i
)
506 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
509 /* See tui-layout.h. */
512 tui_layout_split::adjust_size (const char *name
, int new_height
)
514 /* Look through the children. If one is a layout holding the named
515 window, we're done; or if one actually is the named window,
517 int found_index
= -1;
518 for (int i
= 0; i
< m_splits
.size (); ++i
)
520 tui_adjust_result adjusted
521 = m_splits
[i
].layout
->adjust_size (name
, new_height
);
522 if (adjusted
== HANDLED
)
524 if (adjusted
== FOUND
)
533 if (found_index
== -1)
535 if (m_splits
[found_index
].layout
->height
== new_height
)
538 set_weights_from_heights ();
539 int delta
= m_splits
[found_index
].weight
- new_height
;
540 m_splits
[found_index
].weight
= new_height
;
542 /* Distribute the "delta" over the next window; but if the next
543 window cannot hold it all, keep going until we either find a
544 window that does, or until we loop all the way around. */
545 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
547 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
549 int new_min
, new_max
;
550 m_splits
[index
].layout
->get_sizes (m_vertical
, &new_min
, &new_max
);
554 /* The primary window grew, so we are trying to shrink other
556 int available
= m_splits
[index
].weight
- new_min
;
557 int shrink_by
= std::min (available
, -delta
);
558 m_splits
[index
].weight
-= shrink_by
;
563 /* The primary window shrank, so we are trying to grow other
565 int available
= new_max
- m_splits
[index
].weight
;
566 int grow_by
= std::min (available
, delta
);
567 m_splits
[index
].weight
+= grow_by
;
574 warning (_("Invalid window height specified"));
575 /* Effectively undo any modifications made here. */
576 set_weights_from_heights ();
580 /* Simply re-apply the updated layout. */
581 apply (x
, y
, width
, height
);
587 /* See tui-layout.h. */
590 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
602 /* True if this window will share a box border with the previous
603 window in the list. */
607 std::vector
<size_info
> info (m_splits
.size ());
609 /* Step 1: Find the min and max size of each sub-layout.
610 Fixed-sized layouts are given their desired size, and then the
611 remaining space is distributed among the remaining windows
612 according to the weights given. */
613 int available_size
= m_vertical
? height
: width
;
615 int total_weight
= 0;
616 for (int i
= 0; i
< m_splits
.size (); ++i
)
618 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
620 /* Always call get_sizes, to ensure that the window is
621 instantiated. This is a bit gross but less gross than adding
622 special cases for this in other places. */
623 m_splits
[i
].layout
->get_sizes (m_vertical
, &info
[i
].min_size
,
627 && cmd_win_already_exists
628 && m_splits
[i
].layout
->get_name () != nullptr
629 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
631 /* If this layout has never been applied, then it means the
632 user just changed the layout. In this situation, it's
633 desirable to keep the size of the command window the
634 same. Setting the min and max sizes this way ensures
635 that the resizing step, below, does the right thing with
637 info
[i
].min_size
= (m_vertical
638 ? TUI_CMD_WIN
->height
639 : TUI_CMD_WIN
->width
);
640 info
[i
].max_size
= info
[i
].min_size
;
643 if (info
[i
].min_size
== info
[i
].max_size
)
644 available_size
-= info
[i
].min_size
;
648 total_weight
+= m_splits
[i
].weight
;
651 /* Two adjacent boxed windows will share a border, making a bit
652 more size available. */
654 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
655 && m_splits
[i
].layout
->top_boxed_p ())
656 info
[i
].share_box
= true;
659 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
660 are given their fixed size, while others are resized according to
663 for (int i
= 0; i
< m_splits
.size (); ++i
)
665 /* Compute the height and clamp to the allowable range. */
666 info
[i
].size
= available_size
* m_splits
[i
].weight
/ total_weight
;
667 if (info
[i
].size
> info
[i
].max_size
)
668 info
[i
].size
= info
[i
].max_size
;
669 if (info
[i
].size
< info
[i
].min_size
)
670 info
[i
].size
= info
[i
].min_size
;
671 /* If there is any leftover size, just redistribute it to the
672 last resizeable window, by dropping it from the allocated
673 size. We could try to be fancier here perhaps, by
674 redistributing this size among all windows, not just the
676 if (info
[i
].min_size
!= info
[i
].max_size
)
678 used_size
+= info
[i
].size
;
679 if (info
[i
].share_box
)
684 /* Allocate any leftover size. */
685 if (available_size
>= used_size
&& last_index
!= -1)
686 info
[last_index
].size
+= available_size
- used_size
;
688 /* Step 3: Resize. */
690 const int maximum
= m_vertical
? height
: width
;
691 for (int i
= 0; i
< m_splits
.size (); ++i
)
693 /* If we fall off the bottom, just make allocations overlap.
695 if (size_accum
+ info
[i
].size
> maximum
)
696 size_accum
= maximum
- info
[i
].size
;
697 else if (info
[i
].share_box
)
700 m_splits
[i
].layout
->apply (x
, y
+ size_accum
, width
, info
[i
].size
);
702 m_splits
[i
].layout
->apply (x
+ size_accum
, y
, info
[i
].size
, height
);
703 size_accum
+= info
[i
].size
;
709 /* See tui-layout.h. */
712 tui_layout_split::remove_windows (const char *name
)
714 for (int i
= 0; i
< m_splits
.size (); ++i
)
716 const char *this_name
= m_splits
[i
].layout
->get_name ();
717 if (this_name
== nullptr)
718 m_splits
[i
].layout
->remove_windows (name
);
721 if (strcmp (this_name
, name
) == 0
722 || strcmp (this_name
, "cmd") == 0)
726 m_splits
.erase (m_splits
.begin () + i
);
732 /* See tui-layout.h. */
735 tui_layout_split::replace_window (const char *name
, const char *new_window
)
737 for (auto &item
: m_splits
)
738 item
.layout
->replace_window (name
, new_window
);
741 /* See tui-layout.h. */
744 tui_layout_split::specification (ui_file
*output
, int depth
)
747 fputs_unfiltered ("{", output
);
750 fputs_unfiltered ("-horizontal ", output
);
753 for (auto &item
: m_splits
)
756 fputs_unfiltered (" ", output
);
758 item
.layout
->specification (output
, depth
+ 1);
759 fprintf_unfiltered (output
, " %d", item
.weight
);
763 fputs_unfiltered ("}", output
);
766 /* Destroy the layout associated with SELF. */
769 destroy_layout (struct cmd_list_element
*self
, void *context
)
771 tui_layout_split
*layout
= (tui_layout_split
*) context
;
772 size_t index
= find_layout (layout
);
773 layouts
.erase (layouts
.begin () + index
);
776 /* List holding the sub-commands of "layout". */
778 static struct cmd_list_element
*layout_list
;
780 /* Add a "layout" command with name NAME that switches to LAYOUT. */
782 static struct cmd_list_element
*
783 add_layout_command (const char *name
, tui_layout_split
*layout
)
785 struct cmd_list_element
*cmd
;
788 layout
->specification (&spec
, 0);
790 gdb::unique_xmalloc_ptr
<char> doc
791 (xstrprintf (_("Apply the \"%s\" layout.\n\
792 This layout was created using:\n\
793 tui new-layout %s %s"),
794 name
, name
, spec
.c_str ()));
796 cmd
= add_cmd (name
, class_tui
, nullptr, doc
.get (), &layout_list
);
797 set_cmd_context (cmd
, layout
);
798 /* There is no API to set this. */
799 cmd
->func
= tui_apply_layout
;
800 cmd
->destroyer
= destroy_layout
;
801 cmd
->doc_allocated
= 1;
803 layouts
.emplace_back (layout
);
808 /* Initialize the standard layouts. */
811 initialize_layouts ()
813 tui_layout_split
*layout
;
815 layout
= new tui_layout_split ();
816 layout
->add_window ("src", 2);
817 layout
->add_window ("status", 0);
818 layout
->add_window ("cmd", 1);
819 add_layout_command ("src", layout
);
821 layout
= new tui_layout_split ();
822 layout
->add_window ("asm", 2);
823 layout
->add_window ("status", 0);
824 layout
->add_window ("cmd", 1);
825 add_layout_command ("asm", layout
);
827 layout
= new tui_layout_split ();
828 layout
->add_window ("src", 1);
829 layout
->add_window ("asm", 1);
830 layout
->add_window ("status", 0);
831 layout
->add_window ("cmd", 1);
832 add_layout_command ("split", layout
);
834 layout
= new tui_layout_split ();
835 layout
->add_window ("regs", 1);
836 layout
->add_window ("src", 1);
837 layout
->add_window ("status", 0);
838 layout
->add_window ("cmd", 1);
839 layouts
.emplace_back (layout
);
840 src_regs_layout
= layout
;
842 layout
= new tui_layout_split ();
843 layout
->add_window ("regs", 1);
844 layout
->add_window ("asm", 1);
845 layout
->add_window ("status", 0);
846 layout
->add_window ("cmd", 1);
847 layouts
.emplace_back (layout
);
848 asm_regs_layout
= layout
;
853 /* A helper function that returns true if NAME is the name of an
857 validate_window_name (const std::string
&name
)
859 return (name
== "src" || name
== "cmd"
860 || name
== "regs" || name
== "asm"
861 || name
== "status");
864 /* Implementation of the "tui new-layout" command. */
867 tui_new_layout_command (const char *spec
, int from_tty
)
869 std::string new_name
= extract_arg (&spec
);
870 if (new_name
.empty ())
871 error (_("No layout name specified"));
872 if (new_name
[0] == '-')
873 error (_("Layout name cannot start with '-'"));
875 bool is_vertical
= true;
876 spec
= skip_spaces (spec
);
877 if (check_for_argument (&spec
, "-horizontal"))
880 std::vector
<std::unique_ptr
<tui_layout_split
>> splits
;
881 splits
.emplace_back (new tui_layout_split (is_vertical
));
882 std::unordered_set
<std::string
> seen_windows
;
885 spec
= skip_spaces (spec
);
892 spec
= skip_spaces (spec
+ 1);
893 if (check_for_argument (&spec
, "-horizontal"))
895 splits
.emplace_back (new tui_layout_split (is_vertical
));
899 bool is_close
= false;
905 if (splits
.size () == 1)
906 error (_("Extra '}' in layout specification"));
910 name
= extract_arg (&spec
);
913 if (!validate_window_name (name
))
914 error (_("Unknown window \"%s\""), name
.c_str ());
915 if (seen_windows
.find (name
) != seen_windows
.end ())
916 error (_("Window \"%s\" seen twice in layout"), name
.c_str ());
919 ULONGEST weight
= get_ulongest (&spec
, '}');
920 if ((int) weight
!= weight
)
921 error (_("Weight out of range: %s"), pulongest (weight
));
924 std::unique_ptr
<tui_layout_split
> last_split
925 = std::move (splits
.back ());
927 splits
.back ()->add_split (std::move (last_split
), weight
);
931 splits
.back ()->add_window (name
.c_str (), weight
);
932 seen_windows
.insert (name
);
935 if (splits
.size () > 1)
936 error (_("Missing '}' in layout specification"));
937 if (seen_windows
.empty ())
938 error (_("New layout does not contain any windows"));
939 if (seen_windows
.find ("cmd") == seen_windows
.end ())
940 error (_("New layout does not contain the \"cmd\" window"));
942 gdb::unique_xmalloc_ptr
<char> cmd_name
943 = make_unique_xstrdup (new_name
.c_str ());
944 std::unique_ptr
<tui_layout_split
> new_layout
= std::move (splits
.back ());
945 struct cmd_list_element
*cmd
946 = add_layout_command (cmd_name
.get (), new_layout
.get ());
947 cmd
->name_allocated
= 1;
949 new_layout
.release ();
952 /* Base command for "layout". */
955 tui_layout_command (const char *layout_name
, int from_tty
)
957 help_list (layout_list
, "layout ", all_commands
, gdb_stdout
);
960 /* Function to initialize gdb commands, for tui window layout
963 void _initialize_tui_layout ();
965 _initialize_tui_layout ()
967 add_prefix_cmd ("layout", class_tui
, tui_layout_command
, _("\
968 Change the layout of windows.\n\
969 Usage: layout prev | next | LAYOUT-NAME"),
970 &layout_list
, "layout ", 0, &cmdlist
);
972 add_cmd ("next", class_tui
, tui_next_layout_command
,
973 _("Apply the next TUI layout"),
975 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
976 _("Apply the previous TUI layout"),
978 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
979 _("Apply the TUI register layout"),
982 add_cmd ("new-layout", class_tui
, tui_new_layout_command
,
983 _("Create a new TUI layout.\n\
984 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
985 Create a new TUI layout. The new layout will be named NAME,\n\
986 and can be accessed using \"layout NAME\".\n\
987 The windows will be displayed in the specified order.\n\
988 A WINDOW can also be of the form:\n\
989 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
990 This form indicates a sub-frame.\n\
991 Each WEIGHT is an integer, which holds the relative size\n\
992 to be allocated to the window."),
993 tui_get_cmd_list ());
995 initialize_layouts ();