1 /* TUI layout window management.
3 Copyright (C) 1998-2022 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_map>
33 #include <unordered_set>
36 #include "tui/tui-command.h"
37 #include "tui/tui-data.h"
38 #include "tui/tui-wingeneral.h"
39 #include "tui/tui-stack.h"
40 #include "tui/tui-regs.h"
41 #include "tui/tui-win.h"
42 #include "tui/tui-winsource.h"
43 #include "tui/tui-disasm.h"
44 #include "tui/tui-layout.h"
45 #include "tui/tui-source.h"
46 #include "gdb_curses.h"
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 for (tui_win_info
*win_info
: tui_windows
)
78 win_info
->make_visible (false);
80 applied_layout
->apply (0, 0, tui_term_width (), tui_term_height ());
82 /* Keep the list of internal windows up-to-date. */
83 for (int win_type
= SRC_WIN
; (win_type
< MAX_MAJOR_WINDOWS
); win_type
++)
84 if (tui_win_list
[win_type
] != nullptr
85 && !tui_win_list
[win_type
]->is_visible ())
86 tui_win_list
[win_type
] = nullptr;
88 /* This should always be made visible by a layout. */
89 gdb_assert (TUI_CMD_WIN
!= nullptr);
90 gdb_assert (TUI_CMD_WIN
->is_visible ());
92 /* Get the new list of currently visible windows. */
93 std::vector
<tui_win_info
*> new_tui_windows
;
94 applied_layout
->get_windows (&new_tui_windows
);
96 /* Now delete any window that was not re-applied. */
97 tui_win_info
*focus
= tui_win_with_focus ();
98 for (tui_win_info
*win_info
: tui_windows
)
100 if (!win_info
->is_visible ())
102 if (focus
== win_info
)
103 tui_set_win_focus_to (new_tui_windows
[0]);
108 /* Replace the global list of active windows. */
109 tui_windows
= std::move (new_tui_windows
);
111 if (gdbarch
== nullptr && TUI_DISASM_WIN
!= nullptr)
112 tui_get_begin_asm_address (&gdbarch
, &addr
);
113 tui_update_source_windows_with_addr (gdbarch
, addr
);
116 /* See tui-layout. */
119 tui_adjust_window_height (struct tui_win_info
*win
, int new_height
)
121 applied_layout
->adjust_size (win
->name (), new_height
);
124 /* Set the current layout to LAYOUT. */
127 tui_set_layout (tui_layout_split
*layout
)
129 applied_skeleton
= layout
;
130 applied_layout
= layout
->clone ();
131 tui_apply_current_layout ();
134 /* See tui-layout.h. */
137 tui_add_win_to_layout (enum tui_win_type type
)
139 gdb_assert (type
== SRC_WIN
|| type
== DISASSEM_WIN
);
141 /* If the window already exists, no need to add it. */
142 if (tui_win_list
[type
] != nullptr)
145 /* If the window we are trying to replace doesn't exist, we're
147 enum tui_win_type other
= type
== SRC_WIN
? DISASSEM_WIN
: SRC_WIN
;
148 if (tui_win_list
[other
] == nullptr)
151 const char *name
= type
== SRC_WIN
? SRC_NAME
: DISASSEM_NAME
;
152 applied_layout
->replace_window (tui_win_list
[other
]->name (), name
);
153 tui_apply_current_layout ();
156 /* Find LAYOUT in the "layouts" global and return its index. */
159 find_layout (tui_layout_split
*layout
)
161 for (size_t i
= 0; i
< layouts
.size (); ++i
)
163 if (layout
== layouts
[i
].get ())
166 gdb_assert_not_reached ("layout not found!?");
169 /* Function to set the layout. */
172 tui_apply_layout (const char *args
, int from_tty
, cmd_list_element
*command
)
174 tui_layout_split
*layout
= (tui_layout_split
*) command
->context ();
176 /* Make sure the curses mode is enabled. */
178 tui_set_layout (layout
);
181 /* See tui-layout.h. */
186 size_t index
= find_layout (applied_skeleton
);
188 if (index
== layouts
.size ())
190 tui_set_layout (layouts
[index
].get ());
193 /* Implement the "layout next" command. */
196 tui_next_layout_command (const char *arg
, int from_tty
)
202 /* See tui-layout.h. */
205 tui_set_initial_layout ()
207 tui_set_layout (layouts
[0].get ());
210 /* Implement the "layout prev" command. */
213 tui_prev_layout_command (const char *arg
, int from_tty
)
216 size_t index
= find_layout (applied_skeleton
);
218 index
= layouts
.size ();
220 tui_set_layout (layouts
[index
].get ());
224 /* See tui-layout.h. */
229 /* If there's already a register window, we're done. */
230 if (TUI_DATA_WIN
!= nullptr)
233 tui_set_layout (TUI_DISASM_WIN
!= nullptr
238 /* Implement the "layout regs" command. */
241 tui_regs_layout_command (const char *arg
, int from_tty
)
247 /* See tui-layout.h. */
250 tui_remove_some_windows ()
252 tui_win_info
*focus
= tui_win_with_focus ();
254 if (strcmp (focus
->name (), CMD_NAME
) == 0)
256 /* Try leaving the source or disassembly window. If neither
257 exists, just do nothing. */
259 if (focus
== nullptr)
260 focus
= TUI_DISASM_WIN
;
261 if (focus
== nullptr)
265 applied_layout
->remove_windows (focus
->name ());
266 tui_apply_current_layout ();
270 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
272 if (TUI_SRC_WIN
!= nullptr)
273 TUI_SRC_WIN
->display_start_addr (gdbarch_p
, addr_p
);
274 else if (TUI_DISASM_WIN
!= nullptr)
275 TUI_DISASM_WIN
->display_start_addr (gdbarch_p
, addr_p
);
278 *gdbarch_p
= nullptr;
284 tui_win_info::resize (int height_
, int width_
,
285 int origin_x_
, int origin_y_
)
287 if (width
== width_
&& height
== height_
288 && x
== origin_x_
&& y
== origin_y_
289 && handle
!= nullptr)
297 if (handle
!= nullptr)
300 wresize (handle
.get (), height
, width
);
301 mvwin (handle
.get (), y
, x
);
302 wmove (handle
.get (), 0, 0);
304 handle
.reset (nullptr);
308 if (handle
== nullptr)
316 /* Helper function to create one of the built-in (non-locator)
319 template<enum tui_win_type V
, class T
>
320 static tui_win_info
*
321 make_standard_window (const char *)
323 if (tui_win_list
[V
] == nullptr)
324 tui_win_list
[V
] = new T ();
325 return tui_win_list
[V
];
328 /* A map holding all the known window types, keyed by name. Note that
329 this is heap-allocated and "leaked" at gdb exit. This avoids
330 ordering issues with destroying elements in the map at shutdown.
331 In particular, destroying this map can occur after Python has been
332 shut down, causing crashes if any window destruction requires
333 running Python code. */
335 static std::unordered_map
<std::string
, window_factory
> *known_window_types
;
337 /* Helper function that returns a TUI window, given its name. */
339 static tui_win_info
*
340 tui_get_window_by_name (const std::string
&name
)
342 for (tui_win_info
*window
: tui_windows
)
343 if (name
== window
->name ())
346 auto iter
= known_window_types
->find (name
);
347 if (iter
== known_window_types
->end ())
348 error (_("Unknown window type \"%s\""), name
.c_str ());
350 tui_win_info
*result
= iter
->second (name
.c_str ());
351 if (result
== nullptr)
352 error (_("Could not create window \"%s\""), name
.c_str ());
356 /* Initialize the known window types. */
359 initialize_known_windows ()
361 known_window_types
= new std::unordered_map
<std::string
, window_factory
>;
363 known_window_types
->emplace (SRC_NAME
,
364 make_standard_window
<SRC_WIN
,
366 known_window_types
->emplace (CMD_NAME
,
367 make_standard_window
<CMD_WIN
, tui_cmd_window
>);
368 known_window_types
->emplace (DATA_NAME
,
369 make_standard_window
<DATA_WIN
,
371 known_window_types
->emplace (DISASSEM_NAME
,
372 make_standard_window
<DISASSEM_WIN
,
374 known_window_types
->emplace (STATUS_NAME
,
375 make_standard_window
<STATUS_WIN
,
376 tui_locator_window
>);
379 /* See tui-layout.h. */
382 tui_register_window (const char *name
, window_factory
&&factory
)
384 std::string name_copy
= name
;
386 if (name_copy
== SRC_NAME
|| name_copy
== CMD_NAME
|| name_copy
== DATA_NAME
387 || name_copy
== DISASSEM_NAME
|| name_copy
== STATUS_NAME
)
388 error (_("Window type \"%s\" is built-in"), name
);
390 known_window_types
->emplace (std::move (name_copy
),
391 std::move (factory
));
394 /* See tui-layout.h. */
396 std::unique_ptr
<tui_layout_base
>
397 tui_layout_window::clone () const
399 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
400 return std::unique_ptr
<tui_layout_base
> (result
);
403 /* See tui-layout.h. */
406 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
412 gdb_assert (m_window
!= nullptr);
413 m_window
->resize (height
, width
, x
, y
);
416 /* See tui-layout.h. */
419 tui_layout_window::get_sizes (bool height
, int *min_value
, int *max_value
)
421 if (m_window
== nullptr)
422 m_window
= tui_get_window_by_name (m_contents
);
425 *min_value
= m_window
->min_height ();
426 *max_value
= m_window
->max_height ();
430 *min_value
= m_window
->min_width ();
431 *max_value
= m_window
->max_width ();
435 /* See tui-layout.h. */
438 tui_layout_window::top_boxed_p () const
440 gdb_assert (m_window
!= nullptr);
441 return m_window
->can_box ();
444 /* See tui-layout.h. */
447 tui_layout_window::bottom_boxed_p () const
449 gdb_assert (m_window
!= nullptr);
450 return m_window
->can_box ();
453 /* See tui-layout.h. */
456 tui_layout_window::replace_window (const char *name
, const char *new_window
)
458 if (m_contents
== name
)
460 m_contents
= new_window
;
461 if (m_window
!= nullptr)
463 m_window
->make_visible (false);
464 m_window
= tui_get_window_by_name (m_contents
);
469 /* See tui-layout.h. */
472 tui_layout_window::specification (ui_file
*output
, int depth
)
474 fputs_unfiltered (get_name (), output
);
477 /* See tui-layout.h. */
480 tui_layout_split::add_split (std::unique_ptr
<tui_layout_split
> &&layout
,
483 split s
= {weight
, std::move (layout
)};
484 m_splits
.push_back (std::move (s
));
487 /* See tui-layout.h. */
490 tui_layout_split::add_window (const char *name
, int weight
)
492 tui_layout_window
*result
= new tui_layout_window (name
);
493 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
494 m_splits
.push_back (std::move (s
));
497 /* See tui-layout.h. */
499 std::unique_ptr
<tui_layout_base
>
500 tui_layout_split::clone () const
502 tui_layout_split
*result
= new tui_layout_split (m_vertical
);
503 for (const split
&item
: m_splits
)
505 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
506 split s
= {item
.weight
, std::move (next
)};
507 result
->m_splits
.push_back (std::move (s
));
509 return std::unique_ptr
<tui_layout_base
> (result
);
512 /* See tui-layout.h. */
515 tui_layout_split::get_sizes (bool height
, int *min_value
, int *max_value
)
519 bool first_time
= true;
520 for (const split
&item
: m_splits
)
522 int new_min
, new_max
;
523 item
.layout
->get_sizes (height
, &new_min
, &new_max
);
524 /* For the mismatch case, the first time through we want to set
525 the min and max to the computed values -- the "first_time"
526 check here is just a funny way of doing that. */
527 if (height
== m_vertical
|| first_time
)
529 *min_value
+= new_min
;
530 *max_value
+= new_max
;
534 *min_value
= std::max (*min_value
, new_min
);
535 *max_value
= std::min (*max_value
, new_max
);
541 /* See tui-layout.h. */
544 tui_layout_split::top_boxed_p () const
546 if (m_splits
.empty ())
548 return m_splits
[0].layout
->top_boxed_p ();
551 /* See tui-layout.h. */
554 tui_layout_split::bottom_boxed_p () const
556 if (m_splits
.empty ())
558 return m_splits
.back ().layout
->top_boxed_p ();
561 /* See tui-layout.h. */
564 tui_layout_split::set_weights_from_heights ()
566 for (int i
= 0; i
< m_splits
.size (); ++i
)
567 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
570 /* See tui-layout.h. */
573 tui_layout_split::adjust_size (const char *name
, int new_height
)
575 /* Look through the children. If one is a layout holding the named
576 window, we're done; or if one actually is the named window,
578 int found_index
= -1;
579 for (int i
= 0; i
< m_splits
.size (); ++i
)
581 tui_adjust_result adjusted
582 = m_splits
[i
].layout
->adjust_size (name
, new_height
);
583 if (adjusted
== HANDLED
)
585 if (adjusted
== FOUND
)
594 if (found_index
== -1)
596 if (m_splits
[found_index
].layout
->height
== new_height
)
599 set_weights_from_heights ();
600 int delta
= m_splits
[found_index
].weight
- new_height
;
601 m_splits
[found_index
].weight
= new_height
;
603 /* Distribute the "delta" over the next window; but if the next
604 window cannot hold it all, keep going until we either find a
605 window that does, or until we loop all the way around. */
606 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
608 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
610 int new_min
, new_max
;
611 m_splits
[index
].layout
->get_sizes (m_vertical
, &new_min
, &new_max
);
615 /* The primary window grew, so we are trying to shrink other
617 int available
= m_splits
[index
].weight
- new_min
;
618 int shrink_by
= std::min (available
, -delta
);
619 m_splits
[index
].weight
-= shrink_by
;
624 /* The primary window shrank, so we are trying to grow other
626 int available
= new_max
- m_splits
[index
].weight
;
627 int grow_by
= std::min (available
, delta
);
628 m_splits
[index
].weight
+= grow_by
;
635 warning (_("Invalid window height specified"));
636 /* Effectively undo any modifications made here. */
637 set_weights_from_heights ();
641 /* Simply re-apply the updated layout. */
642 apply (x
, y
, width
, height
);
648 /* See tui-layout.h. */
651 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
663 /* True if this window will share a box border with the previous
664 window in the list. */
668 std::vector
<size_info
> info (m_splits
.size ());
670 /* Step 1: Find the min and max size of each sub-layout.
671 Fixed-sized layouts are given their desired size, and then the
672 remaining space is distributed among the remaining windows
673 according to the weights given. */
674 int available_size
= m_vertical
? height
: width
;
676 int total_weight
= 0;
677 for (int i
= 0; i
< m_splits
.size (); ++i
)
679 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
681 /* Always call get_sizes, to ensure that the window is
682 instantiated. This is a bit gross but less gross than adding
683 special cases for this in other places. */
684 m_splits
[i
].layout
->get_sizes (m_vertical
, &info
[i
].min_size
,
688 && cmd_win_already_exists
689 && m_splits
[i
].layout
->get_name () != nullptr
690 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
692 /* If this layout has never been applied, then it means the
693 user just changed the layout. In this situation, it's
694 desirable to keep the size of the command window the
695 same. Setting the min and max sizes this way ensures
696 that the resizing step, below, does the right thing with
698 info
[i
].min_size
= (m_vertical
699 ? TUI_CMD_WIN
->height
700 : TUI_CMD_WIN
->width
);
701 info
[i
].max_size
= info
[i
].min_size
;
704 if (info
[i
].min_size
== info
[i
].max_size
)
705 available_size
-= info
[i
].min_size
;
709 total_weight
+= m_splits
[i
].weight
;
712 /* Two adjacent boxed windows will share a border, making a bit
713 more size available. */
715 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
716 && m_splits
[i
].layout
->top_boxed_p ())
717 info
[i
].share_box
= true;
720 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
721 are given their fixed size, while others are resized according to
724 for (int i
= 0; i
< m_splits
.size (); ++i
)
726 /* Compute the height and clamp to the allowable range. */
727 info
[i
].size
= available_size
* m_splits
[i
].weight
/ total_weight
;
728 if (info
[i
].size
> info
[i
].max_size
)
729 info
[i
].size
= info
[i
].max_size
;
730 if (info
[i
].size
< info
[i
].min_size
)
731 info
[i
].size
= info
[i
].min_size
;
732 /* If there is any leftover size, just redistribute it to the
733 last resizeable window, by dropping it from the allocated
734 size. We could try to be fancier here perhaps, by
735 redistributing this size among all windows, not just the
737 if (info
[i
].min_size
!= info
[i
].max_size
)
739 used_size
+= info
[i
].size
;
740 if (info
[i
].share_box
)
745 /* Allocate any leftover size. */
746 if (available_size
>= used_size
&& last_index
!= -1)
747 info
[last_index
].size
+= available_size
- used_size
;
749 /* Step 3: Resize. */
751 const int maximum
= m_vertical
? height
: width
;
752 for (int i
= 0; i
< m_splits
.size (); ++i
)
754 /* If we fall off the bottom, just make allocations overlap.
756 if (size_accum
+ info
[i
].size
> maximum
)
757 size_accum
= maximum
- info
[i
].size
;
758 else if (info
[i
].share_box
)
761 m_splits
[i
].layout
->apply (x
, y
+ size_accum
, width
, info
[i
].size
);
763 m_splits
[i
].layout
->apply (x
+ size_accum
, y
, info
[i
].size
, height
);
764 size_accum
+= info
[i
].size
;
770 /* See tui-layout.h. */
773 tui_layout_split::remove_windows (const char *name
)
775 for (int i
= 0; i
< m_splits
.size (); ++i
)
777 const char *this_name
= m_splits
[i
].layout
->get_name ();
778 if (this_name
== nullptr)
779 m_splits
[i
].layout
->remove_windows (name
);
780 else if (strcmp (this_name
, name
) == 0
781 || strcmp (this_name
, CMD_NAME
) == 0
782 || strcmp (this_name
, STATUS_NAME
) == 0)
788 m_splits
.erase (m_splits
.begin () + i
);
794 /* See tui-layout.h. */
797 tui_layout_split::replace_window (const char *name
, const char *new_window
)
799 for (auto &item
: m_splits
)
800 item
.layout
->replace_window (name
, new_window
);
803 /* See tui-layout.h. */
806 tui_layout_split::specification (ui_file
*output
, int depth
)
809 fputs_unfiltered ("{", output
);
812 fputs_unfiltered ("-horizontal ", output
);
815 for (auto &item
: m_splits
)
818 fputs_unfiltered (" ", output
);
820 item
.layout
->specification (output
, depth
+ 1);
821 fprintf_unfiltered (output
, " %d", item
.weight
);
825 fputs_unfiltered ("}", output
);
828 /* Destroy the layout associated with SELF. */
831 destroy_layout (struct cmd_list_element
*self
, void *context
)
833 tui_layout_split
*layout
= (tui_layout_split
*) context
;
834 size_t index
= find_layout (layout
);
835 layouts
.erase (layouts
.begin () + index
);
838 /* List holding the sub-commands of "layout". */
840 static struct cmd_list_element
*layout_list
;
842 /* Add a "layout" command with name NAME that switches to LAYOUT. */
844 static struct cmd_list_element
*
845 add_layout_command (const char *name
, tui_layout_split
*layout
)
847 struct cmd_list_element
*cmd
;
850 layout
->specification (&spec
, 0);
852 gdb::unique_xmalloc_ptr
<char> doc
853 = xstrprintf (_("Apply the \"%s\" layout.\n\
854 This layout was created using:\n\
855 tui new-layout %s %s"),
856 name
, name
, spec
.c_str ());
858 cmd
= add_cmd (name
, class_tui
, nullptr, doc
.get (), &layout_list
);
859 cmd
->set_context (layout
);
860 /* There is no API to set this. */
861 cmd
->func
= tui_apply_layout
;
862 cmd
->destroyer
= destroy_layout
;
863 cmd
->doc_allocated
= 1;
865 layouts
.emplace_back (layout
);
870 /* Initialize the standard layouts. */
873 initialize_layouts ()
875 tui_layout_split
*layout
;
877 layout
= new tui_layout_split ();
878 layout
->add_window (SRC_NAME
, 2);
879 layout
->add_window (STATUS_NAME
, 0);
880 layout
->add_window (CMD_NAME
, 1);
881 add_layout_command (SRC_NAME
, layout
);
883 layout
= new tui_layout_split ();
884 layout
->add_window (DISASSEM_NAME
, 2);
885 layout
->add_window (STATUS_NAME
, 0);
886 layout
->add_window (CMD_NAME
, 1);
887 add_layout_command (DISASSEM_NAME
, layout
);
889 layout
= new tui_layout_split ();
890 layout
->add_window (SRC_NAME
, 1);
891 layout
->add_window (DISASSEM_NAME
, 1);
892 layout
->add_window (STATUS_NAME
, 0);
893 layout
->add_window (CMD_NAME
, 1);
894 add_layout_command ("split", layout
);
896 layout
= new tui_layout_split ();
897 layout
->add_window (DATA_NAME
, 1);
898 layout
->add_window (SRC_NAME
, 1);
899 layout
->add_window (STATUS_NAME
, 0);
900 layout
->add_window (CMD_NAME
, 1);
901 layouts
.emplace_back (layout
);
902 src_regs_layout
= layout
;
904 layout
= new tui_layout_split ();
905 layout
->add_window (DATA_NAME
, 1);
906 layout
->add_window (DISASSEM_NAME
, 1);
907 layout
->add_window (STATUS_NAME
, 0);
908 layout
->add_window (CMD_NAME
, 1);
909 layouts
.emplace_back (layout
);
910 asm_regs_layout
= layout
;
915 /* A helper function that returns true if NAME is the name of an
919 validate_window_name (const std::string
&name
)
921 auto iter
= known_window_types
->find (name
);
922 return iter
!= known_window_types
->end ();
925 /* Implementation of the "tui new-layout" command. */
928 tui_new_layout_command (const char *spec
, int from_tty
)
930 std::string new_name
= extract_arg (&spec
);
931 if (new_name
.empty ())
932 error (_("No layout name specified"));
933 if (new_name
[0] == '-')
934 error (_("Layout name cannot start with '-'"));
936 bool is_vertical
= true;
937 spec
= skip_spaces (spec
);
938 if (check_for_argument (&spec
, "-horizontal"))
941 std::vector
<std::unique_ptr
<tui_layout_split
>> splits
;
942 splits
.emplace_back (new tui_layout_split (is_vertical
));
943 std::unordered_set
<std::string
> seen_windows
;
946 spec
= skip_spaces (spec
);
953 spec
= skip_spaces (spec
+ 1);
954 if (check_for_argument (&spec
, "-horizontal"))
956 splits
.emplace_back (new tui_layout_split (is_vertical
));
960 bool is_close
= false;
966 if (splits
.size () == 1)
967 error (_("Extra '}' in layout specification"));
971 name
= extract_arg (&spec
);
974 if (!validate_window_name (name
))
975 error (_("Unknown window \"%s\""), name
.c_str ());
976 if (seen_windows
.find (name
) != seen_windows
.end ())
977 error (_("Window \"%s\" seen twice in layout"), name
.c_str ());
980 ULONGEST weight
= get_ulongest (&spec
, '}');
981 if ((int) weight
!= weight
)
982 error (_("Weight out of range: %s"), pulongest (weight
));
985 std::unique_ptr
<tui_layout_split
> last_split
986 = std::move (splits
.back ());
988 splits
.back ()->add_split (std::move (last_split
), weight
);
992 splits
.back ()->add_window (name
.c_str (), weight
);
993 seen_windows
.insert (name
);
996 if (splits
.size () > 1)
997 error (_("Missing '}' in layout specification"));
998 if (seen_windows
.empty ())
999 error (_("New layout does not contain any windows"));
1000 if (seen_windows
.find (CMD_NAME
) == seen_windows
.end ())
1001 error (_("New layout does not contain the \"" CMD_NAME
"\" window"));
1003 gdb::unique_xmalloc_ptr
<char> cmd_name
1004 = make_unique_xstrdup (new_name
.c_str ());
1005 std::unique_ptr
<tui_layout_split
> new_layout
= std::move (splits
.back ());
1006 struct cmd_list_element
*cmd
1007 = add_layout_command (cmd_name
.get (), new_layout
.get ());
1008 cmd
->name_allocated
= 1;
1009 cmd_name
.release ();
1010 new_layout
.release ();
1013 /* Function to initialize gdb commands, for tui window layout
1016 void _initialize_tui_layout ();
1018 _initialize_tui_layout ()
1020 add_basic_prefix_cmd ("layout", class_tui
, _("\
1021 Change the layout of windows.\n\
1022 Usage: layout prev | next | LAYOUT-NAME"),
1023 &layout_list
, 0, &cmdlist
);
1025 add_cmd ("next", class_tui
, tui_next_layout_command
,
1026 _("Apply the next TUI layout."),
1028 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
1029 _("Apply the previous TUI layout."),
1031 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
1032 _("Apply the TUI register layout."),
1035 add_cmd ("new-layout", class_tui
, tui_new_layout_command
,
1036 _("Create a new TUI layout.\n\
1037 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1038 Create a new TUI layout. The new layout will be named NAME,\n\
1039 and can be accessed using \"layout NAME\".\n\
1040 The windows will be displayed in the specified order.\n\
1041 A WINDOW can also be of the form:\n\
1042 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1043 This form indicates a sub-frame.\n\
1044 Each WEIGHT is an integer, which holds the relative size\n\
1045 to be allocated to the window."),
1046 tui_get_cmd_list ());
1048 initialize_layouts ();
1049 initialize_known_windows ();