GDB (xrefs)
Loading...
Searching...
No Matches
tui-layout.c
Go to the documentation of this file.
1/* TUI layout window management.
2
3 Copyright (C) 1998-2023 Free Software Foundation, Inc.
4
5 Contributed by Hewlett-Packard Company.
6
7 This file is part of GDB.
8
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.
13
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.
18
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/>. */
21
22#include "defs.h"
23#include "arch-utils.h"
24#include "command.h"
25#include "symtab.h"
26#include "frame.h"
27#include "source.h"
28#include "cli/cli-cmds.h"
29#include "cli/cli-decode.h"
30#include "cli/cli-utils.h"
31#include <ctype.h>
32#include <unordered_set>
33
34#include "tui/tui.h"
35#include "tui/tui-command.h"
36#include "tui/tui-data.h"
37#include "tui/tui-wingeneral.h"
38#include "tui/tui-stack.h"
39#include "tui/tui-regs.h"
40#include "tui/tui-win.h"
41#include "tui/tui-winsource.h"
42#include "tui/tui-disasm.h"
43#include "tui/tui-layout.h"
44#include "tui/tui-source.h"
45#include "gdb_curses.h"
46#include "gdbsupport/gdb-safe-ctype.h"
47
48static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
49
50/* The layouts. */
51static std::vector<std::unique_ptr<tui_layout_split>> layouts;
52
53/* The layout that is currently applied. */
54static std::unique_ptr<tui_layout_base> applied_layout;
55
56/* The "skeleton" version of the layout that is currently applied. */
58
59/* The two special "regs" layouts. Note that these aren't registered
60 as commands and so can never be deleted. */
63
64/* See tui-data.h. */
65std::vector<tui_win_info *> tui_windows;
66
67/* See tui-layout.h. */
68
69void
70tui_apply_current_layout (bool preserve_cmd_win_size_p)
71{
72 struct gdbarch *gdbarch;
73 CORE_ADDR addr;
74
76
77 for (tui_win_info *win_info : tui_windows)
78 win_info->make_visible (false);
79
81 preserve_cmd_win_size_p);
82
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;
88
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 ());
92
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);
96
97 /* Now delete any window that was not re-applied. */
99 for (tui_win_info *win_info : tui_windows)
100 {
101 if (!win_info->is_visible ())
102 {
103 if (focus == win_info)
104 tui_set_win_focus_to (new_tui_windows[0]);
105 delete win_info;
106 }
107 }
108
109 /* Replace the global list of active windows. */
110 tui_windows = std::move (new_tui_windows);
111
112 if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
115}
116
117/* See tui-layout. */
118
119void
120tui_adjust_window_height (struct tui_win_info *win, int new_height)
121{
122 applied_layout->set_height (win->name (), new_height);
123}
124
125/* See tui-layout. */
126
127void
128tui_adjust_window_width (struct tui_win_info *win, int new_width)
129{
130 applied_layout->set_width (win->name (), new_width);
131}
132
133/* Set the current layout to LAYOUT. */
134
135static void
137{
138 std::string old_fingerprint;
139 if (applied_layout != nullptr)
140 old_fingerprint = applied_layout->layout_fingerprint ();
141
142 applied_skeleton = layout;
143 applied_layout = layout->clone ();
144
145 std::string new_fingerprint = applied_layout->layout_fingerprint ();
146 bool preserve_command_window_size
147 = (TUI_CMD_WIN != nullptr && old_fingerprint == new_fingerprint);
148
149 tui_apply_current_layout (preserve_command_window_size);
150}
151
152/* See tui-layout.h. */
153
154void
156{
157 gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
158
159 /* If the window already exists, no need to add it. */
160 if (tui_win_list[type] != nullptr)
161 return;
162
163 /* If the window we are trying to replace doesn't exist, we're
164 done. */
165 enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
166 if (tui_win_list[other] == nullptr)
167 return;
168
169 const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
170 applied_layout->replace_window (tui_win_list[other]->name (), name);
172}
173
174/* Find LAYOUT in the "layouts" global and return its index. */
175
176static size_t
178{
179 for (size_t i = 0; i < layouts.size (); ++i)
180 {
181 if (layout == layouts[i].get ())
182 return i;
183 }
184 gdb_assert_not_reached ("layout not found!?");
185}
186
187/* Function to set the layout. */
188
189static void
190tui_apply_layout (const char *args, int from_tty, cmd_list_element *command)
191{
192 tui_layout_split *layout = (tui_layout_split *) command->context ();
193
194 /* Make sure the curses mode is enabled. */
195 tui_enable ();
196 tui_set_layout (layout);
197}
198
199/* See tui-layout.h. */
200
201void
203{
204 size_t index = find_layout (applied_skeleton);
205 ++index;
206 if (index == layouts.size ())
207 index = 0;
208 tui_set_layout (layouts[index].get ());
209}
210
211/* Implement the "layout next" command. */
212
213static void
214tui_next_layout_command (const char *arg, int from_tty)
215{
216 tui_enable ();
218}
219
220/* See tui-layout.h. */
221
222void
227
228/* Implement the "layout prev" command. */
229
230static void
231tui_prev_layout_command (const char *arg, int from_tty)
232{
233 tui_enable ();
234 size_t index = find_layout (applied_skeleton);
235 if (index == 0)
236 index = layouts.size ();
237 --index;
238 tui_set_layout (layouts[index].get ());
239}
240
241
242/* See tui-layout.h. */
243
244void
246{
247 /* If there's already a register window, we're done. */
248 if (TUI_DATA_WIN != nullptr)
249 return;
250
254}
255
256/* Implement the "layout regs" command. */
257
258static void
259tui_regs_layout_command (const char *arg, int from_tty)
260{
261 tui_enable ();
263}
264
265/* See tui-layout.h. */
266
267void
269{
271
272 if (strcmp (focus->name (), CMD_NAME) == 0)
273 {
274 /* Try leaving the source or disassembly window. If neither
275 exists, just do nothing. */
276 focus = TUI_SRC_WIN;
277 if (focus == nullptr)
278 focus = TUI_DISASM_WIN;
279 if (focus == nullptr)
280 return;
281 }
282
283 applied_layout->remove_windows (focus->name ());
285}
286
287static void
288extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
289{
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);
294 else
295 {
296 *gdbarch_p = nullptr;
297 *addr_p = 0;
298 }
299}
300
301void
302tui_win_info::resize (int height_, int width_,
303 int origin_x_, int origin_y_)
304{
305 if (width == width_ && height == height_
306 && x == origin_x_ && y == origin_y_
307 && handle != nullptr)
308 return;
309
310 width = width_;
311 height = height_;
312 x = origin_x_;
313 y = origin_y_;
314
315 if (handle != nullptr)
316 {
317#ifdef HAVE_WRESIZE
318 wresize (handle.get (), height, width);
319 mvwin (handle.get (), y, x);
320 wmove (handle.get (), 0, 0);
321#else
322 handle.reset (nullptr);
323#endif
324 }
325
326 if (handle == nullptr)
327 make_window ();
328
329 rerender ();
330}
331
332
333
334/* Helper function to create one of the built-in (non-locator)
335 windows. */
336
337template<enum tui_win_type V, class T>
338static tui_win_info *
340{
341 if (tui_win_list[V] == nullptr)
342 tui_win_list[V] = new T ();
343 return tui_win_list[V];
344}
345
346/* A map holding all the known window types, keyed by name. */
347
349
350/* See tui-layout.h. */
351
354{
355 auto begin = known_window_names_iterator (known_window_types.begin ());
357 return known_window_names_range (begin, end);
358}
359
360/* Helper function that returns a TUI window, given its name. */
361
362static tui_win_info *
363tui_get_window_by_name (const std::string &name)
364{
365 for (tui_win_info *window : tui_windows)
366 if (name == window->name ())
367 return window;
368
369 auto iter = known_window_types.find (name);
370 if (iter == known_window_types.end ())
371 error (_("Unknown window type \"%s\""), name.c_str ());
372
373 tui_win_info *result = iter->second (name.c_str ());
374 if (result == nullptr)
375 error (_("Could not create window \"%s\""), name.c_str ());
376 return result;
377}
378
379/* Initialize the known window types. */
380
381static void
399
400/* See tui-layout.h. */
401
402void
404{
405 std::string name_copy = name;
406
407 if (name_copy == SRC_NAME || name_copy == CMD_NAME || name_copy == DATA_NAME
408 || name_copy == DISASSEM_NAME || name_copy == STATUS_NAME)
409 error (_("Window type \"%s\" is built-in"), name);
410
411 for (const char &c : name_copy)
412 {
413 if (ISSPACE (c))
414 error (_("invalid whitespace character in window name"));
415
416 if (!ISALNUM (c) && strchr ("-_.", c) == nullptr)
417 error (_("invalid character '%c' in window name"), c);
418 }
419
420 if (!ISALPHA (name_copy[0]))
421 error (_("window name must start with a letter, not '%c'"), name_copy[0]);
422
423 /* We already check above for all the builtin window names. If we get
424 this far then NAME must be a user defined window. Remove any existing
425 factory and replace it with this new version. */
426
427 auto iter = known_window_types.find (name);
428 if (iter != known_window_types.end ())
429 known_window_types.erase (iter);
430
431 known_window_types.emplace (std::move (name_copy),
432 std::move (factory));
433}
434
435/* See tui-layout.h. */
436
437std::unique_ptr<tui_layout_base>
439{
440 tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
441 return std::unique_ptr<tui_layout_base> (result);
442}
443
444/* See tui-layout.h. */
445
446void
447tui_layout_window::apply (int x_, int y_, int width_, int height_,
448 bool preserve_cmd_win_size_p)
449{
450 x = x_;
451 y = y_;
452 width = width_;
453 height = height_;
454 gdb_assert (m_window != nullptr);
456}
457
458/* See tui-layout.h. */
459
460void
461tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
462{
464
465 if (m_window == nullptr)
467
468 tui_debug_printf ("window = %s, getting %s",
469 m_window->name (), (height ? "height" : "width"));
470
471 if (height)
472 {
473 *min_value = m_window->min_height ();
474 *max_value = m_window->max_height ();
475 }
476 else
477 {
478 *min_value = m_window->min_width ();
479 *max_value = m_window->max_width ();
480 }
481
482 tui_debug_printf ("min = %d, max = %d", *min_value, *max_value);
483}
484
485/* See tui-layout.h. */
486
487bool
489{
490 gdb_assert (m_window != nullptr);
491 return m_window->can_box ();
492}
493
494/* See tui-layout.h. */
495
496bool
498{
499 gdb_assert (m_window != nullptr);
500 return m_window->can_box ();
501}
502
503/* See tui-layout.h. */
504
505void
506tui_layout_window::replace_window (const char *name, const char *new_window)
507{
508 if (m_contents == name)
509 {
510 m_contents = new_window;
511 if (m_window != nullptr)
512 {
513 m_window->make_visible (false);
515 }
516 }
517}
518
519/* See tui-layout.h. */
520
521void
523{
524 gdb_puts (get_name (), output);
525}
526
527/* See tui-layout.h. */
528
529std::string
531{
532 if (strcmp (get_name (), "cmd") == 0)
533 return "C";
534 else
535 return "";
536}
537
538/* See tui-layout.h. */
539
540void
541tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
542 int weight)
543{
544 split s = {weight, std::move (layout)};
545 m_splits.push_back (std::move (s));
546}
547
548/* See tui-layout.h. */
549
550void
551tui_layout_split::add_window (const char *name, int weight)
552{
554 split s = {weight, std::unique_ptr<tui_layout_base> (result)};
555 m_splits.push_back (std::move (s));
556}
557
558/* See tui-layout.h. */
559
560std::unique_ptr<tui_layout_base>
562{
564 for (const split &item : m_splits)
565 {
566 std::unique_ptr<tui_layout_base> next = item.layout->clone ();
567 split s = {item.weight, std::move (next)};
568 result->m_splits.push_back (std::move (s));
569 }
570 return std::unique_ptr<tui_layout_base> (result);
571}
572
573/* See tui-layout.h. */
574
575void
576tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
577{
579
580 *min_value = 0;
581 *max_value = 0;
582 bool first_time = true;
583 for (const split &item : m_splits)
584 {
585 int new_min, new_max;
586 item.layout->get_sizes (height, &new_min, &new_max);
587 /* For the mismatch case, the first time through we want to set
588 the min and max to the computed values -- the "first_time"
589 check here is just a funny way of doing that. */
590 if (height == m_vertical || first_time)
591 {
592 *min_value += new_min;
593 *max_value += new_max;
594 }
595 else
596 {
597 *min_value = std::max (*min_value, new_min);
598 *max_value = std::min (*max_value, new_max);
599 }
600 first_time = false;
601 }
602
603 tui_debug_printf ("min_value = %d, max_value = %d", *min_value, *max_value);
604}
605
606/* See tui-layout.h. */
607
608bool
610{
611 if (m_splits.empty ())
612 return false;
613 return m_splits[0].layout->first_edge_has_border_p ();
614}
615
616/* See tui-layout.h. */
617
618bool
620{
621 if (m_splits.empty ())
622 return false;
623 return m_splits.back ().layout->last_edge_has_border_p ();
624}
625
626/* See tui-layout.h. */
627
628void
630{
631 for (int i = 0; i < m_splits.size (); ++i)
632 m_splits[i].weight
633 = m_vertical ? m_splits[i].layout->height : m_splits[i].layout->width;
634}
635
636/* See tui-layout.h. */
637
638std::string
640{
641 std::string str;
642
643 for (int i = 0; i < m_splits.size (); ++i)
644 {
645 if (i > 0)
646 str += ", ";
647 str += string_printf ("[%d] %d", i, m_splits[i].weight);
648 }
649
650 return str;
651}
652
653/* See tui-layout.h. */
654
655void
657 (const std::vector<tui_layout_split::size_info> &info)
658{
659 gdb_assert (debug_tui);
660
661 tui_debug_printf ("current size info data:");
662 for (int i = 0; i < info.size (); ++i)
663 tui_debug_printf (" [%d] { size = %d, min = %d, max = %d, share_box = %d }",
664 i, info[i].size, info[i].min_size,
665 info[i].max_size, info[i].share_box);
666}
667
668/* See tui-layout.h. */
669
671tui_layout_split::set_size (const char *name, int new_size, bool set_width_p)
672{
674
675 tui_debug_printf ("this = %p, name = %s, new_size = %d",
676 this, name, new_size);
677
678 /* Look through the children. If one is a layout holding the named
679 window, we're done; or if one actually is the named window,
680 update it. */
681 int found_index = -1;
682 for (int i = 0; i < m_splits.size (); ++i)
683 {
684 tui_adjust_result adjusted;
685 if (set_width_p)
686 adjusted = m_splits[i].layout->set_width (name, new_size);
687 else
688 adjusted = m_splits[i].layout->set_height (name, new_size);
689 if (adjusted == HANDLED)
690 return HANDLED;
691 if (adjusted == FOUND)
692 {
693 if (set_width_p ? m_vertical : !m_vertical)
694 return FOUND;
695 found_index = i;
696 break;
697 }
698 }
699
700 if (found_index == -1)
701 return NOT_FOUND;
702 int curr_size = (set_width_p
703 ? m_splits[found_index].layout->width
704 : m_splits[found_index].layout->height);
705 if (curr_size == new_size)
706 return HANDLED;
707
708 tui_debug_printf ("found window %s at index %d", name, found_index);
709
711 int delta = m_splits[found_index].weight - new_size;
712 m_splits[found_index].weight = new_size;
713
714 tui_debug_printf ("before delta (%d) distribution, weights: %s",
715 delta, tui_debug_weights_to_string ().c_str ());
716
717 /* Distribute the "delta" over all other windows, while respecting their
718 min/max sizes. We grow each window by 1 line at a time continually
719 looping over all the windows. However, skip the window that the user
720 just resized, obviously we don't want to readjust that window. */
721 bool found_window_that_can_grow_p = true;
722 for (int i = 0; delta != 0; i = (i + 1) % m_splits.size ())
723 {
724 int index = (found_index + 1 + i) % m_splits.size ();
725 if (index == found_index)
726 {
727 if (!found_window_that_can_grow_p)
728 break;
729 found_window_that_can_grow_p = false;
730 continue;
731 }
732
733 int new_min, new_max;
734 m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
735
736 if (delta < 0)
737 {
738 /* The primary window grew, so we are trying to shrink other
739 windows. */
740 if (m_splits[index].weight > new_min)
741 {
742 m_splits[index].weight -= 1;
743 delta += 1;
744 found_window_that_can_grow_p = true;
745 }
746 }
747 else
748 {
749 /* The primary window shrank, so we are trying to grow other
750 windows. */
751 if (m_splits[index].weight < new_max)
752 {
753 m_splits[index].weight += 1;
754 delta -= 1;
755 found_window_that_can_grow_p = true;
756 }
757 }
758
759 tui_debug_printf ("index = %d, weight now: %d",
760 index, m_splits[index].weight);
761 }
762
763 tui_debug_printf ("after delta (%d) distribution, weights: %s",
764 delta, tui_debug_weights_to_string ().c_str ());
765
766 if (delta != 0)
767 {
768 if (set_width_p)
769 warning (_("Invalid window width specified"));
770 else
771 warning (_("Invalid window height specified"));
772 /* Effectively undo any modifications made here. */
774 }
775 else
776 {
777 /* Simply re-apply the updated layout. We pass false here so that
778 the cmd window can be resized. However, we should have already
779 resized everything above to be "just right", so the apply call
780 here should not end up changing the sizes at all. */
781 apply (x, y, width, height, false);
782 }
783
784 return HANDLED;
785}
786
787/* See tui-layout.h. */
788
789void
790tui_layout_split::apply (int x_, int y_, int width_, int height_,
791 bool preserve_cmd_win_size_p)
792{
794
795 x = x_;
796 y = y_;
797 width = width_;
798 height = height_;
799
800 /* In some situations we fix the size of the cmd window. However,
801 occasionally this turns out to be a mistake. This struct is used to
802 hold the original information about the cmd window, so we can restore
803 it if needed. */
804 struct old_size_info
805 {
806 /* Constructor. */
807 old_size_info (int index_, int min_size_, int max_size_)
808 : index (index_),
809 min_size (min_size_),
810 max_size (max_size_)
811 { /* Nothing. */ }
812
813 /* The index in m_splits where the cmd window was found. */
814 int index;
815
816 /* The previous min/max size. */
817 int min_size;
818 int max_size;
819 };
820
821 /* This is given a value only if we fix the size of the cmd window. */
822 gdb::optional<old_size_info> old_cmd_info;
823
824 std::vector<size_info> info (m_splits.size ());
825
826 tui_debug_printf ("weights are: %s",
827 tui_debug_weights_to_string ().c_str ());
828
829 /* Step 1: Find the min and max size of each sub-layout.
830 Fixed-sized layouts are given their desired size, and then the
831 remaining space is distributed among the remaining windows
832 according to the weights given. */
833 int available_size = m_vertical ? height : width;
834 int last_index = -1;
835 int total_weight = 0;
836 for (int i = 0; i < m_splits.size (); ++i)
837 {
838 bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
839
840 /* Always call get_sizes, to ensure that the window is
841 instantiated. This is a bit gross but less gross than adding
842 special cases for this in other places. */
843 m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
844 &info[i].max_size);
845
846 if (preserve_cmd_win_size_p
847 && cmd_win_already_exists
848 && m_splits[i].layout->get_name () != nullptr
849 && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
850 {
851 /* Save the old cmd window information, in case we need to
852 restore it later. */
853 old_cmd_info.emplace (i, info[i].min_size, info[i].max_size);
854
855 /* If this layout has never been applied, then it means the
856 user just changed the layout. In this situation, it's
857 desirable to keep the size of the command window the
858 same. Setting the min and max sizes this way ensures
859 that the resizing step, below, does the right thing with
860 this window. */
861 info[i].min_size = (m_vertical
862 ? TUI_CMD_WIN->height
863 : TUI_CMD_WIN->width);
864 info[i].max_size = info[i].min_size;
865 }
866
867 if (info[i].min_size == info[i].max_size)
868 available_size -= info[i].min_size;
869 else
870 {
871 last_index = i;
872 total_weight += m_splits[i].weight;
873 }
874
875 /* Two adjacent boxed windows will share a border, making a bit
876 more size available. */
877 if (i > 0
878 && m_splits[i - 1].layout->last_edge_has_border_p ()
879 && m_splits[i].layout->first_edge_has_border_p ())
880 info[i].share_box = true;
881 }
882
883 /* If last_index is set then we have a window that is not of a fixed
884 size. This window will have its size calculated below, which requires
885 that the total_weight not be zero (we divide by total_weight, so don't
886 want a floating-point exception). */
887 gdb_assert (last_index == -1 || total_weight > 0);
888
889 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
890 are given their fixed size, while others are resized according to
891 their weight. */
892 int used_size = 0;
893 for (int i = 0; i < m_splits.size (); ++i)
894 {
895 if (info[i].min_size != info[i].max_size)
896 {
897 /* Compute the height and clamp to the allowable range. */
898 info[i].size = available_size * m_splits[i].weight / total_weight;
899 if (info[i].size > info[i].max_size)
900 info[i].size = info[i].max_size;
901 if (info[i].size < info[i].min_size)
902 info[i].size = info[i].min_size;
903 /* Keep a total of all the size we've used so far (we gain some
904 size back if this window can share a border with a preceding
905 window). Any unused space will be distributed between all of
906 the other windows (while respecting min/max sizes) later in
907 this function. */
908 used_size += info[i].size;
909 if (info[i].share_box)
910 --used_size;
911 }
912 else
913 info[i].size = info[i].min_size;
914 }
915
916 if (debug_tui)
917 {
918 tui_debug_printf ("after initial size calculation");
919 tui_debug_printf ("available_size = %d, used_size = %d",
920 available_size, used_size);
921 tui_debug_printf ("total_weight = %d, last_index = %d",
922 total_weight, last_index);
924 }
925
926 /* If we didn't find any sub-layouts that were of a non-fixed size, but
927 we did find the cmd window, then we can consider that a sort-of
928 non-fixed size sub-layout.
929
930 The cmd window might, initially, be of a fixed size (see above), but,
931 we are willing to relax this constraint if required to correctly apply
932 this layout (see below). */
933 if (last_index == -1 && old_cmd_info.has_value ())
934 last_index = old_cmd_info->index;
935
936 /* Allocate any leftover size. */
937 if (available_size != used_size && last_index != -1)
938 {
939 /* Loop over all windows until the amount of used space is equal to
940 the amount of available space. There's an escape hatch within
941 the loop in case we can't find any sub-layouts to resize. */
942 bool found_window_that_can_grow_p = true;
943 for (int idx = last_index;
944 available_size != used_size;
945 idx = (idx + 1) % m_splits.size ())
946 {
947 /* Every time we get back to last_index, which is where the loop
948 started, we check to make sure that we did assign some space
949 to a window, bringing used_size closer to available_size.
950
951 If we didn't, but the cmd window is of a fixed size, then we
952 can make the console window non-fixed-size, and continue
953 around the loop, hopefully, this will allow the layout to be
954 applied correctly.
955
956 If we still make it around the loop without moving used_size
957 closer to available_size, then there's nothing more we can do,
958 and we break out of the loop. */
959 if (idx == last_index)
960 {
961 /* If the used_size is greater than the available_size then
962 this indicates that the fixed-sized sub-layouts claimed
963 more space than is available. This layout is not going to
964 work. Our only hope at this point is to make the cmd
965 window non-fixed-size (if possible), and hope we can
966 shrink this enough to fit the rest of the sub-layouts in.
967
968 Alternatively, we've made it around the loop without
969 adjusting any window's size. This likely means all
970 windows have hit their min or max size. Again, our only
971 hope is to make the cmd window non-fixed-size, and hope
972 this fixes all our problems. */
973 if (old_cmd_info.has_value ()
974 && ((available_size < used_size)
975 || !found_window_that_can_grow_p))
976 {
977 info[old_cmd_info->index].min_size = old_cmd_info->min_size;
978 info[old_cmd_info->index].max_size = old_cmd_info->max_size;
980 ("restoring index %d (cmd) size limits, min = %d, max = %d",
981 old_cmd_info->index, old_cmd_info->min_size,
982 old_cmd_info->max_size);
983 old_cmd_info.reset ();
984 }
985 else if (!found_window_that_can_grow_p)
986 break;
987 found_window_that_can_grow_p = false;
988 }
989
990 if (available_size > used_size
991 && info[idx].size < info[idx].max_size)
992 {
993 found_window_that_can_grow_p = true;
994 info[idx].size += 1;
995 used_size += 1;
996 }
997 else if (available_size < used_size
998 && info[idx].size > info[idx].min_size)
999 {
1000 found_window_that_can_grow_p = true;
1001 info[idx].size -= 1;
1002 used_size -= 1;
1003 }
1004 }
1005
1006 if (debug_tui)
1007 {
1008 tui_debug_printf ("after final size calculation");
1009 tui_debug_printf ("available_size = %d, used_size = %d",
1010 available_size, used_size);
1011 tui_debug_printf ("total_weight = %d, last_index = %d",
1012 total_weight, last_index);
1014 }
1015 }
1016
1017 /* Step 3: Resize. */
1018 int size_accum = 0;
1019 const int maximum = m_vertical ? height : width;
1020 for (int i = 0; i < m_splits.size (); ++i)
1021 {
1022 /* If we fall off the bottom, just make allocations overlap.
1023 GIGO. */
1024 if (size_accum + info[i].size > maximum)
1025 size_accum = maximum - info[i].size;
1026 else if (info[i].share_box)
1027 --size_accum;
1028 if (m_vertical)
1029 m_splits[i].layout->apply (x, y + size_accum, width, info[i].size,
1030 preserve_cmd_win_size_p);
1031 else
1032 m_splits[i].layout->apply (x + size_accum, y, info[i].size, height,
1033 preserve_cmd_win_size_p);
1034 size_accum += info[i].size;
1035 }
1036}
1037
1038/* See tui-layout.h. */
1039
1040void
1042{
1043 for (int i = 0; i < m_splits.size (); ++i)
1044 {
1045 const char *this_name = m_splits[i].layout->get_name ();
1046 if (this_name == nullptr)
1047 m_splits[i].layout->remove_windows (name);
1048 else if (strcmp (this_name, name) == 0
1049 || strcmp (this_name, CMD_NAME) == 0
1050 || strcmp (this_name, STATUS_NAME) == 0)
1051 {
1052 /* Keep. */
1053 }
1054 else
1055 {
1056 m_splits.erase (m_splits.begin () + i);
1057 --i;
1058 }
1059 }
1060}
1061
1062/* See tui-layout.h. */
1063
1064void
1065tui_layout_split::replace_window (const char *name, const char *new_window)
1066{
1067 for (auto &item : m_splits)
1068 item.layout->replace_window (name, new_window);
1069}
1070
1071/* See tui-layout.h. */
1072
1073void
1075{
1076 if (depth > 0)
1077 gdb_puts ("{", output);
1078
1079 if (!m_vertical)
1080 gdb_puts ("-horizontal ", output);
1081
1082 bool first = true;
1083 for (auto &item : m_splits)
1084 {
1085 if (!first)
1086 gdb_puts (" ", output);
1087 first = false;
1088 item.layout->specification (output, depth + 1);
1089 gdb_printf (output, " %d", item.weight);
1090 }
1091
1092 if (depth > 0)
1093 gdb_puts ("}", output);
1094}
1095
1096/* See tui-layout.h. */
1097
1098std::string
1100{
1101 for (auto &item : m_splits)
1102 {
1103 std::string fp = item.layout->layout_fingerprint ();
1104 if (!fp.empty () && m_splits.size () != 1)
1105 return std::string (m_vertical ? "V" : "H") + fp;
1106 }
1107
1108 return "";
1109}
1110
1111/* Destroy the layout associated with SELF. */
1112
1113static void
1114destroy_layout (struct cmd_list_element *self, void *context)
1115{
1116 tui_layout_split *layout = (tui_layout_split *) context;
1117 size_t index = find_layout (layout);
1118 layouts.erase (layouts.begin () + index);
1119}
1120
1121/* List holding the sub-commands of "layout". */
1122
1124
1125/* Called to implement 'tui layout'. */
1126
1127static void
1128tui_layout_command (const char *args, int from_tty)
1129{
1130 help_list (layout_list, "tui layout ", all_commands, gdb_stdout);
1131}
1132
1133/* Add a "layout" command with name NAME that switches to LAYOUT. */
1134
1135static struct cmd_list_element *
1137{
1138 struct cmd_list_element *cmd;
1139
1140 string_file spec;
1141 layout->specification (&spec, 0);
1142
1143 gdb::unique_xmalloc_ptr<char> doc
1144 = xstrprintf (_("Apply the \"%s\" layout.\n\
1145This layout was created using:\n\
1146 tui new-layout %s %s"),
1147 name, name, spec.c_str ());
1148
1149 cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
1150 cmd->set_context (layout);
1151 /* There is no API to set this. */
1152 cmd->func = tui_apply_layout;
1154 cmd->doc_allocated = 1;
1155 doc.release ();
1156 layouts.emplace_back (layout);
1157
1158 return cmd;
1159}
1160
1161/* Initialize the standard layouts. */
1162
1163static void
1165{
1166 tui_layout_split *layout;
1167
1168 layout = new tui_layout_split ();
1169 layout->add_window (SRC_NAME, 2);
1170 layout->add_window (STATUS_NAME, 0);
1171 layout->add_window (CMD_NAME, 1);
1172 add_layout_command (SRC_NAME, layout);
1173
1174 layout = new tui_layout_split ();
1175 layout->add_window (DISASSEM_NAME, 2);
1176 layout->add_window (STATUS_NAME, 0);
1177 layout->add_window (CMD_NAME, 1);
1179
1180 layout = new tui_layout_split ();
1181 layout->add_window (SRC_NAME, 1);
1182 layout->add_window (DISASSEM_NAME, 1);
1183 layout->add_window (STATUS_NAME, 0);
1184 layout->add_window (CMD_NAME, 1);
1185 add_layout_command ("split", layout);
1186
1187 layout = new tui_layout_split ();
1188 layout->add_window (DATA_NAME, 1);
1189 layout->add_window (SRC_NAME, 1);
1190 layout->add_window (STATUS_NAME, 0);
1191 layout->add_window (CMD_NAME, 1);
1192 layouts.emplace_back (layout);
1193 src_regs_layout = layout;
1194
1195 layout = new tui_layout_split ();
1196 layout->add_window (DATA_NAME, 1);
1197 layout->add_window (DISASSEM_NAME, 1);
1198 layout->add_window (STATUS_NAME, 0);
1199 layout->add_window (CMD_NAME, 1);
1200 layouts.emplace_back (layout);
1201 asm_regs_layout = layout;
1202}
1203
1204
1205
1206/* A helper function that returns true if NAME is the name of an
1207 available window. */
1208
1209static bool
1210validate_window_name (const std::string &name)
1211{
1212 auto iter = known_window_types.find (name);
1213 return iter != known_window_types.end ();
1214}
1215
1216/* Implementation of the "tui new-layout" command. */
1217
1218static void
1219tui_new_layout_command (const char *spec, int from_tty)
1220{
1221 std::string new_name = extract_arg (&spec);
1222 if (new_name.empty ())
1223 error (_("No layout name specified"));
1224 if (new_name[0] == '-')
1225 error (_("Layout name cannot start with '-'"));
1226
1227 bool is_vertical = true;
1228 spec = skip_spaces (spec);
1229 if (check_for_argument (&spec, "-horizontal"))
1230 is_vertical = false;
1231
1232 std::vector<std::unique_ptr<tui_layout_split>> splits;
1233 splits.emplace_back (new tui_layout_split (is_vertical));
1234 std::unordered_set<std::string> seen_windows;
1235 while (true)
1236 {
1237 spec = skip_spaces (spec);
1238 if (spec[0] == '\0')
1239 break;
1240
1241 if (spec[0] == '{')
1242 {
1243 is_vertical = true;
1244 spec = skip_spaces (spec + 1);
1245 if (check_for_argument (&spec, "-horizontal"))
1246 is_vertical = false;
1247 splits.emplace_back (new tui_layout_split (is_vertical));
1248 continue;
1249 }
1250
1251 bool is_close = false;
1252 std::string name;
1253 if (spec[0] == '}')
1254 {
1255 is_close = true;
1256 ++spec;
1257 if (splits.size () == 1)
1258 error (_("Extra '}' in layout specification"));
1259 }
1260 else
1261 {
1262 name = extract_arg (&spec);
1263 if (name.empty ())
1264 break;
1266 error (_("Unknown window \"%s\""), name.c_str ());
1267 if (seen_windows.find (name) != seen_windows.end ())
1268 error (_("Window \"%s\" seen twice in layout"), name.c_str ());
1269 }
1270
1271 ULONGEST weight = get_ulongest (&spec, '}');
1272 if ((int) weight != weight)
1273 error (_("Weight out of range: %s"), pulongest (weight));
1274 if (is_close)
1275 {
1276 std::unique_ptr<tui_layout_split> last_split
1277 = std::move (splits.back ());
1278 splits.pop_back ();
1279 splits.back ()->add_split (std::move (last_split), weight);
1280 }
1281 else
1282 {
1283 splits.back ()->add_window (name.c_str (), weight);
1284 seen_windows.insert (name);
1285 }
1286 }
1287 if (splits.size () > 1)
1288 error (_("Missing '}' in layout specification"));
1289 if (seen_windows.empty ())
1290 error (_("New layout does not contain any windows"));
1291 if (seen_windows.find (CMD_NAME) == seen_windows.end ())
1292 error (_("New layout does not contain the \"" CMD_NAME "\" window"));
1293
1294 gdb::unique_xmalloc_ptr<char> cmd_name
1295 = make_unique_xstrdup (new_name.c_str ());
1296 std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
1297 struct cmd_list_element *cmd
1298 = add_layout_command (cmd_name.get (), new_layout.get ());
1299 cmd->name_allocated = 1;
1300 cmd_name.release ();
1301 new_layout.release ();
1302}
1303
1304/* Function to initialize gdb commands, for tui window layout
1305 manipulation. */
1306
1308void
1310{
1311 struct cmd_list_element *layout_cmd
1312 = add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
1313Change the layout of windows.\n\
1314Usage: tui layout prev | next | LAYOUT-NAME"),
1316 add_com_alias ("layout", layout_cmd, class_tui, 0);
1317
1319 _("Apply the next TUI layout."),
1320 &layout_list);
1322 _("Apply the previous TUI layout."),
1323 &layout_list);
1325 _("Apply the TUI register layout."),
1326 &layout_list);
1327
1329 _("Create a new TUI layout.\n\
1330Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1331Create a new TUI layout. The new layout will be named NAME,\n\
1332and can be accessed using \"layout NAME\".\n\
1333The windows will be displayed in the specified order.\n\
1334A WINDOW can also be of the form:\n\
1335 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1336This form indicates a sub-frame.\n\
1337Each WEIGHT is an integer, which holds the relative size\n\
1338to be allocated to the window."),
1339 tui_get_cmd_list ());
1340
1343}
constexpr string_view get()
Definition 70483.cc:49
const char *const name
const char * c_str() const
Definition ui-file.h:222
void specification(ui_file *output, int depth) override
void set_weights_from_sizes()
Definition tui-layout.c:629
std::string tui_debug_weights_to_string() const
Definition tui-layout.c:639
bool first_edge_has_border_p() const override
Definition tui-layout.c:609
void get_sizes(bool height, int *min_value, int *max_value) override
Definition tui-layout.c:576
static void tui_debug_print_size_info(const std::vector< size_info > &info)
Definition tui-layout.c:657
std::vector< split > m_splits
Definition tui-layout.h:322
std::unique_ptr< tui_layout_base > clone() const override
Definition tui-layout.c:561
void remove_windows(const char *name) override
tui_adjust_result set_size(const char *name, int new_size, bool set_width_p)
Definition tui-layout.c:671
void add_split(std::unique_ptr< tui_layout_split > &&layout, int weight)
Definition tui-layout.c:541
tui_layout_split(bool vertical=true)
Definition tui-layout.h:216
void replace_window(const char *name, const char *new_window) override
void add_window(const char *name, int weight)
Definition tui-layout.c:551
bool last_edge_has_border_p() const override
Definition tui-layout.c:619
void apply(int x, int y, int width, int height, bool preserve_cmd_win_size_p) override
Definition tui-layout.c:790
std::string layout_fingerprint() const override
void get_sizes(bool height, int *min_value, int *max_value) override
Definition tui-layout.c:461
const char * get_name() const override
Definition tui-layout.h:160
bool last_edge_has_border_p() const override
Definition tui-layout.c:497
std::unique_ptr< tui_layout_base > clone() const override
Definition tui-layout.c:438
bool first_edge_has_border_p() const override
Definition tui-layout.c:488
tui_layout_window(const char *name)
Definition tui-layout.h:148
void replace_window(const char *name, const char *new_window) override
Definition tui-layout.c:506
void apply(int x, int y, int width, int height, bool preserve_cmd_win_size_p) override
Definition tui-layout.c:447
std::string m_contents
Definition tui-layout.h:202
tui_win_info * m_window
Definition tui-layout.h:206
void specification(ui_file *output, int depth) override
Definition tui-layout.c:522
std::string layout_fingerprint() const override
Definition tui-layout.c:530
struct cmd_list_element * add_cmd(const char *name, enum command_class theclass, const char *doc, struct cmd_list_element **list)
Definition cli-decode.c:233
cmd_list_element * add_com_alias(const char *name, cmd_list_element *target, command_class theclass, int abbrev_flag)
void help_list(struct cmd_list_element *list, const char *cmdtype, enum command_class theclass, struct ui_file *stream)
struct cmd_list_element * add_prefix_cmd(const char *name, enum command_class theclass, cmd_simple_func_ftype *fun, const char *doc, struct cmd_list_element **subcommands, int allow_unknown, struct cmd_list_element **list)
Definition cli-decode.c:357
ULONGEST get_ulongest(const char **pp, int trailer)
Definition cli-utils.c:29
int check_for_argument(const char **str, const char *arg, int arg_len)
Definition cli-utils.c:421
std::string extract_arg(const char **arg)
Definition cli-utils.c:383
@ class_tui
Definition command.h:66
@ all_commands
Definition command.h:50
size_t size
Definition go32-nat.c:239
info(Component c)
Definition gdbarch.py:41
Definition 1.cc:26
unsigned int doc_allocated
Definition cli-decode.h:145
const char * doc
Definition cli-decode.h:193
cmd_func_ftype * func
Definition cli-decode.h:175
void * context() const
Definition cli-decode.h:109
void(* destroyer)(struct cmd_list_element *self, void *context)
Definition cli-decode.h:233
unsigned int name_allocated
Definition cli-decode.h:149
void set_context(void *context)
Definition cli-decode.h:103
virtual bool can_box() const
Definition tui-data.h:86
virtual void resize(int height, int width, int origin_x, int origin_y)
Definition tui-layout.c:302
virtual void rerender()
Definition tui-data.c:168
int min_width() const
Definition tui-data.h:80
virtual const char * name() const =0
virtual void make_visible(bool visible)
virtual int min_height() const
Definition tui-data.h:71
int max_width() const
Definition tui-win.c:1026
virtual void make_window()
std::unique_ptr< WINDOW, curses_deleter > handle
Definition tui-data.h:158
virtual int max_height() const
Definition tui-win.c:1018
void tui_set_win_focus_to(struct tui_win_info *win_info)
Definition tui-data.c:65
int tui_term_height(void)
Definition tui-data.c:78
int tui_term_width(void)
Definition tui-data.c:94
struct tui_win_info * tui_win_list[MAX_MAJOR_WINDOWS]
Definition tui-data.c:32
struct tui_win_info * tui_win_with_focus(void)
Definition tui-data.c:57
#define TUI_DISASM_WIN
Definition tui-data.h:196
#define CMD_NAME
Definition tui-data.h:187
#define DATA_NAME
Definition tui-data.h:188
#define TUI_SRC_WIN
Definition tui-data.h:195
#define DISASSEM_NAME
Definition tui-data.h:189
#define TUI_CMD_WIN
Definition tui-data.h:198
#define SRC_NAME
Definition tui-data.h:186
#define TUI_DATA_WIN
Definition tui-data.h:197
#define STATUS_NAME
Definition tui-data.h:190
void tui_get_begin_asm_address(struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
Definition tui-disasm.c:392
void tui_regs_layout()
Definition tui-layout.c:245
void tui_set_initial_layout()
Definition tui-layout.c:223
static tui_layout_split * src_regs_layout
Definition tui-layout.c:61
void tui_add_win_to_layout(enum tui_win_type type)
Definition tui-layout.c:155
void _initialize_tui_layout()
static void tui_set_layout(tui_layout_split *layout)
Definition tui-layout.c:136
static void extract_display_start_addr(struct gdbarch **, CORE_ADDR *)
Definition tui-layout.c:288
void tui_register_window(const char *name, window_factory &&factory)
Definition tui-layout.c:403
static void tui_next_layout_command(const char *arg, int from_tty)
Definition tui-layout.c:214
static tui_layout_split * applied_skeleton
Definition tui-layout.c:57
static tui_layout_split * asm_regs_layout
Definition tui-layout.c:62
static void initialize_known_windows()
Definition tui-layout.c:382
static void tui_new_layout_command(const char *spec, int from_tty)
void tui_adjust_window_height(struct tui_win_info *win, int new_height)
Definition tui-layout.c:120
static void tui_apply_layout(const char *args, int from_tty, cmd_list_element *command)
Definition tui-layout.c:190
static void tui_prev_layout_command(const char *arg, int from_tty)
Definition tui-layout.c:231
static size_t find_layout(tui_layout_split *layout)
Definition tui-layout.c:177
static bool validate_window_name(const std::string &name)
static std::unique_ptr< tui_layout_base > applied_layout
Definition tui-layout.c:54
static void destroy_layout(struct cmd_list_element *self, void *context)
static void tui_layout_command(const char *args, int from_tty)
static void tui_regs_layout_command(const char *arg, int from_tty)
Definition tui-layout.c:259
static tui_win_info * tui_get_window_by_name(const std::string &name)
Definition tui-layout.c:363
std::vector< tui_win_info * > tui_windows
Definition tui-layout.c:65
static std::vector< std::unique_ptr< tui_layout_split > > layouts
Definition tui-layout.c:51
static tui_win_info * make_standard_window(const char *)
Definition tui-layout.c:339
static void initialize_layouts()
void tui_remove_some_windows()
Definition tui-layout.c:268
static window_types_map known_window_types
Definition tui-layout.c:348
void tui_next_layout()
Definition tui-layout.c:202
void tui_apply_current_layout(bool preserve_cmd_win_size_p)
Definition tui-layout.c:70
static struct cmd_list_element * layout_list
known_window_names_range all_known_window_names()
Definition tui-layout.c:353
static struct cmd_list_element * add_layout_command(const char *name, tui_layout_split *layout)
void tui_adjust_window_width(struct tui_win_info *win, int new_width)
Definition tui-layout.c:128
std::unordered_map< std::string, window_factory > window_types_map
Definition tui-layout.h:367
iterator_range< known_window_names_iterator > known_window_names_range
Definition tui-layout.h:409
tui_adjust_result
Definition tui-layout.h:36
@ FOUND
Definition tui-layout.h:40
@ NOT_FOUND
Definition tui-layout.h:38
@ HANDLED
Definition tui-layout.h:42
std::function< tui_win_info *(const char *name) window_factory)
Definition tui-layout.h:363
struct cmd_list_element ** tui_get_cmd_list(void)
Definition tui-win.c:300
void tui_update_source_windows_with_addr(struct gdbarch *gdbarch, CORE_ADDR addr)
bool debug_tui
Definition tui.c:56
void tui_enable(void)
Definition tui.c:381
tui_win_type
Definition tui.h:53
@ DISASSEM_WIN
Definition tui.h:55
@ STATUS_WIN
Definition tui.h:58
@ DATA_WIN
Definition tui.h:56
@ SRC_WIN
Definition tui.h:54
@ MAX_MAJOR_WINDOWS
Definition tui.h:60
#define tui_debug_printf(fmt,...)
Definition tui.h:31
#define TUI_SCOPED_DEBUG_ENTER_EXIT
Definition tui.h:36
void gdb_printf(struct ui_file *stream, const char *format,...)
Definition utils.c:1886
void gdb_puts(const char *linebuffer, struct ui_file *stream)
Definition utils.c:1809
#define gdb_stdout
Definition utils.h:182