c10378617c2e67b3b29b70bccc1793cc254d17eb
[deliverable/binutils-gdb.git] / gdb / tui / tui-layout.c
1 /* TUI layout window management.
2
3 Copyright (C) 1998-2020 Free Software Foundation, Inc.
4
5 Contributed by Hewlett-Packard Company.
6
7 This file is part of GDB.
8
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.
13
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.
18
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/>. */
21
22 #include "defs.h"
23 #include "arch-utils.h"
24 #include "command.h"
25 #include "symtab.h"
26 #include "frame.h"
27 #include "source.h"
28 #include "cli/cli-cmds.h"
29 #include "cli/cli-decode.h"
30 #include "cli/cli-utils.h"
31 #include <ctype.h>
32 #include <unordered_set>
33
34 #include "tui/tui.h"
35 #include "tui/tui-command.h"
36 #include "tui/tui-data.h"
37 #include "tui/tui-wingeneral.h"
38 #include "tui/tui-stack.h"
39 #include "tui/tui-regs.h"
40 #include "tui/tui-win.h"
41 #include "tui/tui-winsource.h"
42 #include "tui/tui-disasm.h"
43 #include "tui/tui-layout.h"
44 #include "tui/tui-source.h"
45 #include "gdb_curses.h"
46
47 static void tui_layout_command (const char *, int);
48 static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
49
50 /* The layouts. */
51 static std::vector<std::unique_ptr<tui_layout_split>> layouts;
52
53 /* The layout that is currently applied. */
54 static std::unique_ptr<tui_layout_base> applied_layout;
55
56 /* The "skeleton" version of the layout that is currently applied. */
57 static tui_layout_split *applied_skeleton;
58
59 /* The two special "regs" layouts. Note that these aren't registered
60 as commands and so can never be deleted. */
61 static tui_layout_split *src_regs_layout;
62 static tui_layout_split *asm_regs_layout;
63
64 /* See tui-layout.h. */
65
66 void
67 tui_apply_current_layout ()
68 {
69 applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
70 }
71
72 /* See tui-layout. */
73
74 void
75 tui_adjust_window_height (struct tui_win_info *win, int new_height)
76 {
77 applied_layout->adjust_size (win->name (), new_height);
78 }
79
80 /* Set the current layout to LAYOUT. */
81
82 static void
83 tui_set_layout (tui_layout_split *layout)
84 {
85 struct gdbarch *gdbarch;
86 CORE_ADDR addr;
87
88 extract_display_start_addr (&gdbarch, &addr);
89 tui_make_all_invisible ();
90 applied_skeleton = layout;
91 applied_layout = layout->clone ();
92 tui_apply_current_layout ();
93 tui_delete_invisible_windows ();
94
95 if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
96 tui_get_begin_asm_address (&gdbarch, &addr);
97 tui_update_source_windows_with_addr (gdbarch, addr);
98 }
99
100 /* See tui-layout.h. */
101
102 void
103 tui_add_win_to_layout (enum tui_win_type type)
104 {
105 gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
106
107 /* If the window already exists, no need to add it. */
108 if (tui_win_list[type] != nullptr)
109 return;
110
111 /* If the window we are trying to replace doesn't exist, we're
112 done. */
113 enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
114 if (tui_win_list[other] == nullptr)
115 return;
116
117 const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
118 applied_layout->replace_window (tui_win_list[other]->name (), name);
119 tui_apply_current_layout ();
120 tui_delete_invisible_windows ();
121 }
122
123 /* Find LAYOUT in the "layouts" global and return its index. */
124
125 static size_t
126 find_layout (tui_layout_split *layout)
127 {
128 for (size_t i = 0; i < layouts.size (); ++i)
129 {
130 if (layout == layouts[i].get ())
131 return i;
132 }
133 gdb_assert_not_reached (_("layout not found!?"));
134 }
135
136 /* Function to set the layout. */
137
138 static void
139 tui_apply_layout (struct cmd_list_element *command,
140 const char *args, int from_tty)
141 {
142 tui_layout_split *layout
143 = (tui_layout_split *) get_cmd_context (command);
144
145 /* Make sure the curses mode is enabled. */
146 tui_enable ();
147 tui_set_layout (layout);
148 }
149
150 /* See tui-layout.h. */
151
152 void
153 tui_next_layout ()
154 {
155 size_t index = find_layout (applied_skeleton);
156 ++index;
157 if (index == layouts.size ())
158 index = 0;
159 tui_set_layout (layouts[index].get ());
160 }
161
162 /* Implement the "layout next" command. */
163
164 static void
165 tui_next_layout_command (const char *arg, int from_tty)
166 {
167 tui_enable ();
168 tui_next_layout ();
169 }
170
171 /* See tui-layout.h. */
172
173 void
174 tui_set_initial_layout ()
175 {
176 tui_set_layout (layouts[0].get ());
177 }
178
179 /* Implement the "layout prev" command. */
180
181 static void
182 tui_prev_layout_command (const char *arg, int from_tty)
183 {
184 tui_enable ();
185 size_t index = find_layout (applied_skeleton);
186 if (index == 0)
187 index = layouts.size ();
188 --index;
189 tui_set_layout (layouts[index].get ());
190 }
191
192
193 /* See tui-layout.h. */
194
195 void
196 tui_regs_layout ()
197 {
198 /* If there's already a register window, we're done. */
199 if (TUI_DATA_WIN != nullptr)
200 return;
201
202 tui_set_layout (TUI_DISASM_WIN != nullptr
203 ? asm_regs_layout
204 : src_regs_layout);
205 }
206
207 /* Implement the "layout regs" command. */
208
209 static void
210 tui_regs_layout_command (const char *arg, int from_tty)
211 {
212 tui_enable ();
213 tui_regs_layout ();
214 }
215
216 /* See tui-layout.h. */
217
218 void
219 tui_remove_some_windows ()
220 {
221 tui_win_info *focus = tui_win_with_focus ();
222
223 if (strcmp (focus->name (), "cmd") == 0)
224 {
225 /* Try leaving the source or disassembly window. If neither
226 exists, just do nothing. */
227 focus = TUI_SRC_WIN;
228 if (focus == nullptr)
229 focus = TUI_DISASM_WIN;
230 if (focus == nullptr)
231 return;
232 }
233
234 applied_layout->remove_windows (focus->name ());
235 tui_apply_current_layout ();
236 }
237
238 static void
239 extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
240 {
241 struct gdbarch *gdbarch = nullptr;
242 CORE_ADDR addr = 0;
243 CORE_ADDR pc;
244 struct symtab_and_line cursal = get_current_source_symtab_and_line ();
245
246 if (TUI_SRC_WIN != nullptr)
247 {
248 gdbarch = TUI_SRC_WIN->gdbarch;
249 find_line_pc (cursal.symtab,
250 TUI_SRC_WIN->start_line_or_addr.u.line_no,
251 &pc);
252 addr = pc;
253 }
254 else if (TUI_DISASM_WIN != nullptr)
255 {
256 gdbarch = TUI_DISASM_WIN->gdbarch;
257 addr = TUI_DISASM_WIN->start_line_or_addr.u.addr;
258 }
259
260 *gdbarch_p = gdbarch;
261 *addr_p = addr;
262 }
263
264 void
265 tui_gen_win_info::resize (int height_, int width_,
266 int origin_x_, int origin_y_)
267 {
268 if (width == width_ && height == height_
269 && x == origin_x_ && y == origin_y_
270 && handle != nullptr)
271 return;
272
273 width = width_;
274 height = height_;
275 x = origin_x_;
276 y = origin_y_;
277
278 if (handle != nullptr)
279 {
280 #ifdef HAVE_WRESIZE
281 wresize (handle.get (), height, width);
282 mvwin (handle.get (), y, x);
283 wmove (handle.get (), 0, 0);
284 #else
285 handle.reset (nullptr);
286 #endif
287 }
288
289 if (handle == nullptr)
290 make_window ();
291
292 rerender ();
293 }
294
295 \f
296
297 /* Helper function that returns a TUI window, given its name. */
298
299 static tui_gen_win_info *
300 tui_get_window_by_name (const std::string &name)
301 {
302 if (name == "src")
303 {
304 if (tui_win_list[SRC_WIN] == nullptr)
305 tui_win_list[SRC_WIN] = new tui_source_window ();
306 return tui_win_list[SRC_WIN];
307 }
308 else if (name == "cmd")
309 {
310 if (tui_win_list[CMD_WIN] == nullptr)
311 tui_win_list[CMD_WIN] = new tui_cmd_window ();
312 return tui_win_list[CMD_WIN];
313 }
314 else if (name == "regs")
315 {
316 if (tui_win_list[DATA_WIN] == nullptr)
317 tui_win_list[DATA_WIN] = new tui_data_window ();
318 return tui_win_list[DATA_WIN];
319 }
320 else if (name == "asm")
321 {
322 if (tui_win_list[DISASSEM_WIN] == nullptr)
323 tui_win_list[DISASSEM_WIN] = new tui_disasm_window ();
324 return tui_win_list[DISASSEM_WIN];
325 }
326 else
327 {
328 gdb_assert (name == "status");
329 return tui_locator_win_info_ptr ();
330 }
331 }
332
333 /* See tui-layout.h. */
334
335 std::unique_ptr<tui_layout_base>
336 tui_layout_window::clone () const
337 {
338 tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
339 return std::unique_ptr<tui_layout_base> (result);
340 }
341
342 /* See tui-layout.h. */
343
344 void
345 tui_layout_window::apply (int x_, int y_, int width_, int height_)
346 {
347 x = x_;
348 y = y_;
349 width = width_;
350 height = height_;
351 gdb_assert (m_window != nullptr);
352 m_window->resize (height, width, x, y);
353 }
354
355 /* See tui-layout.h. */
356
357 void
358 tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
359 {
360 if (m_window == nullptr)
361 m_window = tui_get_window_by_name (m_contents);
362 if (height)
363 {
364 *min_value = m_window->min_height ();
365 *max_value = m_window->max_height ();
366 }
367 else
368 {
369 *min_value = m_window->min_width ();
370 *max_value = m_window->max_width ();
371 }
372 }
373
374 /* See tui-layout.h. */
375
376 bool
377 tui_layout_window::top_boxed_p () const
378 {
379 gdb_assert (m_window != nullptr);
380 return m_window->can_box ();
381 }
382
383 /* See tui-layout.h. */
384
385 bool
386 tui_layout_window::bottom_boxed_p () const
387 {
388 gdb_assert (m_window != nullptr);
389 return m_window->can_box ();
390 }
391
392 /* See tui-layout.h. */
393
394 void
395 tui_layout_window::replace_window (const char *name, const char *new_window)
396 {
397 if (m_contents == name)
398 {
399 m_contents = new_window;
400 if (m_window != nullptr)
401 {
402 m_window->make_visible (false);
403 m_window = tui_get_window_by_name (m_contents);
404 }
405 }
406 }
407
408 /* See tui-layout.h. */
409
410 void
411 tui_layout_window::specification (ui_file *output, int depth)
412 {
413 fputs_unfiltered (get_name (), output);
414 }
415
416 /* See tui-layout.h. */
417
418 void
419 tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
420 int weight)
421 {
422 split s = {weight, std::move (layout)};
423 m_splits.push_back (std::move (s));
424 }
425
426 /* See tui-layout.h. */
427
428 void
429 tui_layout_split::add_window (const char *name, int weight)
430 {
431 tui_layout_window *result = new tui_layout_window (name);
432 split s = {weight, std::unique_ptr<tui_layout_base> (result)};
433 m_splits.push_back (std::move (s));
434 }
435
436 /* See tui-layout.h. */
437
438 std::unique_ptr<tui_layout_base>
439 tui_layout_split::clone () const
440 {
441 tui_layout_split *result = new tui_layout_split (m_vertical);
442 for (const split &item : m_splits)
443 {
444 std::unique_ptr<tui_layout_base> next = item.layout->clone ();
445 split s = {item.weight, std::move (next)};
446 result->m_splits.push_back (std::move (s));
447 }
448 return std::unique_ptr<tui_layout_base> (result);
449 }
450
451 /* See tui-layout.h. */
452
453 void
454 tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
455 {
456 *min_value = 0;
457 *max_value = 0;
458 bool first_time = true;
459 for (const split &item : m_splits)
460 {
461 int new_min, new_max;
462 item.layout->get_sizes (height, &new_min, &new_max);
463 /* For the mismatch case, the first time through we want to set
464 the min and max to the computed values -- the "first_time"
465 check here is just a funny way of doing that. */
466 if (height == m_vertical || first_time)
467 {
468 *min_value += new_min;
469 *max_value += new_max;
470 }
471 else
472 {
473 *min_value = std::max (*min_value, new_min);
474 *max_value = std::min (*max_value, new_max);
475 }
476 first_time = false;
477 }
478 }
479
480 /* See tui-layout.h. */
481
482 bool
483 tui_layout_split::top_boxed_p () const
484 {
485 if (m_splits.empty ())
486 return false;
487 return m_splits[0].layout->top_boxed_p ();
488 }
489
490 /* See tui-layout.h. */
491
492 bool
493 tui_layout_split::bottom_boxed_p () const
494 {
495 if (m_splits.empty ())
496 return false;
497 return m_splits.back ().layout->top_boxed_p ();
498 }
499
500 /* See tui-layout.h. */
501
502 void
503 tui_layout_split::set_weights_from_heights ()
504 {
505 for (int i = 0; i < m_splits.size (); ++i)
506 m_splits[i].weight = m_splits[i].layout->height;
507 }
508
509 /* See tui-layout.h. */
510
511 tui_adjust_result
512 tui_layout_split::adjust_size (const char *name, int new_height)
513 {
514 /* Look through the children. If one is a layout holding the named
515 window, we're done; or if one actually is the named window,
516 update it. */
517 int found_index = -1;
518 for (int i = 0; i < m_splits.size (); ++i)
519 {
520 tui_adjust_result adjusted
521 = m_splits[i].layout->adjust_size (name, new_height);
522 if (adjusted == HANDLED)
523 return HANDLED;
524 if (adjusted == FOUND)
525 {
526 if (!m_vertical)
527 return FOUND;
528 found_index = i;
529 break;
530 }
531 }
532
533 if (found_index == -1)
534 return NOT_FOUND;
535 if (m_splits[found_index].layout->height == new_height)
536 return HANDLED;
537
538 set_weights_from_heights ();
539 int delta = m_splits[found_index].weight - new_height;
540 m_splits[found_index].weight = new_height;
541
542 /* Distribute the "delta" over the next window; but if the next
543 window cannot hold it all, keep going until we either find a
544 window that does, or until we loop all the way around. */
545 for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i)
546 {
547 int index = (found_index + 1 + i) % m_splits.size ();
548
549 int new_min, new_max;
550 m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
551
552 if (delta < 0)
553 {
554 /* The primary window grew, so we are trying to shrink other
555 windows. */
556 int available = m_splits[index].weight - new_min;
557 int shrink_by = std::min (available, -delta);
558 m_splits[index].weight -= shrink_by;
559 delta += shrink_by;
560 }
561 else
562 {
563 /* The primary window shrank, so we are trying to grow other
564 windows. */
565 int available = new_max - m_splits[index].weight;
566 int grow_by = std::min (available, delta);
567 m_splits[index].weight += grow_by;
568 delta -= grow_by;
569 }
570 }
571
572 if (delta != 0)
573 {
574 warning (_("Invalid window height specified"));
575 /* Effectively undo any modifications made here. */
576 set_weights_from_heights ();
577 }
578 else
579 {
580 /* Simply re-apply the updated layout. */
581 apply (x, y, width, height);
582 }
583
584 return HANDLED;
585 }
586
587 /* See tui-layout.h. */
588
589 void
590 tui_layout_split::apply (int x_, int y_, int width_, int height_)
591 {
592 x = x_;
593 y = y_;
594 width = width_;
595 height = height_;
596
597 struct size_info
598 {
599 int size;
600 int min_size;
601 int max_size;
602 /* True if this window will share a box border with the previous
603 window in the list. */
604 bool share_box;
605 };
606
607 std::vector<size_info> info (m_splits.size ());
608
609 /* Step 1: Find the min and max size of each sub-layout.
610 Fixed-sized layouts are given their desired size, and then the
611 remaining space is distributed among the remaining windows
612 according to the weights given. */
613 int available_size = m_vertical ? height : width;
614 int last_index = -1;
615 int total_weight = 0;
616 for (int i = 0; i < m_splits.size (); ++i)
617 {
618 bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
619
620 /* Always call get_sizes, to ensure that the window is
621 instantiated. This is a bit gross but less gross than adding
622 special cases for this in other places. */
623 m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
624 &info[i].max_size);
625
626 if (!m_applied
627 && cmd_win_already_exists
628 && m_splits[i].layout->get_name () != nullptr
629 && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
630 {
631 /* If this layout has never been applied, then it means the
632 user just changed the layout. In this situation, it's
633 desirable to keep the size of the command window the
634 same. Setting the min and max sizes this way ensures
635 that the resizing step, below, does the right thing with
636 this window. */
637 info[i].min_size = (m_vertical
638 ? TUI_CMD_WIN->height
639 : TUI_CMD_WIN->width);
640 info[i].max_size = info[i].min_size;
641 }
642
643 if (info[i].min_size == info[i].max_size)
644 available_size -= info[i].min_size;
645 else
646 {
647 last_index = i;
648 total_weight += m_splits[i].weight;
649 }
650
651 /* Two adjacent boxed windows will share a border, making a bit
652 more size available. */
653 if (i > 0
654 && m_splits[i - 1].layout->bottom_boxed_p ()
655 && m_splits[i].layout->top_boxed_p ())
656 info[i].share_box = true;
657 }
658
659 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
660 are given their fixed size, while others are resized according to
661 their weight. */
662 int used_size = 0;
663 for (int i = 0; i < m_splits.size (); ++i)
664 {
665 /* Compute the height and clamp to the allowable range. */
666 info[i].size = available_size * m_splits[i].weight / total_weight;
667 if (info[i].size > info[i].max_size)
668 info[i].size = info[i].max_size;
669 if (info[i].size < info[i].min_size)
670 info[i].size = info[i].min_size;
671 /* If there is any leftover size, just redistribute it to the
672 last resizeable window, by dropping it from the allocated
673 size. We could try to be fancier here perhaps, by
674 redistributing this size among all windows, not just the
675 last window. */
676 if (info[i].min_size != info[i].max_size)
677 {
678 used_size += info[i].size;
679 if (info[i].share_box)
680 --used_size;
681 }
682 }
683
684 /* Allocate any leftover size. */
685 if (available_size >= used_size && last_index != -1)
686 info[last_index].size += available_size - used_size;
687
688 /* Step 3: Resize. */
689 int size_accum = 0;
690 const int maximum = m_vertical ? height : width;
691 for (int i = 0; i < m_splits.size (); ++i)
692 {
693 /* If we fall off the bottom, just make allocations overlap.
694 GIGO. */
695 if (size_accum + info[i].size > maximum)
696 size_accum = maximum - info[i].size;
697 else if (info[i].share_box)
698 --size_accum;
699 if (m_vertical)
700 m_splits[i].layout->apply (x, y + size_accum, width, info[i].size);
701 else
702 m_splits[i].layout->apply (x + size_accum, y, info[i].size, height);
703 size_accum += info[i].size;
704 }
705
706 m_applied = true;
707 }
708
709 /* See tui-layout.h. */
710
711 void
712 tui_layout_split::remove_windows (const char *name)
713 {
714 for (int i = 0; i < m_splits.size (); ++i)
715 {
716 const char *this_name = m_splits[i].layout->get_name ();
717 if (this_name == nullptr)
718 m_splits[i].layout->remove_windows (name);
719 else
720 {
721 if (strcmp (this_name, name) == 0
722 || strcmp (this_name, "cmd") == 0)
723 {
724 /* Keep. */
725 }
726 m_splits.erase (m_splits.begin () + i);
727 --i;
728 }
729 }
730 }
731
732 /* See tui-layout.h. */
733
734 void
735 tui_layout_split::replace_window (const char *name, const char *new_window)
736 {
737 for (auto &item : m_splits)
738 item.layout->replace_window (name, new_window);
739 }
740
741 /* See tui-layout.h. */
742
743 void
744 tui_layout_split::specification (ui_file *output, int depth)
745 {
746 if (depth > 0)
747 fputs_unfiltered ("{", output);
748
749 if (!m_vertical)
750 fputs_unfiltered ("-horizontal ", output);
751
752 bool first = true;
753 for (auto &item : m_splits)
754 {
755 if (!first)
756 fputs_unfiltered (" ", output);
757 first = false;
758 item.layout->specification (output, depth + 1);
759 fprintf_unfiltered (output, " %d", item.weight);
760 }
761
762 if (depth > 0)
763 fputs_unfiltered ("}", output);
764 }
765
766 /* Destroy the layout associated with SELF. */
767
768 static void
769 destroy_layout (struct cmd_list_element *self, void *context)
770 {
771 tui_layout_split *layout = (tui_layout_split *) context;
772 size_t index = find_layout (layout);
773 layouts.erase (layouts.begin () + index);
774 }
775
776 /* List holding the sub-commands of "layout". */
777
778 static struct cmd_list_element *layout_list;
779
780 /* Add a "layout" command with name NAME that switches to LAYOUT. */
781
782 static struct cmd_list_element *
783 add_layout_command (const char *name, tui_layout_split *layout)
784 {
785 struct cmd_list_element *cmd;
786
787 string_file spec;
788 layout->specification (&spec, 0);
789
790 gdb::unique_xmalloc_ptr<char> doc
791 (xstrprintf (_("Apply the \"%s\" layout.\n\
792 This layout was created using:\n\
793 tui new-layout %s %s"),
794 name, name, spec.c_str ()));
795
796 cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
797 set_cmd_context (cmd, layout);
798 /* There is no API to set this. */
799 cmd->func = tui_apply_layout;
800 cmd->destroyer = destroy_layout;
801 cmd->doc_allocated = 1;
802 doc.release ();
803 layouts.emplace_back (layout);
804
805 return cmd;
806 }
807
808 /* Initialize the standard layouts. */
809
810 static void
811 initialize_layouts ()
812 {
813 tui_layout_split *layout;
814
815 layout = new tui_layout_split ();
816 layout->add_window ("src", 2);
817 layout->add_window ("status", 0);
818 layout->add_window ("cmd", 1);
819 add_layout_command ("src", layout);
820
821 layout = new tui_layout_split ();
822 layout->add_window ("asm", 2);
823 layout->add_window ("status", 0);
824 layout->add_window ("cmd", 1);
825 add_layout_command ("asm", layout);
826
827 layout = new tui_layout_split ();
828 layout->add_window ("src", 1);
829 layout->add_window ("asm", 1);
830 layout->add_window ("status", 0);
831 layout->add_window ("cmd", 1);
832 add_layout_command ("split", layout);
833
834 layout = new tui_layout_split ();
835 layout->add_window ("regs", 1);
836 layout->add_window ("src", 1);
837 layout->add_window ("status", 0);
838 layout->add_window ("cmd", 1);
839 layouts.emplace_back (layout);
840 src_regs_layout = layout;
841
842 layout = new tui_layout_split ();
843 layout->add_window ("regs", 1);
844 layout->add_window ("asm", 1);
845 layout->add_window ("status", 0);
846 layout->add_window ("cmd", 1);
847 layouts.emplace_back (layout);
848 asm_regs_layout = layout;
849 }
850
851 \f
852
853 /* A helper function that returns true if NAME is the name of an
854 available window. */
855
856 static bool
857 validate_window_name (const std::string &name)
858 {
859 return (name == "src" || name == "cmd"
860 || name == "regs" || name == "asm"
861 || name == "status");
862 }
863
864 /* Implementation of the "tui new-layout" command. */
865
866 static void
867 tui_new_layout_command (const char *spec, int from_tty)
868 {
869 std::string new_name = extract_arg (&spec);
870 if (new_name.empty ())
871 error (_("No layout name specified"));
872 if (new_name[0] == '-')
873 error (_("Layout name cannot start with '-'"));
874
875 bool is_vertical = true;
876 spec = skip_spaces (spec);
877 if (check_for_argument (&spec, "-horizontal"))
878 is_vertical = false;
879
880 std::vector<std::unique_ptr<tui_layout_split>> splits;
881 splits.emplace_back (new tui_layout_split (is_vertical));
882 std::unordered_set<std::string> seen_windows;
883 while (true)
884 {
885 spec = skip_spaces (spec);
886 if (spec[0] == '\0')
887 break;
888
889 if (spec[0] == '{')
890 {
891 is_vertical = true;
892 spec = skip_spaces (spec + 1);
893 if (check_for_argument (&spec, "-horizontal"))
894 is_vertical = false;
895 splits.emplace_back (new tui_layout_split (is_vertical));
896 continue;
897 }
898
899 bool is_close = false;
900 std::string name;
901 if (spec[0] == '}')
902 {
903 is_close = true;
904 ++spec;
905 if (splits.size () == 1)
906 error (_("Extra '}' in layout specification"));
907 }
908 else
909 {
910 name = extract_arg (&spec);
911 if (name.empty ())
912 break;
913 if (!validate_window_name (name))
914 error (_("Unknown window \"%s\""), name.c_str ());
915 if (seen_windows.find (name) != seen_windows.end ())
916 error (_("Window \"%s\" seen twice in layout"), name.c_str ());
917 }
918
919 ULONGEST weight = get_ulongest (&spec, '}');
920 if ((int) weight != weight)
921 error (_("Weight out of range: %s"), pulongest (weight));
922 if (is_close)
923 {
924 std::unique_ptr<tui_layout_split> last_split
925 = std::move (splits.back ());
926 splits.pop_back ();
927 splits.back ()->add_split (std::move (last_split), weight);
928 }
929 else
930 {
931 splits.back ()->add_window (name.c_str (), weight);
932 seen_windows.insert (name);
933 }
934 }
935 if (splits.size () > 1)
936 error (_("Missing '}' in layout specification"));
937 if (seen_windows.empty ())
938 error (_("New layout does not contain any windows"));
939 if (seen_windows.find ("cmd") == seen_windows.end ())
940 error (_("New layout does not contain the \"cmd\" window"));
941
942 gdb::unique_xmalloc_ptr<char> cmd_name
943 = make_unique_xstrdup (new_name.c_str ());
944 std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
945 struct cmd_list_element *cmd
946 = add_layout_command (cmd_name.get (), new_layout.get ());
947 cmd->name_allocated = 1;
948 cmd_name.release ();
949 new_layout.release ();
950 }
951
952 /* Base command for "layout". */
953
954 static void
955 tui_layout_command (const char *layout_name, int from_tty)
956 {
957 help_list (layout_list, "layout ", all_commands, gdb_stdout);
958 }
959
960 /* Function to initialize gdb commands, for tui window layout
961 manipulation. */
962
963 void _initialize_tui_layout ();
964 void
965 _initialize_tui_layout ()
966 {
967 add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
968 Change the layout of windows.\n\
969 Usage: layout prev | next | LAYOUT-NAME"),
970 &layout_list, "layout ", 0, &cmdlist);
971
972 add_cmd ("next", class_tui, tui_next_layout_command,
973 _("Apply the next TUI layout"),
974 &layout_list);
975 add_cmd ("prev", class_tui, tui_prev_layout_command,
976 _("Apply the previous TUI layout"),
977 &layout_list);
978 add_cmd ("regs", class_tui, tui_regs_layout_command,
979 _("Apply the TUI register layout"),
980 &layout_list);
981
982 add_cmd ("new-layout", class_tui, tui_new_layout_command,
983 _("Create a new TUI layout.\n\
984 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
985 Create a new TUI layout. The new layout will be named NAME,\n\
986 and can be accessed using \"layout NAME\".\n\
987 The windows will be displayed in the specified order.\n\
988 A WINDOW can also be of the form:\n\
989 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
990 This form indicates a sub-frame.\n\
991 Each WEIGHT is an integer, which holds the relative size\n\
992 to be allocated to the window."),
993 tui_get_cmd_list ());
994
995 initialize_layouts ();
996 }
This page took 0.0832270000000001 seconds and 4 git commands to generate.