arm, objdump: print obsolote warning when 26-bit set in instructions
[binutils-gdb.git] / gdb / tui / tui-layout.c
blob2b6cb3194ae024479540d5a032e6dd97ff9e6081
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/>. */
22 #include "command.h"
23 #include "symtab.h"
24 #include "frame.h"
25 #include "cli/cli-decode.h"
26 #include "cli/cli-utils.h"
27 #include <unordered_set>
29 #include "tui/tui.h"
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"
42 /* The layouts. */
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;
56 /* See tui-data.h. */
57 std::vector<tui_win_info *> tui_windows;
59 /* See tui-layout.h. */
61 void
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]);
94 delete win_info;
98 /* Replace the global list of active windows. */
99 tui_windows = std::move (new_tui_windows);
102 /* See tui-layout. */
104 void
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. */
112 void
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. */
120 static void
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. */
139 void
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)
146 return;
148 /* If the window we are trying to replace doesn't exist, we're
149 done. */
150 enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
151 if (tui_win_list[other] == nullptr)
152 return;
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. */
161 static size_t
162 find_layout (tui_layout_split *layout)
164 for (size_t i = 0; i < layouts.size (); ++i)
166 if (layout == layouts[i].get ())
167 return i;
169 gdb_assert_not_reached ("layout not found!?");
172 /* Function to set the layout. */
174 static void
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. */
180 tui_enable ();
181 tui_set_layout (layout);
184 /* See tui-layout.h. */
186 void
187 tui_next_layout ()
189 size_t index = find_layout (applied_skeleton);
190 ++index;
191 if (index == layouts.size ())
192 index = 0;
193 tui_set_layout (layouts[index].get ());
196 /* Implement the "layout next" command. */
198 static void
199 tui_next_layout_command (const char *arg, int from_tty)
201 tui_enable ();
202 tui_next_layout ();
205 /* See tui-layout.h. */
207 void
208 tui_set_initial_layout ()
210 tui_set_layout (layouts[0].get ());
213 /* Implement the "layout prev" command. */
215 static void
216 tui_prev_layout_command (const char *arg, int from_tty)
218 tui_enable ();
219 size_t index = find_layout (applied_skeleton);
220 if (index == 0)
221 index = layouts.size ();
222 --index;
223 tui_set_layout (layouts[index].get ());
227 /* See tui-layout.h. */
229 void
230 tui_regs_layout ()
232 /* If there's already a register window, we're done. */
233 if (tui_data_win () != nullptr)
234 return;
236 tui_set_layout (tui_disasm_win () != nullptr
237 ? asm_regs_layout
238 : src_regs_layout);
241 /* Implement the "layout regs" command. */
243 static void
244 tui_regs_layout_command (const char *arg, int from_tty)
246 tui_enable ();
247 tui_regs_layout ();
250 /* See tui-layout.h. */
252 void
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)
265 return;
268 applied_layout->remove_windows (focus->name ());
269 tui_apply_current_layout (true);
272 void
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)
279 return;
281 width = width_;
282 height = height_;
283 x = origin_x_;
284 y = origin_y_;
286 if (handle != nullptr)
288 #ifdef HAVE_WRESIZE
289 wresize (handle.get (), height, width);
290 mvwin (handle.get (), y, x);
291 wmove (handle.get (), 0, 0);
292 #else
293 handle.reset (nullptr);
294 #endif
297 if (handle == nullptr)
298 make_window ();
300 rerender ();
305 /* Helper function to create one of the built-in (non-status)
306 windows. */
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 ())
338 return window;
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 ());
347 return result;
350 /* Initialize the known window types. */
352 static void
353 initialize_known_windows ()
355 known_window_types.emplace (SRC_NAME,
356 make_standard_window<SRC_WIN,
357 tui_source_window>);
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,
362 tui_data_window>);
363 known_window_types.emplace (DISASSEM_NAME,
364 make_standard_window<DISASSEM_WIN,
365 tui_disasm_window>);
366 known_window_types.emplace (STATUS_NAME,
367 make_standard_window<STATUS_WIN,
368 tui_status_window>);
371 /* See tui-layout.h. */
373 void
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)
384 if (ISSPACE (c))
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. */
417 void
418 tui_layout_window::apply (int x_, int y_, int width_, int height_,
419 bool preserve_cmd_win_size_p)
421 x = x_;
422 y = y_;
423 width = width_;
424 height = height_;
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. */
430 m_window = nullptr;
431 return;
433 m_window->resize (height, width, x, y);
436 /* See tui-layout.h. */
438 void
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"));
449 if (height)
451 *min_value = m_window->min_height ();
452 *max_value = m_window->max_height ();
454 else
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. */
465 bool
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. */
474 bool
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. */
483 void
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. */
499 void
500 tui_layout_window::specification (ui_file *output, int depth)
502 gdb_puts (get_name (), output);
505 /* See tui-layout.h. */
507 std::string
508 tui_layout_window::layout_fingerprint () const
510 if (strcmp (get_name (), "cmd") == 0)
511 return "C";
512 else
513 return "";
516 /* See tui-layout.h. */
518 void
519 tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
520 int weight)
522 split s = {weight, std::move (layout)};
523 m_splits.push_back (std::move (s));
526 /* See tui-layout.h. */
528 void
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. */
553 void
554 tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
556 TUI_SCOPED_DEBUG_ENTER_EXIT;
558 *min_value = 0;
559 *max_value = 0;
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;
573 else
575 *min_value = std::max (*min_value, new_min);
576 *max_value = std::min (*max_value, new_max);
578 first_time = false;
581 tui_debug_printf ("min_value = %d, max_value = %d", *min_value, *max_value);
584 /* See tui-layout.h. */
586 bool
587 tui_layout_split::first_edge_has_border_p () const
589 if (m_splits.empty ())
590 return false;
591 return m_splits[0].layout->first_edge_has_border_p ();
594 /* See tui-layout.h. */
596 bool
597 tui_layout_split::last_edge_has_border_p () const
599 if (m_splits.empty ())
600 return false;
601 return m_splits.back ().layout->last_edge_has_border_p ();
604 /* See tui-layout.h. */
606 void
607 tui_layout_split::set_weights_from_sizes ()
609 for (int i = 0; i < m_splits.size (); ++i)
610 m_splits[i].weight
611 = m_vertical ? m_splits[i].layout->height : m_splits[i].layout->width;
614 /* See tui-layout.h. */
616 std::string
617 tui_layout_split::tui_debug_weights_to_string () const
619 std::string str;
621 for (int i = 0; i < m_splits.size (); ++i)
623 if (i > 0)
624 str += ", ";
625 str += string_printf ("[%d] %d", i, m_splits[i].weight);
628 return str;
631 /* See tui-layout.h. */
633 void
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. */
648 tui_adjust_result
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,
658 update it. */
659 int found_index = -1;
660 for (int i = 0; i < m_splits.size (); ++i)
662 tui_adjust_result adjusted;
663 if (set_width_p)
664 adjusted = m_splits[i].layout->set_width (name, new_size);
665 else
666 adjusted = m_splits[i].layout->set_height (name, new_size);
667 if (adjusted == HANDLED)
668 return HANDLED;
669 if (adjusted == FOUND)
671 if (set_width_p ? m_vertical : !m_vertical)
672 return FOUND;
673 found_index = i;
674 break;
678 if (found_index == -1)
679 return NOT_FOUND;
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)
684 return HANDLED;
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)
706 break;
707 found_window_that_can_grow_p = false;
708 continue;
711 int new_min, new_max;
712 m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
714 if (delta < 0)
716 /* The primary window grew, so we are trying to shrink other
717 windows. */
718 if (m_splits[index].weight > new_min)
720 m_splits[index].weight -= 1;
721 delta += 1;
722 found_window_that_can_grow_p = true;
725 else
727 /* The primary window shrank, so we are trying to grow other
728 windows. */
729 if (m_splits[index].weight < new_max)
731 m_splits[index].weight += 1;
732 delta -= 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 ());
744 if (delta != 0)
746 if (set_width_p)
747 warning (_("Invalid window width specified"));
748 else
749 warning (_("Invalid window height specified"));
750 /* Effectively undo any modifications made here. */
751 set_weights_from_sizes ();
753 else
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);
762 return HANDLED;
765 /* See tui-layout.h. */
767 void
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;
773 x = x_;
774 y = y_;
775 width = width_;
776 height = height_;
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
781 it if needed. */
782 struct old_size_info
784 /* Constructor. */
785 old_size_info (int index_, int min_size_, int max_size_)
786 : index (index_),
787 min_size (min_size_),
788 max_size (max_size_)
789 { /* Nothing. */ }
791 /* The index in m_splits where the cmd window was found. */
792 int index;
794 /* The previous min/max size. */
795 int min_size;
796 int 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;
812 int last_index = -1;
813 int total_weight = 0;
814 int prev = -1;
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,
823 &info[i].max_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
831 restore it later. */
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
839 this window. */
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;
851 continue;
854 /* Two adjacent boxed windows will share a border. */
855 if (prev != -1
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. */
866 ++available_size;
869 else
871 last_index = i;
872 total_weight += m_splits[i].weight;
875 prev = i;
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
886 their weight. */
887 int used_size = 0;
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
902 this function. */
903 used_size += info[i].size;
904 if (info[i].share_box)
906 /* A shared border makes a bit more size available. */
907 --used_size;
910 else
911 info[i].size = info[i].min_size;
914 if (debug_tui)
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
952 applied correctly.
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;
977 tui_debug_printf
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)
984 break;
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;
992 info[idx].size += 1;
993 used_size += 1;
995 else if (available_size < used_size
996 && info[idx].size > info[idx].min_size)
998 found_window_that_can_grow_p = true;
999 info[idx].size -= 1;
1000 used_size -= 1;
1004 if (debug_tui)
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. */
1016 int size_accum = 0;
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.
1021 GIGO. */
1022 if (size_accum + info[i].size > maximum)
1023 size_accum = maximum - info[i].size;
1024 else if (info[i].share_box)
1025 --size_accum;
1026 if (m_vertical)
1027 m_splits[i].layout->apply (x, y + size_accum, width, info[i].size,
1028 preserve_cmd_win_size_p);
1029 else
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. */
1038 void
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)
1050 /* Keep. */
1052 else
1054 m_splits.erase (m_splits.begin () + i);
1055 --i;
1060 /* See tui-layout.h. */
1062 void
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. */
1071 void
1072 tui_layout_split::specification (ui_file *output, int depth)
1074 if (depth > 0)
1075 gdb_puts ("{", output);
1077 if (!m_vertical)
1078 gdb_puts ("-horizontal ", output);
1080 bool first = true;
1081 for (auto &item : m_splits)
1083 if (!first)
1084 gdb_puts (" ", output);
1085 first = false;
1086 item.layout->specification (output, depth + 1);
1087 gdb_printf (output, " %d", item.weight);
1090 if (depth > 0)
1091 gdb_puts ("}", output);
1094 /* See tui-layout.h. */
1096 std::string
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;
1106 return "";
1109 /* Destroy the layout associated with SELF. */
1111 static void
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'. */
1125 static void
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;
1138 string_file spec;
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;
1153 doc.release ();
1154 layouts.emplace_back (layout);
1156 return cmd;
1159 /* Initialize the standard layouts. */
1161 static void
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. */
1207 static bool
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. */
1216 static void
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;
1233 while (true)
1235 spec = skip_spaces (spec);
1236 if (spec[0] == '\0')
1237 break;
1239 if (spec[0] == '{')
1241 is_vertical = true;
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));
1246 continue;
1249 bool is_close = false;
1250 std::string name;
1251 if (spec[0] == '}')
1253 is_close = true;
1254 ++spec;
1255 if (splits.size () == 1)
1256 error (_("Extra '}' in layout specification"));
1258 else
1260 name = extract_arg (&spec);
1261 if (name.empty ())
1262 break;
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));
1272 if (is_close)
1274 std::unique_ptr<tui_layout_split> last_split
1275 = std::move (splits.back ());
1276 splits.pop_back ();
1277 splits.back ()->add_split (std::move (last_split), weight);
1279 else
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
1303 manipulation. */
1305 void _initialize_tui_layout ();
1306 void
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."),
1318 &layout_list);
1319 add_cmd ("prev", class_tui, tui_prev_layout_command,
1320 _("Apply the previous TUI layout."),
1321 &layout_list);
1322 add_cmd ("regs", class_tui, tui_regs_layout_command,
1323 _("Apply the TUI register layout."),
1324 &layout_list);
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 ();