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_set>
35 #include "tui/tui-command.h"
36 #include "tui/tui-data.h"
37 #include "tui/tui-wingeneral.h"
38 #include "tui/tui-stack.h"
39 #include "tui/tui-regs.h"
40 #include "tui/tui-win.h"
41 #include "tui/tui-winsource.h"
42 #include "tui/tui-disasm.h"
43 #include "tui/tui-layout.h"
44 #include "tui/tui-source.h"
45 #include "gdb_curses.h"
46 #include "gdbsupport/gdb-safe-ctype.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 (bool preserve_cmd_win_size_p
)
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 (),
81 preserve_cmd_win_size_p
);
83 /* Keep the list of internal windows up-to-date. */
84 for (int win_type
= SRC_WIN
; (win_type
< MAX_MAJOR_WINDOWS
); win_type
++)
85 if (tui_win_list
[win_type
] != nullptr
86 && !tui_win_list
[win_type
]->is_visible ())
87 tui_win_list
[win_type
] = nullptr;
89 /* This should always be made visible by a layout. */
90 gdb_assert (TUI_CMD_WIN
!= nullptr);
91 gdb_assert (TUI_CMD_WIN
->is_visible ());
93 /* Get the new list of currently visible windows. */
94 std::vector
<tui_win_info
*> new_tui_windows
;
95 applied_layout
->get_windows (&new_tui_windows
);
97 /* Now delete any window that was not re-applied. */
98 tui_win_info
*focus
= tui_win_with_focus ();
99 for (tui_win_info
*win_info
: tui_windows
)
101 if (!win_info
->is_visible ())
103 if (focus
== win_info
)
104 tui_set_win_focus_to (new_tui_windows
[0]);
109 /* Replace the global list of active windows. */
110 tui_windows
= std::move (new_tui_windows
);
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
);
117 /* See tui-layout. */
120 tui_adjust_window_height (struct tui_win_info
*win
, int new_height
)
122 applied_layout
->set_height (win
->name (), new_height
);
125 /* See tui-layout. */
128 tui_adjust_window_width (struct tui_win_info
*win
, int new_width
)
130 applied_layout
->set_width (win
->name (), new_width
);
133 /* Set the current layout to LAYOUT. */
136 tui_set_layout (tui_layout_split
*layout
)
138 std::string old_fingerprint
;
139 if (applied_layout
!= nullptr)
140 old_fingerprint
= applied_layout
->layout_fingerprint ();
142 applied_skeleton
= layout
;
143 applied_layout
= layout
->clone ();
145 std::string new_fingerprint
= applied_layout
->layout_fingerprint ();
146 bool preserve_command_window_size
147 = (TUI_CMD_WIN
!= nullptr && old_fingerprint
== new_fingerprint
);
149 tui_apply_current_layout (preserve_command_window_size
);
152 /* See tui-layout.h. */
155 tui_add_win_to_layout (enum tui_win_type type
)
157 gdb_assert (type
== SRC_WIN
|| type
== DISASSEM_WIN
);
159 /* If the window already exists, no need to add it. */
160 if (tui_win_list
[type
] != nullptr)
163 /* If the window we are trying to replace doesn't exist, we're
165 enum tui_win_type other
= type
== SRC_WIN
? DISASSEM_WIN
: SRC_WIN
;
166 if (tui_win_list
[other
] == nullptr)
169 const char *name
= type
== SRC_WIN
? SRC_NAME
: DISASSEM_NAME
;
170 applied_layout
->replace_window (tui_win_list
[other
]->name (), name
);
171 tui_apply_current_layout (true);
174 /* Find LAYOUT in the "layouts" global and return its index. */
177 find_layout (tui_layout_split
*layout
)
179 for (size_t i
= 0; i
< layouts
.size (); ++i
)
181 if (layout
== layouts
[i
].get ())
184 gdb_assert_not_reached ("layout not found!?");
187 /* Function to set the layout. */
190 tui_apply_layout (const char *args
, int from_tty
, cmd_list_element
*command
)
192 tui_layout_split
*layout
= (tui_layout_split
*) command
->context ();
194 /* Make sure the curses mode is enabled. */
196 tui_set_layout (layout
);
199 /* See tui-layout.h. */
204 size_t index
= find_layout (applied_skeleton
);
206 if (index
== layouts
.size ())
208 tui_set_layout (layouts
[index
].get ());
211 /* Implement the "layout next" command. */
214 tui_next_layout_command (const char *arg
, int from_tty
)
220 /* See tui-layout.h. */
223 tui_set_initial_layout ()
225 tui_set_layout (layouts
[0].get ());
228 /* Implement the "layout prev" command. */
231 tui_prev_layout_command (const char *arg
, int from_tty
)
234 size_t index
= find_layout (applied_skeleton
);
236 index
= layouts
.size ();
238 tui_set_layout (layouts
[index
].get ());
242 /* See tui-layout.h. */
247 /* If there's already a register window, we're done. */
248 if (TUI_DATA_WIN
!= nullptr)
251 tui_set_layout (TUI_DISASM_WIN
!= nullptr
256 /* Implement the "layout regs" command. */
259 tui_regs_layout_command (const char *arg
, int from_tty
)
265 /* See tui-layout.h. */
268 tui_remove_some_windows ()
270 tui_win_info
*focus
= tui_win_with_focus ();
272 if (strcmp (focus
->name (), CMD_NAME
) == 0)
274 /* Try leaving the source or disassembly window. If neither
275 exists, just do nothing. */
277 if (focus
== nullptr)
278 focus
= TUI_DISASM_WIN
;
279 if (focus
== nullptr)
283 applied_layout
->remove_windows (focus
->name ());
284 tui_apply_current_layout (true);
288 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
290 if (TUI_SRC_WIN
!= nullptr)
291 TUI_SRC_WIN
->display_start_addr (gdbarch_p
, addr_p
);
292 else if (TUI_DISASM_WIN
!= nullptr)
293 TUI_DISASM_WIN
->display_start_addr (gdbarch_p
, addr_p
);
296 *gdbarch_p
= nullptr;
302 tui_win_info::resize (int height_
, int width_
,
303 int origin_x_
, int origin_y_
)
305 if (width
== width_
&& height
== height_
306 && x
== origin_x_
&& y
== origin_y_
307 && handle
!= nullptr)
315 if (handle
!= nullptr)
318 wresize (handle
.get (), height
, width
);
319 mvwin (handle
.get (), y
, x
);
320 wmove (handle
.get (), 0, 0);
322 handle
.reset (nullptr);
326 if (handle
== nullptr)
334 /* Helper function to create one of the built-in (non-locator)
337 template<enum tui_win_type V
, class T
>
338 static tui_win_info
*
339 make_standard_window (const char *)
341 if (tui_win_list
[V
] == nullptr)
342 tui_win_list
[V
] = new T ();
343 return tui_win_list
[V
];
346 /* A map holding all the known window types, keyed by name. */
348 static window_types_map known_window_types
;
350 /* See tui-layout.h. */
352 known_window_names_range
353 all_known_window_names ()
355 auto begin
= known_window_names_iterator (known_window_types
.begin ());
356 auto end
= known_window_names_iterator (known_window_types
.end ());
357 return known_window_names_range (begin
, end
);
360 /* Helper function that returns a TUI window, given its name. */
362 static tui_win_info
*
363 tui_get_window_by_name (const std::string
&name
)
365 for (tui_win_info
*window
: tui_windows
)
366 if (name
== window
->name ())
369 auto iter
= known_window_types
.find (name
);
370 if (iter
== known_window_types
.end ())
371 error (_("Unknown window type \"%s\""), name
.c_str ());
373 tui_win_info
*result
= iter
->second (name
.c_str ());
374 if (result
== nullptr)
375 error (_("Could not create window \"%s\""), name
.c_str ());
379 /* Initialize the known window types. */
382 initialize_known_windows ()
384 known_window_types
.emplace (SRC_NAME
,
385 make_standard_window
<SRC_WIN
,
387 known_window_types
.emplace (CMD_NAME
,
388 make_standard_window
<CMD_WIN
, tui_cmd_window
>);
389 known_window_types
.emplace (DATA_NAME
,
390 make_standard_window
<DATA_WIN
,
392 known_window_types
.emplace (DISASSEM_NAME
,
393 make_standard_window
<DISASSEM_WIN
,
395 known_window_types
.emplace (STATUS_NAME
,
396 make_standard_window
<STATUS_WIN
,
397 tui_locator_window
>);
400 /* See tui-layout.h. */
403 tui_register_window (const char *name
, window_factory
&&factory
)
405 std::string name_copy
= name
;
407 if (name_copy
== SRC_NAME
|| name_copy
== CMD_NAME
|| name_copy
== DATA_NAME
408 || name_copy
== DISASSEM_NAME
|| name_copy
== STATUS_NAME
)
409 error (_("Window type \"%s\" is built-in"), name
);
411 for (const char &c
: name_copy
)
414 error (_("invalid whitespace character in window name"));
416 if (!ISALNUM (c
) && strchr ("-_.", c
) == nullptr)
417 error (_("invalid character '%c' in window name"), c
);
420 if (!ISALPHA (name_copy
[0]))
421 error (_("window name must start with a letter, not '%c'"), name_copy
[0]);
423 /* We already check above for all the builtin window names. If we get
424 this far then NAME must be a user defined window. Remove any existing
425 factory and replace it with this new version. */
427 auto iter
= known_window_types
.find (name
);
428 if (iter
!= known_window_types
.end ())
429 known_window_types
.erase (iter
);
431 known_window_types
.emplace (std::move (name_copy
),
432 std::move (factory
));
435 /* See tui-layout.h. */
437 std::unique_ptr
<tui_layout_base
>
438 tui_layout_window::clone () const
440 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
441 return std::unique_ptr
<tui_layout_base
> (result
);
444 /* See tui-layout.h. */
447 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
,
448 bool preserve_cmd_win_size_p
)
454 gdb_assert (m_window
!= nullptr);
455 m_window
->resize (height
, width
, x
, y
);
458 /* See tui-layout.h. */
461 tui_layout_window::get_sizes (bool height
, int *min_value
, int *max_value
)
463 TUI_SCOPED_DEBUG_ENTER_EXIT
;
465 if (m_window
== nullptr)
466 m_window
= tui_get_window_by_name (m_contents
);
468 tui_debug_printf ("window = %s, getting %s",
469 m_window
->name (), (height
? "height" : "width"));
473 *min_value
= m_window
->min_height ();
474 *max_value
= m_window
->max_height ();
478 *min_value
= m_window
->min_width ();
479 *max_value
= m_window
->max_width ();
482 tui_debug_printf ("min = %d, max = %d", *min_value
, *max_value
);
485 /* See tui-layout.h. */
488 tui_layout_window::first_edge_has_border_p () const
490 gdb_assert (m_window
!= nullptr);
491 return m_window
->can_box ();
494 /* See tui-layout.h. */
497 tui_layout_window::last_edge_has_border_p () const
499 gdb_assert (m_window
!= nullptr);
500 return m_window
->can_box ();
503 /* See tui-layout.h. */
506 tui_layout_window::replace_window (const char *name
, const char *new_window
)
508 if (m_contents
== name
)
510 m_contents
= new_window
;
511 if (m_window
!= nullptr)
513 m_window
->make_visible (false);
514 m_window
= tui_get_window_by_name (m_contents
);
519 /* See tui-layout.h. */
522 tui_layout_window::specification (ui_file
*output
, int depth
)
524 gdb_puts (get_name (), output
);
527 /* See tui-layout.h. */
530 tui_layout_window::layout_fingerprint () const
532 if (strcmp (get_name (), "cmd") == 0)
538 /* See tui-layout.h. */
541 tui_layout_split::add_split (std::unique_ptr
<tui_layout_split
> &&layout
,
544 split s
= {weight
, std::move (layout
)};
545 m_splits
.push_back (std::move (s
));
548 /* See tui-layout.h. */
551 tui_layout_split::add_window (const char *name
, int weight
)
553 tui_layout_window
*result
= new tui_layout_window (name
);
554 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
555 m_splits
.push_back (std::move (s
));
558 /* See tui-layout.h. */
560 std::unique_ptr
<tui_layout_base
>
561 tui_layout_split::clone () const
563 tui_layout_split
*result
= new tui_layout_split (m_vertical
);
564 for (const split
&item
: m_splits
)
566 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
567 split s
= {item
.weight
, std::move (next
)};
568 result
->m_splits
.push_back (std::move (s
));
570 return std::unique_ptr
<tui_layout_base
> (result
);
573 /* See tui-layout.h. */
576 tui_layout_split::get_sizes (bool height
, int *min_value
, int *max_value
)
578 TUI_SCOPED_DEBUG_ENTER_EXIT
;
582 bool first_time
= true;
583 for (const split
&item
: m_splits
)
585 int new_min
, new_max
;
586 item
.layout
->get_sizes (height
, &new_min
, &new_max
);
587 /* For the mismatch case, the first time through we want to set
588 the min and max to the computed values -- the "first_time"
589 check here is just a funny way of doing that. */
590 if (height
== m_vertical
|| first_time
)
592 *min_value
+= new_min
;
593 *max_value
+= new_max
;
597 *min_value
= std::max (*min_value
, new_min
);
598 *max_value
= std::min (*max_value
, new_max
);
603 tui_debug_printf ("min_value = %d, max_value = %d", *min_value
, *max_value
);
606 /* See tui-layout.h. */
609 tui_layout_split::first_edge_has_border_p () const
611 if (m_splits
.empty ())
613 return m_splits
[0].layout
->first_edge_has_border_p ();
616 /* See tui-layout.h. */
619 tui_layout_split::last_edge_has_border_p () const
621 if (m_splits
.empty ())
623 return m_splits
.back ().layout
->last_edge_has_border_p ();
626 /* See tui-layout.h. */
629 tui_layout_split::set_weights_from_sizes ()
631 for (int i
= 0; i
< m_splits
.size (); ++i
)
633 = m_vertical
? m_splits
[i
].layout
->height
: m_splits
[i
].layout
->width
;
636 /* See tui-layout.h. */
639 tui_layout_split::tui_debug_weights_to_string () const
643 for (int i
= 0; i
< m_splits
.size (); ++i
)
647 str
+= string_printf ("[%d] %d", i
, m_splits
[i
].weight
);
653 /* See tui-layout.h. */
656 tui_layout_split::tui_debug_print_size_info
657 (const std::vector
<tui_layout_split::size_info
> &info
)
659 gdb_assert (debug_tui
);
661 tui_debug_printf ("current size info data:");
662 for (int i
= 0; i
< info
.size (); ++i
)
663 tui_debug_printf (" [%d] { size = %d, min = %d, max = %d, share_box = %d }",
664 i
, info
[i
].size
, info
[i
].min_size
,
665 info
[i
].max_size
, info
[i
].share_box
);
668 /* See tui-layout.h. */
671 tui_layout_split::set_size (const char *name
, int new_size
, bool set_width_p
)
673 TUI_SCOPED_DEBUG_ENTER_EXIT
;
675 tui_debug_printf ("this = %p, name = %s, new_size = %d",
676 this, name
, new_size
);
678 /* Look through the children. If one is a layout holding the named
679 window, we're done; or if one actually is the named window,
681 int found_index
= -1;
682 for (int i
= 0; i
< m_splits
.size (); ++i
)
684 tui_adjust_result adjusted
;
686 adjusted
= m_splits
[i
].layout
->set_width (name
, new_size
);
688 adjusted
= m_splits
[i
].layout
->set_height (name
, new_size
);
689 if (adjusted
== HANDLED
)
691 if (adjusted
== FOUND
)
693 if (set_width_p
? m_vertical
: !m_vertical
)
700 if (found_index
== -1)
702 int curr_size
= (set_width_p
703 ? m_splits
[found_index
].layout
->width
704 : m_splits
[found_index
].layout
->height
);
705 if (curr_size
== new_size
)
708 tui_debug_printf ("found window %s at index %d", name
, found_index
);
710 set_weights_from_sizes ();
711 int delta
= m_splits
[found_index
].weight
- new_size
;
712 m_splits
[found_index
].weight
= new_size
;
714 tui_debug_printf ("before delta (%d) distribution, weights: %s",
715 delta
, tui_debug_weights_to_string ().c_str ());
717 /* Distribute the "delta" over all other windows, while respecting their
718 min/max sizes. We grow each window by 1 line at a time continually
719 looping over all the windows. However, skip the window that the user
720 just resized, obviously we don't want to readjust that window. */
721 bool found_window_that_can_grow_p
= true;
722 for (int i
= 0; delta
!= 0; i
= (i
+ 1) % m_splits
.size ())
724 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
725 if (index
== found_index
)
727 if (!found_window_that_can_grow_p
)
729 found_window_that_can_grow_p
= false;
733 int new_min
, new_max
;
734 m_splits
[index
].layout
->get_sizes (m_vertical
, &new_min
, &new_max
);
738 /* The primary window grew, so we are trying to shrink other
740 if (m_splits
[index
].weight
> new_min
)
742 m_splits
[index
].weight
-= 1;
744 found_window_that_can_grow_p
= true;
749 /* The primary window shrank, so we are trying to grow other
751 if (m_splits
[index
].weight
< new_max
)
753 m_splits
[index
].weight
+= 1;
755 found_window_that_can_grow_p
= true;
759 tui_debug_printf ("index = %d, weight now: %d",
760 index
, m_splits
[index
].weight
);
763 tui_debug_printf ("after delta (%d) distribution, weights: %s",
764 delta
, tui_debug_weights_to_string ().c_str ());
769 warning (_("Invalid window width specified"));
771 warning (_("Invalid window height specified"));
772 /* Effectively undo any modifications made here. */
773 set_weights_from_sizes ();
777 /* Simply re-apply the updated layout. We pass false here so that
778 the cmd window can be resized. However, we should have already
779 resized everything above to be "just right", so the apply call
780 here should not end up changing the sizes at all. */
781 apply (x
, y
, width
, height
, false);
787 /* See tui-layout.h. */
790 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
,
791 bool preserve_cmd_win_size_p
)
793 TUI_SCOPED_DEBUG_ENTER_EXIT
;
800 /* In some situations we fix the size of the cmd window. However,
801 occasionally this turns out to be a mistake. This struct is used to
802 hold the original information about the cmd window, so we can restore
807 old_size_info (int index_
, int min_size_
, int max_size_
)
809 min_size (min_size_
),
813 /* The index in m_splits where the cmd window was found. */
816 /* The previous min/max size. */
821 /* This is given a value only if we fix the size of the cmd window. */
822 gdb::optional
<old_size_info
> old_cmd_info
;
824 std::vector
<size_info
> info (m_splits
.size ());
826 tui_debug_printf ("weights are: %s",
827 tui_debug_weights_to_string ().c_str ());
829 /* Step 1: Find the min and max size of each sub-layout.
830 Fixed-sized layouts are given their desired size, and then the
831 remaining space is distributed among the remaining windows
832 according to the weights given. */
833 int available_size
= m_vertical
? height
: width
;
835 int total_weight
= 0;
836 for (int i
= 0; i
< m_splits
.size (); ++i
)
838 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
840 /* Always call get_sizes, to ensure that the window is
841 instantiated. This is a bit gross but less gross than adding
842 special cases for this in other places. */
843 m_splits
[i
].layout
->get_sizes (m_vertical
, &info
[i
].min_size
,
846 if (preserve_cmd_win_size_p
847 && cmd_win_already_exists
848 && m_splits
[i
].layout
->get_name () != nullptr
849 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
851 /* Save the old cmd window information, in case we need to
853 old_cmd_info
.emplace (i
, info
[i
].min_size
, info
[i
].max_size
);
855 /* If this layout has never been applied, then it means the
856 user just changed the layout. In this situation, it's
857 desirable to keep the size of the command window the
858 same. Setting the min and max sizes this way ensures
859 that the resizing step, below, does the right thing with
861 info
[i
].min_size
= (m_vertical
862 ? TUI_CMD_WIN
->height
863 : TUI_CMD_WIN
->width
);
864 info
[i
].max_size
= info
[i
].min_size
;
867 if (info
[i
].min_size
== info
[i
].max_size
)
868 available_size
-= info
[i
].min_size
;
872 total_weight
+= m_splits
[i
].weight
;
875 /* Two adjacent boxed windows will share a border, making a bit
876 more size available. */
878 && m_splits
[i
- 1].layout
->last_edge_has_border_p ()
879 && m_splits
[i
].layout
->first_edge_has_border_p ())
880 info
[i
].share_box
= true;
883 /* If last_index is set then we have a window that is not of a fixed
884 size. This window will have its size calculated below, which requires
885 that the total_weight not be zero (we divide by total_weight, so don't
886 want a floating-point exception). */
887 gdb_assert (last_index
== -1 || total_weight
> 0);
889 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
890 are given their fixed size, while others are resized according to
893 for (int i
= 0; i
< m_splits
.size (); ++i
)
895 if (info
[i
].min_size
!= info
[i
].max_size
)
897 /* Compute the height and clamp to the allowable range. */
898 info
[i
].size
= available_size
* m_splits
[i
].weight
/ total_weight
;
899 if (info
[i
].size
> info
[i
].max_size
)
900 info
[i
].size
= info
[i
].max_size
;
901 if (info
[i
].size
< info
[i
].min_size
)
902 info
[i
].size
= info
[i
].min_size
;
903 /* Keep a total of all the size we've used so far (we gain some
904 size back if this window can share a border with a preceding
905 window). Any unused space will be distributed between all of
906 the other windows (while respecting min/max sizes) later in
908 used_size
+= info
[i
].size
;
909 if (info
[i
].share_box
)
913 info
[i
].size
= info
[i
].min_size
;
918 tui_debug_printf ("after initial size calculation");
919 tui_debug_printf ("available_size = %d, used_size = %d",
920 available_size
, used_size
);
921 tui_debug_printf ("total_weight = %d, last_index = %d",
922 total_weight
, last_index
);
923 tui_debug_print_size_info (info
);
926 /* If we didn't find any sub-layouts that were of a non-fixed size, but
927 we did find the cmd window, then we can consider that a sort-of
928 non-fixed size sub-layout.
930 The cmd window might, initially, be of a fixed size (see above), but,
931 we are willing to relax this constraint if required to correctly apply
932 this layout (see below). */
933 if (last_index
== -1 && old_cmd_info
.has_value ())
934 last_index
= old_cmd_info
->index
;
936 /* Allocate any leftover size. */
937 if (available_size
!= used_size
&& last_index
!= -1)
939 /* Loop over all windows until the amount of used space is equal to
940 the amount of available space. There's an escape hatch within
941 the loop in case we can't find any sub-layouts to resize. */
942 bool found_window_that_can_grow_p
= true;
943 for (int idx
= last_index
;
944 available_size
!= used_size
;
945 idx
= (idx
+ 1) % m_splits
.size ())
947 /* Every time we get back to last_index, which is where the loop
948 started, we check to make sure that we did assign some space
949 to a window, bringing used_size closer to available_size.
951 If we didn't, but the cmd window is of a fixed size, then we
952 can make the console window non-fixed-size, and continue
953 around the loop, hopefully, this will allow the layout to be
956 If we still make it around the loop without moving used_size
957 closer to available_size, then there's nothing more we can do,
958 and we break out of the loop. */
959 if (idx
== last_index
)
961 /* If the used_size is greater than the available_size then
962 this indicates that the fixed-sized sub-layouts claimed
963 more space than is available. This layout is not going to
964 work. Our only hope at this point is to make the cmd
965 window non-fixed-size (if possible), and hope we can
966 shrink this enough to fit the rest of the sub-layouts in.
968 Alternatively, we've made it around the loop without
969 adjusting any window's size. This likely means all
970 windows have hit their min or max size. Again, our only
971 hope is to make the cmd window non-fixed-size, and hope
972 this fixes all our problems. */
973 if (old_cmd_info
.has_value ()
974 && ((available_size
< used_size
)
975 || !found_window_that_can_grow_p
))
977 info
[old_cmd_info
->index
].min_size
= old_cmd_info
->min_size
;
978 info
[old_cmd_info
->index
].max_size
= old_cmd_info
->max_size
;
980 ("restoring index %d (cmd) size limits, min = %d, max = %d",
981 old_cmd_info
->index
, old_cmd_info
->min_size
,
982 old_cmd_info
->max_size
);
983 old_cmd_info
.reset ();
985 else if (!found_window_that_can_grow_p
)
987 found_window_that_can_grow_p
= false;
990 if (available_size
> used_size
991 && info
[idx
].size
< info
[idx
].max_size
)
993 found_window_that_can_grow_p
= true;
997 else if (available_size
< used_size
998 && info
[idx
].size
> info
[idx
].min_size
)
1000 found_window_that_can_grow_p
= true;
1001 info
[idx
].size
-= 1;
1008 tui_debug_printf ("after final size calculation");
1009 tui_debug_printf ("available_size = %d, used_size = %d",
1010 available_size
, used_size
);
1011 tui_debug_printf ("total_weight = %d, last_index = %d",
1012 total_weight
, last_index
);
1013 tui_debug_print_size_info (info
);
1017 /* Step 3: Resize. */
1019 const int maximum
= m_vertical
? height
: width
;
1020 for (int i
= 0; i
< m_splits
.size (); ++i
)
1022 /* If we fall off the bottom, just make allocations overlap.
1024 if (size_accum
+ info
[i
].size
> maximum
)
1025 size_accum
= maximum
- info
[i
].size
;
1026 else if (info
[i
].share_box
)
1029 m_splits
[i
].layout
->apply (x
, y
+ size_accum
, width
, info
[i
].size
,
1030 preserve_cmd_win_size_p
);
1032 m_splits
[i
].layout
->apply (x
+ size_accum
, y
, info
[i
].size
, height
,
1033 preserve_cmd_win_size_p
);
1034 size_accum
+= info
[i
].size
;
1038 /* See tui-layout.h. */
1041 tui_layout_split::remove_windows (const char *name
)
1043 for (int i
= 0; i
< m_splits
.size (); ++i
)
1045 const char *this_name
= m_splits
[i
].layout
->get_name ();
1046 if (this_name
== nullptr)
1047 m_splits
[i
].layout
->remove_windows (name
);
1048 else if (strcmp (this_name
, name
) == 0
1049 || strcmp (this_name
, CMD_NAME
) == 0
1050 || strcmp (this_name
, STATUS_NAME
) == 0)
1056 m_splits
.erase (m_splits
.begin () + i
);
1062 /* See tui-layout.h. */
1065 tui_layout_split::replace_window (const char *name
, const char *new_window
)
1067 for (auto &item
: m_splits
)
1068 item
.layout
->replace_window (name
, new_window
);
1071 /* See tui-layout.h. */
1074 tui_layout_split::specification (ui_file
*output
, int depth
)
1077 gdb_puts ("{", output
);
1080 gdb_puts ("-horizontal ", output
);
1083 for (auto &item
: m_splits
)
1086 gdb_puts (" ", output
);
1088 item
.layout
->specification (output
, depth
+ 1);
1089 gdb_printf (output
, " %d", item
.weight
);
1093 gdb_puts ("}", output
);
1096 /* See tui-layout.h. */
1099 tui_layout_split::layout_fingerprint () const
1101 for (auto &item
: m_splits
)
1103 std::string fp
= item
.layout
->layout_fingerprint ();
1104 if (!fp
.empty () && m_splits
.size () != 1)
1105 return std::string (m_vertical
? "V" : "H") + fp
;
1111 /* Destroy the layout associated with SELF. */
1114 destroy_layout (struct cmd_list_element
*self
, void *context
)
1116 tui_layout_split
*layout
= (tui_layout_split
*) context
;
1117 size_t index
= find_layout (layout
);
1118 layouts
.erase (layouts
.begin () + index
);
1121 /* List holding the sub-commands of "layout". */
1123 static struct cmd_list_element
*layout_list
;
1125 /* Called to implement 'tui layout'. */
1128 tui_layout_command (const char *args
, int from_tty
)
1130 help_list (layout_list
, "tui layout ", all_commands
, gdb_stdout
);
1133 /* Add a "layout" command with name NAME that switches to LAYOUT. */
1135 static struct cmd_list_element
*
1136 add_layout_command (const char *name
, tui_layout_split
*layout
)
1138 struct cmd_list_element
*cmd
;
1141 layout
->specification (&spec
, 0);
1143 gdb::unique_xmalloc_ptr
<char> doc
1144 = xstrprintf (_("Apply the \"%s\" layout.\n\
1145 This layout was created using:\n\
1146 tui new-layout %s %s"),
1147 name
, name
, spec
.c_str ());
1149 cmd
= add_cmd (name
, class_tui
, nullptr, doc
.get (), &layout_list
);
1150 cmd
->set_context (layout
);
1151 /* There is no API to set this. */
1152 cmd
->func
= tui_apply_layout
;
1153 cmd
->destroyer
= destroy_layout
;
1154 cmd
->doc_allocated
= 1;
1156 layouts
.emplace_back (layout
);
1161 /* Initialize the standard layouts. */
1164 initialize_layouts ()
1166 tui_layout_split
*layout
;
1168 layout
= new tui_layout_split ();
1169 layout
->add_window (SRC_NAME
, 2);
1170 layout
->add_window (STATUS_NAME
, 0);
1171 layout
->add_window (CMD_NAME
, 1);
1172 add_layout_command (SRC_NAME
, layout
);
1174 layout
= new tui_layout_split ();
1175 layout
->add_window (DISASSEM_NAME
, 2);
1176 layout
->add_window (STATUS_NAME
, 0);
1177 layout
->add_window (CMD_NAME
, 1);
1178 add_layout_command (DISASSEM_NAME
, layout
);
1180 layout
= new tui_layout_split ();
1181 layout
->add_window (SRC_NAME
, 1);
1182 layout
->add_window (DISASSEM_NAME
, 1);
1183 layout
->add_window (STATUS_NAME
, 0);
1184 layout
->add_window (CMD_NAME
, 1);
1185 add_layout_command ("split", layout
);
1187 layout
= new tui_layout_split ();
1188 layout
->add_window (DATA_NAME
, 1);
1189 layout
->add_window (SRC_NAME
, 1);
1190 layout
->add_window (STATUS_NAME
, 0);
1191 layout
->add_window (CMD_NAME
, 1);
1192 layouts
.emplace_back (layout
);
1193 src_regs_layout
= layout
;
1195 layout
= new tui_layout_split ();
1196 layout
->add_window (DATA_NAME
, 1);
1197 layout
->add_window (DISASSEM_NAME
, 1);
1198 layout
->add_window (STATUS_NAME
, 0);
1199 layout
->add_window (CMD_NAME
, 1);
1200 layouts
.emplace_back (layout
);
1201 asm_regs_layout
= layout
;
1206 /* A helper function that returns true if NAME is the name of an
1207 available window. */
1210 validate_window_name (const std::string
&name
)
1212 auto iter
= known_window_types
.find (name
);
1213 return iter
!= known_window_types
.end ();
1216 /* Implementation of the "tui new-layout" command. */
1219 tui_new_layout_command (const char *spec
, int from_tty
)
1221 std::string new_name
= extract_arg (&spec
);
1222 if (new_name
.empty ())
1223 error (_("No layout name specified"));
1224 if (new_name
[0] == '-')
1225 error (_("Layout name cannot start with '-'"));
1227 bool is_vertical
= true;
1228 spec
= skip_spaces (spec
);
1229 if (check_for_argument (&spec
, "-horizontal"))
1230 is_vertical
= false;
1232 std::vector
<std::unique_ptr
<tui_layout_split
>> splits
;
1233 splits
.emplace_back (new tui_layout_split (is_vertical
));
1234 std::unordered_set
<std::string
> seen_windows
;
1237 spec
= skip_spaces (spec
);
1238 if (spec
[0] == '\0')
1244 spec
= skip_spaces (spec
+ 1);
1245 if (check_for_argument (&spec
, "-horizontal"))
1246 is_vertical
= false;
1247 splits
.emplace_back (new tui_layout_split (is_vertical
));
1251 bool is_close
= false;
1257 if (splits
.size () == 1)
1258 error (_("Extra '}' in layout specification"));
1262 name
= extract_arg (&spec
);
1265 if (!validate_window_name (name
))
1266 error (_("Unknown window \"%s\""), name
.c_str ());
1267 if (seen_windows
.find (name
) != seen_windows
.end ())
1268 error (_("Window \"%s\" seen twice in layout"), name
.c_str ());
1271 ULONGEST weight
= get_ulongest (&spec
, '}');
1272 if ((int) weight
!= weight
)
1273 error (_("Weight out of range: %s"), pulongest (weight
));
1276 std::unique_ptr
<tui_layout_split
> last_split
1277 = std::move (splits
.back ());
1279 splits
.back ()->add_split (std::move (last_split
), weight
);
1283 splits
.back ()->add_window (name
.c_str (), weight
);
1284 seen_windows
.insert (name
);
1287 if (splits
.size () > 1)
1288 error (_("Missing '}' in layout specification"));
1289 if (seen_windows
.empty ())
1290 error (_("New layout does not contain any windows"));
1291 if (seen_windows
.find (CMD_NAME
) == seen_windows
.end ())
1292 error (_("New layout does not contain the \"" CMD_NAME
"\" window"));
1294 gdb::unique_xmalloc_ptr
<char> cmd_name
1295 = make_unique_xstrdup (new_name
.c_str ());
1296 std::unique_ptr
<tui_layout_split
> new_layout
= std::move (splits
.back ());
1297 struct cmd_list_element
*cmd
1298 = add_layout_command (cmd_name
.get (), new_layout
.get ());
1299 cmd
->name_allocated
= 1;
1300 cmd_name
.release ();
1301 new_layout
.release ();
1304 /* Function to initialize gdb commands, for tui window layout
1307 void _initialize_tui_layout ();
1309 _initialize_tui_layout ()
1311 struct cmd_list_element
*layout_cmd
1312 = add_prefix_cmd ("layout", class_tui
, tui_layout_command
, _("\
1313 Change the layout of windows.\n\
1314 Usage: tui layout prev | next | LAYOUT-NAME"),
1315 &layout_list
, 0, tui_get_cmd_list ());
1316 add_com_alias ("layout", layout_cmd
, class_tui
, 0);
1318 add_cmd ("next", class_tui
, tui_next_layout_command
,
1319 _("Apply the next TUI layout."),
1321 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
1322 _("Apply the previous TUI layout."),
1324 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
1325 _("Apply the TUI register layout."),
1328 add_cmd ("new-layout", class_tui
, tui_new_layout_command
,
1329 _("Create a new TUI layout.\n\
1330 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1331 Create a new TUI layout. The new layout will be named NAME,\n\
1332 and can be accessed using \"layout NAME\".\n\
1333 The windows will be displayed in the specified order.\n\
1334 A WINDOW can also be of the form:\n\
1335 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1336 This form indicates a sub-frame.\n\
1337 Each WEIGHT is an integer, which holds the relative size\n\
1338 to be allocated to the window."),
1339 tui_get_cmd_list ());
1341 initialize_layouts ();
1342 initialize_known_windows ();