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_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 tui_layout_command (const char *, int);
49 static void extract_display_start_addr (struct gdbarch
**, CORE_ADDR
*);
52 static std::vector
<std::unique_ptr
<tui_layout_split
>> layouts
;
54 /* The layout that is currently applied. */
55 static std::unique_ptr
<tui_layout_base
> applied_layout
;
57 /* The "skeleton" version of the layout that is currently applied. */
58 static tui_layout_split
*applied_skeleton
;
60 /* The two special "regs" layouts. Note that these aren't registered
61 as commands and so can never be deleted. */
62 static tui_layout_split
*src_regs_layout
;
63 static tui_layout_split
*asm_regs_layout
;
66 std::vector
<tui_win_info
*> tui_windows
;
68 /* When applying a layout, this is the list of all windows that were
69 in the previous layout. This is used to re-use windows when
71 static std::vector
<tui_win_info
*> saved_tui_windows
;
73 /* See tui-layout.h. */
76 tui_apply_current_layout ()
78 struct gdbarch
*gdbarch
;
81 extract_display_start_addr (&gdbarch
, &addr
);
83 saved_tui_windows
= std::move (tui_windows
);
86 for (tui_win_info
*win_info
: saved_tui_windows
)
87 win_info
->make_visible (false);
89 applied_layout
->apply (0, 0, tui_term_width (), tui_term_height ());
91 /* Keep the list of internal windows up-to-date. */
92 for (int win_type
= SRC_WIN
; (win_type
< MAX_MAJOR_WINDOWS
); win_type
++)
93 if (tui_win_list
[win_type
] != nullptr
94 && !tui_win_list
[win_type
]->is_visible ())
95 tui_win_list
[win_type
] = nullptr;
97 /* This should always be made visible by a layout. */
98 gdb_assert (TUI_CMD_WIN
->is_visible ());
100 /* Now delete any window that was not re-applied. */
101 tui_win_info
*focus
= tui_win_with_focus ();
102 for (tui_win_info
*win_info
: saved_tui_windows
)
104 if (!win_info
->is_visible ())
106 if (focus
== win_info
)
107 tui_set_win_focus_to (tui_windows
[0]);
112 if (gdbarch
== nullptr && TUI_DISASM_WIN
!= nullptr)
113 tui_get_begin_asm_address (&gdbarch
, &addr
);
114 tui_update_source_windows_with_addr (gdbarch
, addr
);
116 saved_tui_windows
.clear ();
119 /* See tui-layout. */
122 tui_adjust_window_height (struct tui_win_info
*win
, int new_height
)
124 applied_layout
->adjust_size (win
->name (), new_height
);
127 /* Set the current layout to LAYOUT. */
130 tui_set_layout (tui_layout_split
*layout
)
132 applied_skeleton
= layout
;
133 applied_layout
= layout
->clone ();
134 tui_apply_current_layout ();
137 /* See tui-layout.h. */
140 tui_add_win_to_layout (enum tui_win_type type
)
142 gdb_assert (type
== SRC_WIN
|| type
== DISASSEM_WIN
);
144 /* If the window already exists, no need to add it. */
145 if (tui_win_list
[type
] != nullptr)
148 /* If the window we are trying to replace doesn't exist, we're
150 enum tui_win_type other
= type
== SRC_WIN
? DISASSEM_WIN
: SRC_WIN
;
151 if (tui_win_list
[other
] == nullptr)
154 const char *name
= type
== SRC_WIN
? SRC_NAME
: DISASSEM_NAME
;
155 applied_layout
->replace_window (tui_win_list
[other
]->name (), name
);
156 tui_apply_current_layout ();
159 /* Find LAYOUT in the "layouts" global and return its index. */
162 find_layout (tui_layout_split
*layout
)
164 for (size_t i
= 0; i
< layouts
.size (); ++i
)
166 if (layout
== layouts
[i
].get ())
169 gdb_assert_not_reached (_("layout not found!?"));
172 /* Function to set the layout. */
175 tui_apply_layout (struct cmd_list_element
*command
,
176 const char *args
, int from_tty
)
178 tui_layout_split
*layout
179 = (tui_layout_split
*) get_cmd_context (command
);
181 /* Make sure the curses mode is enabled. */
183 tui_set_layout (layout
);
186 /* See tui-layout.h. */
191 size_t index
= find_layout (applied_skeleton
);
193 if (index
== layouts
.size ())
195 tui_set_layout (layouts
[index
].get ());
198 /* Implement the "layout next" command. */
201 tui_next_layout_command (const char *arg
, int from_tty
)
207 /* See tui-layout.h. */
210 tui_set_initial_layout ()
212 tui_set_layout (layouts
[0].get ());
215 /* Implement the "layout prev" command. */
218 tui_prev_layout_command (const char *arg
, int from_tty
)
221 size_t index
= find_layout (applied_skeleton
);
223 index
= layouts
.size ();
225 tui_set_layout (layouts
[index
].get ());
229 /* See tui-layout.h. */
234 /* If there's already a register window, we're done. */
235 if (TUI_DATA_WIN
!= nullptr)
238 tui_set_layout (TUI_DISASM_WIN
!= nullptr
243 /* Implement the "layout regs" command. */
246 tui_regs_layout_command (const char *arg
, int from_tty
)
252 /* See tui-layout.h. */
255 tui_remove_some_windows ()
257 tui_win_info
*focus
= tui_win_with_focus ();
259 if (strcmp (focus
->name (), "cmd") == 0)
261 /* Try leaving the source or disassembly window. If neither
262 exists, just do nothing. */
264 if (focus
== nullptr)
265 focus
= TUI_DISASM_WIN
;
266 if (focus
== nullptr)
270 applied_layout
->remove_windows (focus
->name ());
271 tui_apply_current_layout ();
275 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
277 if (TUI_SRC_WIN
!= nullptr)
278 TUI_SRC_WIN
->display_start_addr (gdbarch_p
, addr_p
);
279 else if (TUI_DISASM_WIN
!= nullptr)
280 TUI_DISASM_WIN
->display_start_addr (gdbarch_p
, addr_p
);
283 *gdbarch_p
= nullptr;
289 tui_gen_win_info::resize (int height_
, int width_
,
290 int origin_x_
, int origin_y_
)
292 if (width
== width_
&& height
== height_
293 && x
== origin_x_
&& y
== origin_y_
294 && handle
!= nullptr)
302 if (handle
!= nullptr)
305 wresize (handle
.get (), height
, width
);
306 mvwin (handle
.get (), y
, x
);
307 wmove (handle
.get (), 0, 0);
309 handle
.reset (nullptr);
313 if (handle
== nullptr)
321 /* Helper function to create one of the built-in (non-locator)
324 template<enum tui_win_type V
, class T
>
325 static tui_gen_win_info
*
326 make_standard_window (const char *)
328 if (tui_win_list
[V
] == nullptr)
329 tui_win_list
[V
] = new T ();
330 return tui_win_list
[V
];
333 /* Helper function to wrap tui_locator_win_info_ptr for
334 tui_get_window_by_name. */
336 static tui_gen_win_info
*
337 get_locator_window (const char *)
339 return tui_locator_win_info_ptr ();
342 /* A map holding all the known window types, keyed by name. Note that
343 this is heap-allocated and "leaked" at gdb exit. This avoids
344 ordering issues with destroying elements in the map at shutdown.
345 In particular, destroying this map can occur after Python has been
346 shut down, causing crashes if any window destruction requires
347 running Python code. */
349 static std::unordered_map
<std::string
, window_factory
> *known_window_types
;
351 /* Helper function that returns a TUI window, given its name. */
353 static tui_gen_win_info
*
354 tui_get_window_by_name (const std::string
&name
)
356 for (tui_win_info
*window
: saved_tui_windows
)
357 if (name
== window
->name ())
360 auto iter
= known_window_types
->find (name
);
361 if (iter
== known_window_types
->end ())
362 error (_("Unknown window type \"%s\""), name
.c_str ());
364 tui_gen_win_info
*result
= iter
->second (name
.c_str ());
365 if (result
== nullptr)
366 error (_("Could not create window \"%s\""), name
.c_str ());
370 /* Initialize the known window types. */
373 initialize_known_windows ()
375 known_window_types
= new std::unordered_map
<std::string
, window_factory
>;
377 known_window_types
->emplace ("src",
378 make_standard_window
<SRC_WIN
,
380 known_window_types
->emplace ("cmd",
381 make_standard_window
<CMD_WIN
, tui_cmd_window
>);
382 known_window_types
->emplace ("regs",
383 make_standard_window
<DATA_WIN
,
385 known_window_types
->emplace ("asm",
386 make_standard_window
<DISASSEM_WIN
,
388 known_window_types
->emplace ("status", get_locator_window
);
391 /* See tui-layout.h. */
394 tui_register_window (const char *name
, window_factory
&&factory
)
396 std::string name_copy
= name
;
398 if (name_copy
== "src" || name_copy
== "cmd" || name_copy
== "regs"
399 || name_copy
== "asm" || name_copy
== "status")
400 error (_("Window type \"%s\" is built-in"), name
);
402 known_window_types
->emplace (std::move (name_copy
),
403 std::move (factory
));
406 /* See tui-layout.h. */
408 std::unique_ptr
<tui_layout_base
>
409 tui_layout_window::clone () const
411 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
412 return std::unique_ptr
<tui_layout_base
> (result
);
415 /* See tui-layout.h. */
418 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
424 gdb_assert (m_window
!= nullptr);
425 m_window
->resize (height
, width
, x
, y
);
426 if (dynamic_cast<tui_win_info
*> (m_window
) != nullptr)
427 tui_windows
.push_back ((tui_win_info
*) m_window
);
430 /* See tui-layout.h. */
433 tui_layout_window::get_sizes (bool height
, int *min_value
, int *max_value
)
435 if (m_window
== nullptr)
436 m_window
= tui_get_window_by_name (m_contents
);
439 *min_value
= m_window
->min_height ();
440 *max_value
= m_window
->max_height ();
444 *min_value
= m_window
->min_width ();
445 *max_value
= m_window
->max_width ();
449 /* See tui-layout.h. */
452 tui_layout_window::top_boxed_p () const
454 gdb_assert (m_window
!= nullptr);
455 return m_window
->can_box ();
458 /* See tui-layout.h. */
461 tui_layout_window::bottom_boxed_p () const
463 gdb_assert (m_window
!= nullptr);
464 return m_window
->can_box ();
467 /* See tui-layout.h. */
470 tui_layout_window::replace_window (const char *name
, const char *new_window
)
472 if (m_contents
== name
)
474 m_contents
= new_window
;
475 if (m_window
!= nullptr)
477 m_window
->make_visible (false);
478 m_window
= tui_get_window_by_name (m_contents
);
483 /* See tui-layout.h. */
486 tui_layout_window::specification (ui_file
*output
, int depth
)
488 fputs_unfiltered (get_name (), output
);
491 /* See tui-layout.h. */
494 tui_layout_split::add_split (std::unique_ptr
<tui_layout_split
> &&layout
,
497 split s
= {weight
, std::move (layout
)};
498 m_splits
.push_back (std::move (s
));
501 /* See tui-layout.h. */
504 tui_layout_split::add_window (const char *name
, int weight
)
506 tui_layout_window
*result
= new tui_layout_window (name
);
507 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
508 m_splits
.push_back (std::move (s
));
511 /* See tui-layout.h. */
513 std::unique_ptr
<tui_layout_base
>
514 tui_layout_split::clone () const
516 tui_layout_split
*result
= new tui_layout_split (m_vertical
);
517 for (const split
&item
: m_splits
)
519 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
520 split s
= {item
.weight
, std::move (next
)};
521 result
->m_splits
.push_back (std::move (s
));
523 return std::unique_ptr
<tui_layout_base
> (result
);
526 /* See tui-layout.h. */
529 tui_layout_split::get_sizes (bool height
, int *min_value
, int *max_value
)
533 bool first_time
= true;
534 for (const split
&item
: m_splits
)
536 int new_min
, new_max
;
537 item
.layout
->get_sizes (height
, &new_min
, &new_max
);
538 /* For the mismatch case, the first time through we want to set
539 the min and max to the computed values -- the "first_time"
540 check here is just a funny way of doing that. */
541 if (height
== m_vertical
|| first_time
)
543 *min_value
+= new_min
;
544 *max_value
+= new_max
;
548 *min_value
= std::max (*min_value
, new_min
);
549 *max_value
= std::min (*max_value
, new_max
);
555 /* See tui-layout.h. */
558 tui_layout_split::top_boxed_p () const
560 if (m_splits
.empty ())
562 return m_splits
[0].layout
->top_boxed_p ();
565 /* See tui-layout.h. */
568 tui_layout_split::bottom_boxed_p () const
570 if (m_splits
.empty ())
572 return m_splits
.back ().layout
->top_boxed_p ();
575 /* See tui-layout.h. */
578 tui_layout_split::set_weights_from_heights ()
580 for (int i
= 0; i
< m_splits
.size (); ++i
)
581 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
584 /* See tui-layout.h. */
587 tui_layout_split::adjust_size (const char *name
, int new_height
)
589 /* Look through the children. If one is a layout holding the named
590 window, we're done; or if one actually is the named window,
592 int found_index
= -1;
593 for (int i
= 0; i
< m_splits
.size (); ++i
)
595 tui_adjust_result adjusted
596 = m_splits
[i
].layout
->adjust_size (name
, new_height
);
597 if (adjusted
== HANDLED
)
599 if (adjusted
== FOUND
)
608 if (found_index
== -1)
610 if (m_splits
[found_index
].layout
->height
== new_height
)
613 set_weights_from_heights ();
614 int delta
= m_splits
[found_index
].weight
- new_height
;
615 m_splits
[found_index
].weight
= new_height
;
617 /* Distribute the "delta" over the next window; but if the next
618 window cannot hold it all, keep going until we either find a
619 window that does, or until we loop all the way around. */
620 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
622 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
624 int new_min
, new_max
;
625 m_splits
[index
].layout
->get_sizes (m_vertical
, &new_min
, &new_max
);
629 /* The primary window grew, so we are trying to shrink other
631 int available
= m_splits
[index
].weight
- new_min
;
632 int shrink_by
= std::min (available
, -delta
);
633 m_splits
[index
].weight
-= shrink_by
;
638 /* The primary window shrank, so we are trying to grow other
640 int available
= new_max
- m_splits
[index
].weight
;
641 int grow_by
= std::min (available
, delta
);
642 m_splits
[index
].weight
+= grow_by
;
649 warning (_("Invalid window height specified"));
650 /* Effectively undo any modifications made here. */
651 set_weights_from_heights ();
655 /* Simply re-apply the updated layout. */
656 apply (x
, y
, width
, height
);
662 /* See tui-layout.h. */
665 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
677 /* True if this window will share a box border with the previous
678 window in the list. */
682 std::vector
<size_info
> info (m_splits
.size ());
684 /* Step 1: Find the min and max size of each sub-layout.
685 Fixed-sized layouts are given their desired size, and then the
686 remaining space is distributed among the remaining windows
687 according to the weights given. */
688 int available_size
= m_vertical
? height
: width
;
690 int total_weight
= 0;
691 for (int i
= 0; i
< m_splits
.size (); ++i
)
693 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
695 /* Always call get_sizes, to ensure that the window is
696 instantiated. This is a bit gross but less gross than adding
697 special cases for this in other places. */
698 m_splits
[i
].layout
->get_sizes (m_vertical
, &info
[i
].min_size
,
702 && cmd_win_already_exists
703 && m_splits
[i
].layout
->get_name () != nullptr
704 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
706 /* If this layout has never been applied, then it means the
707 user just changed the layout. In this situation, it's
708 desirable to keep the size of the command window the
709 same. Setting the min and max sizes this way ensures
710 that the resizing step, below, does the right thing with
712 info
[i
].min_size
= (m_vertical
713 ? TUI_CMD_WIN
->height
714 : TUI_CMD_WIN
->width
);
715 info
[i
].max_size
= info
[i
].min_size
;
718 if (info
[i
].min_size
== info
[i
].max_size
)
719 available_size
-= info
[i
].min_size
;
723 total_weight
+= m_splits
[i
].weight
;
726 /* Two adjacent boxed windows will share a border, making a bit
727 more size available. */
729 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
730 && m_splits
[i
].layout
->top_boxed_p ())
731 info
[i
].share_box
= true;
734 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
735 are given their fixed size, while others are resized according to
738 for (int i
= 0; i
< m_splits
.size (); ++i
)
740 /* Compute the height and clamp to the allowable range. */
741 info
[i
].size
= available_size
* m_splits
[i
].weight
/ total_weight
;
742 if (info
[i
].size
> info
[i
].max_size
)
743 info
[i
].size
= info
[i
].max_size
;
744 if (info
[i
].size
< info
[i
].min_size
)
745 info
[i
].size
= info
[i
].min_size
;
746 /* If there is any leftover size, just redistribute it to the
747 last resizeable window, by dropping it from the allocated
748 size. We could try to be fancier here perhaps, by
749 redistributing this size among all windows, not just the
751 if (info
[i
].min_size
!= info
[i
].max_size
)
753 used_size
+= info
[i
].size
;
754 if (info
[i
].share_box
)
759 /* Allocate any leftover size. */
760 if (available_size
>= used_size
&& last_index
!= -1)
761 info
[last_index
].size
+= available_size
- used_size
;
763 /* Step 3: Resize. */
765 const int maximum
= m_vertical
? height
: width
;
766 for (int i
= 0; i
< m_splits
.size (); ++i
)
768 /* If we fall off the bottom, just make allocations overlap.
770 if (size_accum
+ info
[i
].size
> maximum
)
771 size_accum
= maximum
- info
[i
].size
;
772 else if (info
[i
].share_box
)
775 m_splits
[i
].layout
->apply (x
, y
+ size_accum
, width
, info
[i
].size
);
777 m_splits
[i
].layout
->apply (x
+ size_accum
, y
, info
[i
].size
, height
);
778 size_accum
+= info
[i
].size
;
784 /* See tui-layout.h. */
787 tui_layout_split::remove_windows (const char *name
)
789 for (int i
= 0; i
< m_splits
.size (); ++i
)
791 const char *this_name
= m_splits
[i
].layout
->get_name ();
792 if (this_name
== nullptr)
793 m_splits
[i
].layout
->remove_windows (name
);
796 if (strcmp (this_name
, name
) == 0
797 || strcmp (this_name
, "cmd") == 0)
801 m_splits
.erase (m_splits
.begin () + i
);
807 /* See tui-layout.h. */
810 tui_layout_split::replace_window (const char *name
, const char *new_window
)
812 for (auto &item
: m_splits
)
813 item
.layout
->replace_window (name
, new_window
);
816 /* See tui-layout.h. */
819 tui_layout_split::specification (ui_file
*output
, int depth
)
822 fputs_unfiltered ("{", output
);
825 fputs_unfiltered ("-horizontal ", output
);
828 for (auto &item
: m_splits
)
831 fputs_unfiltered (" ", output
);
833 item
.layout
->specification (output
, depth
+ 1);
834 fprintf_unfiltered (output
, " %d", item
.weight
);
838 fputs_unfiltered ("}", output
);
841 /* Destroy the layout associated with SELF. */
844 destroy_layout (struct cmd_list_element
*self
, void *context
)
846 tui_layout_split
*layout
= (tui_layout_split
*) context
;
847 size_t index
= find_layout (layout
);
848 layouts
.erase (layouts
.begin () + index
);
851 /* List holding the sub-commands of "layout". */
853 static struct cmd_list_element
*layout_list
;
855 /* Add a "layout" command with name NAME that switches to LAYOUT. */
857 static struct cmd_list_element
*
858 add_layout_command (const char *name
, tui_layout_split
*layout
)
860 struct cmd_list_element
*cmd
;
863 layout
->specification (&spec
, 0);
865 gdb::unique_xmalloc_ptr
<char> doc
866 (xstrprintf (_("Apply the \"%s\" layout.\n\
867 This layout was created using:\n\
868 tui new-layout %s %s"),
869 name
, name
, spec
.c_str ()));
871 cmd
= add_cmd (name
, class_tui
, nullptr, doc
.get (), &layout_list
);
872 set_cmd_context (cmd
, layout
);
873 /* There is no API to set this. */
874 cmd
->func
= tui_apply_layout
;
875 cmd
->destroyer
= destroy_layout
;
876 cmd
->doc_allocated
= 1;
878 layouts
.emplace_back (layout
);
883 /* Initialize the standard layouts. */
886 initialize_layouts ()
888 tui_layout_split
*layout
;
890 layout
= new tui_layout_split ();
891 layout
->add_window ("src", 2);
892 layout
->add_window ("status", 0);
893 layout
->add_window ("cmd", 1);
894 add_layout_command ("src", layout
);
896 layout
= new tui_layout_split ();
897 layout
->add_window ("asm", 2);
898 layout
->add_window ("status", 0);
899 layout
->add_window ("cmd", 1);
900 add_layout_command ("asm", layout
);
902 layout
= new tui_layout_split ();
903 layout
->add_window ("src", 1);
904 layout
->add_window ("asm", 1);
905 layout
->add_window ("status", 0);
906 layout
->add_window ("cmd", 1);
907 add_layout_command ("split", layout
);
909 layout
= new tui_layout_split ();
910 layout
->add_window ("regs", 1);
911 layout
->add_window ("src", 1);
912 layout
->add_window ("status", 0);
913 layout
->add_window ("cmd", 1);
914 layouts
.emplace_back (layout
);
915 src_regs_layout
= layout
;
917 layout
= new tui_layout_split ();
918 layout
->add_window ("regs", 1);
919 layout
->add_window ("asm", 1);
920 layout
->add_window ("status", 0);
921 layout
->add_window ("cmd", 1);
922 layouts
.emplace_back (layout
);
923 asm_regs_layout
= layout
;
928 /* A helper function that returns true if NAME is the name of an
932 validate_window_name (const std::string
&name
)
934 auto iter
= known_window_types
->find (name
);
935 return iter
!= known_window_types
->end ();
938 /* Implementation of the "tui new-layout" command. */
941 tui_new_layout_command (const char *spec
, int from_tty
)
943 std::string new_name
= extract_arg (&spec
);
944 if (new_name
.empty ())
945 error (_("No layout name specified"));
946 if (new_name
[0] == '-')
947 error (_("Layout name cannot start with '-'"));
949 bool is_vertical
= true;
950 spec
= skip_spaces (spec
);
951 if (check_for_argument (&spec
, "-horizontal"))
954 std::vector
<std::unique_ptr
<tui_layout_split
>> splits
;
955 splits
.emplace_back (new tui_layout_split (is_vertical
));
956 std::unordered_set
<std::string
> seen_windows
;
959 spec
= skip_spaces (spec
);
966 spec
= skip_spaces (spec
+ 1);
967 if (check_for_argument (&spec
, "-horizontal"))
969 splits
.emplace_back (new tui_layout_split (is_vertical
));
973 bool is_close
= false;
979 if (splits
.size () == 1)
980 error (_("Extra '}' in layout specification"));
984 name
= extract_arg (&spec
);
987 if (!validate_window_name (name
))
988 error (_("Unknown window \"%s\""), name
.c_str ());
989 if (seen_windows
.find (name
) != seen_windows
.end ())
990 error (_("Window \"%s\" seen twice in layout"), name
.c_str ());
993 ULONGEST weight
= get_ulongest (&spec
, '}');
994 if ((int) weight
!= weight
)
995 error (_("Weight out of range: %s"), pulongest (weight
));
998 std::unique_ptr
<tui_layout_split
> last_split
999 = std::move (splits
.back ());
1001 splits
.back ()->add_split (std::move (last_split
), weight
);
1005 splits
.back ()->add_window (name
.c_str (), weight
);
1006 seen_windows
.insert (name
);
1009 if (splits
.size () > 1)
1010 error (_("Missing '}' in layout specification"));
1011 if (seen_windows
.empty ())
1012 error (_("New layout does not contain any windows"));
1013 if (seen_windows
.find ("cmd") == seen_windows
.end ())
1014 error (_("New layout does not contain the \"cmd\" window"));
1016 gdb::unique_xmalloc_ptr
<char> cmd_name
1017 = make_unique_xstrdup (new_name
.c_str ());
1018 std::unique_ptr
<tui_layout_split
> new_layout
= std::move (splits
.back ());
1019 struct cmd_list_element
*cmd
1020 = add_layout_command (cmd_name
.get (), new_layout
.get ());
1021 cmd
->name_allocated
= 1;
1022 cmd_name
.release ();
1023 new_layout
.release ();
1026 /* Base command for "layout". */
1029 tui_layout_command (const char *layout_name
, int from_tty
)
1031 help_list (layout_list
, "layout ", all_commands
, gdb_stdout
);
1034 /* Function to initialize gdb commands, for tui window layout
1037 void _initialize_tui_layout ();
1039 _initialize_tui_layout ()
1041 add_prefix_cmd ("layout", class_tui
, tui_layout_command
, _("\
1042 Change the layout of windows.\n\
1043 Usage: layout prev | next | LAYOUT-NAME"),
1044 &layout_list
, "layout ", 0, &cmdlist
);
1046 add_cmd ("next", class_tui
, tui_next_layout_command
,
1047 _("Apply the next TUI layout."),
1049 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
1050 _("Apply the previous TUI layout."),
1052 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
1053 _("Apply the TUI register layout."),
1056 add_cmd ("new-layout", class_tui
, tui_new_layout_command
,
1057 _("Create a new TUI layout.\n\
1058 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1059 Create a new TUI layout. The new layout will be named NAME,\n\
1060 and can be accessed using \"layout NAME\".\n\
1061 The windows will be displayed in the specified order.\n\
1062 A WINDOW can also be of the form:\n\
1063 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1064 This form indicates a sub-frame.\n\
1065 Each WEIGHT is an integer, which holds the relative size\n\
1066 to be allocated to the window."),
1067 tui_get_cmd_list ());
1069 initialize_layouts ();
1070 initialize_known_windows ();