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 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 /* When applying a layout, this is the list of all windows that were
68 in the previous layout. This is used to re-use windows when
70 static std::vector
<tui_win_info
*> saved_tui_windows
;
72 /* See tui-layout.h. */
75 tui_apply_current_layout ()
77 struct gdbarch
*gdbarch
;
80 extract_display_start_addr (&gdbarch
, &addr
);
82 saved_tui_windows
= std::move (tui_windows
);
85 for (tui_win_info
*win_info
: saved_tui_windows
)
86 win_info
->make_visible (false);
88 applied_layout
->apply (0, 0, tui_term_width (), tui_term_height ());
90 /* Keep the list of internal windows up-to-date. */
91 for (int win_type
= SRC_WIN
; (win_type
< MAX_MAJOR_WINDOWS
); win_type
++)
92 if (tui_win_list
[win_type
] != nullptr
93 && !tui_win_list
[win_type
]->is_visible ())
94 tui_win_list
[win_type
] = nullptr;
96 /* This should always be made visible by a layout. */
97 gdb_assert (TUI_CMD_WIN
->is_visible ());
99 /* Now delete any window that was not re-applied. */
100 tui_win_info
*focus
= tui_win_with_focus ();
101 for (tui_win_info
*win_info
: saved_tui_windows
)
103 if (!win_info
->is_visible ())
105 if (focus
== win_info
)
106 tui_set_win_focus_to (tui_windows
[0]);
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
);
115 saved_tui_windows
.clear ();
118 /* See tui-layout. */
121 tui_adjust_window_height (struct tui_win_info
*win
, int new_height
)
123 applied_layout
->adjust_size (win
->name (), new_height
);
126 /* Set the current layout to LAYOUT. */
129 tui_set_layout (tui_layout_split
*layout
)
131 applied_skeleton
= layout
;
132 applied_layout
= layout
->clone ();
133 tui_apply_current_layout ();
136 /* See tui-layout.h. */
139 tui_add_win_to_layout (enum tui_win_type type
)
141 gdb_assert (type
== SRC_WIN
|| type
== DISASSEM_WIN
);
143 /* If the window already exists, no need to add it. */
144 if (tui_win_list
[type
] != nullptr)
147 /* If the window we are trying to replace doesn't exist, we're
149 enum tui_win_type other
= type
== SRC_WIN
? DISASSEM_WIN
: SRC_WIN
;
150 if (tui_win_list
[other
] == nullptr)
153 const char *name
= type
== SRC_WIN
? SRC_NAME
: DISASSEM_NAME
;
154 applied_layout
->replace_window (tui_win_list
[other
]->name (), name
);
155 tui_apply_current_layout ();
158 /* Find LAYOUT in the "layouts" global and return its index. */
161 find_layout (tui_layout_split
*layout
)
163 for (size_t i
= 0; i
< layouts
.size (); ++i
)
165 if (layout
== layouts
[i
].get ())
168 gdb_assert_not_reached (_("layout not found!?"));
171 /* Function to set the layout. */
174 tui_apply_layout (struct cmd_list_element
*command
,
175 const char *args
, int from_tty
)
177 tui_layout_split
*layout
178 = (tui_layout_split
*) get_cmd_context (command
);
180 /* Make sure the curses mode is enabled. */
182 tui_set_layout (layout
);
185 /* See tui-layout.h. */
190 size_t index
= find_layout (applied_skeleton
);
192 if (index
== layouts
.size ())
194 tui_set_layout (layouts
[index
].get ());
197 /* Implement the "layout next" command. */
200 tui_next_layout_command (const char *arg
, int from_tty
)
206 /* See tui-layout.h. */
209 tui_set_initial_layout ()
211 tui_set_layout (layouts
[0].get ());
214 /* Implement the "layout prev" command. */
217 tui_prev_layout_command (const char *arg
, int from_tty
)
220 size_t index
= find_layout (applied_skeleton
);
222 index
= layouts
.size ();
224 tui_set_layout (layouts
[index
].get ());
228 /* See tui-layout.h. */
233 /* If there's already a register window, we're done. */
234 if (TUI_DATA_WIN
!= nullptr)
237 tui_set_layout (TUI_DISASM_WIN
!= nullptr
242 /* Implement the "layout regs" command. */
245 tui_regs_layout_command (const char *arg
, int from_tty
)
251 /* See tui-layout.h. */
254 tui_remove_some_windows ()
256 tui_win_info
*focus
= tui_win_with_focus ();
258 if (strcmp (focus
->name (), CMD_NAME
) == 0)
260 /* Try leaving the source or disassembly window. If neither
261 exists, just do nothing. */
263 if (focus
== nullptr)
264 focus
= TUI_DISASM_WIN
;
265 if (focus
== nullptr)
269 applied_layout
->remove_windows (focus
->name ());
270 tui_apply_current_layout ();
274 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
276 if (TUI_SRC_WIN
!= nullptr)
277 TUI_SRC_WIN
->display_start_addr (gdbarch_p
, addr_p
);
278 else if (TUI_DISASM_WIN
!= nullptr)
279 TUI_DISASM_WIN
->display_start_addr (gdbarch_p
, addr_p
);
282 *gdbarch_p
= nullptr;
288 tui_gen_win_info::resize (int height_
, int width_
,
289 int origin_x_
, int origin_y_
)
291 if (width
== width_
&& height
== height_
292 && x
== origin_x_
&& y
== origin_y_
293 && handle
!= nullptr)
301 if (handle
!= nullptr)
304 wresize (handle
.get (), height
, width
);
305 mvwin (handle
.get (), y
, x
);
306 wmove (handle
.get (), 0, 0);
308 handle
.reset (nullptr);
312 if (handle
== nullptr)
320 /* Helper function to create one of the built-in (non-locator)
323 template<enum tui_win_type V
, class T
>
324 static tui_gen_win_info
*
325 make_standard_window (const char *)
327 if (tui_win_list
[V
] == nullptr)
328 tui_win_list
[V
] = new T ();
329 return tui_win_list
[V
];
332 /* Helper function to wrap tui_locator_win_info_ptr for
333 tui_get_window_by_name. */
335 static tui_gen_win_info
*
336 get_locator_window (const char *)
338 return tui_locator_win_info_ptr ();
341 /* A map holding all the known window types, keyed by name. Note that
342 this is heap-allocated and "leaked" at gdb exit. This avoids
343 ordering issues with destroying elements in the map at shutdown.
344 In particular, destroying this map can occur after Python has been
345 shut down, causing crashes if any window destruction requires
346 running Python code. */
348 static std::unordered_map
<std::string
, window_factory
> *known_window_types
;
350 /* Helper function that returns a TUI window, given its name. */
352 static tui_gen_win_info
*
353 tui_get_window_by_name (const std::string
&name
)
355 for (tui_win_info
*window
: saved_tui_windows
)
356 if (name
== window
->name ())
359 auto iter
= known_window_types
->find (name
);
360 if (iter
== known_window_types
->end ())
361 error (_("Unknown window type \"%s\""), name
.c_str ());
363 tui_gen_win_info
*result
= iter
->second (name
.c_str ());
364 if (result
== nullptr)
365 error (_("Could not create window \"%s\""), name
.c_str ());
369 /* Initialize the known window types. */
372 initialize_known_windows ()
374 known_window_types
= new std::unordered_map
<std::string
, window_factory
>;
376 known_window_types
->emplace (SRC_NAME
,
377 make_standard_window
<SRC_WIN
,
379 known_window_types
->emplace (CMD_NAME
,
380 make_standard_window
<CMD_WIN
, tui_cmd_window
>);
381 known_window_types
->emplace (DATA_NAME
,
382 make_standard_window
<DATA_WIN
,
384 known_window_types
->emplace (DISASSEM_NAME
,
385 make_standard_window
<DISASSEM_WIN
,
387 known_window_types
->emplace (STATUS_NAME
, get_locator_window
);
390 /* See tui-layout.h. */
393 tui_register_window (const char *name
, window_factory
&&factory
)
395 std::string name_copy
= name
;
397 if (name_copy
== SRC_NAME
|| name_copy
== CMD_NAME
|| name_copy
== DATA_NAME
398 || name_copy
== DISASSEM_NAME
|| name_copy
== STATUS_NAME
)
399 error (_("Window type \"%s\" is built-in"), name
);
401 known_window_types
->emplace (std::move (name_copy
),
402 std::move (factory
));
405 /* See tui-layout.h. */
407 std::unique_ptr
<tui_layout_base
>
408 tui_layout_window::clone () const
410 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
411 return std::unique_ptr
<tui_layout_base
> (result
);
414 /* See tui-layout.h. */
417 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
423 gdb_assert (m_window
!= nullptr);
424 m_window
->resize (height
, width
, x
, y
);
425 if (dynamic_cast<tui_win_info
*> (m_window
) != nullptr)
426 tui_windows
.push_back ((tui_win_info
*) m_window
);
429 /* See tui-layout.h. */
432 tui_layout_window::get_sizes (bool height
, int *min_value
, int *max_value
)
434 if (m_window
== nullptr)
435 m_window
= tui_get_window_by_name (m_contents
);
438 *min_value
= m_window
->min_height ();
439 *max_value
= m_window
->max_height ();
443 *min_value
= m_window
->min_width ();
444 *max_value
= m_window
->max_width ();
448 /* See tui-layout.h. */
451 tui_layout_window::top_boxed_p () const
453 gdb_assert (m_window
!= nullptr);
454 return m_window
->can_box ();
457 /* See tui-layout.h. */
460 tui_layout_window::bottom_boxed_p () const
462 gdb_assert (m_window
!= nullptr);
463 return m_window
->can_box ();
466 /* See tui-layout.h. */
469 tui_layout_window::replace_window (const char *name
, const char *new_window
)
471 if (m_contents
== name
)
473 m_contents
= new_window
;
474 if (m_window
!= nullptr)
476 m_window
->make_visible (false);
477 m_window
= tui_get_window_by_name (m_contents
);
482 /* See tui-layout.h. */
485 tui_layout_window::specification (ui_file
*output
, int depth
)
487 fputs_unfiltered (get_name (), output
);
490 /* See tui-layout.h. */
493 tui_layout_split::add_split (std::unique_ptr
<tui_layout_split
> &&layout
,
496 split s
= {weight
, std::move (layout
)};
497 m_splits
.push_back (std::move (s
));
500 /* See tui-layout.h. */
503 tui_layout_split::add_window (const char *name
, int weight
)
505 tui_layout_window
*result
= new tui_layout_window (name
);
506 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
507 m_splits
.push_back (std::move (s
));
510 /* See tui-layout.h. */
512 std::unique_ptr
<tui_layout_base
>
513 tui_layout_split::clone () const
515 tui_layout_split
*result
= new tui_layout_split (m_vertical
);
516 for (const split
&item
: m_splits
)
518 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
519 split s
= {item
.weight
, std::move (next
)};
520 result
->m_splits
.push_back (std::move (s
));
522 return std::unique_ptr
<tui_layout_base
> (result
);
525 /* See tui-layout.h. */
528 tui_layout_split::get_sizes (bool height
, int *min_value
, int *max_value
)
532 bool first_time
= true;
533 for (const split
&item
: m_splits
)
535 int new_min
, new_max
;
536 item
.layout
->get_sizes (height
, &new_min
, &new_max
);
537 /* For the mismatch case, the first time through we want to set
538 the min and max to the computed values -- the "first_time"
539 check here is just a funny way of doing that. */
540 if (height
== m_vertical
|| first_time
)
542 *min_value
+= new_min
;
543 *max_value
+= new_max
;
547 *min_value
= std::max (*min_value
, new_min
);
548 *max_value
= std::min (*max_value
, new_max
);
554 /* See tui-layout.h. */
557 tui_layout_split::top_boxed_p () const
559 if (m_splits
.empty ())
561 return m_splits
[0].layout
->top_boxed_p ();
564 /* See tui-layout.h. */
567 tui_layout_split::bottom_boxed_p () const
569 if (m_splits
.empty ())
571 return m_splits
.back ().layout
->top_boxed_p ();
574 /* See tui-layout.h. */
577 tui_layout_split::set_weights_from_heights ()
579 for (int i
= 0; i
< m_splits
.size (); ++i
)
580 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
583 /* See tui-layout.h. */
586 tui_layout_split::adjust_size (const char *name
, int new_height
)
588 /* Look through the children. If one is a layout holding the named
589 window, we're done; or if one actually is the named window,
591 int found_index
= -1;
592 for (int i
= 0; i
< m_splits
.size (); ++i
)
594 tui_adjust_result adjusted
595 = m_splits
[i
].layout
->adjust_size (name
, new_height
);
596 if (adjusted
== HANDLED
)
598 if (adjusted
== FOUND
)
607 if (found_index
== -1)
609 if (m_splits
[found_index
].layout
->height
== new_height
)
612 set_weights_from_heights ();
613 int delta
= m_splits
[found_index
].weight
- new_height
;
614 m_splits
[found_index
].weight
= new_height
;
616 /* Distribute the "delta" over the next window; but if the next
617 window cannot hold it all, keep going until we either find a
618 window that does, or until we loop all the way around. */
619 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
621 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
623 int new_min
, new_max
;
624 m_splits
[index
].layout
->get_sizes (m_vertical
, &new_min
, &new_max
);
628 /* The primary window grew, so we are trying to shrink other
630 int available
= m_splits
[index
].weight
- new_min
;
631 int shrink_by
= std::min (available
, -delta
);
632 m_splits
[index
].weight
-= shrink_by
;
637 /* The primary window shrank, so we are trying to grow other
639 int available
= new_max
- m_splits
[index
].weight
;
640 int grow_by
= std::min (available
, delta
);
641 m_splits
[index
].weight
+= grow_by
;
648 warning (_("Invalid window height specified"));
649 /* Effectively undo any modifications made here. */
650 set_weights_from_heights ();
654 /* Simply re-apply the updated layout. */
655 apply (x
, y
, width
, height
);
661 /* See tui-layout.h. */
664 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
676 /* True if this window will share a box border with the previous
677 window in the list. */
681 std::vector
<size_info
> info (m_splits
.size ());
683 /* Step 1: Find the min and max size of each sub-layout.
684 Fixed-sized layouts are given their desired size, and then the
685 remaining space is distributed among the remaining windows
686 according to the weights given. */
687 int available_size
= m_vertical
? height
: width
;
689 int total_weight
= 0;
690 for (int i
= 0; i
< m_splits
.size (); ++i
)
692 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
694 /* Always call get_sizes, to ensure that the window is
695 instantiated. This is a bit gross but less gross than adding
696 special cases for this in other places. */
697 m_splits
[i
].layout
->get_sizes (m_vertical
, &info
[i
].min_size
,
701 && cmd_win_already_exists
702 && m_splits
[i
].layout
->get_name () != nullptr
703 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
705 /* If this layout has never been applied, then it means the
706 user just changed the layout. In this situation, it's
707 desirable to keep the size of the command window the
708 same. Setting the min and max sizes this way ensures
709 that the resizing step, below, does the right thing with
711 info
[i
].min_size
= (m_vertical
712 ? TUI_CMD_WIN
->height
713 : TUI_CMD_WIN
->width
);
714 info
[i
].max_size
= info
[i
].min_size
;
717 if (info
[i
].min_size
== info
[i
].max_size
)
718 available_size
-= info
[i
].min_size
;
722 total_weight
+= m_splits
[i
].weight
;
725 /* Two adjacent boxed windows will share a border, making a bit
726 more size available. */
728 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
729 && m_splits
[i
].layout
->top_boxed_p ())
730 info
[i
].share_box
= true;
733 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
734 are given their fixed size, while others are resized according to
737 for (int i
= 0; i
< m_splits
.size (); ++i
)
739 /* Compute the height and clamp to the allowable range. */
740 info
[i
].size
= available_size
* m_splits
[i
].weight
/ total_weight
;
741 if (info
[i
].size
> info
[i
].max_size
)
742 info
[i
].size
= info
[i
].max_size
;
743 if (info
[i
].size
< info
[i
].min_size
)
744 info
[i
].size
= info
[i
].min_size
;
745 /* If there is any leftover size, just redistribute it to the
746 last resizeable window, by dropping it from the allocated
747 size. We could try to be fancier here perhaps, by
748 redistributing this size among all windows, not just the
750 if (info
[i
].min_size
!= info
[i
].max_size
)
752 used_size
+= info
[i
].size
;
753 if (info
[i
].share_box
)
758 /* Allocate any leftover size. */
759 if (available_size
>= used_size
&& last_index
!= -1)
760 info
[last_index
].size
+= available_size
- used_size
;
762 /* Step 3: Resize. */
764 const int maximum
= m_vertical
? height
: width
;
765 for (int i
= 0; i
< m_splits
.size (); ++i
)
767 /* If we fall off the bottom, just make allocations overlap.
769 if (size_accum
+ info
[i
].size
> maximum
)
770 size_accum
= maximum
- info
[i
].size
;
771 else if (info
[i
].share_box
)
774 m_splits
[i
].layout
->apply (x
, y
+ size_accum
, width
, info
[i
].size
);
776 m_splits
[i
].layout
->apply (x
+ size_accum
, y
, info
[i
].size
, height
);
777 size_accum
+= info
[i
].size
;
783 /* See tui-layout.h. */
786 tui_layout_split::remove_windows (const char *name
)
788 for (int i
= 0; i
< m_splits
.size (); ++i
)
790 const char *this_name
= m_splits
[i
].layout
->get_name ();
791 if (this_name
== nullptr)
792 m_splits
[i
].layout
->remove_windows (name
);
793 else if (strcmp (this_name
, name
) == 0
794 || strcmp (this_name
, CMD_NAME
) == 0
795 || strcmp (this_name
, STATUS_NAME
) == 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_NAME
, 2);
892 layout
->add_window (STATUS_NAME
, 0);
893 layout
->add_window (CMD_NAME
, 1);
894 add_layout_command (SRC_NAME
, layout
);
896 layout
= new tui_layout_split ();
897 layout
->add_window (DISASSEM_NAME
, 2);
898 layout
->add_window (STATUS_NAME
, 0);
899 layout
->add_window (CMD_NAME
, 1);
900 add_layout_command (DISASSEM_NAME
, layout
);
902 layout
= new tui_layout_split ();
903 layout
->add_window (SRC_NAME
, 1);
904 layout
->add_window (DISASSEM_NAME
, 1);
905 layout
->add_window (STATUS_NAME
, 0);
906 layout
->add_window (CMD_NAME
, 1);
907 add_layout_command ("split", layout
);
909 layout
= new tui_layout_split ();
910 layout
->add_window (DATA_NAME
, 1);
911 layout
->add_window (SRC_NAME
, 1);
912 layout
->add_window (STATUS_NAME
, 0);
913 layout
->add_window (CMD_NAME
, 1);
914 layouts
.emplace_back (layout
);
915 src_regs_layout
= layout
;
917 layout
= new tui_layout_split ();
918 layout
->add_window (DATA_NAME
, 1);
919 layout
->add_window (DISASSEM_NAME
, 1);
920 layout
->add_window (STATUS_NAME
, 0);
921 layout
->add_window (CMD_NAME
, 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_NAME
) == seen_windows
.end ())
1014 error (_("New layout does not contain the \"" CMD_NAME
"\" 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 /* Function to initialize gdb commands, for tui window layout
1029 void _initialize_tui_layout ();
1031 _initialize_tui_layout ()
1033 add_basic_prefix_cmd ("layout", class_tui
, _("\
1034 Change the layout of windows.\n\
1035 Usage: layout prev | next | LAYOUT-NAME"),
1036 &layout_list
, "layout ", 0, &cmdlist
);
1038 add_cmd ("next", class_tui
, tui_next_layout_command
,
1039 _("Apply the next TUI layout."),
1041 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
1042 _("Apply the previous TUI layout."),
1044 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
1045 _("Apply the TUI register layout."),
1048 add_cmd ("new-layout", class_tui
, tui_new_layout_command
,
1049 _("Create a new TUI layout.\n\
1050 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1051 Create a new TUI layout. The new layout will be named NAME,\n\
1052 and can be accessed using \"layout NAME\".\n\
1053 The windows will be displayed in the specified order.\n\
1054 A WINDOW can also be of the form:\n\
1055 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1056 This form indicates a sub-frame.\n\
1057 Each WEIGHT is an integer, which holds the relative size\n\
1058 to be allocated to the window."),
1059 tui_get_cmd_list ());
1061 initialize_layouts ();
1062 initialize_known_windows ();