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);
290 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
292 enum tui_layout_type cur_layout
= tui_current_layout ();
293 struct gdbarch
*gdbarch
= get_current_arch ();
296 struct symtab_and_line cursal
= get_current_source_symtab_and_line ();
301 case SRC_DATA_COMMAND
:
302 gdbarch
= TUI_SRC_WIN
->gdbarch
;
303 find_line_pc (cursal
.symtab
,
304 TUI_SRC_WIN
->start_line_or_addr
.u
.line_no
,
308 case DISASSEM_COMMAND
:
309 case SRC_DISASSEM_COMMAND
:
310 case DISASSEM_DATA_COMMAND
:
311 gdbarch
= TUI_DISASM_WIN
->gdbarch
;
312 addr
= TUI_DISASM_WIN
->start_line_or_addr
.u
.addr
;
319 *gdbarch_p
= gdbarch
;
324 /* Answer the previous layout to cycle to. */
325 static enum tui_layout_type
330 new_layout
= tui_current_layout ();
331 if (new_layout
== UNDEFINED_LAYOUT
)
332 new_layout
= SRC_COMMAND
;
336 if (new_layout
== UNDEFINED_LAYOUT
)
337 new_layout
= SRC_COMMAND
;
340 return (enum tui_layout_type
) new_layout
;
344 /* Answer the next layout to cycle to. */
345 static enum tui_layout_type
350 new_layout
= tui_current_layout ();
351 if (new_layout
== SRC_COMMAND
)
352 new_layout
= DISASSEM_DATA_COMMAND
;
356 if (new_layout
== UNDEFINED_LAYOUT
)
357 new_layout
= DISASSEM_DATA_COMMAND
;
360 return (enum tui_layout_type
) new_layout
;
364 tui_gen_win_info::resize (int height_
, int width_
,
365 int origin_x_
, int origin_y_
)
367 if (width
== width_
&& height
== height_
368 && x
== origin_x_
&& y
== origin_y_
369 && handle
!= nullptr)
377 if (handle
!= nullptr)
380 wresize (handle
.get (), height
, width
);
381 mvwin (handle
.get (), y
, x
);
382 wmove (handle
.get (), 0, 0);
384 handle
.reset (nullptr);
388 if (handle
== nullptr)
396 /* Helper function that returns a TUI window, given its name. */
398 static tui_gen_win_info
*
399 tui_get_window_by_name (const std::string
&name
)
403 if (tui_win_list
[SRC_WIN
] == nullptr)
404 tui_win_list
[SRC_WIN
] = new tui_source_window ();
405 return tui_win_list
[SRC_WIN
];
407 else if (name
== "cmd")
409 if (tui_win_list
[CMD_WIN
] == nullptr)
410 tui_win_list
[CMD_WIN
] = new tui_cmd_window ();
411 return tui_win_list
[CMD_WIN
];
413 else if (name
== "regs")
415 if (tui_win_list
[DATA_WIN
] == nullptr)
416 tui_win_list
[DATA_WIN
] = new tui_data_window ();
417 return tui_win_list
[DATA_WIN
];
419 else if (name
== "asm")
421 if (tui_win_list
[DISASSEM_WIN
] == nullptr)
422 tui_win_list
[DISASSEM_WIN
] = new tui_disasm_window ();
423 return tui_win_list
[DISASSEM_WIN
];
427 gdb_assert (name
== "locator");
428 return tui_locator_win_info_ptr ();
432 /* See tui-layout.h. */
434 std::unique_ptr
<tui_layout_base
>
435 tui_layout_window::clone () const
437 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
438 return std::unique_ptr
<tui_layout_base
> (result
);
441 /* See tui-layout.h. */
444 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
450 gdb_assert (m_window
!= nullptr);
451 m_window
->resize (height
, width
, x
, y
);
454 /* See tui-layout.h. */
457 tui_layout_window::get_sizes (int *min_height
, int *max_height
)
459 if (m_window
== nullptr)
460 m_window
= tui_get_window_by_name (m_contents
);
461 *min_height
= m_window
->min_height ();
462 *max_height
= m_window
->max_height ();
465 /* See tui-layout.h. */
468 tui_layout_window::top_boxed_p () const
470 gdb_assert (m_window
!= nullptr);
471 return m_window
->can_box ();
474 /* See tui-layout.h. */
477 tui_layout_window::bottom_boxed_p () const
479 gdb_assert (m_window
!= nullptr);
480 return m_window
->can_box ();
483 /* See tui-layout.h. */
486 tui_layout_split::add_split (int weight
)
488 tui_layout_split
*result
= new tui_layout_split ();
489 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
490 m_splits
.push_back (std::move (s
));
494 /* See tui-layout.h. */
497 tui_layout_split::add_window (const char *name
, int weight
)
499 tui_layout_window
*result
= new tui_layout_window (name
);
500 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
501 m_splits
.push_back (std::move (s
));
504 /* See tui-layout.h. */
506 std::unique_ptr
<tui_layout_base
>
507 tui_layout_split::clone () const
509 tui_layout_split
*result
= new tui_layout_split ();
510 for (const split
&item
: m_splits
)
512 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
513 split s
= {item
.weight
, std::move (next
)};
514 result
->m_splits
.push_back (std::move (s
));
516 return std::unique_ptr
<tui_layout_base
> (result
);
519 /* See tui-layout.h. */
522 tui_layout_split::get_sizes (int *min_height
, int *max_height
)
526 for (const split
&item
: m_splits
)
528 int new_min
, new_max
;
529 item
.layout
->get_sizes (&new_min
, &new_max
);
530 *min_height
+= new_min
;
531 *max_height
+= new_max
;
535 /* See tui-layout.h. */
538 tui_layout_split::top_boxed_p () const
540 if (m_splits
.empty ())
542 return m_splits
[0].layout
->top_boxed_p ();
545 /* See tui-layout.h. */
548 tui_layout_split::bottom_boxed_p () const
550 if (m_splits
.empty ())
552 return m_splits
.back ().layout
->top_boxed_p ();
555 /* See tui-layout.h. */
558 tui_layout_split::set_weights_from_heights ()
560 for (int i
= 0; i
< m_splits
.size (); ++i
)
561 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
564 /* See tui-layout.h. */
567 tui_layout_split::adjust_size (const char *name
, int new_height
)
569 /* Look through the children. If one is a layout holding the named
570 window, we're done; or if one actually is the named window,
572 int found_index
= -1;
573 for (int i
= 0; i
< m_splits
.size (); ++i
)
575 if (m_splits
[i
].layout
->adjust_size (name
, new_height
))
577 const char *win_name
= m_splits
[i
].layout
->get_name ();
578 if (win_name
!= nullptr && strcmp (name
, win_name
) == 0)
585 if (found_index
== -1)
587 if (m_splits
[found_index
].layout
->height
== new_height
)
590 set_weights_from_heights ();
591 int delta
= m_splits
[found_index
].weight
- new_height
;
592 m_splits
[found_index
].weight
= new_height
;
594 /* Distribute the "delta" over the next window; but if the next
595 window cannot hold it all, keep going until we either find a
596 window that does, or until we loop all the way around. */
597 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
599 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
601 int new_min
, new_max
;
602 m_splits
[index
].layout
->get_sizes (&new_min
, &new_max
);
606 /* The primary window grew, so we are trying to shrink other
608 int available
= m_splits
[index
].weight
- new_min
;
609 int shrink_by
= std::min (available
, -delta
);
610 m_splits
[index
].weight
-= shrink_by
;
615 /* The primary window shrank, so we are trying to grow other
617 int available
= new_max
- m_splits
[index
].weight
;
618 int grow_by
= std::min (available
, delta
);
619 m_splits
[index
].weight
+= grow_by
;
626 warning (_("Invalid window height specified"));
627 /* Effectively undo any modifications made here. */
628 set_weights_from_heights ();
632 /* Simply re-apply the updated layout. */
633 apply (x
, y
, width
, height
);
639 /* See tui-layout.h. */
642 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
654 /* True if this window will share a box border with the previous
655 window in the list. */
659 std::vector
<height_info
> info (m_splits
.size ());
661 /* Step 1: Find the min and max height of each sub-layout.
662 Fixed-sized layouts are given their desired height, and then the
663 remaining space is distributed among the remaining windows
664 according to the weights given. */
665 int available_height
= height
;
667 int total_weight
= 0;
668 for (int i
= 0; i
< m_splits
.size (); ++i
)
670 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
672 /* Always call get_sizes, to ensure that the window is
673 instantiated. This is a bit gross but less gross than adding
674 special cases for this in other places. */
675 m_splits
[i
].layout
->get_sizes (&info
[i
].min_height
, &info
[i
].max_height
);
678 && cmd_win_already_exists
679 && m_splits
[i
].layout
->get_name () != nullptr
680 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
682 /* If this layout has never been applied, then it means the
683 user just changed the layout. In this situation, it's
684 desirable to keep the size of the command window the
685 same. Setting the min and max heights this way ensures
686 that the resizing step, below, does the right thing with
688 info
[i
].min_height
= TUI_CMD_WIN
->height
;
689 info
[i
].max_height
= TUI_CMD_WIN
->height
;
692 if (info
[i
].min_height
== info
[i
].max_height
)
693 available_height
-= info
[i
].min_height
;
697 total_weight
+= m_splits
[i
].weight
;
700 /* Two adjacent boxed windows will share a border, making a bit
701 more height available. */
703 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
704 && m_splits
[i
].layout
->top_boxed_p ())
705 info
[i
].share_box
= true;
708 /* Step 2: Compute the height of each sub-layout. Fixed-sized items
709 are given their fixed size, while others are resized according to
712 for (int i
= 0; i
< m_splits
.size (); ++i
)
714 /* Compute the height and clamp to the allowable range. */
715 info
[i
].height
= available_height
* m_splits
[i
].weight
/ total_weight
;
716 if (info
[i
].height
> info
[i
].max_height
)
717 info
[i
].height
= info
[i
].max_height
;
718 if (info
[i
].height
< info
[i
].min_height
)
719 info
[i
].height
= info
[i
].min_height
;
720 /* If there is any leftover height, just redistribute it to the
721 last resizeable window, by dropping it from the allocated
722 height. We could try to be fancier here perhaps, by
723 redistributing this height among all windows, not just the
725 if (info
[i
].min_height
!= info
[i
].max_height
)
727 used_height
+= info
[i
].height
;
728 if (info
[i
].share_box
)
733 /* Allocate any leftover height. */
734 if (available_height
>= used_height
&& last_index
!= -1)
735 info
[last_index
].height
+= available_height
- used_height
;
737 /* Step 3: Resize. */
738 int height_accum
= 0;
739 for (int i
= 0; i
< m_splits
.size (); ++i
)
741 /* If we fall off the bottom, just make allocations overlap.
743 if (height_accum
+ info
[i
].height
> height
)
744 height_accum
= height
- info
[i
].height
;
745 else if (info
[i
].share_box
)
747 m_splits
[i
].layout
->apply (x
, y
+ height_accum
, width
, info
[i
].height
);
748 height_accum
+= info
[i
].height
;
755 initialize_layouts ()
757 standard_layouts
[SRC_COMMAND
] = new tui_layout_split ();
758 standard_layouts
[SRC_COMMAND
]->add_window ("src", 2);
759 standard_layouts
[SRC_COMMAND
]->add_window ("locator", 0);
760 standard_layouts
[SRC_COMMAND
]->add_window ("cmd", 1);
762 standard_layouts
[DISASSEM_COMMAND
] = new tui_layout_split ();
763 standard_layouts
[DISASSEM_COMMAND
]->add_window ("asm", 2);
764 standard_layouts
[DISASSEM_COMMAND
]->add_window ("locator", 0);
765 standard_layouts
[DISASSEM_COMMAND
]->add_window ("cmd", 1);
767 standard_layouts
[SRC_DATA_COMMAND
] = new tui_layout_split ();
768 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("regs", 1);
769 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("src", 1);
770 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("locator", 0);
771 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("cmd", 1);
773 standard_layouts
[DISASSEM_DATA_COMMAND
] = new tui_layout_split ();
774 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("regs", 1);
775 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("asm", 1);
776 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("locator", 0);
777 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("cmd", 1);
779 standard_layouts
[SRC_DISASSEM_COMMAND
] = new tui_layout_split ();
780 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("src", 1);
781 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("asm", 1);
782 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("locator", 0);
783 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("cmd", 1);
788 /* Function to initialize gdb commands, for tui window layout
791 void _initialize_tui_layout ();
793 _initialize_tui_layout ()
795 struct cmd_list_element
*cmd
;
797 cmd
= add_com ("layout", class_tui
, tui_layout_command
, _("\
798 Change the layout of windows.\n\
799 Usage: layout prev | next | LAYOUT-NAME\n\
801 src : Displays source and command windows.\n\
802 asm : Displays disassembly and command windows.\n\
803 split : Displays source, disassembly and command windows.\n\
804 regs : Displays register window. If existing layout\n\
805 is source/command or assembly/command, the \n\
806 register window is displayed. If the\n\
807 source/assembly/command (split) is displayed, \n\
808 the register window is displayed with \n\
809 the window that has current logical focus."));
810 set_cmd_completer (cmd
, layout_completer
);
812 initialize_layouts ();