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 (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. Note that
347 this is heap-allocated and "leaked" at gdb exit. This avoids
348 ordering issues with destroying elements in the map at shutdown.
349 In particular, destroying this map can occur after Python has been
350 shut down, causing crashes if any window destruction requires
351 running Python code. */
353 static std::unordered_map
<std::string
, window_factory
> *known_window_types
;
355 /* Helper function that returns a TUI window, given its name. */
357 static tui_win_info
*
358 tui_get_window_by_name (const std::string
&name
)
360 for (tui_win_info
*window
: tui_windows
)
361 if (name
== window
->name ())
364 auto iter
= known_window_types
->find (name
);
365 if (iter
== known_window_types
->end ())
366 error (_("Unknown window type \"%s\""), name
.c_str ());
368 tui_win_info
*result
= iter
->second (name
.c_str ());
369 if (result
== nullptr)
370 error (_("Could not create window \"%s\""), name
.c_str ());
374 /* Initialize the known window types. */
377 initialize_known_windows ()
379 known_window_types
= new std::unordered_map
<std::string
, window_factory
>;
381 known_window_types
->emplace (SRC_NAME
,
382 make_standard_window
<SRC_WIN
,
384 known_window_types
->emplace (CMD_NAME
,
385 make_standard_window
<CMD_WIN
, tui_cmd_window
>);
386 known_window_types
->emplace (DATA_NAME
,
387 make_standard_window
<DATA_WIN
,
389 known_window_types
->emplace (DISASSEM_NAME
,
390 make_standard_window
<DISASSEM_WIN
,
392 known_window_types
->emplace (STATUS_NAME
,
393 make_standard_window
<STATUS_WIN
,
394 tui_locator_window
>);
397 /* See tui-layout.h. */
400 tui_register_window (const char *name
, window_factory
&&factory
)
402 std::string name_copy
= name
;
404 if (name_copy
== SRC_NAME
|| name_copy
== CMD_NAME
|| name_copy
== DATA_NAME
405 || name_copy
== DISASSEM_NAME
|| name_copy
== STATUS_NAME
)
406 error (_("Window type \"%s\" is built-in"), name
);
408 known_window_types
->emplace (std::move (name_copy
),
409 std::move (factory
));
412 /* See tui-layout.h. */
414 std::unique_ptr
<tui_layout_base
>
415 tui_layout_window::clone () const
417 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
418 return std::unique_ptr
<tui_layout_base
> (result
);
421 /* See tui-layout.h. */
424 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
,
425 bool preserve_cmd_win_size_p
)
431 gdb_assert (m_window
!= nullptr);
432 m_window
->resize (height
, width
, x
, y
);
435 /* See tui-layout.h. */
438 tui_layout_window::get_sizes (bool height
, int *min_value
, int *max_value
)
440 TUI_SCOPED_DEBUG_ENTER_EXIT
;
442 if (m_window
== nullptr)
443 m_window
= tui_get_window_by_name (m_contents
);
445 tui_debug_printf ("window = %s, getting %s",
446 m_window
->name (), (height
? "height" : "width"));
450 *min_value
= m_window
->min_height ();
451 *max_value
= m_window
->max_height ();
455 *min_value
= m_window
->min_width ();
456 *max_value
= m_window
->max_width ();
459 tui_debug_printf ("min = %d, max = %d", *min_value
, *max_value
);
462 /* See tui-layout.h. */
465 tui_layout_window::first_edge_has_border_p () const
467 gdb_assert (m_window
!= nullptr);
468 return m_window
->can_box ();
471 /* See tui-layout.h. */
474 tui_layout_window::last_edge_has_border_p () const
476 gdb_assert (m_window
!= nullptr);
477 return m_window
->can_box ();
480 /* See tui-layout.h. */
483 tui_layout_window::replace_window (const char *name
, const char *new_window
)
485 if (m_contents
== name
)
487 m_contents
= new_window
;
488 if (m_window
!= nullptr)
490 m_window
->make_visible (false);
491 m_window
= tui_get_window_by_name (m_contents
);
496 /* See tui-layout.h. */
499 tui_layout_window::specification (ui_file
*output
, int depth
)
501 gdb_puts (get_name (), output
);
504 /* See tui-layout.h. */
507 tui_layout_window::layout_fingerprint () const
509 if (strcmp (get_name (), "cmd") == 0)
515 /* See tui-layout.h. */
518 tui_layout_split::add_split (std::unique_ptr
<tui_layout_split
> &&layout
,
521 split s
= {weight
, std::move (layout
)};
522 m_splits
.push_back (std::move (s
));
525 /* See tui-layout.h. */
528 tui_layout_split::add_window (const char *name
, int weight
)
530 tui_layout_window
*result
= new tui_layout_window (name
);
531 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
532 m_splits
.push_back (std::move (s
));
535 /* See tui-layout.h. */
537 std::unique_ptr
<tui_layout_base
>
538 tui_layout_split::clone () const
540 tui_layout_split
*result
= new tui_layout_split (m_vertical
);
541 for (const split
&item
: m_splits
)
543 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
544 split s
= {item
.weight
, std::move (next
)};
545 result
->m_splits
.push_back (std::move (s
));
547 return std::unique_ptr
<tui_layout_base
> (result
);
550 /* See tui-layout.h. */
553 tui_layout_split::get_sizes (bool height
, int *min_value
, int *max_value
)
555 TUI_SCOPED_DEBUG_ENTER_EXIT
;
559 bool first_time
= true;
560 for (const split
&item
: m_splits
)
562 int new_min
, new_max
;
563 item
.layout
->get_sizes (height
, &new_min
, &new_max
);
564 /* For the mismatch case, the first time through we want to set
565 the min and max to the computed values -- the "first_time"
566 check here is just a funny way of doing that. */
567 if (height
== m_vertical
|| first_time
)
569 *min_value
+= new_min
;
570 *max_value
+= new_max
;
574 *min_value
= std::max (*min_value
, new_min
);
575 *max_value
= std::min (*max_value
, new_max
);
580 tui_debug_printf ("min_value = %d, max_value = %d", *min_value
, *max_value
);
583 /* See tui-layout.h. */
586 tui_layout_split::first_edge_has_border_p () const
588 if (m_splits
.empty ())
590 return m_splits
[0].layout
->first_edge_has_border_p ();
593 /* See tui-layout.h. */
596 tui_layout_split::last_edge_has_border_p () const
598 if (m_splits
.empty ())
600 return m_splits
.back ().layout
->last_edge_has_border_p ();
603 /* See tui-layout.h. */
606 tui_layout_split::set_weights_from_sizes ()
608 for (int i
= 0; i
< m_splits
.size (); ++i
)
610 = m_vertical
? m_splits
[i
].layout
->height
: m_splits
[i
].layout
->width
;
613 /* See tui-layout.h. */
616 tui_layout_split::tui_debug_weights_to_string () const
620 for (int i
= 0; i
< m_splits
.size (); ++i
)
624 str
+= string_printf ("[%d] %d", i
, m_splits
[i
].weight
);
630 /* See tui-layout.h. */
633 tui_layout_split::tui_debug_print_size_info
634 (const std::vector
<tui_layout_split::size_info
> &info
)
636 gdb_assert (debug_tui
);
638 tui_debug_printf ("current size info data:");
639 for (int i
= 0; i
< info
.size (); ++i
)
640 tui_debug_printf (" [%d] { size = %d, min = %d, max = %d, share_box = %d }",
641 i
, info
[i
].size
, info
[i
].min_size
,
642 info
[i
].max_size
, info
[i
].share_box
);
645 /* See tui-layout.h. */
648 tui_layout_split::set_size (const char *name
, int new_size
, bool set_width_p
)
650 TUI_SCOPED_DEBUG_ENTER_EXIT
;
652 tui_debug_printf ("this = %p, name = %s, new_size = %d",
653 this, name
, new_size
);
655 /* Look through the children. If one is a layout holding the named
656 window, we're done; or if one actually is the named window,
658 int found_index
= -1;
659 for (int i
= 0; i
< m_splits
.size (); ++i
)
661 tui_adjust_result adjusted
;
663 adjusted
= m_splits
[i
].layout
->set_width (name
, new_size
);
665 adjusted
= m_splits
[i
].layout
->set_height (name
, new_size
);
666 if (adjusted
== HANDLED
)
668 if (adjusted
== FOUND
)
670 if (set_width_p
? m_vertical
: !m_vertical
)
677 if (found_index
== -1)
679 int curr_size
= (set_width_p
680 ? m_splits
[found_index
].layout
->width
681 : m_splits
[found_index
].layout
->height
);
682 if (curr_size
== new_size
)
685 tui_debug_printf ("found window %s at index %d", name
, found_index
);
687 set_weights_from_sizes ();
688 int delta
= m_splits
[found_index
].weight
- new_size
;
689 m_splits
[found_index
].weight
= new_size
;
691 tui_debug_printf ("before delta (%d) distribution, weights: %s",
692 delta
, tui_debug_weights_to_string ().c_str ());
694 /* Distribute the "delta" over all other windows, while respecting their
695 min/max sizes. We grow each window by 1 line at a time continually
696 looping over all the windows. However, skip the window that the user
697 just resized, obviously we don't want to readjust that window. */
698 bool found_window_that_can_grow_p
= true;
699 for (int i
= 0; delta
!= 0; i
= (i
+ 1) % m_splits
.size ())
701 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
702 if (index
== found_index
)
704 if (!found_window_that_can_grow_p
)
706 found_window_that_can_grow_p
= false;
710 int new_min
, new_max
;
711 m_splits
[index
].layout
->get_sizes (m_vertical
, &new_min
, &new_max
);
715 /* The primary window grew, so we are trying to shrink other
717 if (m_splits
[index
].weight
> new_min
)
719 m_splits
[index
].weight
-= 1;
721 found_window_that_can_grow_p
= true;
726 /* The primary window shrank, so we are trying to grow other
728 if (m_splits
[index
].weight
< new_max
)
730 m_splits
[index
].weight
+= 1;
732 found_window_that_can_grow_p
= true;
736 tui_debug_printf ("index = %d, weight now: %d",
737 index
, m_splits
[index
].weight
);
740 tui_debug_printf ("after delta (%d) distribution, weights: %s",
741 delta
, tui_debug_weights_to_string ().c_str ());
746 warning (_("Invalid window width specified"));
748 warning (_("Invalid window height specified"));
749 /* Effectively undo any modifications made here. */
750 set_weights_from_sizes ();
754 /* Simply re-apply the updated layout. We pass false here so that
755 the cmd window can be resized. However, we should have already
756 resized everything above to be "just right", so the apply call
757 here should not end up changing the sizes at all. */
758 apply (x
, y
, width
, height
, false);
764 /* See tui-layout.h. */
767 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
,
768 bool preserve_cmd_win_size_p
)
770 TUI_SCOPED_DEBUG_ENTER_EXIT
;
777 /* In some situations we fix the size of the cmd window. However,
778 occasionally this turns out to be a mistake. This struct is used to
779 hold the original information about the cmd window, so we can restore
784 old_size_info (int index_
, int min_size_
, int max_size_
)
786 min_size (min_size_
),
790 /* The index in m_splits where the cmd window was found. */
793 /* The previous min/max size. */
798 /* This is given a value only if we fix the size of the cmd window. */
799 gdb::optional
<old_size_info
> old_cmd_info
;
801 std::vector
<size_info
> info (m_splits
.size ());
803 tui_debug_printf ("weights are: %s",
804 tui_debug_weights_to_string ().c_str ());
806 /* Step 1: Find the min and max size of each sub-layout.
807 Fixed-sized layouts are given their desired size, and then the
808 remaining space is distributed among the remaining windows
809 according to the weights given. */
810 int available_size
= m_vertical
? height
: width
;
812 int total_weight
= 0;
813 for (int i
= 0; i
< m_splits
.size (); ++i
)
815 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
817 /* Always call get_sizes, to ensure that the window is
818 instantiated. This is a bit gross but less gross than adding
819 special cases for this in other places. */
820 m_splits
[i
].layout
->get_sizes (m_vertical
, &info
[i
].min_size
,
823 if (preserve_cmd_win_size_p
824 && cmd_win_already_exists
825 && m_splits
[i
].layout
->get_name () != nullptr
826 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
828 /* Save the old cmd window information, in case we need to
830 old_cmd_info
.emplace (i
, info
[i
].min_size
, info
[i
].max_size
);
832 /* If this layout has never been applied, then it means the
833 user just changed the layout. In this situation, it's
834 desirable to keep the size of the command window the
835 same. Setting the min and max sizes this way ensures
836 that the resizing step, below, does the right thing with
838 info
[i
].min_size
= (m_vertical
839 ? TUI_CMD_WIN
->height
840 : TUI_CMD_WIN
->width
);
841 info
[i
].max_size
= info
[i
].min_size
;
844 if (info
[i
].min_size
== info
[i
].max_size
)
845 available_size
-= info
[i
].min_size
;
849 total_weight
+= m_splits
[i
].weight
;
852 /* Two adjacent boxed windows will share a border, making a bit
853 more size available. */
855 && m_splits
[i
- 1].layout
->last_edge_has_border_p ()
856 && m_splits
[i
].layout
->first_edge_has_border_p ())
857 info
[i
].share_box
= true;
860 /* If last_index is set then we have a window that is not of a fixed
861 size. This window will have its size calculated below, which requires
862 that the total_weight not be zero (we divide by total_weight, so don't
863 want a floating-point exception). */
864 gdb_assert (last_index
== -1 || total_weight
> 0);
866 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
867 are given their fixed size, while others are resized according to
870 for (int i
= 0; i
< m_splits
.size (); ++i
)
872 if (info
[i
].min_size
!= info
[i
].max_size
)
874 /* Compute the height and clamp to the allowable range. */
875 info
[i
].size
= available_size
* m_splits
[i
].weight
/ total_weight
;
876 if (info
[i
].size
> info
[i
].max_size
)
877 info
[i
].size
= info
[i
].max_size
;
878 if (info
[i
].size
< info
[i
].min_size
)
879 info
[i
].size
= info
[i
].min_size
;
880 /* Keep a total of all the size we've used so far (we gain some
881 size back if this window can share a border with a preceding
882 window). Any unused space will be distributed between all of
883 the other windows (while respecting min/max sizes) later in
885 used_size
+= info
[i
].size
;
886 if (info
[i
].share_box
)
890 info
[i
].size
= info
[i
].min_size
;
895 tui_debug_printf ("after initial size calculation");
896 tui_debug_printf ("available_size = %d, used_size = %d",
897 available_size
, used_size
);
898 tui_debug_printf ("total_weight = %d, last_index = %d",
899 total_weight
, last_index
);
900 tui_debug_print_size_info (info
);
903 /* If we didn't find any sub-layouts that were of a non-fixed size, but
904 we did find the cmd window, then we can consider that a sort-of
905 non-fixed size sub-layout.
907 The cmd window might, initially, be of a fixed size (see above), but,
908 we are willing to relax this constraint if required to correctly apply
909 this layout (see below). */
910 if (last_index
== -1 && old_cmd_info
.has_value ())
911 last_index
= old_cmd_info
->index
;
913 /* Allocate any leftover size. */
914 if (available_size
!= used_size
&& last_index
!= -1)
916 /* Loop over all windows until the amount of used space is equal to
917 the amount of available space. There's an escape hatch within
918 the loop in case we can't find any sub-layouts to resize. */
919 bool found_window_that_can_grow_p
= true;
920 for (int idx
= last_index
;
921 available_size
!= used_size
;
922 idx
= (idx
+ 1) % m_splits
.size ())
924 /* Every time we get back to last_index, which is where the loop
925 started, we check to make sure that we did assign some space
926 to a window, bringing used_size closer to available_size.
928 If we didn't, but the cmd window is of a fixed size, then we
929 can make the console window non-fixed-size, and continue
930 around the loop, hopefully, this will allow the layout to be
933 If we still make it around the loop without moving used_size
934 closer to available_size, then there's nothing more we can do,
935 and we break out of the loop. */
936 if (idx
== last_index
)
938 /* If the used_size is greater than the available_size then
939 this indicates that the fixed-sized sub-layouts claimed
940 more space than is available. This layout is not going to
941 work. Our only hope at this point is to make the cmd
942 window non-fixed-size (if possible), and hope we can
943 shrink this enough to fit the rest of the sub-layouts in.
945 Alternatively, we've made it around the loop without
946 adjusting any window's size. This likely means all
947 windows have hit their min or max size. Again, our only
948 hope is to make the cmd window non-fixed-size, and hope
949 this fixes all our problems. */
950 if (old_cmd_info
.has_value ()
951 && ((available_size
< used_size
)
952 || !found_window_that_can_grow_p
))
954 info
[old_cmd_info
->index
].min_size
= old_cmd_info
->min_size
;
955 info
[old_cmd_info
->index
].max_size
= old_cmd_info
->max_size
;
957 ("restoring index %d (cmd) size limits, min = %d, max = %d",
958 old_cmd_info
->index
, old_cmd_info
->min_size
,
959 old_cmd_info
->max_size
);
960 old_cmd_info
.reset ();
962 else if (!found_window_that_can_grow_p
)
964 found_window_that_can_grow_p
= false;
967 if (available_size
> used_size
968 && info
[idx
].size
< info
[idx
].max_size
)
970 found_window_that_can_grow_p
= true;
974 else if (available_size
< used_size
975 && info
[idx
].size
> info
[idx
].min_size
)
977 found_window_that_can_grow_p
= true;
985 tui_debug_printf ("after final size calculation");
986 tui_debug_printf ("available_size = %d, used_size = %d",
987 available_size
, used_size
);
988 tui_debug_printf ("total_weight = %d, last_index = %d",
989 total_weight
, last_index
);
990 tui_debug_print_size_info (info
);
994 /* Step 3: Resize. */
996 const int maximum
= m_vertical
? height
: width
;
997 for (int i
= 0; i
< m_splits
.size (); ++i
)
999 /* If we fall off the bottom, just make allocations overlap.
1001 if (size_accum
+ info
[i
].size
> maximum
)
1002 size_accum
= maximum
- info
[i
].size
;
1003 else if (info
[i
].share_box
)
1006 m_splits
[i
].layout
->apply (x
, y
+ size_accum
, width
, info
[i
].size
,
1007 preserve_cmd_win_size_p
);
1009 m_splits
[i
].layout
->apply (x
+ size_accum
, y
, info
[i
].size
, height
,
1010 preserve_cmd_win_size_p
);
1011 size_accum
+= info
[i
].size
;
1015 /* See tui-layout.h. */
1018 tui_layout_split::remove_windows (const char *name
)
1020 for (int i
= 0; i
< m_splits
.size (); ++i
)
1022 const char *this_name
= m_splits
[i
].layout
->get_name ();
1023 if (this_name
== nullptr)
1024 m_splits
[i
].layout
->remove_windows (name
);
1025 else if (strcmp (this_name
, name
) == 0
1026 || strcmp (this_name
, CMD_NAME
) == 0
1027 || strcmp (this_name
, STATUS_NAME
) == 0)
1033 m_splits
.erase (m_splits
.begin () + i
);
1039 /* See tui-layout.h. */
1042 tui_layout_split::replace_window (const char *name
, const char *new_window
)
1044 for (auto &item
: m_splits
)
1045 item
.layout
->replace_window (name
, new_window
);
1048 /* See tui-layout.h. */
1051 tui_layout_split::specification (ui_file
*output
, int depth
)
1054 gdb_puts ("{", output
);
1057 gdb_puts ("-horizontal ", output
);
1060 for (auto &item
: m_splits
)
1063 gdb_puts (" ", output
);
1065 item
.layout
->specification (output
, depth
+ 1);
1066 gdb_printf (output
, " %d", item
.weight
);
1070 gdb_puts ("}", output
);
1073 /* See tui-layout.h. */
1076 tui_layout_split::layout_fingerprint () const
1078 for (auto &item
: m_splits
)
1080 std::string fp
= item
.layout
->layout_fingerprint ();
1082 return std::string (m_vertical
? "V" : "H") + fp
;
1088 /* Destroy the layout associated with SELF. */
1091 destroy_layout (struct cmd_list_element
*self
, void *context
)
1093 tui_layout_split
*layout
= (tui_layout_split
*) context
;
1094 size_t index
= find_layout (layout
);
1095 layouts
.erase (layouts
.begin () + index
);
1098 /* List holding the sub-commands of "layout". */
1100 static struct cmd_list_element
*layout_list
;
1102 /* Called to implement 'tui layout'. */
1105 tui_layout_command (const char *args
, int from_tty
)
1107 help_list (layout_list
, "tui layout ", all_commands
, gdb_stdout
);
1110 /* Add a "layout" command with name NAME that switches to LAYOUT. */
1112 static struct cmd_list_element
*
1113 add_layout_command (const char *name
, tui_layout_split
*layout
)
1115 struct cmd_list_element
*cmd
;
1118 layout
->specification (&spec
, 0);
1120 gdb::unique_xmalloc_ptr
<char> doc
1121 = xstrprintf (_("Apply the \"%s\" layout.\n\
1122 This layout was created using:\n\
1123 tui new-layout %s %s"),
1124 name
, name
, spec
.c_str ());
1126 cmd
= add_cmd (name
, class_tui
, nullptr, doc
.get (), &layout_list
);
1127 cmd
->set_context (layout
);
1128 /* There is no API to set this. */
1129 cmd
->func
= tui_apply_layout
;
1130 cmd
->destroyer
= destroy_layout
;
1131 cmd
->doc_allocated
= 1;
1133 layouts
.emplace_back (layout
);
1138 /* Initialize the standard layouts. */
1141 initialize_layouts ()
1143 tui_layout_split
*layout
;
1145 layout
= new tui_layout_split ();
1146 layout
->add_window (SRC_NAME
, 2);
1147 layout
->add_window (STATUS_NAME
, 0);
1148 layout
->add_window (CMD_NAME
, 1);
1149 add_layout_command (SRC_NAME
, layout
);
1151 layout
= new tui_layout_split ();
1152 layout
->add_window (DISASSEM_NAME
, 2);
1153 layout
->add_window (STATUS_NAME
, 0);
1154 layout
->add_window (CMD_NAME
, 1);
1155 add_layout_command (DISASSEM_NAME
, layout
);
1157 layout
= new tui_layout_split ();
1158 layout
->add_window (SRC_NAME
, 1);
1159 layout
->add_window (DISASSEM_NAME
, 1);
1160 layout
->add_window (STATUS_NAME
, 0);
1161 layout
->add_window (CMD_NAME
, 1);
1162 add_layout_command ("split", layout
);
1164 layout
= new tui_layout_split ();
1165 layout
->add_window (DATA_NAME
, 1);
1166 layout
->add_window (SRC_NAME
, 1);
1167 layout
->add_window (STATUS_NAME
, 0);
1168 layout
->add_window (CMD_NAME
, 1);
1169 layouts
.emplace_back (layout
);
1170 src_regs_layout
= layout
;
1172 layout
= new tui_layout_split ();
1173 layout
->add_window (DATA_NAME
, 1);
1174 layout
->add_window (DISASSEM_NAME
, 1);
1175 layout
->add_window (STATUS_NAME
, 0);
1176 layout
->add_window (CMD_NAME
, 1);
1177 layouts
.emplace_back (layout
);
1178 asm_regs_layout
= layout
;
1183 /* A helper function that returns true if NAME is the name of an
1184 available window. */
1187 validate_window_name (const std::string
&name
)
1189 auto iter
= known_window_types
->find (name
);
1190 return iter
!= known_window_types
->end ();
1193 /* Implementation of the "tui new-layout" command. */
1196 tui_new_layout_command (const char *spec
, int from_tty
)
1198 std::string new_name
= extract_arg (&spec
);
1199 if (new_name
.empty ())
1200 error (_("No layout name specified"));
1201 if (new_name
[0] == '-')
1202 error (_("Layout name cannot start with '-'"));
1204 bool is_vertical
= true;
1205 spec
= skip_spaces (spec
);
1206 if (check_for_argument (&spec
, "-horizontal"))
1207 is_vertical
= false;
1209 std::vector
<std::unique_ptr
<tui_layout_split
>> splits
;
1210 splits
.emplace_back (new tui_layout_split (is_vertical
));
1211 std::unordered_set
<std::string
> seen_windows
;
1214 spec
= skip_spaces (spec
);
1215 if (spec
[0] == '\0')
1221 spec
= skip_spaces (spec
+ 1);
1222 if (check_for_argument (&spec
, "-horizontal"))
1223 is_vertical
= false;
1224 splits
.emplace_back (new tui_layout_split (is_vertical
));
1228 bool is_close
= false;
1234 if (splits
.size () == 1)
1235 error (_("Extra '}' in layout specification"));
1239 name
= extract_arg (&spec
);
1242 if (!validate_window_name (name
))
1243 error (_("Unknown window \"%s\""), name
.c_str ());
1244 if (seen_windows
.find (name
) != seen_windows
.end ())
1245 error (_("Window \"%s\" seen twice in layout"), name
.c_str ());
1248 ULONGEST weight
= get_ulongest (&spec
, '}');
1249 if ((int) weight
!= weight
)
1250 error (_("Weight out of range: %s"), pulongest (weight
));
1253 std::unique_ptr
<tui_layout_split
> last_split
1254 = std::move (splits
.back ());
1256 splits
.back ()->add_split (std::move (last_split
), weight
);
1260 splits
.back ()->add_window (name
.c_str (), weight
);
1261 seen_windows
.insert (name
);
1264 if (splits
.size () > 1)
1265 error (_("Missing '}' in layout specification"));
1266 if (seen_windows
.empty ())
1267 error (_("New layout does not contain any windows"));
1268 if (seen_windows
.find (CMD_NAME
) == seen_windows
.end ())
1269 error (_("New layout does not contain the \"" CMD_NAME
"\" window"));
1271 gdb::unique_xmalloc_ptr
<char> cmd_name
1272 = make_unique_xstrdup (new_name
.c_str ());
1273 std::unique_ptr
<tui_layout_split
> new_layout
= std::move (splits
.back ());
1274 struct cmd_list_element
*cmd
1275 = add_layout_command (cmd_name
.get (), new_layout
.get ());
1276 cmd
->name_allocated
= 1;
1277 cmd_name
.release ();
1278 new_layout
.release ();
1281 /* Function to initialize gdb commands, for tui window layout
1284 void _initialize_tui_layout ();
1286 _initialize_tui_layout ()
1288 struct cmd_list_element
*layout_cmd
1289 = add_prefix_cmd ("layout", class_tui
, tui_layout_command
, _("\
1290 Change the layout of windows.\n\
1291 Usage: tui layout prev | next | LAYOUT-NAME"),
1292 &layout_list
, 0, tui_get_cmd_list ());
1293 add_com_alias ("layout", layout_cmd
, class_tui
, 0);
1295 add_cmd ("next", class_tui
, tui_next_layout_command
,
1296 _("Apply the next TUI layout."),
1298 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
1299 _("Apply the previous TUI layout."),
1301 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
1302 _("Apply the TUI register layout."),
1305 add_cmd ("new-layout", class_tui
, tui_new_layout_command
,
1306 _("Create a new TUI layout.\n\
1307 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1308 Create a new TUI layout. The new layout will be named NAME,\n\
1309 and can be accessed using \"layout NAME\".\n\
1310 The windows will be displayed in the specified order.\n\
1311 A WINDOW can also be of the form:\n\
1312 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1313 This form indicates a sub-frame.\n\
1314 Each WEIGHT is an integer, which holds the relative size\n\
1315 to be allocated to the window."),
1316 tui_get_cmd_list ());
1318 initialize_layouts ();
1319 initialize_known_windows ();