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"
28 #include "cli/cli-cmds.h"
29 #include "cli/cli-decode.h"
33 #include "tui/tui-command.h"
34 #include "tui/tui-data.h"
35 #include "tui/tui-wingeneral.h"
36 #include "tui/tui-stack.h"
37 #include "tui/tui-regs.h"
38 #include "tui/tui-win.h"
39 #include "tui/tui-winsource.h"
40 #include "tui/tui-disasm.h"
41 #include "tui/tui-layout.h"
42 #include "tui/tui-source.h"
43 #include "gdb_curses.h"
45 static void tui_layout_command (const char *, int);
46 static void extract_display_start_addr (struct gdbarch
**, CORE_ADDR
*);
49 static std::vector
<std::unique_ptr
<tui_layout_split
>> layouts
;
51 /* The layout that is currently applied. */
52 static std::unique_ptr
<tui_layout_base
> applied_layout
;
54 /* The "skeleton" version of the layout that is currently applied. */
55 static tui_layout_split
*applied_skeleton
;
57 /* The two special "regs" layouts. Note that these aren't registered
58 as commands and so can never be deleted. */
59 static tui_layout_split
*src_regs_layout
;
60 static tui_layout_split
*asm_regs_layout
;
62 /* See tui-layout.h. */
65 tui_apply_current_layout ()
67 applied_layout
->apply (0, 0, tui_term_width (), tui_term_height ());
73 tui_adjust_window_height (struct tui_win_info
*win
, int new_height
)
75 applied_layout
->adjust_size (win
->name (), new_height
);
78 /* Set the current layout to LAYOUT. */
81 tui_set_layout (tui_layout_split
*layout
)
83 struct gdbarch
*gdbarch
;
86 extract_display_start_addr (&gdbarch
, &addr
);
87 tui_make_all_invisible ();
88 applied_skeleton
= layout
;
89 applied_layout
= layout
->clone ();
90 tui_apply_current_layout ();
91 tui_delete_invisible_windows ();
93 if (gdbarch
== nullptr && TUI_DISASM_WIN
!= nullptr)
94 tui_get_begin_asm_address (&gdbarch
, &addr
);
95 tui_update_source_windows_with_addr (gdbarch
, addr
);
98 /* See tui-layout.h. */
101 tui_add_win_to_layout (enum tui_win_type type
)
103 gdb_assert (type
== SRC_WIN
|| type
== DISASSEM_WIN
);
105 /* If the window already exists, no need to add it. */
106 if (tui_win_list
[type
] != nullptr)
109 /* If the window we are trying to replace doesn't exist, we're
111 enum tui_win_type other
= type
== SRC_WIN
? DISASSEM_WIN
: SRC_WIN
;
112 if (tui_win_list
[other
] == nullptr)
115 const char *name
= type
== SRC_WIN
? SRC_NAME
: DISASSEM_NAME
;
116 applied_layout
->replace_window (tui_win_list
[other
]->name (), name
);
117 tui_apply_current_layout ();
118 tui_delete_invisible_windows ();
121 /* Find LAYOUT in the "layouts" global and return its index. */
124 find_layout (tui_layout_split
*layout
)
126 for (size_t i
= 0; i
< layouts
.size (); ++i
)
128 if (layout
== layouts
[i
].get ())
131 gdb_assert_not_reached (_("layout not found!?"));
134 /* Function to set the layout. */
137 tui_apply_layout (struct cmd_list_element
*command
,
138 const char *args
, int from_tty
)
140 tui_layout_split
*layout
141 = (tui_layout_split
*) get_cmd_context (command
);
143 /* Make sure the curses mode is enabled. */
145 tui_set_layout (layout
);
148 /* See tui-layout.h. */
153 size_t index
= find_layout (applied_skeleton
);
155 if (index
== layouts
.size ())
157 tui_set_layout (layouts
[index
].get ());
160 /* Implement the "layout next" command. */
163 tui_next_layout_command (const char *arg
, int from_tty
)
169 /* See tui-layout.h. */
172 tui_set_initial_layout ()
174 tui_set_layout (layouts
[0].get ());
177 /* Implement the "layout prev" command. */
180 tui_prev_layout_command (const char *arg
, int from_tty
)
183 size_t index
= find_layout (applied_skeleton
);
185 index
= layouts
.size ();
187 tui_set_layout (layouts
[index
].get ());
191 /* See tui-layout.h. */
196 /* If there's already a register window, we're done. */
197 if (TUI_DATA_WIN
!= nullptr)
200 tui_set_layout (TUI_DISASM_WIN
!= nullptr
205 /* Implement the "layout regs" command. */
208 tui_regs_layout_command (const char *arg
, int from_tty
)
214 /* See tui-layout.h. */
217 tui_remove_some_windows ()
219 tui_win_info
*focus
= tui_win_with_focus ();
221 if (strcmp (focus
->name (), "cmd") == 0)
223 /* Try leaving the source or disassembly window. If neither
224 exists, just do nothing. */
226 if (focus
== nullptr)
227 focus
= TUI_DISASM_WIN
;
228 if (focus
== nullptr)
232 applied_layout
->remove_windows (focus
->name ());
233 tui_apply_current_layout ();
237 extract_display_start_addr (struct gdbarch
**gdbarch_p
, CORE_ADDR
*addr_p
)
239 struct gdbarch
*gdbarch
= nullptr;
242 struct symtab_and_line cursal
= get_current_source_symtab_and_line ();
244 if (TUI_SRC_WIN
!= nullptr)
246 gdbarch
= TUI_SRC_WIN
->gdbarch
;
247 find_line_pc (cursal
.symtab
,
248 TUI_SRC_WIN
->start_line_or_addr
.u
.line_no
,
252 else if (TUI_DISASM_WIN
!= nullptr)
254 gdbarch
= TUI_DISASM_WIN
->gdbarch
;
255 addr
= TUI_DISASM_WIN
->start_line_or_addr
.u
.addr
;
258 *gdbarch_p
= gdbarch
;
263 tui_gen_win_info::resize (int height_
, int width_
,
264 int origin_x_
, int origin_y_
)
266 if (width
== width_
&& height
== height_
267 && x
== origin_x_
&& y
== origin_y_
268 && handle
!= nullptr)
276 if (handle
!= nullptr)
279 wresize (handle
.get (), height
, width
);
280 mvwin (handle
.get (), y
, x
);
281 wmove (handle
.get (), 0, 0);
283 handle
.reset (nullptr);
287 if (handle
== nullptr)
295 /* Helper function that returns a TUI window, given its name. */
297 static tui_gen_win_info
*
298 tui_get_window_by_name (const std::string
&name
)
302 if (tui_win_list
[SRC_WIN
] == nullptr)
303 tui_win_list
[SRC_WIN
] = new tui_source_window ();
304 return tui_win_list
[SRC_WIN
];
306 else if (name
== "cmd")
308 if (tui_win_list
[CMD_WIN
] == nullptr)
309 tui_win_list
[CMD_WIN
] = new tui_cmd_window ();
310 return tui_win_list
[CMD_WIN
];
312 else if (name
== "regs")
314 if (tui_win_list
[DATA_WIN
] == nullptr)
315 tui_win_list
[DATA_WIN
] = new tui_data_window ();
316 return tui_win_list
[DATA_WIN
];
318 else if (name
== "asm")
320 if (tui_win_list
[DISASSEM_WIN
] == nullptr)
321 tui_win_list
[DISASSEM_WIN
] = new tui_disasm_window ();
322 return tui_win_list
[DISASSEM_WIN
];
326 gdb_assert (name
== "status");
327 return tui_locator_win_info_ptr ();
331 /* See tui-layout.h. */
333 std::unique_ptr
<tui_layout_base
>
334 tui_layout_window::clone () const
336 tui_layout_window
*result
= new tui_layout_window (m_contents
.c_str ());
337 return std::unique_ptr
<tui_layout_base
> (result
);
340 /* See tui-layout.h. */
343 tui_layout_window::apply (int x_
, int y_
, int width_
, int height_
)
349 gdb_assert (m_window
!= nullptr);
350 m_window
->resize (height
, width
, x
, y
);
353 /* See tui-layout.h. */
356 tui_layout_window::get_sizes (int *min_height
, int *max_height
)
358 if (m_window
== nullptr)
359 m_window
= tui_get_window_by_name (m_contents
);
360 *min_height
= m_window
->min_height ();
361 *max_height
= m_window
->max_height ();
364 /* See tui-layout.h. */
367 tui_layout_window::top_boxed_p () const
369 gdb_assert (m_window
!= nullptr);
370 return m_window
->can_box ();
373 /* See tui-layout.h. */
376 tui_layout_window::bottom_boxed_p () const
378 gdb_assert (m_window
!= nullptr);
379 return m_window
->can_box ();
382 /* See tui-layout.h. */
385 tui_layout_window::replace_window (const char *name
, const char *new_window
)
387 if (m_contents
== name
)
389 m_contents
= new_window
;
390 if (m_window
!= nullptr)
392 m_window
->make_visible (false);
393 m_window
= tui_get_window_by_name (m_contents
);
398 /* See tui-layout.h. */
401 tui_layout_split::add_split (int weight
)
403 tui_layout_split
*result
= new tui_layout_split ();
404 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
405 m_splits
.push_back (std::move (s
));
409 /* See tui-layout.h. */
412 tui_layout_split::add_window (const char *name
, int weight
)
414 tui_layout_window
*result
= new tui_layout_window (name
);
415 split s
= {weight
, std::unique_ptr
<tui_layout_base
> (result
)};
416 m_splits
.push_back (std::move (s
));
419 /* See tui-layout.h. */
421 std::unique_ptr
<tui_layout_base
>
422 tui_layout_split::clone () const
424 tui_layout_split
*result
= new tui_layout_split ();
425 for (const split
&item
: m_splits
)
427 std::unique_ptr
<tui_layout_base
> next
= item
.layout
->clone ();
428 split s
= {item
.weight
, std::move (next
)};
429 result
->m_splits
.push_back (std::move (s
));
431 return std::unique_ptr
<tui_layout_base
> (result
);
434 /* See tui-layout.h. */
437 tui_layout_split::get_sizes (int *min_height
, int *max_height
)
441 for (const split
&item
: m_splits
)
443 int new_min
, new_max
;
444 item
.layout
->get_sizes (&new_min
, &new_max
);
445 *min_height
+= new_min
;
446 *max_height
+= new_max
;
450 /* See tui-layout.h. */
453 tui_layout_split::top_boxed_p () const
455 if (m_splits
.empty ())
457 return m_splits
[0].layout
->top_boxed_p ();
460 /* See tui-layout.h. */
463 tui_layout_split::bottom_boxed_p () const
465 if (m_splits
.empty ())
467 return m_splits
.back ().layout
->top_boxed_p ();
470 /* See tui-layout.h. */
473 tui_layout_split::set_weights_from_heights ()
475 for (int i
= 0; i
< m_splits
.size (); ++i
)
476 m_splits
[i
].weight
= m_splits
[i
].layout
->height
;
479 /* See tui-layout.h. */
482 tui_layout_split::adjust_size (const char *name
, int new_height
)
484 /* Look through the children. If one is a layout holding the named
485 window, we're done; or if one actually is the named window,
487 int found_index
= -1;
488 for (int i
= 0; i
< m_splits
.size (); ++i
)
490 if (m_splits
[i
].layout
->adjust_size (name
, new_height
))
492 const char *win_name
= m_splits
[i
].layout
->get_name ();
493 if (win_name
!= nullptr && strcmp (name
, win_name
) == 0)
500 if (found_index
== -1)
502 if (m_splits
[found_index
].layout
->height
== new_height
)
505 set_weights_from_heights ();
506 int delta
= m_splits
[found_index
].weight
- new_height
;
507 m_splits
[found_index
].weight
= new_height
;
509 /* Distribute the "delta" over the next window; but if the next
510 window cannot hold it all, keep going until we either find a
511 window that does, or until we loop all the way around. */
512 for (int i
= 0; delta
!= 0 && i
< m_splits
.size () - 1; ++i
)
514 int index
= (found_index
+ 1 + i
) % m_splits
.size ();
516 int new_min
, new_max
;
517 m_splits
[index
].layout
->get_sizes (&new_min
, &new_max
);
521 /* The primary window grew, so we are trying to shrink other
523 int available
= m_splits
[index
].weight
- new_min
;
524 int shrink_by
= std::min (available
, -delta
);
525 m_splits
[index
].weight
-= shrink_by
;
530 /* The primary window shrank, so we are trying to grow other
532 int available
= new_max
- m_splits
[index
].weight
;
533 int grow_by
= std::min (available
, delta
);
534 m_splits
[index
].weight
+= grow_by
;
541 warning (_("Invalid window height specified"));
542 /* Effectively undo any modifications made here. */
543 set_weights_from_heights ();
547 /* Simply re-apply the updated layout. */
548 apply (x
, y
, width
, height
);
554 /* See tui-layout.h. */
557 tui_layout_split::apply (int x_
, int y_
, int width_
, int height_
)
569 /* True if this window will share a box border with the previous
570 window in the list. */
574 std::vector
<height_info
> info (m_splits
.size ());
576 /* Step 1: Find the min and max height of each sub-layout.
577 Fixed-sized layouts are given their desired height, and then the
578 remaining space is distributed among the remaining windows
579 according to the weights given. */
580 int available_height
= height
;
582 int total_weight
= 0;
583 for (int i
= 0; i
< m_splits
.size (); ++i
)
585 bool cmd_win_already_exists
= TUI_CMD_WIN
!= nullptr;
587 /* Always call get_sizes, to ensure that the window is
588 instantiated. This is a bit gross but less gross than adding
589 special cases for this in other places. */
590 m_splits
[i
].layout
->get_sizes (&info
[i
].min_height
, &info
[i
].max_height
);
593 && cmd_win_already_exists
594 && m_splits
[i
].layout
->get_name () != nullptr
595 && strcmp (m_splits
[i
].layout
->get_name (), "cmd") == 0)
597 /* If this layout has never been applied, then it means the
598 user just changed the layout. In this situation, it's
599 desirable to keep the size of the command window the
600 same. Setting the min and max heights this way ensures
601 that the resizing step, below, does the right thing with
603 info
[i
].min_height
= TUI_CMD_WIN
->height
;
604 info
[i
].max_height
= TUI_CMD_WIN
->height
;
607 if (info
[i
].min_height
== info
[i
].max_height
)
608 available_height
-= info
[i
].min_height
;
612 total_weight
+= m_splits
[i
].weight
;
615 /* Two adjacent boxed windows will share a border, making a bit
616 more height available. */
618 && m_splits
[i
- 1].layout
->bottom_boxed_p ()
619 && m_splits
[i
].layout
->top_boxed_p ())
620 info
[i
].share_box
= true;
623 /* Step 2: Compute the height of each sub-layout. Fixed-sized items
624 are given their fixed size, while others are resized according to
627 for (int i
= 0; i
< m_splits
.size (); ++i
)
629 /* Compute the height and clamp to the allowable range. */
630 info
[i
].height
= available_height
* m_splits
[i
].weight
/ total_weight
;
631 if (info
[i
].height
> info
[i
].max_height
)
632 info
[i
].height
= info
[i
].max_height
;
633 if (info
[i
].height
< info
[i
].min_height
)
634 info
[i
].height
= info
[i
].min_height
;
635 /* If there is any leftover height, just redistribute it to the
636 last resizeable window, by dropping it from the allocated
637 height. We could try to be fancier here perhaps, by
638 redistributing this height among all windows, not just the
640 if (info
[i
].min_height
!= info
[i
].max_height
)
642 used_height
+= info
[i
].height
;
643 if (info
[i
].share_box
)
648 /* Allocate any leftover height. */
649 if (available_height
>= used_height
&& last_index
!= -1)
650 info
[last_index
].height
+= available_height
- used_height
;
652 /* Step 3: Resize. */
653 int height_accum
= 0;
654 for (int i
= 0; i
< m_splits
.size (); ++i
)
656 /* If we fall off the bottom, just make allocations overlap.
658 if (height_accum
+ info
[i
].height
> height
)
659 height_accum
= height
- info
[i
].height
;
660 else if (info
[i
].share_box
)
662 m_splits
[i
].layout
->apply (x
, y
+ height_accum
, width
, info
[i
].height
);
663 height_accum
+= info
[i
].height
;
669 /* See tui-layout.h. */
672 tui_layout_split::remove_windows (const char *name
)
674 for (int i
= 0; i
< m_splits
.size (); ++i
)
676 const char *this_name
= m_splits
[i
].layout
->get_name ();
677 if (this_name
== nullptr)
678 m_splits
[i
].layout
->remove_windows (name
);
681 if (strcmp (this_name
, name
) == 0
682 || strcmp (this_name
, "cmd") == 0)
686 m_splits
.erase (m_splits
.begin () + i
);
692 /* See tui-layout.h. */
695 tui_layout_split::replace_window (const char *name
, const char *new_window
)
697 for (auto &item
: m_splits
)
698 item
.layout
->replace_window (name
, new_window
);
701 /* Destroy the layout associated with SELF. */
704 destroy_layout (struct cmd_list_element
*self
, void *context
)
706 tui_layout_split
*layout
= (tui_layout_split
*) context
;
707 size_t index
= find_layout (layout
);
708 layouts
.erase (layouts
.begin () + index
);
711 /* List holding the sub-commands of "layout". */
713 static struct cmd_list_element
*layout_list
;
715 /* Add a "layout" command with name NAME that switches to LAYOUT. */
718 add_layout_command (const char *name
, tui_layout_split
*layout
)
720 struct cmd_list_element
*cmd
;
722 gdb::unique_xmalloc_ptr
<char> doc (xstrprintf (_("Apply the \"%s\" layout"),
725 cmd
= add_cmd (name
, class_tui
, nullptr, doc
.get (), &layout_list
);
726 set_cmd_context (cmd
, layout
);
727 /* There is no API to set this. */
728 cmd
->func
= tui_apply_layout
;
729 cmd
->destroyer
= destroy_layout
;
730 cmd
->doc_allocated
= 1;
732 layouts
.emplace_back (layout
);
735 /* Initialize the standard layouts. */
738 initialize_layouts ()
740 tui_layout_split
*layout
;
742 layout
= new tui_layout_split ();
743 layout
->add_window ("src", 2);
744 layout
->add_window ("status", 0);
745 layout
->add_window ("cmd", 1);
746 add_layout_command ("src", layout
);
748 layout
= new tui_layout_split ();
749 layout
->add_window ("asm", 2);
750 layout
->add_window ("status", 0);
751 layout
->add_window ("cmd", 1);
752 add_layout_command ("asm", layout
);
754 layout
= new tui_layout_split ();
755 layout
->add_window ("src", 1);
756 layout
->add_window ("asm", 1);
757 layout
->add_window ("status", 0);
758 layout
->add_window ("cmd", 1);
759 add_layout_command ("split", layout
);
761 layout
= new tui_layout_split ();
762 layout
->add_window ("regs", 1);
763 layout
->add_window ("src", 1);
764 layout
->add_window ("status", 0);
765 layout
->add_window ("cmd", 1);
766 layouts
.emplace_back (layout
);
767 src_regs_layout
= layout
;
769 layout
= new tui_layout_split ();
770 layout
->add_window ("regs", 1);
771 layout
->add_window ("asm", 1);
772 layout
->add_window ("status", 0);
773 layout
->add_window ("cmd", 1);
774 layouts
.emplace_back (layout
);
775 asm_regs_layout
= layout
;
780 /* Base command for "layout". */
783 tui_layout_command (const char *layout_name
, int from_tty
)
785 help_list (layout_list
, "layout ", all_commands
, gdb_stdout
);
788 /* Function to initialize gdb commands, for tui window layout
791 void _initialize_tui_layout ();
793 _initialize_tui_layout ()
795 add_prefix_cmd ("layout", class_tui
, tui_layout_command
, _("\
796 Change the layout of windows.\n\
797 Usage: layout prev | next | LAYOUT-NAME"),
798 &layout_list
, "layout ", 0, &cmdlist
);
800 add_cmd ("next", class_tui
, tui_next_layout_command
,
801 _("Apply the next TUI layout"),
803 add_cmd ("prev", class_tui
, tui_prev_layout_command
,
804 _("Apply the previous TUI layout"),
806 add_cmd ("regs", class_tui
, tui_regs_layout_command
,
807 _("Apply the TUI register layout"),
810 initialize_layouts ();
This page took 0.045777 seconds and 5 git commands to generate.