1 /* TUI layout window management.
3 Copyright (C) 1998-2024 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/>. */
25 #include "cli/cli-decode.h"
26 #include "cli/cli-utils.h"
27 #include <unordered_set>
30 #include "tui/tui-command.h"
31 #include "tui/tui-data.h"
32 #include "tui/tui-wingeneral.h"
33 #include "tui/tui-status.h"
34 #include "tui/tui-regs.h"
35 #include "tui/tui-win.h"
36 #include "tui/tui-disasm.h"
37 #include "tui/tui-layout.h"
38 #include "tui/tui-source.h"
39 #include "gdb_curses.h"
40 #include "gdbsupport/gdb-safe-ctype.h"
43 static std::vector
<std::unique_ptr
<tui_layout_split
>> layouts
;
45 /* The layout that is currently applied. */
46 static std::unique_ptr
<tui_layout_base
> applied_layout
;
48 /* The "skeleton" version of the layout that is currently applied. */
49 static tui_layout_split
*applied_skeleton
;
51 /* The two special "regs" layouts. Note that these aren't registered
52 as commands and so can never be deleted. */
53 static tui_layout_split
*src_regs_layout
;
54 static tui_layout_split
*asm_regs_layout
;
57 std::vector
<tui_win_info
*> tui_windows
;
59 /* See tui-layout.h. */
62 tui_apply_current_layout (bool preserve_cmd_win_size_p
)
64 tui_batch_rendering defer
;
66 for (tui_win_info
*win_info
: tui_windows
)
67 win_info
->make_visible (false);
69 applied_layout
->apply (0, 0, tui_term_width (), tui_term_height (),
70 preserve_cmd_win_size_p
);
72 /* Keep the list of internal windows up-to-date. */
73 for (int win_type
= SRC_WIN
; (win_type
< MAX_MAJOR_WINDOWS
); win_type
++)
74 if (tui_win_list
[win_type
] != nullptr
75 && !tui_win_list
[win_type
]->is_visible ())
76 tui_win_list
[win_type
] = nullptr;
78 /* This should always be made visible by a layout. */
79 gdb_assert (tui_cmd_win () != nullptr);
80 gdb_assert (tui_cmd_win ()->is_visible ());
82 /* Get the new list of currently visible windows. */
83 std::vector
<tui_win_info
*> new_tui_windows
;
84 applied_layout
->get_windows (&new_tui_windows
);
86 /* Now delete any window that was not re-applied. */
87 tui_win_info
*focus
= tui_win_with_focus ();
88 for (tui_win_info
*win_info
: tui_windows
)
90 if (!win_info
->is_visible ())
92 if (focus
== win_info
)
93 tui_set_win_focus_to (new_tui_windows
[0]);
98 /* Replace the global list of active windows. */
99 tui_windows
= std::move (new_tui_windows
);
102 /* See tui-layout. */
105 tui_adjust_window_height (struct tui_win_info
*win
, int new_height
)
107 applied_layout
->set_height (win
->name (), new_height
);
110 /* See tui-layout. */
113 tui_adjust_window_width (struct tui_win_info
*win
, int new_width
)
115 applied_layout
->set_width (win
->name (), new_width
);
118 /* Set the current layout to LAYOUT. */
121 tui_set_layout (tui_layout_split
*layout
)
123 std::string old_fingerprint
;
124 if (applied_layout
!= nullptr)
125 old_fingerprint
= applied_layout
->layout_fingerprint ();
127 applied_skeleton
= layout
;
128 applied_layout
= layout
->clone ();
130 std::string new_fingerprint
= applied_layout
->layout_fingerprint ();
131 bool preserve_command_window_size
132 = (tui_cmd_win () != nullptr && old_fingerprint
== new_fingerprint
);
134 tui_apply_current_layout (preserve_command_window_size
);
137 /* See tui-layout.h. */
140 tui_add_win_to_layout (enum tui_win_type type
)
142 gdb_assert (type
== SRC_WIN
|| type
== DISASSEM_WIN
);
144 /* If the window already exists, no need to add it. */
145 if (tui_win_list
[type
] != nullptr)
148 /* If the window we are trying to replace doesn't exist, we're
150 enum tui_win_type other
= type
== SRC_WIN
? DISASSEM_WIN
: SRC_WIN
;
151 if (tui_win_list
[other
] == nullptr)
154 const char *name
= type
== SRC_WIN
? SRC_NAME
: DISASSEM_NAME
;
155 applied_layout
->replace_window (tui_win_list
[other
]->name (), name
);
156 tui_apply_current_layout (true);
159 /* Find LAYOUT in the "layouts" global and return its index. */
162 find_layout (tui_layout_split
*layout
)
164 for (size_t i
= 0; i
< layouts
.size (); ++i
)
166 if (layout
== layouts
[i
].get ())
169 gdb_assert_not_reached ("layout not found!?");
172 /* Function to set the layout. */
175 tui_apply_layout (const char *args
, int from_tty
, cmd_list_element
*command
)
177 tui_layout_split
*layout
= (tui_layout_split
*) command
->context ();
179 /* Make sure the curses mode is enabled. */
181 tui_set_layout (layout
);
184 /* See tui-layout.h. */
189 size_t index
= find_layout (applied_skeleton
);
191 if (index
== layouts
.size ())
193 tui_set_layout (layouts
[index
].get ());
196 /* Implement the "layout next" command. */
199 tui_next_layout_command (const char *arg
, int from_tty
)
205 /* See tui-layout.h. */
208 tui_set_initial_layout ()
210 tui_set_layout (layouts
[0].get ());
213 /* Implement the "layout prev" command. */
216 tui_prev_layout_command (const char *arg
, int from_tty
)
219 size_t index
= find_layout (applied_skeleton
);
221 index
= layouts
.size ();
223 tui_set_layout (layouts
[index
].get ());
227 /* See tui-layout.h. */
232 /* If there's already a register window, we're done. */
233 if (tui_data_win () != nullptr)
236 tui_set_layout (tui_disasm_win () != nullptr
241 /* Implement the "layout regs" command. */
244 tui_regs_layout_command (const char *arg
, int from_tty
)
250 /* See tui-layout.h. */
253 tui_remove_some_windows ()
255 tui_win_info
*focus
= tui_win_with_focus ();
257 if (strcmp (focus
->name (), CMD_NAME
) == 0)
259 /* Try leaving the source or disassembly window. If neither
260 exists, just do nothing. */
261 focus
= tui_src_win ();
262 if (focus
== nullptr)
263 focus
= tui_disasm_win ();
264 if (focus
== nullptr)
268 applied_layout
->remove_windows (focus
->name ());
269 tui_apply_current_layout (true);
273 tui_win_info::resize (int height_
, int width_
,
274 int origin_x_
, int origin_y_
)
276 if (width
== width_
&& height
== height_
277 && x
== origin_x_
&& y
== origin_y_
278 && handle
!= nullptr)
286 if (handle
!= nullptr)
289 wresize (handle
.get (), height
, width
);
290 mvwin (handle
.get (), y
, x
);
291 wmove (handle
.get (), 0, 0);
293 handle
.reset (nullptr);
297 if (handle
== nullptr)
305 /* Helper function to create one of the built-in (non-status)
308 template<enum tui_win_type V
, class T
>
309 static tui_win_info
*
310 make_standard_window (const char *)
312 if (tui_win_list
[V
] == nullptr)
313 tui_win_list
[V
] = new T ();
314 return tui_win_list
[V
];
317 /* A map holding all the known window types, keyed by name. */
319 static window_types_map known_window_types
;
321 /* See tui-layout.h. */
323 known_window_names_range
324 all_known_window_names ()
326 auto begin
= known_window_names_iterator (known_window_types
.begin ());
327 auto end
= known_window_names_iterator (known_window_types
.end ());
328 return known_window_names_range (begin
, end
);
331 /* Helper function that returns a TUI window, given its name. */
333 static tui_win_info
*
334 tui_get_window_by_name (const std::string
&name
)
336 for (tui_win_info
*window
: tui_windows
)
337 if (name
== window
->name ())
340 auto iter
= known_window_types
.find (name
);
341 if (iter
== known_window_types
.end ())
342 error (_("Unknown window type \"%s\""), name
.c_str ());
344 tui_win_info
*result
= iter
->second (name
.c_str ());
345 if (result
== nullptr)
346 error (_("Could not create window \"%s\""), name
.c_str ());
350 /* Initialize the known window types. */
353 initialize_known_windows ()
355 known_window_types
.emplace (SRC_NAME
,
356 make_standard_window
<SRC_WIN
,
358 known_window_types
.emplace (CMD_NAME
,
359 make_standard_window
<CMD_WIN
, tui_cmd_window
>);
360 known_window_types
.emplace (DATA_NAME
,
361 make_standard_window
<DATA_WIN
,
363 known_window_types
.emplace (DISASSEM_NAME
,
364 make_standard_window
<DISASSEM_WIN
,
366 known_window_types
.emplace (STATUS_NAME
,
367 make_standard_window
<STATUS_WIN
,
371 /* See tui-layout.h. */
374 tui_register_window (const char *name
, window_factory
&&factory
)
376 std::string name_copy
= name
;
378 if (name_copy
== SRC_NAME
|| name_copy
== CMD_NAME
|| name_copy
== DATA_NAME
379 || name_copy
== DISASSEM_NAME
|| name_copy
== STATUS_NAME
)
380 error (_("Window type \"%s\" is built-in"), name
);
382 for (const char &c
: name_copy
)
385 error (_("invalid whitespace character in window name"));
387 if (!ISALNUM (c
) && strchr ("-_.", c
) == nullptr)
388 error (_("invalid character '%c' in window name"), c
);
391 if (!ISALPHA (name_copy
[0]))
392 error (_("window name must start with a letter, not '%c'"), name_copy
[0]);
394 /* We already check above for all the builtin window names. If we get
395 this far then NAME must be a user defined window. Remove any existing
396 factory and replace it with this new version. */
398 auto iter
= known_window_types
.find (name
);
399 if (iter
!= known_window_types
.end ())
400 known_window_types
.erase (iter
);
402 known_window_types
.emplace (std::move (name_copy
),
403 std::move (factory
));
406 /* See tui-layout.h. */
408 std::unique_ptr
<tui_layout_base
>
409 tui_layout_window::clone () const
411 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
412 return std::unique_ptr
<tui_layout_base
> (result
);
415 /* See tui-layout.h. */
418 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
,
419 bool preserve_cmd_win_size_p
)
425 gdb_assert (m_window
!= nullptr);
426 if (width
== 0 || height
== 0)
428 /* The window was dropped, so it's going to be deleted, reset the
429 soon to be dangling pointer. */
433 m_window
->resize (height
, width
, x
, y
);
436 /* See tui-layout.h. */
439 tui_layout_window::get_sizes (bool height
, int *min_value
, int *max_value
)
441 TUI_SCOPED_DEBUG_ENTER_EXIT
;
443 if (m_window
== nullptr)
444 m_window
= tui_get_window_by_name (m_contents
);
446 tui_debug_printf ("window = %s, getting %s",
447 m_window
->name (), (height
? "height" : "width"));
451 *min_value
= m_window
->min_height ();
452 *max_value
= m_window
->max_height ();
456 *min_value
= m_window
->min_width ();
457 *max_value
= m_window
->max_width ();
460 tui_debug_printf ("min = %d, max = %d", *min_value
, *max_value
);
463 /* See tui-layout.h. */
466 tui_layout_window::first_edge_has_border_p () const
468 gdb_assert (m_window
!= nullptr);
469 return m_window
->can_box ();
472 /* See tui-layout.h. */
475 tui_layout_window::last_edge_has_border_p () const
477 gdb_assert (m_window
!= nullptr);
478 return m_window
->can_box ();
481 /* See tui-layout.h. */
484 tui_layout_window::replace_window (const char *name
, const char *new_window
)
486 if (m_contents
== name
)
488 m_contents
= new_window
;
489 if (m_window
!= nullptr)
491 m_window
->make_visible (false);
492 m_window
= tui_get_window_by_name (m_contents
);
497 /* See tui-layout.h. */
500 tui_layout_window::specification (ui_file
*output
, int depth
)
502 gdb_puts (get_name (), output
);
505 /* See tui-layout.h. */
508 tui_layout_window::layout_fingerprint () const
510 if (strcmp (get_name (), "cmd") == 0)
516 /* See tui-layout.h. */
519 tui_layout_split::add_split (std::unique_ptr
<tui_layout_split
> &&layout
,
522 split s
= {weight
, std::move (layout
)};
523 m_splits
.push_back (std::move (s
));
526 /* See tui-layout.h. */
529 tui_layout_split::add_window (const char *name
, int weight
)
531 tui_layout_window
*result
= new tui_layout_window (name
);
532 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
533 m_splits
.push_back (std::move (s
));
536 /* See tui-layout.h. */
538 std::unique_ptr
<tui_layout_base
>
539 tui_layout_split::clone () const
541 tui_layout_split
*result
= new tui_layout_split (m_vertical
);
542 for (const split
&item
: m_splits
)
544 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
545 split s
= {item
.weight
, std::move (next
)};
546 result
->m_splits
.push_back (std::move (s
));
548 return std::unique_ptr
<tui_layout_base
> (result
);
551 /* See tui-layout.h. */
554 tui_layout_split::get_sizes (bool height
, int *min_value
, int *max_value
)
556 TUI_SCOPED_DEBUG_ENTER_EXIT
;
560 bool first_time
= true;
561 for (const split
&item
: m_splits
)
563 int new_min
, new_max
;
564 item
.layout
->get_sizes (height
, &new_min
, &new_max
);
565 /* For the mismatch case, the first time through we want to set
566 the min and max to the computed values -- the "first_time"
567 check here is just a funny way of doing that. */
568 if (height
== m_vertical
|| first_time
)
570 *min_value
+= new_min
;
571 *max_value
+= new_max
;
575 *min_value
= std::max (*min_value
, new_min
);
576 *max_value
= std::min (*max_value
, new_max
);
581 tui_debug_printf ("min_value = %d, max_value = %d", *min_value
, *max_value
);
584 /* See tui-layout.h. */
587 tui_layout_split::first_edge_has_border_p () const
589 if (m_splits
.empty ())
591 return m_splits
[0].layout
->first_edge_has_border_p ();
594 /* See tui-layout.h. */
597 tui_layout_split::last_edge_has_border_p () const
599 if (m_splits
.empty ())
601 return m_splits
.back ().layout
->last_edge_has_border_p ();
604 /* See tui-layout.h. */
607 tui_layout_split::set_weights_from_sizes ()
609 for (int i
= 0; i
< m_splits
.size (); ++i
)
611 = m_vertical
? m_splits
[i
].layout
->height
: m_splits
[i
].layout
->width
;
614 /* See tui-layout.h. */
617 tui_layout_split::tui_debug_weights_to_string () const
621 for (int i
= 0; i
< m_splits
.size (); ++i
)
625 str
+= string_printf ("[%d] %d", i
, m_splits
[i
].weight
);
631 /* See tui-layout.h. */
634 tui_layout_split::tui_debug_print_size_info
635 (const std::vector
<tui_layout_split::size_info
> &info
)
637 gdb_assert (debug_tui
);
639 tui_debug_printf ("current size info data:");
640 for (int i
= 0; i
< info
.size (); ++i
)
641 tui_debug_printf (" [%d] { size = %d, min = %d, max = %d, share_box = %d }",
642 i
, info
[i
].size
, info
[i
].min_size
,
643 info
[i
].max_size
, info
[i
].share_box
);
646 /* See tui-layout.h. */
649 tui_layout_split::set_size (const char *name
, int new_size
, bool set_width_p
)
651 TUI_SCOPED_DEBUG_ENTER_EXIT
;
653 tui_debug_printf ("this = %p, name = %s, new_size = %d",
654 this, name
, new_size
);
656 /* Look through the children. If one is a layout holding the named
657 window, we're done; or if one actually is the named window,
659 int found_index
= -1;
660 for (int i
= 0; i
< m_splits
.size (); ++i
)
662 tui_adjust_result adjusted
;
664 adjusted
= m_splits
[i
].layout
->set_width (name
, new_size
);
666 adjusted
= m_splits
[i
].layout
->set_height (name
, new_size
);
667 if (adjusted
== HANDLED
)
669 if (adjusted
== FOUND
)
671 if (set_width_p
? m_vertical
: !m_vertical
)
678 if (found_index
== -1)
680 int curr_size
= (set_width_p
681 ? m_splits
[found_index
].layout
->width
682 : m_splits
[found_index
].layout
->height
);
683 if (curr_size
== new_size
)
686 tui_debug_printf ("found window %s at index %d", name
, found_index
);
688 set_weights_from_sizes ();
689 int delta
= m_splits
[found_index
].weight
- new_size
;
690 m_splits
[found_index
].weight
= new_size
;
692 tui_debug_printf ("before delta (%d) distribution, weights: %s",
693 delta
, tui_debug_weights_to_string ().c_str ());
695 /* Distribute the "delta" over all other windows, while respecting their
696 min/max sizes. We grow each window by 1 line at a time continually
697 looping over all the windows. However, skip the window that the user
698 just resized, obviously we don't want to readjust that window. */
699 bool found_window_that_can_grow_p
= true;
700 for (int i
= 0; delta
!= 0; i
= (i
+ 1) % m_splits
.size ())
702 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
703 if (index
== found_index
)
705 if (!found_window_that_can_grow_p
)
707 found_window_that_can_grow_p
= false;
711 int new_min
, new_max
;
712 m_splits
[index
].layout
->get_sizes (m_vertical
, &new_min
, &new_max
);
716 /* The primary window grew, so we are trying to shrink other
718 if (m_splits
[index
].weight
> new_min
)
720 m_splits
[index
].weight
-= 1;
722 found_window_that_can_grow_p
= true;
727 /* The primary window shrank, so we are trying to grow other
729 if (m_splits
[index
].weight
< new_max
)
731 m_splits
[index
].weight
+= 1;
733 found_window_that_can_grow_p
= true;
737 tui_debug_printf ("index = %d, weight now: %d",
738 index
, m_splits
[index
].weight
);
741 tui_debug_printf ("after delta (%d) distribution, weights: %s",
742 delta
, tui_debug_weights_to_string ().c_str ());
747 warning (_("Invalid window width specified"));
749 warning (_("Invalid window height specified"));
750 /* Effectively undo any modifications made here. */
751 set_weights_from_sizes ();
755 /* Simply re-apply the updated layout. We pass false here so that
756 the cmd window can be resized. However, we should have already
757 resized everything above to be "just right", so the apply call
758 here should not end up changing the sizes at all. */
759 apply (x
, y
, width
, height
, false);
765 /* See tui-layout.h. */
768 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
,
769 bool preserve_cmd_win_size_p
)
771 TUI_SCOPED_DEBUG_ENTER_EXIT
;
778 /* In some situations we fix the size of the cmd window. However,
779 occasionally this turns out to be a mistake. This struct is used to
780 hold the original information about the cmd window, so we can restore
785 old_size_info (int index_
, int min_size_
, int max_size_
)
787 min_size (min_size_
),
791 /* The index in m_splits where the cmd window was found. */
794 /* The previous min/max size. */
799 /* This is given a value only if we fix the size of the cmd window. */
800 std::optional
<old_size_info
> old_cmd_info
;
802 std::vector
<size_info
> info (m_splits
.size ());
804 tui_debug_printf ("weights are: %s",
805 tui_debug_weights_to_string ().c_str ());
807 /* Step 1: Find the min and max size of each sub-layout.
808 Fixed-sized layouts are given their desired size, and then the
809 remaining space is distributed among the remaining windows
810 according to the weights given. */
811 int available_size
= m_vertical
? height
: width
;
813 int total_weight
= 0;
815 for (int i
= 0; i
< m_splits
.size (); ++i
)
817 bool cmd_win_already_exists
= tui_cmd_win () != nullptr;
819 /* Always call get_sizes, to ensure that the window is
820 instantiated. This is a bit gross but less gross than adding
821 special cases for this in other places. */
822 m_splits
[i
].layout
->get_sizes (m_vertical
, &info
[i
].min_size
,
825 if (preserve_cmd_win_size_p
826 && cmd_win_already_exists
827 && m_splits
[i
].layout
->get_name () != nullptr
828 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
830 /* Save the old cmd window information, in case we need to
832 old_cmd_info
.emplace (i
, info
[i
].min_size
, info
[i
].max_size
);
834 /* If this layout has never been applied, then it means the
835 user just changed the layout. In this situation, it's
836 desirable to keep the size of the command window the
837 same. Setting the min and max sizes this way ensures
838 that the resizing step, below, does the right thing with
840 info
[i
].min_size
= (m_vertical
841 ? tui_cmd_win ()->height
842 : tui_cmd_win ()->width
);
843 info
[i
].max_size
= info
[i
].min_size
;
846 if (info
[i
].min_size
> info
[i
].max_size
)
848 /* There is not enough room for this window, drop it. */
849 info
[i
].min_size
= 0;
850 info
[i
].max_size
= 0;
854 /* Two adjacent boxed windows will share a border. */
856 && m_splits
[prev
].layout
->last_edge_has_border_p ()
857 && m_splits
[i
].layout
->first_edge_has_border_p ())
858 info
[i
].share_box
= true;
860 if (info
[i
].min_size
== info
[i
].max_size
)
862 available_size
-= info
[i
].min_size
;
863 if (info
[i
].share_box
)
865 /* A shared border makes a bit more size available. */
872 total_weight
+= m_splits
[i
].weight
;
878 /* If last_index is set then we have a window that is not of a fixed
879 size. This window will have its size calculated below, which requires
880 that the total_weight not be zero (we divide by total_weight, so don't
881 want a floating-point exception). */
882 gdb_assert (last_index
== -1 || total_weight
> 0);
884 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
885 are given their fixed size, while others are resized according to
888 for (int i
= 0; i
< m_splits
.size (); ++i
)
890 if (info
[i
].min_size
!= info
[i
].max_size
)
892 /* Compute the height and clamp to the allowable range. */
893 info
[i
].size
= available_size
* m_splits
[i
].weight
/ total_weight
;
894 if (info
[i
].size
> info
[i
].max_size
)
895 info
[i
].size
= info
[i
].max_size
;
896 if (info
[i
].size
< info
[i
].min_size
)
897 info
[i
].size
= info
[i
].min_size
;
898 /* Keep a total of all the size we've used so far (we gain some
899 size back if this window can share a border with a preceding
900 window). Any unused space will be distributed between all of
901 the other windows (while respecting min/max sizes) later in
903 used_size
+= info
[i
].size
;
904 if (info
[i
].share_box
)
906 /* A shared border makes a bit more size available. */
911 info
[i
].size
= info
[i
].min_size
;
916 tui_debug_printf ("after initial size calculation");
917 tui_debug_printf ("available_size = %d, used_size = %d",
918 available_size
, used_size
);
919 tui_debug_printf ("total_weight = %d, last_index = %d",
920 total_weight
, last_index
);
921 tui_debug_print_size_info (info
);
924 /* If we didn't find any sub-layouts that were of a non-fixed size, but
925 we did find the cmd window, then we can consider that a sort-of
926 non-fixed size sub-layout.
928 The cmd window might, initially, be of a fixed size (see above), but,
929 we are willing to relax this constraint if required to correctly apply
930 this layout (see below). */
931 if (last_index
== -1 && old_cmd_info
.has_value ())
932 last_index
= old_cmd_info
->index
;
934 /* Allocate any leftover size. */
935 if (available_size
!= used_size
&& last_index
!= -1)
937 /* Loop over all windows until the amount of used space is equal to
938 the amount of available space. There's an escape hatch within
939 the loop in case we can't find any sub-layouts to resize. */
940 bool found_window_that_can_grow_p
= true;
941 for (int idx
= last_index
;
942 available_size
!= used_size
;
943 idx
= (idx
+ 1) % m_splits
.size ())
945 /* Every time we get back to last_index, which is where the loop
946 started, we check to make sure that we did assign some space
947 to a window, bringing used_size closer to available_size.
949 If we didn't, but the cmd window is of a fixed size, then we
950 can make the console window non-fixed-size, and continue
951 around the loop, hopefully, this will allow the layout to be
954 If we still make it around the loop without moving used_size
955 closer to available_size, then there's nothing more we can do,
956 and we break out of the loop. */
957 if (idx
== last_index
)
959 /* If the used_size is greater than the available_size then
960 this indicates that the fixed-sized sub-layouts claimed
961 more space than is available. This layout is not going to
962 work. Our only hope at this point is to make the cmd
963 window non-fixed-size (if possible), and hope we can
964 shrink this enough to fit the rest of the sub-layouts in.
966 Alternatively, we've made it around the loop without
967 adjusting any window's size. This likely means all
968 windows have hit their min or max size. Again, our only
969 hope is to make the cmd window non-fixed-size, and hope
970 this fixes all our problems. */
971 if (old_cmd_info
.has_value ()
972 && ((available_size
< used_size
)
973 || !found_window_that_can_grow_p
))
975 info
[old_cmd_info
->index
].min_size
= old_cmd_info
->min_size
;
976 info
[old_cmd_info
->index
].max_size
= old_cmd_info
->max_size
;
978 ("restoring index %d (cmd) size limits, min = %d, max = %d",
979 old_cmd_info
->index
, old_cmd_info
->min_size
,
980 old_cmd_info
->max_size
);
981 old_cmd_info
.reset ();
983 else if (!found_window_that_can_grow_p
)
985 found_window_that_can_grow_p
= false;
988 if (available_size
> used_size
989 && info
[idx
].size
< info
[idx
].max_size
)
991 found_window_that_can_grow_p
= true;
995 else if (available_size
< used_size
996 && info
[idx
].size
> info
[idx
].min_size
)
998 found_window_that_can_grow_p
= true;
1006 tui_debug_printf ("after final size calculation");
1007 tui_debug_printf ("available_size = %d, used_size = %d",
1008 available_size
, used_size
);
1009 tui_debug_printf ("total_weight = %d, last_index = %d",
1010 total_weight
, last_index
);
1011 tui_debug_print_size_info (info
);
1015 /* Step 3: Resize. */
1017 const int maximum
= m_vertical
? height
: width
;
1018 for (int i
= 0; i
< m_splits
.size (); ++i
)
1020 /* If we fall off the bottom, just make allocations overlap.
1022 if (size_accum
+ info
[i
].size
> maximum
)
1023 size_accum
= maximum
- info
[i
].size
;
1024 else if (info
[i
].share_box
)
1027 m_splits
[i
].layout
->apply (x
, y
+ size_accum
, width
, info
[i
].size
,
1028 preserve_cmd_win_size_p
);
1030 m_splits
[i
].layout
->apply (x
+ size_accum
, y
, info
[i
].size
, height
,
1031 preserve_cmd_win_size_p
);
1032 size_accum
+= info
[i
].size
;
1036 /* See tui-layout.h. */
1039 tui_layout_split::remove_windows (const char *name
)
1041 for (int i
= 0; i
< m_splits
.size (); ++i
)
1043 const char *this_name
= m_splits
[i
].layout
->get_name ();
1044 if (this_name
== nullptr)
1045 m_splits
[i
].layout
->remove_windows (name
);
1046 else if (strcmp (this_name
, name
) == 0
1047 || strcmp (this_name
, CMD_NAME
) == 0
1048 || strcmp (this_name
, STATUS_NAME
) == 0)
1054 m_splits
.erase (m_splits
.begin () + i
);
1060 /* See tui-layout.h. */
1063 tui_layout_split::replace_window (const char *name
, const char *new_window
)
1065 for (auto &item
: m_splits
)
1066 item
.layout
->replace_window (name
, new_window
);
1069 /* See tui-layout.h. */
1072 tui_layout_split::specification (ui_file
*output
, int depth
)
1075 gdb_puts ("{", output
);
1078 gdb_puts ("-horizontal ", output
);
1081 for (auto &item
: m_splits
)
1084 gdb_puts (" ", output
);
1086 item
.layout
->specification (output
, depth
+ 1);
1087 gdb_printf (output
, " %d", item
.weight
);
1091 gdb_puts ("}", output
);
1094 /* See tui-layout.h. */
1097 tui_layout_split::layout_fingerprint () const
1099 for (auto &item
: m_splits
)
1101 std::string fp
= item
.layout
->layout_fingerprint ();
1102 if (!fp
.empty () && m_splits
.size () != 1)
1103 return std::string (m_vertical
? "V" : "H") + fp
;
1109 /* Destroy the layout associated with SELF. */
1112 destroy_layout (struct cmd_list_element
*self
, void *context
)
1114 tui_layout_split
*layout
= (tui_layout_split
*) context
;
1115 size_t index
= find_layout (layout
);
1116 layouts
.erase (layouts
.begin () + index
);
1119 /* List holding the sub-commands of "layout". */
1121 static struct cmd_list_element
*layout_list
;
1123 /* Called to implement 'tui layout'. */
1126 tui_layout_command (const char *args
, int from_tty
)
1128 help_list (layout_list
, "tui layout ", all_commands
, gdb_stdout
);
1131 /* Add a "layout" command with name NAME that switches to LAYOUT. */
1133 static struct cmd_list_element
*
1134 add_layout_command (const char *name
, tui_layout_split
*layout
)
1136 struct cmd_list_element
*cmd
;
1139 layout
->specification (&spec
, 0);
1141 gdb::unique_xmalloc_ptr
<char> doc
1142 = xstrprintf (_("Apply the \"%s\" layout.\n\
1143 This layout was created using:\n\
1144 tui new-layout %s %s"),
1145 name
, name
, spec
.c_str ());
1147 cmd
= add_cmd (name
, class_tui
, nullptr, doc
.get (), &layout_list
);
1148 cmd
->set_context (layout
);
1149 /* There is no API to set this. */
1150 cmd
->func
= tui_apply_layout
;
1151 cmd
->destroyer
= destroy_layout
;
1152 cmd
->doc_allocated
= 1;
1154 layouts
.emplace_back (layout
);
1159 /* Initialize the standard layouts. */
1162 initialize_layouts ()
1164 tui_layout_split
*layout
;
1166 layout
= new tui_layout_split ();
1167 layout
->add_window (SRC_NAME
, 2);
1168 layout
->add_window (STATUS_NAME
, 0);
1169 layout
->add_window (CMD_NAME
, 1);
1170 add_layout_command (SRC_NAME
, layout
);
1172 layout
= new tui_layout_split ();
1173 layout
->add_window (DISASSEM_NAME
, 2);
1174 layout
->add_window (STATUS_NAME
, 0);
1175 layout
->add_window (CMD_NAME
, 1);
1176 add_layout_command (DISASSEM_NAME
, layout
);
1178 layout
= new tui_layout_split ();
1179 layout
->add_window (SRC_NAME
, 1);
1180 layout
->add_window (DISASSEM_NAME
, 1);
1181 layout
->add_window (STATUS_NAME
, 0);
1182 layout
->add_window (CMD_NAME
, 1);
1183 add_layout_command ("split", layout
);
1185 layout
= new tui_layout_split ();
1186 layout
->add_window (DATA_NAME
, 1);
1187 layout
->add_window (SRC_NAME
, 1);
1188 layout
->add_window (STATUS_NAME
, 0);
1189 layout
->add_window (CMD_NAME
, 1);
1190 layouts
.emplace_back (layout
);
1191 src_regs_layout
= layout
;
1193 layout
= new tui_layout_split ();
1194 layout
->add_window (DATA_NAME
, 1);
1195 layout
->add_window (DISASSEM_NAME
, 1);
1196 layout
->add_window (STATUS_NAME
, 0);
1197 layout
->add_window (CMD_NAME
, 1);
1198 layouts
.emplace_back (layout
);
1199 asm_regs_layout
= layout
;
1204 /* A helper function that returns true if NAME is the name of an
1205 available window. */
1208 validate_window_name (const std::string
&name
)
1210 auto iter
= known_window_types
.find (name
);
1211 return iter
!= known_window_types
.end ();
1214 /* Implementation of the "tui new-layout" command. */
1217 tui_new_layout_command (const char *spec
, int from_tty
)
1219 std::string new_name
= extract_arg (&spec
);
1220 if (new_name
.empty ())
1221 error (_("No layout name specified"));
1222 if (new_name
[0] == '-')
1223 error (_("Layout name cannot start with '-'"));
1225 bool is_vertical
= true;
1226 spec
= skip_spaces (spec
);
1227 if (check_for_argument (&spec
, "-horizontal"))
1228 is_vertical
= false;
1230 std::vector
<std::unique_ptr
<tui_layout_split
>> splits
;
1231 splits
.emplace_back (new tui_layout_split (is_vertical
));
1232 std::unordered_set
<std::string
> seen_windows
;
1235 spec
= skip_spaces (spec
);
1236 if (spec
[0] == '\0')
1242 spec
= skip_spaces (spec
+ 1);
1243 if (check_for_argument (&spec
, "-horizontal"))
1244 is_vertical
= false;
1245 splits
.emplace_back (new tui_layout_split (is_vertical
));
1249 bool is_close
= false;
1255 if (splits
.size () == 1)
1256 error (_("Extra '}' in layout specification"));
1260 name
= extract_arg (&spec
);
1263 if (!validate_window_name (name
))
1264 error (_("Unknown window \"%s\""), name
.c_str ());
1265 if (seen_windows
.find (name
) != seen_windows
.end ())
1266 error (_("Window \"%s\" seen twice in layout"), name
.c_str ());
1269 ULONGEST weight
= get_ulongest (&spec
, '}');
1270 if ((int) weight
!= weight
)
1271 error (_("Weight out of range: %s"), pulongest (weight
));
1274 std::unique_ptr
<tui_layout_split
> last_split
1275 = std::move (splits
.back ());
1277 splits
.back ()->add_split (std::move (last_split
), weight
);
1281 splits
.back ()->add_window (name
.c_str (), weight
);
1282 seen_windows
.insert (name
);
1285 if (splits
.size () > 1)
1286 error (_("Missing '}' in layout specification"));
1287 if (seen_windows
.empty ())
1288 error (_("New layout does not contain any windows"));
1289 if (seen_windows
.find (CMD_NAME
) == seen_windows
.end ())
1290 error (_("New layout does not contain the \"" CMD_NAME
"\" window"));
1292 gdb::unique_xmalloc_ptr
<char> cmd_name
1293 = make_unique_xstrdup (new_name
.c_str ());
1294 std::unique_ptr
<tui_layout_split
> new_layout
= std::move (splits
.back ());
1295 struct cmd_list_element
*cmd
1296 = add_layout_command (cmd_name
.get (), new_layout
.get ());
1297 cmd
->name_allocated
= 1;
1298 cmd_name
.release ();
1299 new_layout
.release ();
1302 /* Function to initialize gdb commands, for tui window layout
1305 void _initialize_tui_layout ();
1307 _initialize_tui_layout ()
1309 struct cmd_list_element
*layout_cmd
1310 = add_prefix_cmd ("layout", class_tui
, tui_layout_command
, _("\
1311 Change the layout of windows.\n\
1312 Usage: tui layout prev | next | LAYOUT-NAME"),
1313 &layout_list
, 0, tui_get_cmd_list ());
1314 add_com_alias ("layout", layout_cmd
, class_tui
, 0);
1316 add_cmd ("next", class_tui
, tui_next_layout_command
,
1317 _("Apply the next TUI layout."),
1319 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
1320 _("Apply the previous TUI layout."),
1322 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
1323 _("Apply the TUI register layout."),
1326 add_cmd ("new-layout", class_tui
, tui_new_layout_command
,
1327 _("Create a new TUI layout.\n\
1328 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1329 Create a new TUI layout. The new layout will be named NAME,\n\
1330 and can be accessed using \"layout NAME\".\n\
1331 The windows will be displayed in the specified order.\n\
1332 A WINDOW can also be of the form:\n\
1333 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1334 This form indicates a sub-frame.\n\
1335 Each WEIGHT is an integer, which holds the relative size\n\
1336 to be allocated to the window."),
1337 tui_get_cmd_list ());
1339 initialize_layouts ();
1340 initialize_known_windows ();