1 /* TUI layout window management.
3 Copyright (C) 1998-2019 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 show_layout (new_layout
);
118 /* Now determine where focus should be. */
119 if (win_with_focus
!= TUI_CMD_WIN
)
124 tui_set_win_focus_to (TUI_SRC_WIN
);
126 case DISASSEM_COMMAND
:
127 /* The previous layout was not showing code.
128 This can happen if there is no source
131 1. if the source file is in another dir OR
132 2. if target was compiled without -g
133 We still want to show the assembly though! */
135 tui_get_begin_asm_address (&gdbarch
, &addr
);
136 tui_set_win_focus_to (TUI_DISASM_WIN
);
138 case SRC_DISASSEM_COMMAND
:
139 /* The previous layout was not showing code.
140 This can happen if there is no source
143 1. if the source file is in another dir OR
144 2. if target was compiled without -g
145 We still want to show the assembly though! */
147 tui_get_begin_asm_address (&gdbarch
, &addr
);
148 if (win_with_focus
== TUI_SRC_WIN
)
149 tui_set_win_focus_to (TUI_SRC_WIN
);
151 tui_set_win_focus_to (TUI_DISASM_WIN
);
153 case SRC_DATA_COMMAND
:
154 if (win_with_focus
!= TUI_DATA_WIN
)
155 tui_set_win_focus_to (TUI_SRC_WIN
);
157 tui_set_win_focus_to (TUI_DATA_WIN
);
159 case DISASSEM_DATA_COMMAND
:
160 /* The previous layout was not showing code.
161 This can happen if there is no source
164 1. if the source file is in another dir OR
165 2. if target was compiled without -g
166 We still want to show the assembly though! */
168 tui_get_begin_asm_address (&gdbarch
, &addr
);
169 if (win_with_focus
!= TUI_DATA_WIN
)
170 tui_set_win_focus_to (TUI_DISASM_WIN
);
172 tui_set_win_focus_to (TUI_DATA_WIN
);
179 * Now update the window content.
181 tui_update_source_windows_with_addr (gdbarch
, addr
);
182 if (new_layout
== SRC_DATA_COMMAND
183 || new_layout
== DISASSEM_DATA_COMMAND
)
184 TUI_DATA_WIN
->show_registers (TUI_DATA_WIN
->get_current_group ());
188 /* Add the specified window to the layout in a logical way. This
189 means setting up the most logical layout given the window to be
192 tui_add_win_to_layout (enum tui_win_type type
)
194 enum tui_layout_type cur_layout
= tui_current_layout ();
199 if (cur_layout
!= SRC_COMMAND
200 && cur_layout
!= SRC_DISASSEM_COMMAND
201 && cur_layout
!= SRC_DATA_COMMAND
)
203 if (cur_layout
== DISASSEM_DATA_COMMAND
)
204 show_layout (SRC_DATA_COMMAND
);
206 show_layout (SRC_COMMAND
);
210 if (cur_layout
!= DISASSEM_COMMAND
211 && cur_layout
!= SRC_DISASSEM_COMMAND
212 && cur_layout
!= DISASSEM_DATA_COMMAND
)
214 if (cur_layout
== SRC_DATA_COMMAND
)
215 show_layout (DISASSEM_DATA_COMMAND
);
217 show_layout (DISASSEM_COMMAND
);
221 if (cur_layout
!= SRC_DATA_COMMAND
222 && cur_layout
!= DISASSEM_DATA_COMMAND
)
224 if (cur_layout
== DISASSEM_COMMAND
)
225 show_layout (DISASSEM_DATA_COMMAND
);
227 show_layout (SRC_DATA_COMMAND
);
235 /* Complete possible layout names. TEXT is the complete text entered so
236 far, WORD is the word currently being completed. */
239 layout_completer (struct cmd_list_element
*ignore
,
240 completion_tracker
&tracker
,
241 const char *text
, const char *word
)
243 static const char *layout_names
[] =
244 { "src", "asm", "split", "regs", "next", "prev", NULL
};
246 complete_on_enum (tracker
, layout_names
, text
, word
);
249 /* Function to set the layout to SRC, ASM, SPLIT, NEXT, PREV, DATA, or
252 tui_layout_command (const char *layout_name
, int from_tty
)
254 enum tui_layout_type new_layout
= UNDEFINED_LAYOUT
;
255 enum tui_layout_type cur_layout
= tui_current_layout ();
257 if (layout_name
== NULL
|| *layout_name
== '\0')
258 error (_("Usage: layout prev | next | LAYOUT-NAME"));
260 /* First check for ambiguous input. */
261 if (strcmp (layout_name
, "s") == 0)
262 error (_("Ambiguous command input."));
264 if (subset_compare (layout_name
, "src"))
265 new_layout
= SRC_COMMAND
;
266 else if (subset_compare (layout_name
, "asm"))
267 new_layout
= DISASSEM_COMMAND
;
268 else if (subset_compare (layout_name
, "split"))
269 new_layout
= SRC_DISASSEM_COMMAND
;
270 else if (subset_compare (layout_name
, "regs"))
272 if (cur_layout
== SRC_COMMAND
273 || cur_layout
== SRC_DATA_COMMAND
)
274 new_layout
= SRC_DATA_COMMAND
;
276 new_layout
= DISASSEM_DATA_COMMAND
;
278 else if (subset_compare (layout_name
, "next"))
279 new_layout
= next_layout ();
280 else if (subset_compare (layout_name
, "prev"))
281 new_layout
= prev_layout ();
283 error (_("Unrecognized layout: %s"), layout_name
);
285 /* Make sure the curses mode is enabled. */
287 tui_set_layout (new_layout
);
292 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
294 enum tui_layout_type cur_layout
= tui_current_layout ();
295 struct gdbarch
*gdbarch
= get_current_arch ();
298 struct symtab_and_line cursal
= get_current_source_symtab_and_line ();
303 case SRC_DATA_COMMAND
:
304 gdbarch
= TUI_SRC_WIN
->gdbarch
;
305 find_line_pc (cursal
.symtab
,
306 TUI_SRC_WIN
->start_line_or_addr
.u
.line_no
,
310 case DISASSEM_COMMAND
:
311 case SRC_DISASSEM_COMMAND
:
312 case DISASSEM_DATA_COMMAND
:
313 gdbarch
= TUI_DISASM_WIN
->gdbarch
;
314 addr
= TUI_DISASM_WIN
->start_line_or_addr
.u
.addr
;
321 *gdbarch_p
= gdbarch
;
326 /* Answer the previous layout to cycle to. */
327 static enum tui_layout_type
332 new_layout
= tui_current_layout ();
333 if (new_layout
== UNDEFINED_LAYOUT
)
334 new_layout
= SRC_COMMAND
;
338 if (new_layout
== UNDEFINED_LAYOUT
)
339 new_layout
= SRC_COMMAND
;
342 return (enum tui_layout_type
) new_layout
;
346 /* Answer the next layout to cycle to. */
347 static enum tui_layout_type
352 new_layout
= tui_current_layout ();
353 if (new_layout
== SRC_COMMAND
)
354 new_layout
= DISASSEM_DATA_COMMAND
;
358 if (new_layout
== UNDEFINED_LAYOUT
)
359 new_layout
= DISASSEM_DATA_COMMAND
;
362 return (enum tui_layout_type
) new_layout
;
366 tui_gen_win_info::resize (int height_
, int width_
,
367 int origin_x_
, int origin_y_
)
369 if (width
== width_
&& height
== height_
370 && x
== origin_x_
&& y
== origin_y_
371 && handle
!= nullptr)
377 viewport_height
= height
- 2;
383 if (handle
!= nullptr)
386 wresize (handle
.get (), height
, width
);
387 mvwin (handle
.get (), y
, x
);
388 wmove (handle
.get (), 0, 0);
390 handle
.reset (nullptr);
394 if (handle
== nullptr)
402 /* Helper function that returns a TUI window, given its name. */
404 static tui_gen_win_info
*
405 tui_get_window_by_name (const std::string
&name
)
409 if (tui_win_list
[SRC_WIN
] == nullptr)
410 tui_win_list
[SRC_WIN
] = new tui_source_window ();
411 return tui_win_list
[SRC_WIN
];
413 else if (name
== "cmd")
415 if (tui_win_list
[CMD_WIN
] == nullptr)
416 tui_win_list
[CMD_WIN
] = new tui_cmd_window ();
417 return tui_win_list
[CMD_WIN
];
419 else if (name
== "regs")
421 if (tui_win_list
[DATA_WIN
] == nullptr)
422 tui_win_list
[DATA_WIN
] = new tui_data_window ();
423 return tui_win_list
[DATA_WIN
];
425 else if (name
== "asm")
427 if (tui_win_list
[DISASSEM_WIN
] == nullptr)
428 tui_win_list
[DISASSEM_WIN
] = new tui_disasm_window ();
429 return tui_win_list
[DISASSEM_WIN
];
433 gdb_assert (name
== "locator");
434 return tui_locator_win_info_ptr ();
438 /* See tui-layout.h. */
440 std::unique_ptr
<tui_layout_base
>
441 tui_layout_window::clone () const
443 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
444 return std::unique_ptr
<tui_layout_base
> (result
);
447 /* See tui-layout.h. */
450 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
456 gdb_assert (m_window
!= nullptr);
457 m_window
->resize (height
, width
, x
, y
);
460 /* See tui-layout.h. */
463 tui_layout_window::get_sizes (int *min_height
, int *max_height
)
465 if (m_window
== nullptr)
466 m_window
= tui_get_window_by_name (m_contents
);
467 *min_height
= m_window
->min_height ();
468 *max_height
= m_window
->max_height ();
471 /* See tui-layout.h. */
474 tui_layout_window::top_boxed_p () const
476 gdb_assert (m_window
!= nullptr);
477 return m_window
->can_box ();
480 /* See tui-layout.h. */
483 tui_layout_window::bottom_boxed_p () const
485 gdb_assert (m_window
!= nullptr);
486 return m_window
->can_box ();
489 /* See tui-layout.h. */
492 tui_layout_split::add_split (int weight
)
494 tui_layout_split
*result
= new tui_layout_split ();
495 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
496 m_splits
.push_back (std::move (s
));
500 /* See tui-layout.h. */
503 tui_layout_split::add_window (const char *name
, int weight
)
505 tui_layout_window
*result
= new tui_layout_window (name
);
506 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
507 m_splits
.push_back (std::move (s
));
510 /* See tui-layout.h. */
512 std::unique_ptr
<tui_layout_base
>
513 tui_layout_split::clone () const
515 tui_layout_split
*result
= new tui_layout_split ();
516 for (const split
&item
: m_splits
)
518 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
519 split s
= {item
.weight
, std::move (next
)};
520 result
->m_splits
.push_back (std::move (s
));
522 return std::unique_ptr
<tui_layout_base
> (result
);
525 /* See tui-layout.h. */
528 tui_layout_split::get_sizes (int *min_height
, int *max_height
)
532 for (const split
&item
: m_splits
)
534 int new_min
, new_max
;
535 item
.layout
->get_sizes (&new_min
, &new_max
);
536 *min_height
+= new_min
;
537 *max_height
+= new_max
;
541 /* See tui-layout.h. */
544 tui_layout_split::top_boxed_p () const
546 if (m_splits
.empty ())
548 return m_splits
[0].layout
->top_boxed_p ();
551 /* See tui-layout.h. */
554 tui_layout_split::bottom_boxed_p () const
556 if (m_splits
.empty ())
558 return m_splits
.back ().layout
->top_boxed_p ();
561 /* See tui-layout.h. */
564 tui_layout_split::set_weights_from_heights ()
566 for (int i
= 0; i
< m_splits
.size (); ++i
)
567 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
570 /* See tui-layout.h. */
573 tui_layout_split::adjust_size (const char *name
, int new_height
)
575 /* Look through the children. If one is a layout holding the named
576 window, we're done; or if one actually is the named window,
578 int found_index
= -1;
579 for (int i
= 0; i
< m_splits
.size (); ++i
)
581 if (m_splits
[i
].layout
->adjust_size (name
, new_height
))
583 const char *win_name
= m_splits
[i
].layout
->get_name ();
584 if (win_name
!= nullptr && strcmp (name
, win_name
) == 0)
591 if (found_index
== -1)
593 if (m_splits
[found_index
].layout
->height
== new_height
)
596 set_weights_from_heights ();
597 int delta
= m_splits
[found_index
].weight
- new_height
;
598 m_splits
[found_index
].weight
= new_height
;
600 /* Distribute the "delta" over the next window; but if the next
601 window cannot hold it all, keep going until we either find a
602 window that does, or until we loop all the way around. */
603 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
605 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
607 int new_min
, new_max
;
608 m_splits
[index
].layout
->get_sizes (&new_min
, &new_max
);
612 /* The primary window grew, so we are trying to shrink other
614 int available
= m_splits
[index
].weight
- new_min
;
615 int shrink_by
= std::min (available
, -delta
);
616 m_splits
[index
].weight
-= shrink_by
;
621 /* The primary window shrank, so we are trying to grow other
623 int available
= new_max
- m_splits
[index
].weight
;
624 int grow_by
= std::min (available
, delta
);
625 m_splits
[index
].weight
+= grow_by
;
632 warning (_("Invalid window height specified"));
633 /* Effectively undo any modifications made here. */
634 set_weights_from_heights ();
638 /* Simply re-apply the updated layout. */
639 apply (x
, y
, width
, height
);
645 /* See tui-layout.h. */
648 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
660 /* True if this window will share a box border with the previous
661 window in the list. */
665 std::vector
<height_info
> info (m_splits
.size ());
667 /* Step 1: Find the min and max height of each sub-layout.
668 Fixed-sized layouts are given their desired height, and then the
669 remaining space is distributed among the remaining windows
670 according to the weights given. */
671 int available_height
= height
;
673 int total_weight
= 0;
674 for (int i
= 0; i
< m_splits
.size (); ++i
)
676 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
678 /* Always call get_sizes, to ensure that the window is
679 instantiated. This is a bit gross but less gross than adding
680 special cases for this in other places. */
681 m_splits
[i
].layout
->get_sizes (&info
[i
].min_height
, &info
[i
].max_height
);
684 && cmd_win_already_exists
685 && m_splits
[i
].layout
->get_name () != nullptr
686 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
688 /* If this layout has never been applied, then it means the
689 user just changed the layout. In this situation, it's
690 desirable to keep the size of the command window the
691 same. Setting the min and max heights this way ensures
692 that the resizing step, below, does the right thing with
694 info
[i
].min_height
= TUI_CMD_WIN
->height
;
695 info
[i
].max_height
= TUI_CMD_WIN
->height
;
698 if (info
[i
].min_height
== info
[i
].max_height
)
699 available_height
-= info
[i
].min_height
;
703 total_weight
+= m_splits
[i
].weight
;
706 /* Two adjacent boxed windows will share a border, making a bit
707 more height available. */
709 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
710 && m_splits
[i
].layout
->top_boxed_p ())
711 info
[i
].share_box
= true;
714 /* Step 2: Compute the height of each sub-layout. Fixed-sized items
715 are given their fixed size, while others are resized according to
718 for (int i
= 0; i
< m_splits
.size (); ++i
)
720 /* Compute the height and clamp to the allowable range. */
721 info
[i
].height
= available_height
* m_splits
[i
].weight
/ total_weight
;
722 if (info
[i
].height
> info
[i
].max_height
)
723 info
[i
].height
= info
[i
].max_height
;
724 if (info
[i
].height
< info
[i
].min_height
)
725 info
[i
].height
= info
[i
].min_height
;
726 /* If there is any leftover height, just redistribute it to the
727 last resizeable window, by dropping it from the allocated
728 height. We could try to be fancier here perhaps, by
729 redistributing this height among all windows, not just the
731 if (info
[i
].min_height
!= info
[i
].max_height
)
733 used_height
+= info
[i
].height
;
734 if (info
[i
].share_box
)
739 /* Allocate any leftover height. */
740 if (available_height
>= used_height
&& last_index
!= -1)
741 info
[last_index
].height
+= available_height
- used_height
;
743 /* Step 3: Resize. */
744 int height_accum
= 0;
745 for (int i
= 0; i
< m_splits
.size (); ++i
)
747 /* If we fall off the bottom, just make allocations overlap.
749 if (height_accum
+ info
[i
].height
> height
)
750 height_accum
= height
- info
[i
].height
;
751 else if (info
[i
].share_box
)
753 m_splits
[i
].layout
->apply (x
, y
+ height_accum
, width
, info
[i
].height
);
754 height_accum
+= info
[i
].height
;
761 initialize_layouts ()
763 standard_layouts
[SRC_COMMAND
] = new tui_layout_split ();
764 standard_layouts
[SRC_COMMAND
]->add_window ("src", 2);
765 standard_layouts
[SRC_COMMAND
]->add_window ("locator", 0);
766 standard_layouts
[SRC_COMMAND
]->add_window ("cmd", 1);
768 standard_layouts
[DISASSEM_COMMAND
] = new tui_layout_split ();
769 standard_layouts
[DISASSEM_COMMAND
]->add_window ("asm", 2);
770 standard_layouts
[DISASSEM_COMMAND
]->add_window ("locator", 0);
771 standard_layouts
[DISASSEM_COMMAND
]->add_window ("cmd", 1);
773 standard_layouts
[SRC_DATA_COMMAND
] = new tui_layout_split ();
774 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("regs", 1);
775 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("src", 1);
776 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("locator", 0);
777 standard_layouts
[SRC_DATA_COMMAND
]->add_window ("cmd", 1);
779 standard_layouts
[DISASSEM_DATA_COMMAND
] = new tui_layout_split ();
780 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("regs", 1);
781 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("asm", 1);
782 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("locator", 0);
783 standard_layouts
[DISASSEM_DATA_COMMAND
]->add_window ("cmd", 1);
785 standard_layouts
[SRC_DISASSEM_COMMAND
] = new tui_layout_split ();
786 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("src", 1);
787 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("asm", 1);
788 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("locator", 0);
789 standard_layouts
[SRC_DISASSEM_COMMAND
]->add_window ("cmd", 1);
794 /* Function to initialize gdb commands, for tui window layout
798 _initialize_tui_layout (void)
800 struct cmd_list_element
*cmd
;
802 cmd
= add_com ("layout", class_tui
, tui_layout_command
, _("\
803 Change the layout of windows.\n\
804 Usage: layout prev | next | LAYOUT-NAME\n\
806 src : Displays source and command windows.\n\
807 asm : Displays disassembly and command windows.\n\
808 split : Displays source, disassembly and command windows.\n\
809 regs : Displays register window. If existing layout\n\
810 is source/command or assembly/command, the \n\
811 register window is displayed. If the\n\
812 source/assembly/command (split) is displayed, \n\
813 the register window is displayed with \n\
814 the window that has current logical focus."));
815 set_cmd_completer (cmd
, layout_completer
);
817 initialize_layouts ();