1 /* TUI layout window management.
3 Copyright (C) 1998-2020 Free Software Foundation, Inc.
5 Contributed by Hewlett-Packard Company.
7 This file is part of GDB.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include "arch-utils.h"
31 #include "tui/tui-command.h"
32 #include "tui/tui-data.h"
33 #include "tui/tui-wingeneral.h"
34 #include "tui/tui-stack.h"
35 #include "tui/tui-regs.h"
36 #include "tui/tui-win.h"
37 #include "tui/tui-winsource.h"
38 #include "tui/tui-disasm.h"
39 #include "tui/tui-layout.h"
40 #include "tui/tui-source.h"
41 #include "gdb_curses.h"
43 static void show_layout (enum tui_layout_type
);
44 static enum tui_layout_type
next_layout (void);
45 static enum tui_layout_type
prev_layout (void);
46 static void tui_layout_command (const char *, int);
47 static void extract_display_start_addr (struct gdbarch
**, CORE_ADDR
*);
50 /* The pre-defined layouts. */
51 static tui_layout_split
*standard_layouts
[UNDEFINED_LAYOUT
];
53 /* The layout that is currently applied. */
54 static std::unique_ptr
<tui_layout_base
> applied_layout
;
56 static enum tui_layout_type current_layout
= UNDEFINED_LAYOUT
;
58 /* Accessor for the current layout. */
60 tui_current_layout (void)
62 return current_layout
;
65 /* See tui-layout.h. */
68 tui_apply_current_layout ()
70 applied_layout
->apply (0, 0, tui_term_width (), tui_term_height ());
76 tui_adjust_window_height (struct tui_win_info
*win
, int new_height
)
78 applied_layout
->adjust_size (win
->name (), new_height
);
81 /* Show the screen layout defined. */
83 show_layout (enum tui_layout_type layout
)
85 enum tui_layout_type cur_layout
= tui_current_layout ();
87 if (layout
!= cur_layout
)
89 tui_make_all_invisible ();
90 applied_layout
= standard_layouts
[layout
]->clone ();
91 tui_apply_current_layout ();
92 current_layout
= layout
;
93 tui_delete_invisible_windows ();
98 /* Function to set the layout to SRC_COMMAND, DISASSEM_COMMAND,
99 SRC_DISASSEM_COMMAND, SRC_DATA_COMMAND, or DISASSEM_DATA_COMMAND. */
101 tui_set_layout (enum tui_layout_type layout_type
)
103 gdb_assert (layout_type
!= UNDEFINED_LAYOUT
);
105 enum tui_layout_type cur_layout
= tui_current_layout ();
106 struct gdbarch
*gdbarch
;
108 struct tui_win_info
*win_with_focus
= tui_win_with_focus ();
110 extract_display_start_addr (&gdbarch
, &addr
);
112 enum tui_layout_type new_layout
= layout_type
;
114 if (new_layout
!= cur_layout
)
116 tui_suppress_output suppress
;
118 show_layout (new_layout
);
120 /* Now determine where focus should be. */
121 if (win_with_focus
!= TUI_CMD_WIN
)
126 tui_set_win_focus_to (TUI_SRC_WIN
);
128 case DISASSEM_COMMAND
:
129 /* The previous layout was not showing code.
130 This can happen if there is no source
133 1. if the source file is in another dir OR
134 2. if target was compiled without -g
135 We still want to show the assembly though! */
137 tui_get_begin_asm_address (&gdbarch
, &addr
);
138 tui_set_win_focus_to (TUI_DISASM_WIN
);
140 case SRC_DISASSEM_COMMAND
:
141 /* The previous layout was not showing code.
142 This can happen if there is no source
145 1. if the source file is in another dir OR
146 2. if target was compiled without -g
147 We still want to show the assembly though! */
149 tui_get_begin_asm_address (&gdbarch
, &addr
);
150 if (win_with_focus
== TUI_SRC_WIN
)
151 tui_set_win_focus_to (TUI_SRC_WIN
);
153 tui_set_win_focus_to (TUI_DISASM_WIN
);
155 case SRC_DATA_COMMAND
:
156 if (win_with_focus
!= TUI_DATA_WIN
)
157 tui_set_win_focus_to (TUI_SRC_WIN
);
159 tui_set_win_focus_to (TUI_DATA_WIN
);
161 case DISASSEM_DATA_COMMAND
:
162 /* The previous layout was not showing code.
163 This can happen if there is no source
166 1. if the source file is in another dir OR
167 2. if target was compiled without -g
168 We still want to show the assembly though! */
170 tui_get_begin_asm_address (&gdbarch
, &addr
);
171 if (win_with_focus
!= TUI_DATA_WIN
)
172 tui_set_win_focus_to (TUI_DISASM_WIN
);
174 tui_set_win_focus_to (TUI_DATA_WIN
);
181 * Now update the window content.
183 tui_update_source_windows_with_addr (gdbarch
, addr
);
184 if (new_layout
== SRC_DATA_COMMAND
185 || new_layout
== DISASSEM_DATA_COMMAND
)
186 TUI_DATA_WIN
->show_registers (TUI_DATA_WIN
->get_current_group ());
190 /* Add the specified window to the layout in a logical way. This
191 means setting up the most logical layout given the window to be
194 tui_add_win_to_layout (enum tui_win_type type
)
196 enum tui_layout_type cur_layout
= tui_current_layout ();
201 if (cur_layout
!= SRC_COMMAND
202 && cur_layout
!= SRC_DISASSEM_COMMAND
203 && cur_layout
!= SRC_DATA_COMMAND
)
205 if (cur_layout
== DISASSEM_DATA_COMMAND
)
206 tui_set_layout (SRC_DATA_COMMAND
);
208 tui_set_layout (SRC_COMMAND
);
212 if (cur_layout
!= DISASSEM_COMMAND
213 && cur_layout
!= SRC_DISASSEM_COMMAND
214 && cur_layout
!= DISASSEM_DATA_COMMAND
)
216 if (cur_layout
== SRC_DATA_COMMAND
)
217 tui_set_layout (DISASSEM_DATA_COMMAND
);
219 tui_set_layout (DISASSEM_COMMAND
);
223 if (cur_layout
!= SRC_DATA_COMMAND
224 && cur_layout
!= DISASSEM_DATA_COMMAND
)
226 if (cur_layout
== DISASSEM_COMMAND
)
227 tui_set_layout (DISASSEM_DATA_COMMAND
);
229 tui_set_layout (SRC_DATA_COMMAND
);
237 /* Complete possible layout names. TEXT is the complete text entered so
238 far, WORD is the word currently being completed. */
241 layout_completer (struct cmd_list_element
*ignore
,
242 completion_tracker
&tracker
,
243 const char *text
, const char *word
)
245 static const char *layout_names
[] =
246 { "src", "asm", "split", "regs", "next", "prev", NULL
};
248 complete_on_enum (tracker
, layout_names
, text
, word
);
251 /* Function to set the layout to SRC, ASM, SPLIT, NEXT, PREV, DATA, or
254 tui_layout_command (const char *layout_name
, int from_tty
)
256 enum tui_layout_type new_layout
= UNDEFINED_LAYOUT
;
257 enum tui_layout_type cur_layout
= tui_current_layout ();
259 if (layout_name
== NULL
|| *layout_name
== '\0')
260 error (_("Usage: layout prev | next | LAYOUT-NAME"));
262 /* First check for ambiguous input. */
263 if (strcmp (layout_name
, "s") == 0)
264 error (_("Ambiguous command input."));
266 if (subset_compare (layout_name
, "src"))
267 new_layout
= SRC_COMMAND
;
268 else if (subset_compare (layout_name
, "asm"))
269 new_layout
= DISASSEM_COMMAND
;
270 else if (subset_compare (layout_name
, "split"))
271 new_layout
= SRC_DISASSEM_COMMAND
;
272 else if (subset_compare (layout_name
, "regs"))
274 if (cur_layout
== SRC_COMMAND
275 || cur_layout
== SRC_DATA_COMMAND
)
276 new_layout
= SRC_DATA_COMMAND
;
278 new_layout
= DISASSEM_DATA_COMMAND
;
280 else if (subset_compare (layout_name
, "next"))
281 new_layout
= next_layout ();
282 else if (subset_compare (layout_name
, "prev"))
283 new_layout
= prev_layout ();
285 error (_("Unrecognized layout: %s"), layout_name
);
287 /* Make sure the curses mode is enabled. */
289 tui_set_layout (new_layout
);
294 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
296 enum tui_layout_type cur_layout
= tui_current_layout ();
297 struct gdbarch
*gdbarch
= get_current_arch ();
300 struct symtab_and_line cursal
= get_current_source_symtab_and_line ();
305 case SRC_DATA_COMMAND
:
306 gdbarch
= TUI_SRC_WIN
->gdbarch
;
307 find_line_pc (cursal
.symtab
,
308 TUI_SRC_WIN
->start_line_or_addr
.u
.line_no
,
312 case DISASSEM_COMMAND
:
313 case SRC_DISASSEM_COMMAND
:
314 case DISASSEM_DATA_COMMAND
:
315 gdbarch
= TUI_DISASM_WIN
->gdbarch
;
316 addr
= TUI_DISASM_WIN
->start_line_or_addr
.u
.addr
;
323 *gdbarch_p
= gdbarch
;
328 /* Answer the previous layout to cycle to. */
329 static enum tui_layout_type
334 new_layout
= tui_current_layout ();
335 if (new_layout
== UNDEFINED_LAYOUT
)
336 new_layout
= SRC_COMMAND
;
340 if (new_layout
== UNDEFINED_LAYOUT
)
341 new_layout
= SRC_COMMAND
;
344 return (enum tui_layout_type
) new_layout
;
348 /* Answer the next layout to cycle to. */
349 static enum tui_layout_type
354 new_layout
= tui_current_layout ();
355 if (new_layout
== SRC_COMMAND
)
356 new_layout
= DISASSEM_DATA_COMMAND
;
360 if (new_layout
== UNDEFINED_LAYOUT
)
361 new_layout
= DISASSEM_DATA_COMMAND
;
364 return (enum tui_layout_type
) new_layout
;
368 tui_gen_win_info::resize (int height_
, int width_
,
369 int origin_x_
, int origin_y_
)
371 if (width
== width_
&& height
== height_
372 && x
== origin_x_
&& y
== origin_y_
373 && handle
!= nullptr)
381 if (handle
!= nullptr)
384 wresize (handle
.get (), height
, width
);
385 mvwin (handle
.get (), y
, x
);
386 wmove (handle
.get (), 0, 0);
388 handle
.reset (nullptr);
392 if (handle
== nullptr)
400 /* Helper function that returns a TUI window, given its name. */
402 static tui_gen_win_info
*
403 tui_get_window_by_name (const std::string
&name
)
407 if (tui_win_list
[SRC_WIN
] == nullptr)
408 tui_win_list
[SRC_WIN
] = new tui_source_window ();
409 return tui_win_list
[SRC_WIN
];
411 else if (name
== "cmd")
413 if (tui_win_list
[CMD_WIN
] == nullptr)
414 tui_win_list
[CMD_WIN
] = new tui_cmd_window ();
415 return tui_win_list
[CMD_WIN
];
417 else if (name
== "regs")
419 if (tui_win_list
[DATA_WIN
] == nullptr)
420 tui_win_list
[DATA_WIN
] = new tui_data_window ();
421 return tui_win_list
[DATA_WIN
];
423 else if (name
== "asm")
425 if (tui_win_list
[DISASSEM_WIN
] == nullptr)
426 tui_win_list
[DISASSEM_WIN
] = new tui_disasm_window ();
427 return tui_win_list
[DISASSEM_WIN
];
431 gdb_assert (name
== "locator");
432 return tui_locator_win_info_ptr ();
436 /* See tui-layout.h. */
438 std::unique_ptr
<tui_layout_base
>
439 tui_layout_window::clone () const
441 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
442 return std::unique_ptr
<tui_layout_base
> (result
);
445 /* See tui-layout.h. */
448 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
454 gdb_assert (m_window
!= nullptr);
455 m_window
->resize (height
, width
, x
, y
);
458 /* See tui-layout.h. */
461 tui_layout_window::get_sizes (int *min_height
, int *max_height
)
463 if (m_window
== nullptr)
464 m_window
= tui_get_window_by_name (m_contents
);
465 *min_height
= m_window
->min_height ();
466 *max_height
= m_window
->max_height ();
469 /* See tui-layout.h. */
472 tui_layout_window::top_boxed_p () const
474 gdb_assert (m_window
!= nullptr);
475 return m_window
->can_box ();
478 /* See tui-layout.h. */
481 tui_layout_window::bottom_boxed_p () const
483 gdb_assert (m_window
!= nullptr);
484 return m_window
->can_box ();
487 /* See tui-layout.h. */
490 tui_layout_split::add_split (int weight
)
492 tui_layout_split
*result
= new tui_layout_split ();
493 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
494 m_splits
.push_back (std::move (s
));
498 /* See tui-layout.h. */
501 tui_layout_split::add_window (const char *name
, int weight
)
503 tui_layout_window
*result
= new tui_layout_window (name
);
504 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
505 m_splits
.push_back (std::move (s
));
508 /* See tui-layout.h. */
510 std::unique_ptr
<tui_layout_base
>
511 tui_layout_split::clone () const
513 tui_layout_split
*result
= new tui_layout_split ();
514 for (const split
&item
: m_splits
)
516 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
517 split s
= {item
.weight
, std::move (next
)};
518 result
->m_splits
.push_back (std::move (s
));
520 return std::unique_ptr
<tui_layout_base
> (result
);
523 /* See tui-layout.h. */
526 tui_layout_split::get_sizes (int *min_height
, int *max_height
)
530 for (const split
&item
: m_splits
)
532 int new_min
, new_max
;
533 item
.layout
->get_sizes (&new_min
, &new_max
);
534 *min_height
+= new_min
;
535 *max_height
+= new_max
;
539 /* See tui-layout.h. */
542 tui_layout_split::top_boxed_p () const
544 if (m_splits
.empty ())
546 return m_splits
[0].layout
->top_boxed_p ();
549 /* See tui-layout.h. */
552 tui_layout_split::bottom_boxed_p () const
554 if (m_splits
.empty ())
556 return m_splits
.back ().layout
->top_boxed_p ();
559 /* See tui-layout.h. */
562 tui_layout_split::set_weights_from_heights ()
564 for (int i
= 0; i
< m_splits
.size (); ++i
)
565 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
568 /* See tui-layout.h. */
571 tui_layout_split::adjust_size (const char *name
, int new_height
)
573 /* Look through the children. If one is a layout holding the named
574 window, we're done; or if one actually is the named window,
576 int found_index
= -1;
577 for (int i
= 0; i
< m_splits
.size (); ++i
)
579 if (m_splits
[i
].layout
->adjust_size (name
, new_height
))
581 const char *win_name
= m_splits
[i
].layout
->get_name ();
582 if (win_name
!= nullptr && strcmp (name
, win_name
) == 0)
589 if (found_index
== -1)
591 if (m_splits
[found_index
].layout
->height
== new_height
)
594 set_weights_from_heights ();
595 int delta
= m_splits
[found_index
].weight
- new_height
;
596 m_splits
[found_index
].weight
= new_height
;
598 /* Distribute the "delta" over the next window; but if the next
599 window cannot hold it all, keep going until we either find a
600 window that does, or until we loop all the way around. */
601 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
603 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
605 int new_min
, new_max
;
606 m_splits
[index
].layout
->get_sizes (&new_min
, &new_max
);
610 /* The primary window grew, so we are trying to shrink other
612 int available
= m_splits
[index
].weight
- new_min
;
613 int shrink_by
= std::min (available
, -delta
);
614 m_splits
[index
].weight
-= shrink_by
;
619 /* The primary window shrank, so we are trying to grow other
621 int available
= new_max
- m_splits
[index
].weight
;
622 int grow_by
= std::min (available
, delta
);
623 m_splits
[index
].weight
+= grow_by
;
630 warning (_("Invalid window height specified"));
631 /* Effectively undo any modifications made here. */
632 set_weights_from_heights ();
636 /* Simply re-apply the updated layout. */
637 apply (x
, y
, width
, height
);
643 /* See tui-layout.h. */
646 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
658 /* True if this window will share a box border with the previous
659 window in the list. */
663 std::vector
<height_info
> info (m_splits
.size ());
665 /* Step 1: Find the min and max height of each sub-layout.
666 Fixed-sized layouts are given their desired height, and then the
667 remaining space is distributed among the remaining windows
668 according to the weights given. */
669 int available_height
= height
;
671 int total_weight
= 0;
672 for (int i
= 0; i
< m_splits
.size (); ++i
)
674 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
676 /* Always call get_sizes, to ensure that the window is
677 instantiated. This is a bit gross but less gross than adding
678 special cases for this in other places. */
679 m_splits
[i
].layout
->get_sizes (&info
[i
].min_height
, &info
[i
].max_height
);
682 && cmd_win_already_exists
683 && m_splits
[i
].layout
->get_name () != nullptr
684 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
686 /* If this layout has never been applied, then it means the
687 user just changed the layout. In this situation, it's
688 desirable to keep the size of the command window the
689 same. Setting the min and max heights this way ensures
690 that the resizing step, below, does the right thing with
692 info
[i
].min_height
= TUI_CMD_WIN
->height
;
693 info
[i
].max_height
= TUI_CMD_WIN
->height
;
696 if (info
[i
].min_height
== info
[i
].max_height
)
697 available_height
-= info
[i
].min_height
;
701 total_weight
+= m_splits
[i
].weight
;
704 /* Two adjacent boxed windows will share a border, making a bit
705 more height available. */
707 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
708 && m_splits
[i
].layout
->top_boxed_p ())
709 info
[i
].share_box
= true;
712 /* Step 2: Compute the height of each sub-layout. Fixed-sized items
713 are given their fixed size, while others are resized according to
716 for (int i
= 0; i
< m_splits
.size (); ++i
)
718 /* Compute the height and clamp to the allowable range. */
719 info
[i
].height
= available_height
* m_splits
[i
].weight
/ total_weight
;
720 if (info
[i
].height
> info
[i
].max_height
)
721 info
[i
].height
= info
[i
].max_height
;
722 if (info
[i
].height
< info
[i
].min_height
)
723 info
[i
].height
= info
[i
].min_height
;
724 /* If there is any leftover height, just redistribute it to the
725 last resizeable window, by dropping it from the allocated
726 height. We could try to be fancier here perhaps, by
727 redistributing this height among all windows, not just the
729 if (info
[i
].min_height
!= info
[i
].max_height
)
731 used_height
+= info
[i
].height
;
732 if (info
[i
].share_box
)
737 /* Allocate any leftover height. */
738 if (available_height
>= used_height
&& last_index
!= -1)
739 info
[last_index
].height
+= available_height
- used_height
;
741 /* Step 3: Resize. */
742 int height_accum
= 0;
743 for (int i
= 0; i
< m_splits
.size (); ++i
)
745 /* If we fall off the bottom, just make allocations overlap.
747 if (height_accum
+ info
[i
].height
> height
)
748 height_accum
= height
- info
[i
].height
;
749 else if (info
[i
].share_box
)
751 m_splits
[i
].layout
->apply (x
, y
+ height_accum
, width
, info
[i
].height
);
752 height_accum
+= info
[i
].height
;
759 initialize_layouts ()
761 standard_layouts
[SRC_COMMAND
] = new tui_layout_split ();
762 standard_layouts
[SRC_COMMAND
]->add_window ("src", 2);
763 standard_layouts
[SRC_COMMAND
]->add_window ("locator", 0);
764 standard_layouts
[SRC_COMMAND
]->add_window ("cmd", 1);
766 standard_layouts
[DISASSEM_COMMAND
] = new tui_layout_split ();
767 standard_layouts
[DISASSEM_COMMAND
]->add_window ("asm", 2);
768 standard_layouts
[DISASSEM_COMMAND
]->add_window ("locator", 0);
769 standard_layouts
[DISASSEM_COMMAND
]->add_window ("cmd", 1);
771 standard_layouts
[SRC_DATA_COMMAND
] = new tui_layout_split ();
772 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("regs", 1);
773 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("src", 1);
774 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("locator", 0);
775 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("cmd", 1);
777 standard_layouts
[DISASSEM_DATA_COMMAND
] = new tui_layout_split ();
778 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("regs", 1);
779 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("asm", 1);
780 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("locator", 0);
781 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("cmd", 1);
783 standard_layouts
[SRC_DISASSEM_COMMAND
] = new tui_layout_split ();
784 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("src", 1);
785 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("asm", 1);
786 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("locator", 0);
787 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("cmd", 1);
792 /* Function to initialize gdb commands, for tui window layout
795 void _initialize_tui_layout ();
797 _initialize_tui_layout ()
799 struct cmd_list_element
*cmd
;
801 cmd
= add_com ("layout", class_tui
, tui_layout_command
, _("\
802 Change the layout of windows.\n\
803 Usage: layout prev | next | LAYOUT-NAME\n\
805 src : Displays source and command windows.\n\
806 asm : Displays disassembly and command windows.\n\
807 split : Displays source, disassembly and command windows.\n\
808 regs : Displays register window. If existing layout\n\
809 is source/command or assembly/command, the \n\
810 register window is displayed. If the\n\
811 source/assembly/command (split) is displayed, \n\
812 the register window is displayed with \n\
813 the window that has current logical focus."));
814 set_cmd_completer (cmd
, layout_completer
);
816 initialize_layouts ();