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 /* See tui-layout.h. */
193 tui_add_win_to_layout (enum tui_win_type type
)
195 gdb_assert (type
== SRC_WIN
|| type
== DISASSEM_WIN
);
197 enum tui_layout_type cur_layout
= tui_current_layout ();
202 if (cur_layout
!= SRC_COMMAND
203 && cur_layout
!= SRC_DISASSEM_COMMAND
204 && cur_layout
!= SRC_DATA_COMMAND
)
206 if (cur_layout
== DISASSEM_DATA_COMMAND
)
207 tui_set_layout (SRC_DATA_COMMAND
);
209 tui_set_layout (SRC_COMMAND
);
213 if (cur_layout
!= DISASSEM_COMMAND
214 && cur_layout
!= SRC_DISASSEM_COMMAND
215 && cur_layout
!= DISASSEM_DATA_COMMAND
)
217 if (cur_layout
== SRC_DATA_COMMAND
)
218 tui_set_layout (DISASSEM_DATA_COMMAND
);
220 tui_set_layout (DISASSEM_COMMAND
);
226 /* Complete possible layout names. TEXT is the complete text entered so
227 far, WORD is the word currently being completed. */
230 layout_completer (struct cmd_list_element
*ignore
,
231 completion_tracker
&tracker
,
232 const char *text
, const char *word
)
234 static const char *layout_names
[] =
235 { "src", "asm", "split", "regs", "next", "prev", NULL
};
237 complete_on_enum (tracker
, layout_names
, text
, word
);
240 /* Function to set the layout to SRC, ASM, SPLIT, NEXT, PREV, DATA, or
243 tui_layout_command (const char *layout_name
, int from_tty
)
245 enum tui_layout_type new_layout
= UNDEFINED_LAYOUT
;
246 enum tui_layout_type cur_layout
= tui_current_layout ();
248 if (layout_name
== NULL
|| *layout_name
== '\0')
249 error (_("Usage: layout prev | next | LAYOUT-NAME"));
251 /* First check for ambiguous input. */
252 if (strcmp (layout_name
, "s") == 0)
253 error (_("Ambiguous command input."));
255 if (subset_compare (layout_name
, "src"))
256 new_layout
= SRC_COMMAND
;
257 else if (subset_compare (layout_name
, "asm"))
258 new_layout
= DISASSEM_COMMAND
;
259 else if (subset_compare (layout_name
, "split"))
260 new_layout
= SRC_DISASSEM_COMMAND
;
261 else if (subset_compare (layout_name
, "regs"))
263 if (cur_layout
== SRC_COMMAND
264 || cur_layout
== SRC_DATA_COMMAND
)
265 new_layout
= SRC_DATA_COMMAND
;
267 new_layout
= DISASSEM_DATA_COMMAND
;
269 else if (subset_compare (layout_name
, "next"))
270 new_layout
= next_layout ();
271 else if (subset_compare (layout_name
, "prev"))
272 new_layout
= prev_layout ();
274 error (_("Unrecognized layout: %s"), layout_name
);
276 /* Make sure the curses mode is enabled. */
278 tui_set_layout (new_layout
);
281 /* See tui-layout.h. */
286 tui_layout_command ("next", 0);
289 /* See tui-layout.h. */
292 tui_remove_some_windows ()
294 tui_win_info
*focus
= tui_win_with_focus ();
296 if (strcmp (focus
->name (), "cmd") == 0)
298 /* Try leaving the source or disassembly window. If neither
299 exists, just do nothing. */
301 if (focus
== nullptr)
302 focus
= TUI_DISASM_WIN
;
303 if (focus
== nullptr)
307 applied_layout
->remove_windows (focus
->name ());
308 tui_apply_current_layout ();
312 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
314 enum tui_layout_type cur_layout
= tui_current_layout ();
315 struct gdbarch
*gdbarch
= get_current_arch ();
318 struct symtab_and_line cursal
= get_current_source_symtab_and_line ();
323 case SRC_DATA_COMMAND
:
324 gdbarch
= TUI_SRC_WIN
->gdbarch
;
325 find_line_pc (cursal
.symtab
,
326 TUI_SRC_WIN
->start_line_or_addr
.u
.line_no
,
330 case DISASSEM_COMMAND
:
331 case SRC_DISASSEM_COMMAND
:
332 case DISASSEM_DATA_COMMAND
:
333 gdbarch
= TUI_DISASM_WIN
->gdbarch
;
334 addr
= TUI_DISASM_WIN
->start_line_or_addr
.u
.addr
;
341 *gdbarch_p
= gdbarch
;
346 /* Answer the previous layout to cycle to. */
347 static enum tui_layout_type
352 new_layout
= tui_current_layout ();
353 if (new_layout
== UNDEFINED_LAYOUT
)
354 new_layout
= SRC_COMMAND
;
358 if (new_layout
== UNDEFINED_LAYOUT
)
359 new_layout
= SRC_COMMAND
;
362 return (enum tui_layout_type
) new_layout
;
366 /* Answer the next layout to cycle to. */
367 static enum tui_layout_type
372 new_layout
= tui_current_layout ();
373 if (new_layout
== SRC_COMMAND
)
374 new_layout
= DISASSEM_DATA_COMMAND
;
378 if (new_layout
== UNDEFINED_LAYOUT
)
379 new_layout
= DISASSEM_DATA_COMMAND
;
382 return (enum tui_layout_type
) new_layout
;
386 tui_gen_win_info::resize (int height_
, int width_
,
387 int origin_x_
, int origin_y_
)
389 if (width
== width_
&& height
== height_
390 && x
== origin_x_
&& y
== origin_y_
391 && handle
!= nullptr)
399 if (handle
!= nullptr)
402 wresize (handle
.get (), height
, width
);
403 mvwin (handle
.get (), y
, x
);
404 wmove (handle
.get (), 0, 0);
406 handle
.reset (nullptr);
410 if (handle
== nullptr)
418 /* Helper function that returns a TUI window, given its name. */
420 static tui_gen_win_info
*
421 tui_get_window_by_name (const std::string
&name
)
425 if (tui_win_list
[SRC_WIN
] == nullptr)
426 tui_win_list
[SRC_WIN
] = new tui_source_window ();
427 return tui_win_list
[SRC_WIN
];
429 else if (name
== "cmd")
431 if (tui_win_list
[CMD_WIN
] == nullptr)
432 tui_win_list
[CMD_WIN
] = new tui_cmd_window ();
433 return tui_win_list
[CMD_WIN
];
435 else if (name
== "regs")
437 if (tui_win_list
[DATA_WIN
] == nullptr)
438 tui_win_list
[DATA_WIN
] = new tui_data_window ();
439 return tui_win_list
[DATA_WIN
];
441 else if (name
== "asm")
443 if (tui_win_list
[DISASSEM_WIN
] == nullptr)
444 tui_win_list
[DISASSEM_WIN
] = new tui_disasm_window ();
445 return tui_win_list
[DISASSEM_WIN
];
449 gdb_assert (name
== "locator");
450 return tui_locator_win_info_ptr ();
454 /* See tui-layout.h. */
456 std::unique_ptr
<tui_layout_base
>
457 tui_layout_window::clone () const
459 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
460 return std::unique_ptr
<tui_layout_base
> (result
);
463 /* See tui-layout.h. */
466 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
472 gdb_assert (m_window
!= nullptr);
473 m_window
->resize (height
, width
, x
, y
);
476 /* See tui-layout.h. */
479 tui_layout_window::get_sizes (int *min_height
, int *max_height
)
481 if (m_window
== nullptr)
482 m_window
= tui_get_window_by_name (m_contents
);
483 *min_height
= m_window
->min_height ();
484 *max_height
= m_window
->max_height ();
487 /* See tui-layout.h. */
490 tui_layout_window::top_boxed_p () const
492 gdb_assert (m_window
!= nullptr);
493 return m_window
->can_box ();
496 /* See tui-layout.h. */
499 tui_layout_window::bottom_boxed_p () const
501 gdb_assert (m_window
!= nullptr);
502 return m_window
->can_box ();
505 /* See tui-layout.h. */
508 tui_layout_split::add_split (int weight
)
510 tui_layout_split
*result
= new tui_layout_split ();
511 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
512 m_splits
.push_back (std::move (s
));
516 /* See tui-layout.h. */
519 tui_layout_split::add_window (const char *name
, int weight
)
521 tui_layout_window
*result
= new tui_layout_window (name
);
522 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
523 m_splits
.push_back (std::move (s
));
526 /* See tui-layout.h. */
528 std::unique_ptr
<tui_layout_base
>
529 tui_layout_split::clone () const
531 tui_layout_split
*result
= new tui_layout_split ();
532 for (const split
&item
: m_splits
)
534 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
535 split s
= {item
.weight
, std::move (next
)};
536 result
->m_splits
.push_back (std::move (s
));
538 return std::unique_ptr
<tui_layout_base
> (result
);
541 /* See tui-layout.h. */
544 tui_layout_split::get_sizes (int *min_height
, int *max_height
)
548 for (const split
&item
: m_splits
)
550 int new_min
, new_max
;
551 item
.layout
->get_sizes (&new_min
, &new_max
);
552 *min_height
+= new_min
;
553 *max_height
+= new_max
;
557 /* See tui-layout.h. */
560 tui_layout_split::top_boxed_p () const
562 if (m_splits
.empty ())
564 return m_splits
[0].layout
->top_boxed_p ();
567 /* See tui-layout.h. */
570 tui_layout_split::bottom_boxed_p () const
572 if (m_splits
.empty ())
574 return m_splits
.back ().layout
->top_boxed_p ();
577 /* See tui-layout.h. */
580 tui_layout_split::set_weights_from_heights ()
582 for (int i
= 0; i
< m_splits
.size (); ++i
)
583 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
586 /* See tui-layout.h. */
589 tui_layout_split::adjust_size (const char *name
, int new_height
)
591 /* Look through the children. If one is a layout holding the named
592 window, we're done; or if one actually is the named window,
594 int found_index
= -1;
595 for (int i
= 0; i
< m_splits
.size (); ++i
)
597 if (m_splits
[i
].layout
->adjust_size (name
, new_height
))
599 const char *win_name
= m_splits
[i
].layout
->get_name ();
600 if (win_name
!= nullptr && strcmp (name
, win_name
) == 0)
607 if (found_index
== -1)
609 if (m_splits
[found_index
].layout
->height
== new_height
)
612 set_weights_from_heights ();
613 int delta
= m_splits
[found_index
].weight
- new_height
;
614 m_splits
[found_index
].weight
= new_height
;
616 /* Distribute the "delta" over the next window; but if the next
617 window cannot hold it all, keep going until we either find a
618 window that does, or until we loop all the way around. */
619 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
621 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
623 int new_min
, new_max
;
624 m_splits
[index
].layout
->get_sizes (&new_min
, &new_max
);
628 /* The primary window grew, so we are trying to shrink other
630 int available
= m_splits
[index
].weight
- new_min
;
631 int shrink_by
= std::min (available
, -delta
);
632 m_splits
[index
].weight
-= shrink_by
;
637 /* The primary window shrank, so we are trying to grow other
639 int available
= new_max
- m_splits
[index
].weight
;
640 int grow_by
= std::min (available
, delta
);
641 m_splits
[index
].weight
+= grow_by
;
648 warning (_("Invalid window height specified"));
649 /* Effectively undo any modifications made here. */
650 set_weights_from_heights ();
654 /* Simply re-apply the updated layout. */
655 apply (x
, y
, width
, height
);
661 /* See tui-layout.h. */
664 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
676 /* True if this window will share a box border with the previous
677 window in the list. */
681 std::vector
<height_info
> info (m_splits
.size ());
683 /* Step 1: Find the min and max height of each sub-layout.
684 Fixed-sized layouts are given their desired height, and then the
685 remaining space is distributed among the remaining windows
686 according to the weights given. */
687 int available_height
= height
;
689 int total_weight
= 0;
690 for (int i
= 0; i
< m_splits
.size (); ++i
)
692 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
694 /* Always call get_sizes, to ensure that the window is
695 instantiated. This is a bit gross but less gross than adding
696 special cases for this in other places. */
697 m_splits
[i
].layout
->get_sizes (&info
[i
].min_height
, &info
[i
].max_height
);
700 && cmd_win_already_exists
701 && m_splits
[i
].layout
->get_name () != nullptr
702 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
704 /* If this layout has never been applied, then it means the
705 user just changed the layout. In this situation, it's
706 desirable to keep the size of the command window the
707 same. Setting the min and max heights this way ensures
708 that the resizing step, below, does the right thing with
710 info
[i
].min_height
= TUI_CMD_WIN
->height
;
711 info
[i
].max_height
= TUI_CMD_WIN
->height
;
714 if (info
[i
].min_height
== info
[i
].max_height
)
715 available_height
-= info
[i
].min_height
;
719 total_weight
+= m_splits
[i
].weight
;
722 /* Two adjacent boxed windows will share a border, making a bit
723 more height available. */
725 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
726 && m_splits
[i
].layout
->top_boxed_p ())
727 info
[i
].share_box
= true;
730 /* Step 2: Compute the height of each sub-layout. Fixed-sized items
731 are given their fixed size, while others are resized according to
734 for (int i
= 0; i
< m_splits
.size (); ++i
)
736 /* Compute the height and clamp to the allowable range. */
737 info
[i
].height
= available_height
* m_splits
[i
].weight
/ total_weight
;
738 if (info
[i
].height
> info
[i
].max_height
)
739 info
[i
].height
= info
[i
].max_height
;
740 if (info
[i
].height
< info
[i
].min_height
)
741 info
[i
].height
= info
[i
].min_height
;
742 /* If there is any leftover height, just redistribute it to the
743 last resizeable window, by dropping it from the allocated
744 height. We could try to be fancier here perhaps, by
745 redistributing this height among all windows, not just the
747 if (info
[i
].min_height
!= info
[i
].max_height
)
749 used_height
+= info
[i
].height
;
750 if (info
[i
].share_box
)
755 /* Allocate any leftover height. */
756 if (available_height
>= used_height
&& last_index
!= -1)
757 info
[last_index
].height
+= available_height
- used_height
;
759 /* Step 3: Resize. */
760 int height_accum
= 0;
761 for (int i
= 0; i
< m_splits
.size (); ++i
)
763 /* If we fall off the bottom, just make allocations overlap.
765 if (height_accum
+ info
[i
].height
> height
)
766 height_accum
= height
- info
[i
].height
;
767 else if (info
[i
].share_box
)
769 m_splits
[i
].layout
->apply (x
, y
+ height_accum
, width
, info
[i
].height
);
770 height_accum
+= info
[i
].height
;
776 /* See tui-layout.h. */
779 tui_layout_split::remove_windows (const char *name
)
781 for (int i
= 0; i
< m_splits
.size (); ++i
)
783 const char *this_name
= m_splits
[i
].layout
->get_name ();
784 if (this_name
== nullptr)
785 m_splits
[i
].layout
->remove_windows (name
);
788 if (strcmp (this_name
, name
) == 0
789 || strcmp (this_name
, "cmd") == 0)
793 m_splits
.erase (m_splits
.begin () + i
);
800 initialize_layouts ()
802 standard_layouts
[SRC_COMMAND
] = new tui_layout_split ();
803 standard_layouts
[SRC_COMMAND
]->add_window ("src", 2);
804 standard_layouts
[SRC_COMMAND
]->add_window ("locator", 0);
805 standard_layouts
[SRC_COMMAND
]->add_window ("cmd", 1);
807 standard_layouts
[DISASSEM_COMMAND
] = new tui_layout_split ();
808 standard_layouts
[DISASSEM_COMMAND
]->add_window ("asm", 2);
809 standard_layouts
[DISASSEM_COMMAND
]->add_window ("locator", 0);
810 standard_layouts
[DISASSEM_COMMAND
]->add_window ("cmd", 1);
812 standard_layouts
[SRC_DATA_COMMAND
] = new tui_layout_split ();
813 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("regs", 1);
814 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("src", 1);
815 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("locator", 0);
816 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("cmd", 1);
818 standard_layouts
[DISASSEM_DATA_COMMAND
] = new tui_layout_split ();
819 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("regs", 1);
820 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("asm", 1);
821 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("locator", 0);
822 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("cmd", 1);
824 standard_layouts
[SRC_DISASSEM_COMMAND
] = new tui_layout_split ();
825 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("src", 1);
826 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("asm", 1);
827 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("locator", 0);
828 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("cmd", 1);
833 /* Function to initialize gdb commands, for tui window layout
836 void _initialize_tui_layout ();
838 _initialize_tui_layout ()
840 struct cmd_list_element
*cmd
;
842 cmd
= add_com ("layout", class_tui
, tui_layout_command
, _("\
843 Change the layout of windows.\n\
844 Usage: layout prev | next | LAYOUT-NAME\n\
846 src : Displays source and command windows.\n\
847 asm : Displays disassembly and command windows.\n\
848 split : Displays source, disassembly and command windows.\n\
849 regs : Displays register window. If existing layout\n\
850 is source/command or assembly/command, the \n\
851 register window is displayed. If the\n\
852 source/assembly/command (split) is displayed, \n\
853 the register window is displayed with \n\
854 the window that has current logical focus."));
855 set_cmd_completer (cmd
, layout_completer
);
857 initialize_layouts ();