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
;
65 std::vector
<tui_win_info
*> tui_windows
;
67 /* See tui-layout.h. */
70 tui_apply_current_layout ()
72 struct gdbarch
*gdbarch
;
75 extract_display_start_addr (&gdbarch
, &addr
);
77 std::vector
<tui_win_info
*> saved_windows
= std::move (tui_windows
);
80 for (tui_win_info
*win_info
: saved_windows
)
81 win_info
->make_visible (false);
83 applied_layout
->apply (0, 0, tui_term_width (), tui_term_height ());
85 /* Keep the list of internal windows up-to-date. */
86 for (int win_type
= SRC_WIN
; (win_type
< MAX_MAJOR_WINDOWS
); win_type
++)
87 if (tui_win_list
[win_type
] != nullptr
88 && !tui_win_list
[win_type
]->is_visible ())
89 tui_win_list
[win_type
] = nullptr;
91 /* This should always be made visible by a layout. */
92 gdb_assert (TUI_CMD_WIN
->is_visible ());
94 /* Now delete any window that was not re-applied. */
95 tui_win_info
*focus
= tui_win_with_focus ();
96 for (tui_win_info
*win_info
: saved_windows
)
98 if (!win_info
->is_visible ())
100 if (focus
== win_info
)
101 tui_set_win_focus_to (tui_windows
[0]);
106 if (gdbarch
== nullptr && TUI_DISASM_WIN
!= nullptr)
107 tui_get_begin_asm_address (&gdbarch
, &addr
);
108 tui_update_source_windows_with_addr (gdbarch
, addr
);
111 /* See tui-layout. */
114 tui_adjust_window_height (struct tui_win_info
*win
, int new_height
)
116 applied_layout
->adjust_size (win
->name (), new_height
);
119 /* Set the current layout to LAYOUT. */
122 tui_set_layout (tui_layout_split
*layout
)
124 applied_skeleton
= layout
;
125 applied_layout
= layout
->clone ();
126 tui_apply_current_layout ();
129 /* See tui-layout.h. */
132 tui_add_win_to_layout (enum tui_win_type type
)
134 gdb_assert (type
== SRC_WIN
|| type
== DISASSEM_WIN
);
136 /* If the window already exists, no need to add it. */
137 if (tui_win_list
[type
] != nullptr)
140 /* If the window we are trying to replace doesn't exist, we're
142 enum tui_win_type other
= type
== SRC_WIN
? DISASSEM_WIN
: SRC_WIN
;
143 if (tui_win_list
[other
] == nullptr)
146 const char *name
= type
== SRC_WIN
? SRC_NAME
: DISASSEM_NAME
;
147 applied_layout
->replace_window (tui_win_list
[other
]->name (), name
);
148 tui_apply_current_layout ();
151 /* Find LAYOUT in the "layouts" global and return its index. */
154 find_layout (tui_layout_split
*layout
)
156 for (size_t i
= 0; i
< layouts
.size (); ++i
)
158 if (layout
== layouts
[i
].get ())
161 gdb_assert_not_reached (_("layout not found!?"));
164 /* Function to set the layout. */
167 tui_apply_layout (struct cmd_list_element
*command
,
168 const char *args
, int from_tty
)
170 tui_layout_split
*layout
171 = (tui_layout_split
*) get_cmd_context (command
);
173 /* Make sure the curses mode is enabled. */
175 tui_set_layout (layout
);
178 /* See tui-layout.h. */
183 size_t index
= find_layout (applied_skeleton
);
185 if (index
== layouts
.size ())
187 tui_set_layout (layouts
[index
].get ());
190 /* Implement the "layout next" command. */
193 tui_next_layout_command (const char *arg
, int from_tty
)
199 /* See tui-layout.h. */
202 tui_set_initial_layout ()
204 tui_set_layout (layouts
[0].get ());
207 /* Implement the "layout prev" command. */
210 tui_prev_layout_command (const char *arg
, int from_tty
)
213 size_t index
= find_layout (applied_skeleton
);
215 index
= layouts
.size ();
217 tui_set_layout (layouts
[index
].get ());
221 /* See tui-layout.h. */
226 /* If there's already a register window, we're done. */
227 if (TUI_DATA_WIN
!= nullptr)
230 tui_set_layout (TUI_DISASM_WIN
!= nullptr
235 /* Implement the "layout regs" command. */
238 tui_regs_layout_command (const char *arg
, int from_tty
)
244 /* See tui-layout.h. */
247 tui_remove_some_windows ()
249 tui_win_info
*focus
= tui_win_with_focus ();
251 if (strcmp (focus
->name (), "cmd") == 0)
253 /* Try leaving the source or disassembly window. If neither
254 exists, just do nothing. */
256 if (focus
== nullptr)
257 focus
= TUI_DISASM_WIN
;
258 if (focus
== nullptr)
262 applied_layout
->remove_windows (focus
->name ());
263 tui_apply_current_layout ();
267 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
269 struct gdbarch
*gdbarch
= nullptr;
272 struct symtab_and_line cursal
= get_current_source_symtab_and_line ();
274 if (TUI_SRC_WIN
!= nullptr)
276 gdbarch
= TUI_SRC_WIN
->gdbarch
;
277 find_line_pc (cursal
.symtab
,
278 TUI_SRC_WIN
->start_line_or_addr
.u
.line_no
,
282 else if (TUI_DISASM_WIN
!= nullptr)
284 gdbarch
= TUI_DISASM_WIN
->gdbarch
;
285 addr
= TUI_DISASM_WIN
->start_line_or_addr
.u
.addr
;
288 *gdbarch_p
= gdbarch
;
293 tui_gen_win_info::resize (int height_
, int width_
,
294 int origin_x_
, int origin_y_
)
296 if (width
== width_
&& height
== height_
297 && x
== origin_x_
&& y
== origin_y_
298 && handle
!= nullptr)
306 if (handle
!= nullptr)
309 wresize (handle
.get (), height
, width
);
310 mvwin (handle
.get (), y
, x
);
311 wmove (handle
.get (), 0, 0);
313 handle
.reset (nullptr);
317 if (handle
== nullptr)
325 /* Helper function that returns a TUI window, given its name. */
327 static tui_gen_win_info
*
328 tui_get_window_by_name (const std::string
&name
)
332 if (tui_win_list
[SRC_WIN
] == nullptr)
333 tui_win_list
[SRC_WIN
] = new tui_source_window ();
334 return tui_win_list
[SRC_WIN
];
336 else if (name
== "cmd")
338 if (tui_win_list
[CMD_WIN
] == nullptr)
339 tui_win_list
[CMD_WIN
] = new tui_cmd_window ();
340 return tui_win_list
[CMD_WIN
];
342 else if (name
== "regs")
344 if (tui_win_list
[DATA_WIN
] == nullptr)
345 tui_win_list
[DATA_WIN
] = new tui_data_window ();
346 return tui_win_list
[DATA_WIN
];
348 else if (name
== "asm")
350 if (tui_win_list
[DISASSEM_WIN
] == nullptr)
351 tui_win_list
[DISASSEM_WIN
] = new tui_disasm_window ();
352 return tui_win_list
[DISASSEM_WIN
];
356 gdb_assert (name
== "status");
357 return tui_locator_win_info_ptr ();
361 /* See tui-layout.h. */
363 std::unique_ptr
<tui_layout_base
>
364 tui_layout_window::clone () const
366 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
367 return std::unique_ptr
<tui_layout_base
> (result
);
370 /* See tui-layout.h. */
373 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
379 gdb_assert (m_window
!= nullptr);
380 m_window
->resize (height
, width
, x
, y
);
381 if (dynamic_cast<tui_win_info
*> (m_window
) != nullptr)
382 tui_windows
.push_back ((tui_win_info
*) m_window
);
385 /* See tui-layout.h. */
388 tui_layout_window::get_sizes (bool height
, int *min_value
, int *max_value
)
390 if (m_window
== nullptr)
391 m_window
= tui_get_window_by_name (m_contents
);
394 *min_value
= m_window
->min_height ();
395 *max_value
= m_window
->max_height ();
399 *min_value
= m_window
->min_width ();
400 *max_value
= m_window
->max_width ();
404 /* See tui-layout.h. */
407 tui_layout_window::top_boxed_p () const
409 gdb_assert (m_window
!= nullptr);
410 return m_window
->can_box ();
413 /* See tui-layout.h. */
416 tui_layout_window::bottom_boxed_p () const
418 gdb_assert (m_window
!= nullptr);
419 return m_window
->can_box ();
422 /* See tui-layout.h. */
425 tui_layout_window::replace_window (const char *name
, const char *new_window
)
427 if (m_contents
== name
)
429 m_contents
= new_window
;
430 if (m_window
!= nullptr)
432 m_window
->make_visible (false);
433 m_window
= tui_get_window_by_name (m_contents
);
438 /* See tui-layout.h. */
441 tui_layout_window::specification (ui_file
*output
, int depth
)
443 fputs_unfiltered (get_name (), output
);
446 /* See tui-layout.h. */
449 tui_layout_split::add_split (std::unique_ptr
<tui_layout_split
> &&layout
,
452 split s
= {weight
, std::move (layout
)};
453 m_splits
.push_back (std::move (s
));
456 /* See tui-layout.h. */
459 tui_layout_split::add_window (const char *name
, int weight
)
461 tui_layout_window
*result
= new tui_layout_window (name
);
462 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
463 m_splits
.push_back (std::move (s
));
466 /* See tui-layout.h. */
468 std::unique_ptr
<tui_layout_base
>
469 tui_layout_split::clone () const
471 tui_layout_split
*result
= new tui_layout_split (m_vertical
);
472 for (const split
&item
: m_splits
)
474 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
475 split s
= {item
.weight
, std::move (next
)};
476 result
->m_splits
.push_back (std::move (s
));
478 return std::unique_ptr
<tui_layout_base
> (result
);
481 /* See tui-layout.h. */
484 tui_layout_split::get_sizes (bool height
, int *min_value
, int *max_value
)
488 bool first_time
= true;
489 for (const split
&item
: m_splits
)
491 int new_min
, new_max
;
492 item
.layout
->get_sizes (height
, &new_min
, &new_max
);
493 /* For the mismatch case, the first time through we want to set
494 the min and max to the computed values -- the "first_time"
495 check here is just a funny way of doing that. */
496 if (height
== m_vertical
|| first_time
)
498 *min_value
+= new_min
;
499 *max_value
+= new_max
;
503 *min_value
= std::max (*min_value
, new_min
);
504 *max_value
= std::min (*max_value
, new_max
);
510 /* See tui-layout.h. */
513 tui_layout_split::top_boxed_p () const
515 if (m_splits
.empty ())
517 return m_splits
[0].layout
->top_boxed_p ();
520 /* See tui-layout.h. */
523 tui_layout_split::bottom_boxed_p () const
525 if (m_splits
.empty ())
527 return m_splits
.back ().layout
->top_boxed_p ();
530 /* See tui-layout.h. */
533 tui_layout_split::set_weights_from_heights ()
535 for (int i
= 0; i
< m_splits
.size (); ++i
)
536 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
539 /* See tui-layout.h. */
542 tui_layout_split::adjust_size (const char *name
, int new_height
)
544 /* Look through the children. If one is a layout holding the named
545 window, we're done; or if one actually is the named window,
547 int found_index
= -1;
548 for (int i
= 0; i
< m_splits
.size (); ++i
)
550 tui_adjust_result adjusted
551 = m_splits
[i
].layout
->adjust_size (name
, new_height
);
552 if (adjusted
== HANDLED
)
554 if (adjusted
== FOUND
)
563 if (found_index
== -1)
565 if (m_splits
[found_index
].layout
->height
== new_height
)
568 set_weights_from_heights ();
569 int delta
= m_splits
[found_index
].weight
- new_height
;
570 m_splits
[found_index
].weight
= new_height
;
572 /* Distribute the "delta" over the next window; but if the next
573 window cannot hold it all, keep going until we either find a
574 window that does, or until we loop all the way around. */
575 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
577 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
579 int new_min
, new_max
;
580 m_splits
[index
].layout
->get_sizes (m_vertical
, &new_min
, &new_max
);
584 /* The primary window grew, so we are trying to shrink other
586 int available
= m_splits
[index
].weight
- new_min
;
587 int shrink_by
= std::min (available
, -delta
);
588 m_splits
[index
].weight
-= shrink_by
;
593 /* The primary window shrank, so we are trying to grow other
595 int available
= new_max
- m_splits
[index
].weight
;
596 int grow_by
= std::min (available
, delta
);
597 m_splits
[index
].weight
+= grow_by
;
604 warning (_("Invalid window height specified"));
605 /* Effectively undo any modifications made here. */
606 set_weights_from_heights ();
610 /* Simply re-apply the updated layout. */
611 apply (x
, y
, width
, height
);
617 /* See tui-layout.h. */
620 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
632 /* True if this window will share a box border with the previous
633 window in the list. */
637 std::vector
<size_info
> info (m_splits
.size ());
639 /* Step 1: Find the min and max size of each sub-layout.
640 Fixed-sized layouts are given their desired size, and then the
641 remaining space is distributed among the remaining windows
642 according to the weights given. */
643 int available_size
= m_vertical
? height
: width
;
645 int total_weight
= 0;
646 for (int i
= 0; i
< m_splits
.size (); ++i
)
648 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
650 /* Always call get_sizes, to ensure that the window is
651 instantiated. This is a bit gross but less gross than adding
652 special cases for this in other places. */
653 m_splits
[i
].layout
->get_sizes (m_vertical
, &info
[i
].min_size
,
657 && cmd_win_already_exists
658 && m_splits
[i
].layout
->get_name () != nullptr
659 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
661 /* If this layout has never been applied, then it means the
662 user just changed the layout. In this situation, it's
663 desirable to keep the size of the command window the
664 same. Setting the min and max sizes this way ensures
665 that the resizing step, below, does the right thing with
667 info
[i
].min_size
= (m_vertical
668 ? TUI_CMD_WIN
->height
669 : TUI_CMD_WIN
->width
);
670 info
[i
].max_size
= info
[i
].min_size
;
673 if (info
[i
].min_size
== info
[i
].max_size
)
674 available_size
-= info
[i
].min_size
;
678 total_weight
+= m_splits
[i
].weight
;
681 /* Two adjacent boxed windows will share a border, making a bit
682 more size available. */
684 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
685 && m_splits
[i
].layout
->top_boxed_p ())
686 info
[i
].share_box
= true;
689 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
690 are given their fixed size, while others are resized according to
693 for (int i
= 0; i
< m_splits
.size (); ++i
)
695 /* Compute the height and clamp to the allowable range. */
696 info
[i
].size
= available_size
* m_splits
[i
].weight
/ total_weight
;
697 if (info
[i
].size
> info
[i
].max_size
)
698 info
[i
].size
= info
[i
].max_size
;
699 if (info
[i
].size
< info
[i
].min_size
)
700 info
[i
].size
= info
[i
].min_size
;
701 /* If there is any leftover size, just redistribute it to the
702 last resizeable window, by dropping it from the allocated
703 size. We could try to be fancier here perhaps, by
704 redistributing this size among all windows, not just the
706 if (info
[i
].min_size
!= info
[i
].max_size
)
708 used_size
+= info
[i
].size
;
709 if (info
[i
].share_box
)
714 /* Allocate any leftover size. */
715 if (available_size
>= used_size
&& last_index
!= -1)
716 info
[last_index
].size
+= available_size
- used_size
;
718 /* Step 3: Resize. */
720 const int maximum
= m_vertical
? height
: width
;
721 for (int i
= 0; i
< m_splits
.size (); ++i
)
723 /* If we fall off the bottom, just make allocations overlap.
725 if (size_accum
+ info
[i
].size
> maximum
)
726 size_accum
= maximum
- info
[i
].size
;
727 else if (info
[i
].share_box
)
730 m_splits
[i
].layout
->apply (x
, y
+ size_accum
, width
, info
[i
].size
);
732 m_splits
[i
].layout
->apply (x
+ size_accum
, y
, info
[i
].size
, height
);
733 size_accum
+= info
[i
].size
;
739 /* See tui-layout.h. */
742 tui_layout_split::remove_windows (const char *name
)
744 for (int i
= 0; i
< m_splits
.size (); ++i
)
746 const char *this_name
= m_splits
[i
].layout
->get_name ();
747 if (this_name
== nullptr)
748 m_splits
[i
].layout
->remove_windows (name
);
751 if (strcmp (this_name
, name
) == 0
752 || strcmp (this_name
, "cmd") == 0)
756 m_splits
.erase (m_splits
.begin () + i
);
762 /* See tui-layout.h. */
765 tui_layout_split::replace_window (const char *name
, const char *new_window
)
767 for (auto &item
: m_splits
)
768 item
.layout
->replace_window (name
, new_window
);
771 /* See tui-layout.h. */
774 tui_layout_split::specification (ui_file
*output
, int depth
)
777 fputs_unfiltered ("{", output
);
780 fputs_unfiltered ("-horizontal ", output
);
783 for (auto &item
: m_splits
)
786 fputs_unfiltered (" ", output
);
788 item
.layout
->specification (output
, depth
+ 1);
789 fprintf_unfiltered (output
, " %d", item
.weight
);
793 fputs_unfiltered ("}", output
);
796 /* Destroy the layout associated with SELF. */
799 destroy_layout (struct cmd_list_element
*self
, void *context
)
801 tui_layout_split
*layout
= (tui_layout_split
*) context
;
802 size_t index
= find_layout (layout
);
803 layouts
.erase (layouts
.begin () + index
);
806 /* List holding the sub-commands of "layout". */
808 static struct cmd_list_element
*layout_list
;
810 /* Add a "layout" command with name NAME that switches to LAYOUT. */
812 static struct cmd_list_element
*
813 add_layout_command (const char *name
, tui_layout_split
*layout
)
815 struct cmd_list_element
*cmd
;
818 layout
->specification (&spec
, 0);
820 gdb::unique_xmalloc_ptr
<char> doc
821 (xstrprintf (_("Apply the \"%s\" layout.\n\
822 This layout was created using:\n\
823 tui new-layout %s %s"),
824 name
, name
, spec
.c_str ()));
826 cmd
= add_cmd (name
, class_tui
, nullptr, doc
.get (), &layout_list
);
827 set_cmd_context (cmd
, layout
);
828 /* There is no API to set this. */
829 cmd
->func
= tui_apply_layout
;
830 cmd
->destroyer
= destroy_layout
;
831 cmd
->doc_allocated
= 1;
833 layouts
.emplace_back (layout
);
838 /* Initialize the standard layouts. */
841 initialize_layouts ()
843 tui_layout_split
*layout
;
845 layout
= new tui_layout_split ();
846 layout
->add_window ("src", 2);
847 layout
->add_window ("status", 0);
848 layout
->add_window ("cmd", 1);
849 add_layout_command ("src", layout
);
851 layout
= new tui_layout_split ();
852 layout
->add_window ("asm", 2);
853 layout
->add_window ("status", 0);
854 layout
->add_window ("cmd", 1);
855 add_layout_command ("asm", layout
);
857 layout
= new tui_layout_split ();
858 layout
->add_window ("src", 1);
859 layout
->add_window ("asm", 1);
860 layout
->add_window ("status", 0);
861 layout
->add_window ("cmd", 1);
862 add_layout_command ("split", layout
);
864 layout
= new tui_layout_split ();
865 layout
->add_window ("regs", 1);
866 layout
->add_window ("src", 1);
867 layout
->add_window ("status", 0);
868 layout
->add_window ("cmd", 1);
869 layouts
.emplace_back (layout
);
870 src_regs_layout
= layout
;
872 layout
= new tui_layout_split ();
873 layout
->add_window ("regs", 1);
874 layout
->add_window ("asm", 1);
875 layout
->add_window ("status", 0);
876 layout
->add_window ("cmd", 1);
877 layouts
.emplace_back (layout
);
878 asm_regs_layout
= layout
;
883 /* A helper function that returns true if NAME is the name of an
887 validate_window_name (const std::string
&name
)
889 return (name
== "src" || name
== "cmd"
890 || name
== "regs" || name
== "asm"
891 || name
== "status");
894 /* Implementation of the "tui new-layout" command. */
897 tui_new_layout_command (const char *spec
, int from_tty
)
899 std::string new_name
= extract_arg (&spec
);
900 if (new_name
.empty ())
901 error (_("No layout name specified"));
902 if (new_name
[0] == '-')
903 error (_("Layout name cannot start with '-'"));
905 bool is_vertical
= true;
906 spec
= skip_spaces (spec
);
907 if (check_for_argument (&spec
, "-horizontal"))
910 std::vector
<std::unique_ptr
<tui_layout_split
>> splits
;
911 splits
.emplace_back (new tui_layout_split (is_vertical
));
912 std::unordered_set
<std::string
> seen_windows
;
915 spec
= skip_spaces (spec
);
922 spec
= skip_spaces (spec
+ 1);
923 if (check_for_argument (&spec
, "-horizontal"))
925 splits
.emplace_back (new tui_layout_split (is_vertical
));
929 bool is_close
= false;
935 if (splits
.size () == 1)
936 error (_("Extra '}' in layout specification"));
940 name
= extract_arg (&spec
);
943 if (!validate_window_name (name
))
944 error (_("Unknown window \"%s\""), name
.c_str ());
945 if (seen_windows
.find (name
) != seen_windows
.end ())
946 error (_("Window \"%s\" seen twice in layout"), name
.c_str ());
949 ULONGEST weight
= get_ulongest (&spec
, '}');
950 if ((int) weight
!= weight
)
951 error (_("Weight out of range: %s"), pulongest (weight
));
954 std::unique_ptr
<tui_layout_split
> last_split
955 = std::move (splits
.back ());
957 splits
.back ()->add_split (std::move (last_split
), weight
);
961 splits
.back ()->add_window (name
.c_str (), weight
);
962 seen_windows
.insert (name
);
965 if (splits
.size () > 1)
966 error (_("Missing '}' in layout specification"));
967 if (seen_windows
.empty ())
968 error (_("New layout does not contain any windows"));
969 if (seen_windows
.find ("cmd") == seen_windows
.end ())
970 error (_("New layout does not contain the \"cmd\" window"));
972 gdb::unique_xmalloc_ptr
<char> cmd_name
973 = make_unique_xstrdup (new_name
.c_str ());
974 std::unique_ptr
<tui_layout_split
> new_layout
= std::move (splits
.back ());
975 struct cmd_list_element
*cmd
976 = add_layout_command (cmd_name
.get (), new_layout
.get ());
977 cmd
->name_allocated
= 1;
979 new_layout
.release ();
982 /* Base command for "layout". */
985 tui_layout_command (const char *layout_name
, int from_tty
)
987 help_list (layout_list
, "layout ", all_commands
, gdb_stdout
);
990 /* Function to initialize gdb commands, for tui window layout
993 void _initialize_tui_layout ();
995 _initialize_tui_layout ()
997 add_prefix_cmd ("layout", class_tui
, tui_layout_command
, _("\
998 Change the layout of windows.\n\
999 Usage: layout prev | next | LAYOUT-NAME"),
1000 &layout_list
, "layout ", 0, &cmdlist
);
1002 add_cmd ("next", class_tui
, tui_next_layout_command
,
1003 _("Apply the next TUI layout"),
1005 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
1006 _("Apply the previous TUI layout"),
1008 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
1009 _("Apply the TUI register layout"),
1012 add_cmd ("new-layout", class_tui
, tui_new_layout_command
,
1013 _("Create a new TUI layout.\n\
1014 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1015 Create a new TUI layout. The new layout will be named NAME,\n\
1016 and can be accessed using \"layout NAME\".\n\
1017 The windows will be displayed in the specified order.\n\
1018 A WINDOW can also be of the form:\n\
1019 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1020 This form indicates a sub-frame.\n\
1021 Each WEIGHT is an integer, which holds the relative size\n\
1022 to be allocated to the window."),
1023 tui_get_cmd_list ());
1025 initialize_layouts ();