1 /* TUI layout window management.
3 Copyright (C) 1998-2023 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"
47 #include "safe-ctype.h"
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 /* See tui-layout.h. */
71 tui_apply_current_layout (bool preserve_cmd_win_size_p
)
73 struct gdbarch
*gdbarch
;
76 extract_display_start_addr (&gdbarch
, &addr
);
78 for (tui_win_info
*win_info
: tui_windows
)
79 win_info
->make_visible (false);
81 applied_layout
->apply (0, 0, tui_term_width (), tui_term_height (),
82 preserve_cmd_win_size_p
);
84 /* Keep the list of internal windows up-to-date. */
85 for (int win_type
= SRC_WIN
; (win_type
< MAX_MAJOR_WINDOWS
); win_type
++)
86 if (tui_win_list
[win_type
] != nullptr
87 && !tui_win_list
[win_type
]->is_visible ())
88 tui_win_list
[win_type
] = nullptr;
90 /* This should always be made visible by a layout. */
91 gdb_assert (TUI_CMD_WIN
!= nullptr);
92 gdb_assert (TUI_CMD_WIN
->is_visible ());
94 /* Get the new list of currently visible windows. */
95 std::vector
<tui_win_info
*> new_tui_windows
;
96 applied_layout
->get_windows (&new_tui_windows
);
98 /* Now delete any window that was not re-applied. */
99 tui_win_info
*focus
= tui_win_with_focus ();
100 for (tui_win_info
*win_info
: tui_windows
)
102 if (!win_info
->is_visible ())
104 if (focus
== win_info
)
105 tui_set_win_focus_to (new_tui_windows
[0]);
110 /* Replace the global list of active windows. */
111 tui_windows
= std::move (new_tui_windows
);
113 if (gdbarch
== nullptr && TUI_DISASM_WIN
!= nullptr)
114 tui_get_begin_asm_address (&gdbarch
, &addr
);
115 tui_update_source_windows_with_addr (gdbarch
, addr
);
118 /* See tui-layout. */
121 tui_adjust_window_height (struct tui_win_info
*win
, int new_height
)
123 applied_layout
->set_height (win
->name (), new_height
);
126 /* See tui-layout. */
129 tui_adjust_window_width (struct tui_win_info
*win
, int new_width
)
131 applied_layout
->set_width (win
->name (), new_width
);
134 /* Set the current layout to LAYOUT. */
137 tui_set_layout (tui_layout_split
*layout
)
139 std::string old_fingerprint
;
140 if (applied_layout
!= nullptr)
141 old_fingerprint
= applied_layout
->layout_fingerprint ();
143 applied_skeleton
= layout
;
144 applied_layout
= layout
->clone ();
146 std::string new_fingerprint
= applied_layout
->layout_fingerprint ();
147 bool preserve_command_window_size
148 = (TUI_CMD_WIN
!= nullptr && old_fingerprint
== new_fingerprint
);
150 tui_apply_current_layout (preserve_command_window_size
);
153 /* See tui-layout.h. */
156 tui_add_win_to_layout (enum tui_win_type type
)
158 gdb_assert (type
== SRC_WIN
|| type
== DISASSEM_WIN
);
160 /* If the window already exists, no need to add it. */
161 if (tui_win_list
[type
] != nullptr)
164 /* If the window we are trying to replace doesn't exist, we're
166 enum tui_win_type other
= type
== SRC_WIN
? DISASSEM_WIN
: SRC_WIN
;
167 if (tui_win_list
[other
] == nullptr)
170 const char *name
= type
== SRC_WIN
? SRC_NAME
: DISASSEM_NAME
;
171 applied_layout
->replace_window (tui_win_list
[other
]->name (), name
);
172 tui_apply_current_layout (true);
175 /* Find LAYOUT in the "layouts" global and return its index. */
178 find_layout (tui_layout_split
*layout
)
180 for (size_t i
= 0; i
< layouts
.size (); ++i
)
182 if (layout
== layouts
[i
].get ())
185 gdb_assert_not_reached ("layout not found!?");
188 /* Function to set the layout. */
191 tui_apply_layout (const char *args
, int from_tty
, cmd_list_element
*command
)
193 tui_layout_split
*layout
= (tui_layout_split
*) command
->context ();
195 /* Make sure the curses mode is enabled. */
197 tui_set_layout (layout
);
200 /* See tui-layout.h. */
205 size_t index
= find_layout (applied_skeleton
);
207 if (index
== layouts
.size ())
209 tui_set_layout (layouts
[index
].get ());
212 /* Implement the "layout next" command. */
215 tui_next_layout_command (const char *arg
, int from_tty
)
221 /* See tui-layout.h. */
224 tui_set_initial_layout ()
226 tui_set_layout (layouts
[0].get ());
229 /* Implement the "layout prev" command. */
232 tui_prev_layout_command (const char *arg
, int from_tty
)
235 size_t index
= find_layout (applied_skeleton
);
237 index
= layouts
.size ();
239 tui_set_layout (layouts
[index
].get ());
243 /* See tui-layout.h. */
248 /* If there's already a register window, we're done. */
249 if (TUI_DATA_WIN
!= nullptr)
252 tui_set_layout (TUI_DISASM_WIN
!= nullptr
257 /* Implement the "layout regs" command. */
260 tui_regs_layout_command (const char *arg
, int from_tty
)
266 /* See tui-layout.h. */
269 tui_remove_some_windows ()
271 tui_win_info
*focus
= tui_win_with_focus ();
273 if (strcmp (focus
->name (), CMD_NAME
) == 0)
275 /* Try leaving the source or disassembly window. If neither
276 exists, just do nothing. */
278 if (focus
== nullptr)
279 focus
= TUI_DISASM_WIN
;
280 if (focus
== nullptr)
284 applied_layout
->remove_windows (focus
->name ());
285 tui_apply_current_layout (true);
289 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
291 if (TUI_SRC_WIN
!= nullptr)
292 TUI_SRC_WIN
->display_start_addr (gdbarch_p
, addr_p
);
293 else if (TUI_DISASM_WIN
!= nullptr)
294 TUI_DISASM_WIN
->display_start_addr (gdbarch_p
, addr_p
);
297 *gdbarch_p
= nullptr;
303 tui_win_info::resize (int height_
, int width_
,
304 int origin_x_
, int origin_y_
)
306 if (width
== width_
&& height
== height_
307 && x
== origin_x_
&& y
== origin_y_
308 && handle
!= nullptr)
316 if (handle
!= nullptr)
319 wresize (handle
.get (), height
, width
);
320 mvwin (handle
.get (), y
, x
);
321 wmove (handle
.get (), 0, 0);
323 handle
.reset (nullptr);
327 if (handle
== nullptr)
335 /* Helper function to create one of the built-in (non-locator)
338 template<enum tui_win_type V
, class T
>
339 static tui_win_info
*
340 make_standard_window (const char *)
342 if (tui_win_list
[V
] == nullptr)
343 tui_win_list
[V
] = new T ();
344 return tui_win_list
[V
];
347 /* A map holding all the known window types, keyed by name. Note that
348 this is heap-allocated and "leaked" at gdb exit. This avoids
349 ordering issues with destroying elements in the map at shutdown.
350 In particular, destroying this map can occur after Python has been
351 shut down, causing crashes if any window destruction requires
352 running Python code. */
354 static std::unordered_map
<std::string
, window_factory
> *known_window_types
;
356 /* Helper function that returns a TUI window, given its name. */
358 static tui_win_info
*
359 tui_get_window_by_name (const std::string
&name
)
361 for (tui_win_info
*window
: tui_windows
)
362 if (name
== window
->name ())
365 auto iter
= known_window_types
->find (name
);
366 if (iter
== known_window_types
->end ())
367 error (_("Unknown window type \"%s\""), name
.c_str ());
369 tui_win_info
*result
= iter
->second (name
.c_str ());
370 if (result
== nullptr)
371 error (_("Could not create window \"%s\""), name
.c_str ());
375 /* Initialize the known window types. */
378 initialize_known_windows ()
380 known_window_types
= new std::unordered_map
<std::string
, window_factory
>;
382 known_window_types
->emplace (SRC_NAME
,
383 make_standard_window
<SRC_WIN
,
385 known_window_types
->emplace (CMD_NAME
,
386 make_standard_window
<CMD_WIN
, tui_cmd_window
>);
387 known_window_types
->emplace (DATA_NAME
,
388 make_standard_window
<DATA_WIN
,
390 known_window_types
->emplace (DISASSEM_NAME
,
391 make_standard_window
<DISASSEM_WIN
,
393 known_window_types
->emplace (STATUS_NAME
,
394 make_standard_window
<STATUS_WIN
,
395 tui_locator_window
>);
398 /* See tui-layout.h. */
401 tui_register_window (const char *name
, window_factory
&&factory
)
403 std::string name_copy
= name
;
405 if (name_copy
== SRC_NAME
|| name_copy
== CMD_NAME
|| name_copy
== DATA_NAME
406 || name_copy
== DISASSEM_NAME
|| name_copy
== STATUS_NAME
)
407 error (_("Window type \"%s\" is built-in"), name
);
409 for (const char &c
: name_copy
)
412 error (_("invalid whitespace character in window name"));
414 if (!ISALNUM (c
) && strchr ("-_.", c
) == nullptr)
415 error (_("invalid character '%c' in window name"), c
);
418 if (!ISALPHA (name_copy
[0]))
419 error (_("window name must start with a letter, not '%c'"), name_copy
[0]);
421 known_window_types
->emplace (std::move (name_copy
),
422 std::move (factory
));
425 /* See tui-layout.h. */
427 std::unique_ptr
<tui_layout_base
>
428 tui_layout_window::clone () const
430 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
431 return std::unique_ptr
<tui_layout_base
> (result
);
434 /* See tui-layout.h. */
437 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
,
438 bool preserve_cmd_win_size_p
)
444 gdb_assert (m_window
!= nullptr);
445 m_window
->resize (height
, width
, x
, y
);
448 /* See tui-layout.h. */
451 tui_layout_window::get_sizes (bool height
, int *min_value
, int *max_value
)
453 TUI_SCOPED_DEBUG_ENTER_EXIT
;
455 if (m_window
== nullptr)
456 m_window
= tui_get_window_by_name (m_contents
);
458 tui_debug_printf ("window = %s, getting %s",
459 m_window
->name (), (height
? "height" : "width"));
463 *min_value
= m_window
->min_height ();
464 *max_value
= m_window
->max_height ();
468 *min_value
= m_window
->min_width ();
469 *max_value
= m_window
->max_width ();
472 tui_debug_printf ("min = %d, max = %d", *min_value
, *max_value
);
475 /* See tui-layout.h. */
478 tui_layout_window::first_edge_has_border_p () const
480 gdb_assert (m_window
!= nullptr);
481 return m_window
->can_box ();
484 /* See tui-layout.h. */
487 tui_layout_window::last_edge_has_border_p () const
489 gdb_assert (m_window
!= nullptr);
490 return m_window
->can_box ();
493 /* See tui-layout.h. */
496 tui_layout_window::replace_window (const char *name
, const char *new_window
)
498 if (m_contents
== name
)
500 m_contents
= new_window
;
501 if (m_window
!= nullptr)
503 m_window
->make_visible (false);
504 m_window
= tui_get_window_by_name (m_contents
);
509 /* See tui-layout.h. */
512 tui_layout_window::specification (ui_file
*output
, int depth
)
514 gdb_puts (get_name (), output
);
517 /* See tui-layout.h. */
520 tui_layout_window::layout_fingerprint () const
522 if (strcmp (get_name (), "cmd") == 0)
528 /* See tui-layout.h. */
531 tui_layout_split::add_split (std::unique_ptr
<tui_layout_split
> &&layout
,
534 split s
= {weight
, std::move (layout
)};
535 m_splits
.push_back (std::move (s
));
538 /* See tui-layout.h. */
541 tui_layout_split::add_window (const char *name
, int weight
)
543 tui_layout_window
*result
= new tui_layout_window (name
);
544 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
545 m_splits
.push_back (std::move (s
));
548 /* See tui-layout.h. */
550 std::unique_ptr
<tui_layout_base
>
551 tui_layout_split::clone () const
553 tui_layout_split
*result
= new tui_layout_split (m_vertical
);
554 for (const split
&item
: m_splits
)
556 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
557 split s
= {item
.weight
, std::move (next
)};
558 result
->m_splits
.push_back (std::move (s
));
560 return std::unique_ptr
<tui_layout_base
> (result
);
563 /* See tui-layout.h. */
566 tui_layout_split::get_sizes (bool height
, int *min_value
, int *max_value
)
568 TUI_SCOPED_DEBUG_ENTER_EXIT
;
572 bool first_time
= true;
573 for (const split
&item
: m_splits
)
575 int new_min
, new_max
;
576 item
.layout
->get_sizes (height
, &new_min
, &new_max
);
577 /* For the mismatch case, the first time through we want to set
578 the min and max to the computed values -- the "first_time"
579 check here is just a funny way of doing that. */
580 if (height
== m_vertical
|| first_time
)
582 *min_value
+= new_min
;
583 *max_value
+= new_max
;
587 *min_value
= std::max (*min_value
, new_min
);
588 *max_value
= std::min (*max_value
, new_max
);
593 tui_debug_printf ("min_value = %d, max_value = %d", *min_value
, *max_value
);
596 /* See tui-layout.h. */
599 tui_layout_split::first_edge_has_border_p () const
601 if (m_splits
.empty ())
603 return m_splits
[0].layout
->first_edge_has_border_p ();
606 /* See tui-layout.h. */
609 tui_layout_split::last_edge_has_border_p () const
611 if (m_splits
.empty ())
613 return m_splits
.back ().layout
->last_edge_has_border_p ();
616 /* See tui-layout.h. */
619 tui_layout_split::set_weights_from_sizes ()
621 for (int i
= 0; i
< m_splits
.size (); ++i
)
623 = m_vertical
? m_splits
[i
].layout
->height
: m_splits
[i
].layout
->width
;
626 /* See tui-layout.h. */
629 tui_layout_split::tui_debug_weights_to_string () const
633 for (int i
= 0; i
< m_splits
.size (); ++i
)
637 str
+= string_printf ("[%d] %d", i
, m_splits
[i
].weight
);
643 /* See tui-layout.h. */
646 tui_layout_split::tui_debug_print_size_info
647 (const std::vector
<tui_layout_split::size_info
> &info
)
649 gdb_assert (debug_tui
);
651 tui_debug_printf ("current size info data:");
652 for (int i
= 0; i
< info
.size (); ++i
)
653 tui_debug_printf (" [%d] { size = %d, min = %d, max = %d, share_box = %d }",
654 i
, info
[i
].size
, info
[i
].min_size
,
655 info
[i
].max_size
, info
[i
].share_box
);
658 /* See tui-layout.h. */
661 tui_layout_split::set_size (const char *name
, int new_size
, bool set_width_p
)
663 TUI_SCOPED_DEBUG_ENTER_EXIT
;
665 tui_debug_printf ("this = %p, name = %s, new_size = %d",
666 this, name
, new_size
);
668 /* Look through the children. If one is a layout holding the named
669 window, we're done; or if one actually is the named window,
671 int found_index
= -1;
672 for (int i
= 0; i
< m_splits
.size (); ++i
)
674 tui_adjust_result adjusted
;
676 adjusted
= m_splits
[i
].layout
->set_width (name
, new_size
);
678 adjusted
= m_splits
[i
].layout
->set_height (name
, new_size
);
679 if (adjusted
== HANDLED
)
681 if (adjusted
== FOUND
)
683 if (set_width_p
? m_vertical
: !m_vertical
)
690 if (found_index
== -1)
692 int curr_size
= (set_width_p
693 ? m_splits
[found_index
].layout
->width
694 : m_splits
[found_index
].layout
->height
);
695 if (curr_size
== new_size
)
698 tui_debug_printf ("found window %s at index %d", name
, found_index
);
700 set_weights_from_sizes ();
701 int delta
= m_splits
[found_index
].weight
- new_size
;
702 m_splits
[found_index
].weight
= new_size
;
704 tui_debug_printf ("before delta (%d) distribution, weights: %s",
705 delta
, tui_debug_weights_to_string ().c_str ());
707 /* Distribute the "delta" over all other windows, while respecting their
708 min/max sizes. We grow each window by 1 line at a time continually
709 looping over all the windows. However, skip the window that the user
710 just resized, obviously we don't want to readjust that window. */
711 bool found_window_that_can_grow_p
= true;
712 for (int i
= 0; delta
!= 0; i
= (i
+ 1) % m_splits
.size ())
714 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
715 if (index
== found_index
)
717 if (!found_window_that_can_grow_p
)
719 found_window_that_can_grow_p
= false;
723 int new_min
, new_max
;
724 m_splits
[index
].layout
->get_sizes (m_vertical
, &new_min
, &new_max
);
728 /* The primary window grew, so we are trying to shrink other
730 if (m_splits
[index
].weight
> new_min
)
732 m_splits
[index
].weight
-= 1;
734 found_window_that_can_grow_p
= true;
739 /* The primary window shrank, so we are trying to grow other
741 if (m_splits
[index
].weight
< new_max
)
743 m_splits
[index
].weight
+= 1;
745 found_window_that_can_grow_p
= true;
749 tui_debug_printf ("index = %d, weight now: %d",
750 index
, m_splits
[index
].weight
);
753 tui_debug_printf ("after delta (%d) distribution, weights: %s",
754 delta
, tui_debug_weights_to_string ().c_str ());
759 warning (_("Invalid window width specified"));
761 warning (_("Invalid window height specified"));
762 /* Effectively undo any modifications made here. */
763 set_weights_from_sizes ();
767 /* Simply re-apply the updated layout. We pass false here so that
768 the cmd window can be resized. However, we should have already
769 resized everything above to be "just right", so the apply call
770 here should not end up changing the sizes at all. */
771 apply (x
, y
, width
, height
, false);
777 /* See tui-layout.h. */
780 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
,
781 bool preserve_cmd_win_size_p
)
783 TUI_SCOPED_DEBUG_ENTER_EXIT
;
790 /* In some situations we fix the size of the cmd window. However,
791 occasionally this turns out to be a mistake. This struct is used to
792 hold the original information about the cmd window, so we can restore
797 old_size_info (int index_
, int min_size_
, int max_size_
)
799 min_size (min_size_
),
803 /* The index in m_splits where the cmd window was found. */
806 /* The previous min/max size. */
811 /* This is given a value only if we fix the size of the cmd window. */
812 gdb::optional
<old_size_info
> old_cmd_info
;
814 std::vector
<size_info
> info (m_splits
.size ());
816 tui_debug_printf ("weights are: %s",
817 tui_debug_weights_to_string ().c_str ());
819 /* Step 1: Find the min and max size of each sub-layout.
820 Fixed-sized layouts are given their desired size, and then the
821 remaining space is distributed among the remaining windows
822 according to the weights given. */
823 int available_size
= m_vertical
? height
: width
;
825 int total_weight
= 0;
826 for (int i
= 0; i
< m_splits
.size (); ++i
)
828 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
830 /* Always call get_sizes, to ensure that the window is
831 instantiated. This is a bit gross but less gross than adding
832 special cases for this in other places. */
833 m_splits
[i
].layout
->get_sizes (m_vertical
, &info
[i
].min_size
,
836 if (preserve_cmd_win_size_p
837 && cmd_win_already_exists
838 && m_splits
[i
].layout
->get_name () != nullptr
839 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
841 /* Save the old cmd window information, in case we need to
843 old_cmd_info
.emplace (i
, info
[i
].min_size
, info
[i
].max_size
);
845 /* If this layout has never been applied, then it means the
846 user just changed the layout. In this situation, it's
847 desirable to keep the size of the command window the
848 same. Setting the min and max sizes this way ensures
849 that the resizing step, below, does the right thing with
851 info
[i
].min_size
= (m_vertical
852 ? TUI_CMD_WIN
->height
853 : TUI_CMD_WIN
->width
);
854 info
[i
].max_size
= info
[i
].min_size
;
857 if (info
[i
].min_size
== info
[i
].max_size
)
858 available_size
-= info
[i
].min_size
;
862 total_weight
+= m_splits
[i
].weight
;
865 /* Two adjacent boxed windows will share a border, making a bit
866 more size available. */
868 && m_splits
[i
- 1].layout
->last_edge_has_border_p ()
869 && m_splits
[i
].layout
->first_edge_has_border_p ())
870 info
[i
].share_box
= true;
873 /* If last_index is set then we have a window that is not of a fixed
874 size. This window will have its size calculated below, which requires
875 that the total_weight not be zero (we divide by total_weight, so don't
876 want a floating-point exception). */
877 gdb_assert (last_index
== -1 || total_weight
> 0);
879 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
880 are given their fixed size, while others are resized according to
883 for (int i
= 0; i
< m_splits
.size (); ++i
)
885 if (info
[i
].min_size
!= info
[i
].max_size
)
887 /* Compute the height and clamp to the allowable range. */
888 info
[i
].size
= available_size
* m_splits
[i
].weight
/ total_weight
;
889 if (info
[i
].size
> info
[i
].max_size
)
890 info
[i
].size
= info
[i
].max_size
;
891 if (info
[i
].size
< info
[i
].min_size
)
892 info
[i
].size
= info
[i
].min_size
;
893 /* Keep a total of all the size we've used so far (we gain some
894 size back if this window can share a border with a preceding
895 window). Any unused space will be distributed between all of
896 the other windows (while respecting min/max sizes) later in
898 used_size
+= info
[i
].size
;
899 if (info
[i
].share_box
)
903 info
[i
].size
= info
[i
].min_size
;
908 tui_debug_printf ("after initial size calculation");
909 tui_debug_printf ("available_size = %d, used_size = %d",
910 available_size
, used_size
);
911 tui_debug_printf ("total_weight = %d, last_index = %d",
912 total_weight
, last_index
);
913 tui_debug_print_size_info (info
);
916 /* If we didn't find any sub-layouts that were of a non-fixed size, but
917 we did find the cmd window, then we can consider that a sort-of
918 non-fixed size sub-layout.
920 The cmd window might, initially, be of a fixed size (see above), but,
921 we are willing to relax this constraint if required to correctly apply
922 this layout (see below). */
923 if (last_index
== -1 && old_cmd_info
.has_value ())
924 last_index
= old_cmd_info
->index
;
926 /* Allocate any leftover size. */
927 if (available_size
!= used_size
&& last_index
!= -1)
929 /* Loop over all windows until the amount of used space is equal to
930 the amount of available space. There's an escape hatch within
931 the loop in case we can't find any sub-layouts to resize. */
932 bool found_window_that_can_grow_p
= true;
933 for (int idx
= last_index
;
934 available_size
!= used_size
;
935 idx
= (idx
+ 1) % m_splits
.size ())
937 /* Every time we get back to last_index, which is where the loop
938 started, we check to make sure that we did assign some space
939 to a window, bringing used_size closer to available_size.
941 If we didn't, but the cmd window is of a fixed size, then we
942 can make the console window non-fixed-size, and continue
943 around the loop, hopefully, this will allow the layout to be
946 If we still make it around the loop without moving used_size
947 closer to available_size, then there's nothing more we can do,
948 and we break out of the loop. */
949 if (idx
== last_index
)
951 /* If the used_size is greater than the available_size then
952 this indicates that the fixed-sized sub-layouts claimed
953 more space than is available. This layout is not going to
954 work. Our only hope at this point is to make the cmd
955 window non-fixed-size (if possible), and hope we can
956 shrink this enough to fit the rest of the sub-layouts in.
958 Alternatively, we've made it around the loop without
959 adjusting any window's size. This likely means all
960 windows have hit their min or max size. Again, our only
961 hope is to make the cmd window non-fixed-size, and hope
962 this fixes all our problems. */
963 if (old_cmd_info
.has_value ()
964 && ((available_size
< used_size
)
965 || !found_window_that_can_grow_p
))
967 info
[old_cmd_info
->index
].min_size
= old_cmd_info
->min_size
;
968 info
[old_cmd_info
->index
].max_size
= old_cmd_info
->max_size
;
970 ("restoring index %d (cmd) size limits, min = %d, max = %d",
971 old_cmd_info
->index
, old_cmd_info
->min_size
,
972 old_cmd_info
->max_size
);
973 old_cmd_info
.reset ();
975 else if (!found_window_that_can_grow_p
)
977 found_window_that_can_grow_p
= false;
980 if (available_size
> used_size
981 && info
[idx
].size
< info
[idx
].max_size
)
983 found_window_that_can_grow_p
= true;
987 else if (available_size
< used_size
988 && info
[idx
].size
> info
[idx
].min_size
)
990 found_window_that_can_grow_p
= true;
998 tui_debug_printf ("after final size calculation");
999 tui_debug_printf ("available_size = %d, used_size = %d",
1000 available_size
, used_size
);
1001 tui_debug_printf ("total_weight = %d, last_index = %d",
1002 total_weight
, last_index
);
1003 tui_debug_print_size_info (info
);
1007 /* Step 3: Resize. */
1009 const int maximum
= m_vertical
? height
: width
;
1010 for (int i
= 0; i
< m_splits
.size (); ++i
)
1012 /* If we fall off the bottom, just make allocations overlap.
1014 if (size_accum
+ info
[i
].size
> maximum
)
1015 size_accum
= maximum
- info
[i
].size
;
1016 else if (info
[i
].share_box
)
1019 m_splits
[i
].layout
->apply (x
, y
+ size_accum
, width
, info
[i
].size
,
1020 preserve_cmd_win_size_p
);
1022 m_splits
[i
].layout
->apply (x
+ size_accum
, y
, info
[i
].size
, height
,
1023 preserve_cmd_win_size_p
);
1024 size_accum
+= info
[i
].size
;
1028 /* See tui-layout.h. */
1031 tui_layout_split::remove_windows (const char *name
)
1033 for (int i
= 0; i
< m_splits
.size (); ++i
)
1035 const char *this_name
= m_splits
[i
].layout
->get_name ();
1036 if (this_name
== nullptr)
1037 m_splits
[i
].layout
->remove_windows (name
);
1038 else if (strcmp (this_name
, name
) == 0
1039 || strcmp (this_name
, CMD_NAME
) == 0
1040 || strcmp (this_name
, STATUS_NAME
) == 0)
1046 m_splits
.erase (m_splits
.begin () + i
);
1052 /* See tui-layout.h. */
1055 tui_layout_split::replace_window (const char *name
, const char *new_window
)
1057 for (auto &item
: m_splits
)
1058 item
.layout
->replace_window (name
, new_window
);
1061 /* See tui-layout.h. */
1064 tui_layout_split::specification (ui_file
*output
, int depth
)
1067 gdb_puts ("{", output
);
1070 gdb_puts ("-horizontal ", output
);
1073 for (auto &item
: m_splits
)
1076 gdb_puts (" ", output
);
1078 item
.layout
->specification (output
, depth
+ 1);
1079 gdb_printf (output
, " %d", item
.weight
);
1083 gdb_puts ("}", output
);
1086 /* See tui-layout.h. */
1089 tui_layout_split::layout_fingerprint () const
1091 for (auto &item
: m_splits
)
1093 std::string fp
= item
.layout
->layout_fingerprint ();
1095 return std::string (m_vertical
? "V" : "H") + fp
;
1101 /* Destroy the layout associated with SELF. */
1104 destroy_layout (struct cmd_list_element
*self
, void *context
)
1106 tui_layout_split
*layout
= (tui_layout_split
*) context
;
1107 size_t index
= find_layout (layout
);
1108 layouts
.erase (layouts
.begin () + index
);
1111 /* List holding the sub-commands of "layout". */
1113 static struct cmd_list_element
*layout_list
;
1115 /* Called to implement 'tui layout'. */
1118 tui_layout_command (const char *args
, int from_tty
)
1120 help_list (layout_list
, "tui layout ", all_commands
, gdb_stdout
);
1123 /* Add a "layout" command with name NAME that switches to LAYOUT. */
1125 static struct cmd_list_element
*
1126 add_layout_command (const char *name
, tui_layout_split
*layout
)
1128 struct cmd_list_element
*cmd
;
1131 layout
->specification (&spec
, 0);
1133 gdb::unique_xmalloc_ptr
<char> doc
1134 = xstrprintf (_("Apply the \"%s\" layout.\n\
1135 This layout was created using:\n\
1136 tui new-layout %s %s"),
1137 name
, name
, spec
.c_str ());
1139 cmd
= add_cmd (name
, class_tui
, nullptr, doc
.get (), &layout_list
);
1140 cmd
->set_context (layout
);
1141 /* There is no API to set this. */
1142 cmd
->func
= tui_apply_layout
;
1143 cmd
->destroyer
= destroy_layout
;
1144 cmd
->doc_allocated
= 1;
1146 layouts
.emplace_back (layout
);
1151 /* Initialize the standard layouts. */
1154 initialize_layouts ()
1156 tui_layout_split
*layout
;
1158 layout
= new tui_layout_split ();
1159 layout
->add_window (SRC_NAME
, 2);
1160 layout
->add_window (STATUS_NAME
, 0);
1161 layout
->add_window (CMD_NAME
, 1);
1162 add_layout_command (SRC_NAME
, layout
);
1164 layout
= new tui_layout_split ();
1165 layout
->add_window (DISASSEM_NAME
, 2);
1166 layout
->add_window (STATUS_NAME
, 0);
1167 layout
->add_window (CMD_NAME
, 1);
1168 add_layout_command (DISASSEM_NAME
, layout
);
1170 layout
= new tui_layout_split ();
1171 layout
->add_window (SRC_NAME
, 1);
1172 layout
->add_window (DISASSEM_NAME
, 1);
1173 layout
->add_window (STATUS_NAME
, 0);
1174 layout
->add_window (CMD_NAME
, 1);
1175 add_layout_command ("split", layout
);
1177 layout
= new tui_layout_split ();
1178 layout
->add_window (DATA_NAME
, 1);
1179 layout
->add_window (SRC_NAME
, 1);
1180 layout
->add_window (STATUS_NAME
, 0);
1181 layout
->add_window (CMD_NAME
, 1);
1182 layouts
.emplace_back (layout
);
1183 src_regs_layout
= layout
;
1185 layout
= new tui_layout_split ();
1186 layout
->add_window (DATA_NAME
, 1);
1187 layout
->add_window (DISASSEM_NAME
, 1);
1188 layout
->add_window (STATUS_NAME
, 0);
1189 layout
->add_window (CMD_NAME
, 1);
1190 layouts
.emplace_back (layout
);
1191 asm_regs_layout
= layout
;
1196 /* A helper function that returns true if NAME is the name of an
1197 available window. */
1200 validate_window_name (const std::string
&name
)
1202 auto iter
= known_window_types
->find (name
);
1203 return iter
!= known_window_types
->end ();
1206 /* Implementation of the "tui new-layout" command. */
1209 tui_new_layout_command (const char *spec
, int from_tty
)
1211 std::string new_name
= extract_arg (&spec
);
1212 if (new_name
.empty ())
1213 error (_("No layout name specified"));
1214 if (new_name
[0] == '-')
1215 error (_("Layout name cannot start with '-'"));
1217 bool is_vertical
= true;
1218 spec
= skip_spaces (spec
);
1219 if (check_for_argument (&spec
, "-horizontal"))
1220 is_vertical
= false;
1222 std::vector
<std::unique_ptr
<tui_layout_split
>> splits
;
1223 splits
.emplace_back (new tui_layout_split (is_vertical
));
1224 std::unordered_set
<std::string
> seen_windows
;
1227 spec
= skip_spaces (spec
);
1228 if (spec
[0] == '\0')
1234 spec
= skip_spaces (spec
+ 1);
1235 if (check_for_argument (&spec
, "-horizontal"))
1236 is_vertical
= false;
1237 splits
.emplace_back (new tui_layout_split (is_vertical
));
1241 bool is_close
= false;
1247 if (splits
.size () == 1)
1248 error (_("Extra '}' in layout specification"));
1252 name
= extract_arg (&spec
);
1255 if (!validate_window_name (name
))
1256 error (_("Unknown window \"%s\""), name
.c_str ());
1257 if (seen_windows
.find (name
) != seen_windows
.end ())
1258 error (_("Window \"%s\" seen twice in layout"), name
.c_str ());
1261 ULONGEST weight
= get_ulongest (&spec
, '}');
1262 if ((int) weight
!= weight
)
1263 error (_("Weight out of range: %s"), pulongest (weight
));
1266 std::unique_ptr
<tui_layout_split
> last_split
1267 = std::move (splits
.back ());
1269 splits
.back ()->add_split (std::move (last_split
), weight
);
1273 splits
.back ()->add_window (name
.c_str (), weight
);
1274 seen_windows
.insert (name
);
1277 if (splits
.size () > 1)
1278 error (_("Missing '}' in layout specification"));
1279 if (seen_windows
.empty ())
1280 error (_("New layout does not contain any windows"));
1281 if (seen_windows
.find (CMD_NAME
) == seen_windows
.end ())
1282 error (_("New layout does not contain the \"" CMD_NAME
"\" window"));
1284 gdb::unique_xmalloc_ptr
<char> cmd_name
1285 = make_unique_xstrdup (new_name
.c_str ());
1286 std::unique_ptr
<tui_layout_split
> new_layout
= std::move (splits
.back ());
1287 struct cmd_list_element
*cmd
1288 = add_layout_command (cmd_name
.get (), new_layout
.get ());
1289 cmd
->name_allocated
= 1;
1290 cmd_name
.release ();
1291 new_layout
.release ();
1294 /* Function to initialize gdb commands, for tui window layout
1297 void _initialize_tui_layout ();
1299 _initialize_tui_layout ()
1301 struct cmd_list_element
*layout_cmd
1302 = add_prefix_cmd ("layout", class_tui
, tui_layout_command
, _("\
1303 Change the layout of windows.\n\
1304 Usage: tui layout prev | next | LAYOUT-NAME"),
1305 &layout_list
, 0, tui_get_cmd_list ());
1306 add_com_alias ("layout", layout_cmd
, class_tui
, 0);
1308 add_cmd ("next", class_tui
, tui_next_layout_command
,
1309 _("Apply the next TUI layout."),
1311 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
1312 _("Apply the previous TUI layout."),
1314 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
1315 _("Apply the TUI register layout."),
1318 add_cmd ("new-layout", class_tui
, tui_new_layout_command
,
1319 _("Create a new TUI layout.\n\
1320 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1321 Create a new TUI layout. The new layout will be named NAME,\n\
1322 and can be accessed using \"layout NAME\".\n\
1323 The windows will be displayed in the specified order.\n\
1324 A WINDOW can also be of the form:\n\
1325 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1326 This form indicates a sub-frame.\n\
1327 Each WEIGHT is an integer, which holds the relative size\n\
1328 to be allocated to the window."),
1329 tui_get_cmd_list ());
1331 initialize_layouts ();
1332 initialize_known_windows ();