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 (struct cmd_list_element
*command
,
173 const char *args
, int from_tty
)
175 tui_layout_split
*layout
= (tui_layout_split
*) command
->context ();
177 /* Make sure the curses mode is enabled. */
179 tui_set_layout (layout
);
182 /* See tui-layout.h. */
187 size_t index
= find_layout (applied_skeleton
);
189 if (index
== layouts
.size ())
191 tui_set_layout (layouts
[index
].get ());
194 /* Implement the "layout next" command. */
197 tui_next_layout_command (const char *arg
, int from_tty
)
203 /* See tui-layout.h. */
206 tui_set_initial_layout ()
208 tui_set_layout (layouts
[0].get ());
211 /* Implement the "layout prev" command. */
214 tui_prev_layout_command (const char *arg
, int from_tty
)
217 size_t index
= find_layout (applied_skeleton
);
219 index
= layouts
.size ();
221 tui_set_layout (layouts
[index
].get ());
225 /* See tui-layout.h. */
230 /* If there's already a register window, we're done. */
231 if (TUI_DATA_WIN
!= nullptr)
234 tui_set_layout (TUI_DISASM_WIN
!= nullptr
239 /* Implement the "layout regs" command. */
242 tui_regs_layout_command (const char *arg
, int from_tty
)
248 /* See tui-layout.h. */
251 tui_remove_some_windows ()
253 tui_win_info
*focus
= tui_win_with_focus ();
255 if (strcmp (focus
->name (), CMD_NAME
) == 0)
257 /* Try leaving the source or disassembly window. If neither
258 exists, just do nothing. */
260 if (focus
== nullptr)
261 focus
= TUI_DISASM_WIN
;
262 if (focus
== nullptr)
266 applied_layout
->remove_windows (focus
->name ());
267 tui_apply_current_layout ();
271 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
273 if (TUI_SRC_WIN
!= nullptr)
274 TUI_SRC_WIN
->display_start_addr (gdbarch_p
, addr_p
);
275 else if (TUI_DISASM_WIN
!= nullptr)
276 TUI_DISASM_WIN
->display_start_addr (gdbarch_p
, addr_p
);
279 *gdbarch_p
= nullptr;
285 tui_win_info::resize (int height_
, int width_
,
286 int origin_x_
, int origin_y_
)
288 if (width
== width_
&& height
== height_
289 && x
== origin_x_
&& y
== origin_y_
290 && handle
!= nullptr)
298 if (handle
!= nullptr)
301 wresize (handle
.get (), height
, width
);
302 mvwin (handle
.get (), y
, x
);
303 wmove (handle
.get (), 0, 0);
305 handle
.reset (nullptr);
309 if (handle
== nullptr)
317 /* Helper function to create one of the built-in (non-locator)
320 template<enum tui_win_type V
, class T
>
321 static tui_win_info
*
322 make_standard_window (const char *)
324 if (tui_win_list
[V
] == nullptr)
325 tui_win_list
[V
] = new T ();
326 return tui_win_list
[V
];
329 /* A map holding all the known window types, keyed by name. Note that
330 this is heap-allocated and "leaked" at gdb exit. This avoids
331 ordering issues with destroying elements in the map at shutdown.
332 In particular, destroying this map can occur after Python has been
333 shut down, causing crashes if any window destruction requires
334 running Python code. */
336 static std::unordered_map
<std::string
, window_factory
> *known_window_types
;
338 /* Helper function that returns a TUI window, given its name. */
340 static tui_win_info
*
341 tui_get_window_by_name (const std::string
&name
)
343 for (tui_win_info
*window
: tui_windows
)
344 if (name
== window
->name ())
347 auto iter
= known_window_types
->find (name
);
348 if (iter
== known_window_types
->end ())
349 error (_("Unknown window type \"%s\""), name
.c_str ());
351 tui_win_info
*result
= iter
->second (name
.c_str ());
352 if (result
== nullptr)
353 error (_("Could not create window \"%s\""), name
.c_str ());
357 /* Initialize the known window types. */
360 initialize_known_windows ()
362 known_window_types
= new std::unordered_map
<std::string
, window_factory
>;
364 known_window_types
->emplace (SRC_NAME
,
365 make_standard_window
<SRC_WIN
,
367 known_window_types
->emplace (CMD_NAME
,
368 make_standard_window
<CMD_WIN
, tui_cmd_window
>);
369 known_window_types
->emplace (DATA_NAME
,
370 make_standard_window
<DATA_WIN
,
372 known_window_types
->emplace (DISASSEM_NAME
,
373 make_standard_window
<DISASSEM_WIN
,
375 known_window_types
->emplace (STATUS_NAME
,
376 make_standard_window
<STATUS_WIN
,
377 tui_locator_window
>);
380 /* See tui-layout.h. */
383 tui_register_window (const char *name
, window_factory
&&factory
)
385 std::string name_copy
= name
;
387 if (name_copy
== SRC_NAME
|| name_copy
== CMD_NAME
|| name_copy
== DATA_NAME
388 || name_copy
== DISASSEM_NAME
|| name_copy
== STATUS_NAME
)
389 error (_("Window type \"%s\" is built-in"), name
);
391 known_window_types
->emplace (std::move (name_copy
),
392 std::move (factory
));
395 /* See tui-layout.h. */
397 std::unique_ptr
<tui_layout_base
>
398 tui_layout_window::clone () const
400 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
401 return std::unique_ptr
<tui_layout_base
> (result
);
404 /* See tui-layout.h. */
407 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
413 gdb_assert (m_window
!= nullptr);
414 m_window
->resize (height
, width
, x
, y
);
417 /* See tui-layout.h. */
420 tui_layout_window::get_sizes (bool height
, int *min_value
, int *max_value
)
422 if (m_window
== nullptr)
423 m_window
= tui_get_window_by_name (m_contents
);
426 *min_value
= m_window
->min_height ();
427 *max_value
= m_window
->max_height ();
431 *min_value
= m_window
->min_width ();
432 *max_value
= m_window
->max_width ();
436 /* See tui-layout.h. */
439 tui_layout_window::top_boxed_p () const
441 gdb_assert (m_window
!= nullptr);
442 return m_window
->can_box ();
445 /* See tui-layout.h. */
448 tui_layout_window::bottom_boxed_p () const
450 gdb_assert (m_window
!= nullptr);
451 return m_window
->can_box ();
454 /* See tui-layout.h. */
457 tui_layout_window::replace_window (const char *name
, const char *new_window
)
459 if (m_contents
== name
)
461 m_contents
= new_window
;
462 if (m_window
!= nullptr)
464 m_window
->make_visible (false);
465 m_window
= tui_get_window_by_name (m_contents
);
470 /* See tui-layout.h. */
473 tui_layout_window::specification (ui_file
*output
, int depth
)
475 fputs_unfiltered (get_name (), output
);
478 /* See tui-layout.h. */
481 tui_layout_split::add_split (std::unique_ptr
<tui_layout_split
> &&layout
,
484 split s
= {weight
, std::move (layout
)};
485 m_splits
.push_back (std::move (s
));
488 /* See tui-layout.h. */
491 tui_layout_split::add_window (const char *name
, int weight
)
493 tui_layout_window
*result
= new tui_layout_window (name
);
494 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
495 m_splits
.push_back (std::move (s
));
498 /* See tui-layout.h. */
500 std::unique_ptr
<tui_layout_base
>
501 tui_layout_split::clone () const
503 tui_layout_split
*result
= new tui_layout_split (m_vertical
);
504 for (const split
&item
: m_splits
)
506 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
507 split s
= {item
.weight
, std::move (next
)};
508 result
->m_splits
.push_back (std::move (s
));
510 return std::unique_ptr
<tui_layout_base
> (result
);
513 /* See tui-layout.h. */
516 tui_layout_split::get_sizes (bool height
, int *min_value
, int *max_value
)
520 bool first_time
= true;
521 for (const split
&item
: m_splits
)
523 int new_min
, new_max
;
524 item
.layout
->get_sizes (height
, &new_min
, &new_max
);
525 /* For the mismatch case, the first time through we want to set
526 the min and max to the computed values -- the "first_time"
527 check here is just a funny way of doing that. */
528 if (height
== m_vertical
|| first_time
)
530 *min_value
+= new_min
;
531 *max_value
+= new_max
;
535 *min_value
= std::max (*min_value
, new_min
);
536 *max_value
= std::min (*max_value
, new_max
);
542 /* See tui-layout.h. */
545 tui_layout_split::top_boxed_p () const
547 if (m_splits
.empty ())
549 return m_splits
[0].layout
->top_boxed_p ();
552 /* See tui-layout.h. */
555 tui_layout_split::bottom_boxed_p () const
557 if (m_splits
.empty ())
559 return m_splits
.back ().layout
->top_boxed_p ();
562 /* See tui-layout.h. */
565 tui_layout_split::set_weights_from_heights ()
567 for (int i
= 0; i
< m_splits
.size (); ++i
)
568 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
571 /* See tui-layout.h. */
574 tui_layout_split::adjust_size (const char *name
, int new_height
)
576 /* Look through the children. If one is a layout holding the named
577 window, we're done; or if one actually is the named window,
579 int found_index
= -1;
580 for (int i
= 0; i
< m_splits
.size (); ++i
)
582 tui_adjust_result adjusted
583 = m_splits
[i
].layout
->adjust_size (name
, new_height
);
584 if (adjusted
== HANDLED
)
586 if (adjusted
== FOUND
)
595 if (found_index
== -1)
597 if (m_splits
[found_index
].layout
->height
== new_height
)
600 set_weights_from_heights ();
601 int delta
= m_splits
[found_index
].weight
- new_height
;
602 m_splits
[found_index
].weight
= new_height
;
604 /* Distribute the "delta" over the next window; but if the next
605 window cannot hold it all, keep going until we either find a
606 window that does, or until we loop all the way around. */
607 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
609 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
611 int new_min
, new_max
;
612 m_splits
[index
].layout
->get_sizes (m_vertical
, &new_min
, &new_max
);
616 /* The primary window grew, so we are trying to shrink other
618 int available
= m_splits
[index
].weight
- new_min
;
619 int shrink_by
= std::min (available
, -delta
);
620 m_splits
[index
].weight
-= shrink_by
;
625 /* The primary window shrank, so we are trying to grow other
627 int available
= new_max
- m_splits
[index
].weight
;
628 int grow_by
= std::min (available
, delta
);
629 m_splits
[index
].weight
+= grow_by
;
636 warning (_("Invalid window height specified"));
637 /* Effectively undo any modifications made here. */
638 set_weights_from_heights ();
642 /* Simply re-apply the updated layout. */
643 apply (x
, y
, width
, height
);
649 /* See tui-layout.h. */
652 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
664 /* True if this window will share a box border with the previous
665 window in the list. */
669 std::vector
<size_info
> info (m_splits
.size ());
671 /* Step 1: Find the min and max size of each sub-layout.
672 Fixed-sized layouts are given their desired size, and then the
673 remaining space is distributed among the remaining windows
674 according to the weights given. */
675 int available_size
= m_vertical
? height
: width
;
677 int total_weight
= 0;
678 for (int i
= 0; i
< m_splits
.size (); ++i
)
680 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
682 /* Always call get_sizes, to ensure that the window is
683 instantiated. This is a bit gross but less gross than adding
684 special cases for this in other places. */
685 m_splits
[i
].layout
->get_sizes (m_vertical
, &info
[i
].min_size
,
689 && cmd_win_already_exists
690 && m_splits
[i
].layout
->get_name () != nullptr
691 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
693 /* If this layout has never been applied, then it means the
694 user just changed the layout. In this situation, it's
695 desirable to keep the size of the command window the
696 same. Setting the min and max sizes this way ensures
697 that the resizing step, below, does the right thing with
699 info
[i
].min_size
= (m_vertical
700 ? TUI_CMD_WIN
->height
701 : TUI_CMD_WIN
->width
);
702 info
[i
].max_size
= info
[i
].min_size
;
705 if (info
[i
].min_size
== info
[i
].max_size
)
706 available_size
-= info
[i
].min_size
;
710 total_weight
+= m_splits
[i
].weight
;
713 /* Two adjacent boxed windows will share a border, making a bit
714 more size available. */
716 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
717 && m_splits
[i
].layout
->top_boxed_p ())
718 info
[i
].share_box
= true;
721 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
722 are given their fixed size, while others are resized according to
725 for (int i
= 0; i
< m_splits
.size (); ++i
)
727 /* Compute the height and clamp to the allowable range. */
728 info
[i
].size
= available_size
* m_splits
[i
].weight
/ total_weight
;
729 if (info
[i
].size
> info
[i
].max_size
)
730 info
[i
].size
= info
[i
].max_size
;
731 if (info
[i
].size
< info
[i
].min_size
)
732 info
[i
].size
= info
[i
].min_size
;
733 /* If there is any leftover size, just redistribute it to the
734 last resizeable window, by dropping it from the allocated
735 size. We could try to be fancier here perhaps, by
736 redistributing this size among all windows, not just the
738 if (info
[i
].min_size
!= info
[i
].max_size
)
740 used_size
+= info
[i
].size
;
741 if (info
[i
].share_box
)
746 /* Allocate any leftover size. */
747 if (available_size
>= used_size
&& last_index
!= -1)
748 info
[last_index
].size
+= available_size
- used_size
;
750 /* Step 3: Resize. */
752 const int maximum
= m_vertical
? height
: width
;
753 for (int i
= 0; i
< m_splits
.size (); ++i
)
755 /* If we fall off the bottom, just make allocations overlap.
757 if (size_accum
+ info
[i
].size
> maximum
)
758 size_accum
= maximum
- info
[i
].size
;
759 else if (info
[i
].share_box
)
762 m_splits
[i
].layout
->apply (x
, y
+ size_accum
, width
, info
[i
].size
);
764 m_splits
[i
].layout
->apply (x
+ size_accum
, y
, info
[i
].size
, height
);
765 size_accum
+= info
[i
].size
;
771 /* See tui-layout.h. */
774 tui_layout_split::remove_windows (const char *name
)
776 for (int i
= 0; i
< m_splits
.size (); ++i
)
778 const char *this_name
= m_splits
[i
].layout
->get_name ();
779 if (this_name
== nullptr)
780 m_splits
[i
].layout
->remove_windows (name
);
781 else if (strcmp (this_name
, name
) == 0
782 || strcmp (this_name
, CMD_NAME
) == 0
783 || strcmp (this_name
, STATUS_NAME
) == 0)
789 m_splits
.erase (m_splits
.begin () + i
);
795 /* See tui-layout.h. */
798 tui_layout_split::replace_window (const char *name
, const char *new_window
)
800 for (auto &item
: m_splits
)
801 item
.layout
->replace_window (name
, new_window
);
804 /* See tui-layout.h. */
807 tui_layout_split::specification (ui_file
*output
, int depth
)
810 fputs_unfiltered ("{", output
);
813 fputs_unfiltered ("-horizontal ", output
);
816 for (auto &item
: m_splits
)
819 fputs_unfiltered (" ", output
);
821 item
.layout
->specification (output
, depth
+ 1);
822 fprintf_unfiltered (output
, " %d", item
.weight
);
826 fputs_unfiltered ("}", output
);
829 /* Destroy the layout associated with SELF. */
832 destroy_layout (struct cmd_list_element
*self
, void *context
)
834 tui_layout_split
*layout
= (tui_layout_split
*) context
;
835 size_t index
= find_layout (layout
);
836 layouts
.erase (layouts
.begin () + index
);
839 /* List holding the sub-commands of "layout". */
841 static struct cmd_list_element
*layout_list
;
843 /* Add a "layout" command with name NAME that switches to LAYOUT. */
845 static struct cmd_list_element
*
846 add_layout_command (const char *name
, tui_layout_split
*layout
)
848 struct cmd_list_element
*cmd
;
851 layout
->specification (&spec
, 0);
853 gdb::unique_xmalloc_ptr
<char> doc
854 (xstrprintf (_("Apply the \"%s\" layout.\n\
855 This layout was created using:\n\
856 tui new-layout %s %s"),
857 name
, name
, spec
.c_str ()));
859 cmd
= add_cmd (name
, class_tui
, nullptr, doc
.get (), &layout_list
);
860 cmd
->set_context (layout
);
861 /* There is no API to set this. */
862 cmd
->func
= tui_apply_layout
;
863 cmd
->destroyer
= destroy_layout
;
864 cmd
->doc_allocated
= 1;
866 layouts
.emplace_back (layout
);
871 /* Initialize the standard layouts. */
874 initialize_layouts ()
876 tui_layout_split
*layout
;
878 layout
= new tui_layout_split ();
879 layout
->add_window (SRC_NAME
, 2);
880 layout
->add_window (STATUS_NAME
, 0);
881 layout
->add_window (CMD_NAME
, 1);
882 add_layout_command (SRC_NAME
, layout
);
884 layout
= new tui_layout_split ();
885 layout
->add_window (DISASSEM_NAME
, 2);
886 layout
->add_window (STATUS_NAME
, 0);
887 layout
->add_window (CMD_NAME
, 1);
888 add_layout_command (DISASSEM_NAME
, layout
);
890 layout
= new tui_layout_split ();
891 layout
->add_window (SRC_NAME
, 1);
892 layout
->add_window (DISASSEM_NAME
, 1);
893 layout
->add_window (STATUS_NAME
, 0);
894 layout
->add_window (CMD_NAME
, 1);
895 add_layout_command ("split", layout
);
897 layout
= new tui_layout_split ();
898 layout
->add_window (DATA_NAME
, 1);
899 layout
->add_window (SRC_NAME
, 1);
900 layout
->add_window (STATUS_NAME
, 0);
901 layout
->add_window (CMD_NAME
, 1);
902 layouts
.emplace_back (layout
);
903 src_regs_layout
= layout
;
905 layout
= new tui_layout_split ();
906 layout
->add_window (DATA_NAME
, 1);
907 layout
->add_window (DISASSEM_NAME
, 1);
908 layout
->add_window (STATUS_NAME
, 0);
909 layout
->add_window (CMD_NAME
, 1);
910 layouts
.emplace_back (layout
);
911 asm_regs_layout
= layout
;
916 /* A helper function that returns true if NAME is the name of an
920 validate_window_name (const std::string
&name
)
922 auto iter
= known_window_types
->find (name
);
923 return iter
!= known_window_types
->end ();
926 /* Implementation of the "tui new-layout" command. */
929 tui_new_layout_command (const char *spec
, int from_tty
)
931 std::string new_name
= extract_arg (&spec
);
932 if (new_name
.empty ())
933 error (_("No layout name specified"));
934 if (new_name
[0] == '-')
935 error (_("Layout name cannot start with '-'"));
937 bool is_vertical
= true;
938 spec
= skip_spaces (spec
);
939 if (check_for_argument (&spec
, "-horizontal"))
942 std::vector
<std::unique_ptr
<tui_layout_split
>> splits
;
943 splits
.emplace_back (new tui_layout_split (is_vertical
));
944 std::unordered_set
<std::string
> seen_windows
;
947 spec
= skip_spaces (spec
);
954 spec
= skip_spaces (spec
+ 1);
955 if (check_for_argument (&spec
, "-horizontal"))
957 splits
.emplace_back (new tui_layout_split (is_vertical
));
961 bool is_close
= false;
967 if (splits
.size () == 1)
968 error (_("Extra '}' in layout specification"));
972 name
= extract_arg (&spec
);
975 if (!validate_window_name (name
))
976 error (_("Unknown window \"%s\""), name
.c_str ());
977 if (seen_windows
.find (name
) != seen_windows
.end ())
978 error (_("Window \"%s\" seen twice in layout"), name
.c_str ());
981 ULONGEST weight
= get_ulongest (&spec
, '}');
982 if ((int) weight
!= weight
)
983 error (_("Weight out of range: %s"), pulongest (weight
));
986 std::unique_ptr
<tui_layout_split
> last_split
987 = std::move (splits
.back ());
989 splits
.back ()->add_split (std::move (last_split
), weight
);
993 splits
.back ()->add_window (name
.c_str (), weight
);
994 seen_windows
.insert (name
);
997 if (splits
.size () > 1)
998 error (_("Missing '}' in layout specification"));
999 if (seen_windows
.empty ())
1000 error (_("New layout does not contain any windows"));
1001 if (seen_windows
.find (CMD_NAME
) == seen_windows
.end ())
1002 error (_("New layout does not contain the \"" CMD_NAME
"\" window"));
1004 gdb::unique_xmalloc_ptr
<char> cmd_name
1005 = make_unique_xstrdup (new_name
.c_str ());
1006 std::unique_ptr
<tui_layout_split
> new_layout
= std::move (splits
.back ());
1007 struct cmd_list_element
*cmd
1008 = add_layout_command (cmd_name
.get (), new_layout
.get ());
1009 cmd
->name_allocated
= 1;
1010 cmd_name
.release ();
1011 new_layout
.release ();
1014 /* Function to initialize gdb commands, for tui window layout
1017 void _initialize_tui_layout ();
1019 _initialize_tui_layout ()
1021 add_basic_prefix_cmd ("layout", class_tui
, _("\
1022 Change the layout of windows.\n\
1023 Usage: layout prev | next | LAYOUT-NAME"),
1024 &layout_list
, 0, &cmdlist
);
1026 add_cmd ("next", class_tui
, tui_next_layout_command
,
1027 _("Apply the next TUI layout."),
1029 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
1030 _("Apply the previous TUI layout."),
1032 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
1033 _("Apply the TUI register layout."),
1036 add_cmd ("new-layout", class_tui
, tui_new_layout_command
,
1037 _("Create a new TUI layout.\n\
1038 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1039 Create a new TUI layout. The new layout will be named NAME,\n\
1040 and can be accessed using \"layout NAME\".\n\
1041 The windows will be displayed in the specified order.\n\
1042 A WINDOW can also be of the form:\n\
1043 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1044 This form indicates a sub-frame.\n\
1045 Each WEIGHT is an integer, which holds the relative size\n\
1046 to be allocated to the window."),
1047 tui_get_cmd_list ());
1049 initialize_layouts ();
1050 initialize_known_windows ();