Change return type of tui_layout_base::adjust_size
[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 (int *min_height, int *max_height)
359 {
360 if (m_window == nullptr)
361 m_window = tui_get_window_by_name (m_contents);
362 *min_height = m_window->min_height ();
363 *max_height = m_window->max_height ();
364 }
365
366 /* See tui-layout.h. */
367
368 bool
369 tui_layout_window::top_boxed_p () const
370 {
371 gdb_assert (m_window != nullptr);
372 return m_window->can_box ();
373 }
374
375 /* See tui-layout.h. */
376
377 bool
378 tui_layout_window::bottom_boxed_p () const
379 {
380 gdb_assert (m_window != nullptr);
381 return m_window->can_box ();
382 }
383
384 /* See tui-layout.h. */
385
386 void
387 tui_layout_window::replace_window (const char *name, const char *new_window)
388 {
389 if (m_contents == name)
390 {
391 m_contents = new_window;
392 if (m_window != nullptr)
393 {
394 m_window->make_visible (false);
395 m_window = tui_get_window_by_name (m_contents);
396 }
397 }
398 }
399
400 /* See tui-layout.h. */
401
402 void
403 tui_layout_window::specification (ui_file *output, int depth)
404 {
405 fputs_unfiltered (get_name (), output);
406 }
407
408 /* See tui-layout.h. */
409
410 void
411 tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
412 int weight)
413 {
414 split s = {weight, std::move (layout)};
415 m_splits.push_back (std::move (s));
416 }
417
418 /* See tui-layout.h. */
419
420 void
421 tui_layout_split::add_window (const char *name, int weight)
422 {
423 tui_layout_window *result = new tui_layout_window (name);
424 split s = {weight, std::unique_ptr<tui_layout_base> (result)};
425 m_splits.push_back (std::move (s));
426 }
427
428 /* See tui-layout.h. */
429
430 std::unique_ptr<tui_layout_base>
431 tui_layout_split::clone () const
432 {
433 tui_layout_split *result = new tui_layout_split ();
434 for (const split &item : m_splits)
435 {
436 std::unique_ptr<tui_layout_base> next = item.layout->clone ();
437 split s = {item.weight, std::move (next)};
438 result->m_splits.push_back (std::move (s));
439 }
440 return std::unique_ptr<tui_layout_base> (result);
441 }
442
443 /* See tui-layout.h. */
444
445 void
446 tui_layout_split::get_sizes (int *min_height, int *max_height)
447 {
448 *min_height = 0;
449 *max_height = 0;
450 for (const split &item : m_splits)
451 {
452 int new_min, new_max;
453 item.layout->get_sizes (&new_min, &new_max);
454 *min_height += new_min;
455 *max_height += new_max;
456 }
457 }
458
459 /* See tui-layout.h. */
460
461 bool
462 tui_layout_split::top_boxed_p () const
463 {
464 if (m_splits.empty ())
465 return false;
466 return m_splits[0].layout->top_boxed_p ();
467 }
468
469 /* See tui-layout.h. */
470
471 bool
472 tui_layout_split::bottom_boxed_p () const
473 {
474 if (m_splits.empty ())
475 return false;
476 return m_splits.back ().layout->top_boxed_p ();
477 }
478
479 /* See tui-layout.h. */
480
481 void
482 tui_layout_split::set_weights_from_heights ()
483 {
484 for (int i = 0; i < m_splits.size (); ++i)
485 m_splits[i].weight = m_splits[i].layout->height;
486 }
487
488 /* See tui-layout.h. */
489
490 tui_adjust_result
491 tui_layout_split::adjust_size (const char *name, int new_height)
492 {
493 /* Look through the children. If one is a layout holding the named
494 window, we're done; or if one actually is the named window,
495 update it. */
496 int found_index = -1;
497 for (int i = 0; i < m_splits.size (); ++i)
498 {
499 tui_adjust_result adjusted
500 = m_splits[i].layout->adjust_size (name, new_height);
501 if (adjusted == HANDLED)
502 return HANDLED;
503 if (adjusted == FOUND)
504 {
505 found_index = i;
506 break;
507 }
508 }
509
510 if (found_index == -1)
511 return NOT_FOUND;
512 if (m_splits[found_index].layout->height == new_height)
513 return HANDLED;
514
515 set_weights_from_heights ();
516 int delta = m_splits[found_index].weight - new_height;
517 m_splits[found_index].weight = new_height;
518
519 /* Distribute the "delta" over the next window; but if the next
520 window cannot hold it all, keep going until we either find a
521 window that does, or until we loop all the way around. */
522 for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i)
523 {
524 int index = (found_index + 1 + i) % m_splits.size ();
525
526 int new_min, new_max;
527 m_splits[index].layout->get_sizes (&new_min, &new_max);
528
529 if (delta < 0)
530 {
531 /* The primary window grew, so we are trying to shrink other
532 windows. */
533 int available = m_splits[index].weight - new_min;
534 int shrink_by = std::min (available, -delta);
535 m_splits[index].weight -= shrink_by;
536 delta += shrink_by;
537 }
538 else
539 {
540 /* The primary window shrank, so we are trying to grow other
541 windows. */
542 int available = new_max - m_splits[index].weight;
543 int grow_by = std::min (available, delta);
544 m_splits[index].weight += grow_by;
545 delta -= grow_by;
546 }
547 }
548
549 if (delta != 0)
550 {
551 warning (_("Invalid window height specified"));
552 /* Effectively undo any modifications made here. */
553 set_weights_from_heights ();
554 }
555 else
556 {
557 /* Simply re-apply the updated layout. */
558 apply (x, y, width, height);
559 }
560
561 return HANDLED;
562 }
563
564 /* See tui-layout.h. */
565
566 void
567 tui_layout_split::apply (int x_, int y_, int width_, int height_)
568 {
569 x = x_;
570 y = y_;
571 width = width_;
572 height = height_;
573
574 struct height_info
575 {
576 int height;
577 int min_height;
578 int max_height;
579 /* True if this window will share a box border with the previous
580 window in the list. */
581 bool share_box;
582 };
583
584 std::vector<height_info> info (m_splits.size ());
585
586 /* Step 1: Find the min and max height of each sub-layout.
587 Fixed-sized layouts are given their desired height, and then the
588 remaining space is distributed among the remaining windows
589 according to the weights given. */
590 int available_height = height;
591 int last_index = -1;
592 int total_weight = 0;
593 for (int i = 0; i < m_splits.size (); ++i)
594 {
595 bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
596
597 /* Always call get_sizes, to ensure that the window is
598 instantiated. This is a bit gross but less gross than adding
599 special cases for this in other places. */
600 m_splits[i].layout->get_sizes (&info[i].min_height, &info[i].max_height);
601
602 if (!m_applied
603 && cmd_win_already_exists
604 && m_splits[i].layout->get_name () != nullptr
605 && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
606 {
607 /* If this layout has never been applied, then it means the
608 user just changed the layout. In this situation, it's
609 desirable to keep the size of the command window the
610 same. Setting the min and max heights this way ensures
611 that the resizing step, below, does the right thing with
612 this window. */
613 info[i].min_height = TUI_CMD_WIN->height;
614 info[i].max_height = TUI_CMD_WIN->height;
615 }
616
617 if (info[i].min_height == info[i].max_height)
618 available_height -= info[i].min_height;
619 else
620 {
621 last_index = i;
622 total_weight += m_splits[i].weight;
623 }
624
625 /* Two adjacent boxed windows will share a border, making a bit
626 more height available. */
627 if (i > 0
628 && m_splits[i - 1].layout->bottom_boxed_p ()
629 && m_splits[i].layout->top_boxed_p ())
630 info[i].share_box = true;
631 }
632
633 /* Step 2: Compute the height of each sub-layout. Fixed-sized items
634 are given their fixed size, while others are resized according to
635 their weight. */
636 int used_height = 0;
637 for (int i = 0; i < m_splits.size (); ++i)
638 {
639 /* Compute the height and clamp to the allowable range. */
640 info[i].height = available_height * m_splits[i].weight / total_weight;
641 if (info[i].height > info[i].max_height)
642 info[i].height = info[i].max_height;
643 if (info[i].height < info[i].min_height)
644 info[i].height = info[i].min_height;
645 /* If there is any leftover height, just redistribute it to the
646 last resizeable window, by dropping it from the allocated
647 height. We could try to be fancier here perhaps, by
648 redistributing this height among all windows, not just the
649 last window. */
650 if (info[i].min_height != info[i].max_height)
651 {
652 used_height += info[i].height;
653 if (info[i].share_box)
654 --used_height;
655 }
656 }
657
658 /* Allocate any leftover height. */
659 if (available_height >= used_height && last_index != -1)
660 info[last_index].height += available_height - used_height;
661
662 /* Step 3: Resize. */
663 int height_accum = 0;
664 for (int i = 0; i < m_splits.size (); ++i)
665 {
666 /* If we fall off the bottom, just make allocations overlap.
667 GIGO. */
668 if (height_accum + info[i].height > height)
669 height_accum = height - info[i].height;
670 else if (info[i].share_box)
671 --height_accum;
672 m_splits[i].layout->apply (x, y + height_accum, width, info[i].height);
673 height_accum += info[i].height;
674 }
675
676 m_applied = true;
677 }
678
679 /* See tui-layout.h. */
680
681 void
682 tui_layout_split::remove_windows (const char *name)
683 {
684 for (int i = 0; i < m_splits.size (); ++i)
685 {
686 const char *this_name = m_splits[i].layout->get_name ();
687 if (this_name == nullptr)
688 m_splits[i].layout->remove_windows (name);
689 else
690 {
691 if (strcmp (this_name, name) == 0
692 || strcmp (this_name, "cmd") == 0)
693 {
694 /* Keep. */
695 }
696 m_splits.erase (m_splits.begin () + i);
697 --i;
698 }
699 }
700 }
701
702 /* See tui-layout.h. */
703
704 void
705 tui_layout_split::replace_window (const char *name, const char *new_window)
706 {
707 for (auto &item : m_splits)
708 item.layout->replace_window (name, new_window);
709 }
710
711 /* See tui-layout.h. */
712
713 void
714 tui_layout_split::specification (ui_file *output, int depth)
715 {
716 if (depth > 0)
717 fputs_unfiltered ("{", output);
718
719 bool first = true;
720 for (auto &item : m_splits)
721 {
722 if (!first)
723 fputs_unfiltered (" ", output);
724 first = false;
725 item.layout->specification (output, depth + 1);
726 fprintf_unfiltered (output, " %d", item.weight);
727 }
728
729 if (depth > 0)
730 fputs_unfiltered ("}", output);
731 }
732
733 /* Destroy the layout associated with SELF. */
734
735 static void
736 destroy_layout (struct cmd_list_element *self, void *context)
737 {
738 tui_layout_split *layout = (tui_layout_split *) context;
739 size_t index = find_layout (layout);
740 layouts.erase (layouts.begin () + index);
741 }
742
743 /* List holding the sub-commands of "layout". */
744
745 static struct cmd_list_element *layout_list;
746
747 /* Add a "layout" command with name NAME that switches to LAYOUT. */
748
749 static struct cmd_list_element *
750 add_layout_command (const char *name, tui_layout_split *layout)
751 {
752 struct cmd_list_element *cmd;
753
754 string_file spec;
755 layout->specification (&spec, 0);
756
757 gdb::unique_xmalloc_ptr<char> doc
758 (xstrprintf (_("Apply the \"%s\" layout.\n\
759 This layout was created using:\n\
760 tui new-layout %s %s"),
761 name, name, spec.c_str ()));
762
763 cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
764 set_cmd_context (cmd, layout);
765 /* There is no API to set this. */
766 cmd->func = tui_apply_layout;
767 cmd->destroyer = destroy_layout;
768 cmd->doc_allocated = 1;
769 doc.release ();
770 layouts.emplace_back (layout);
771
772 return cmd;
773 }
774
775 /* Initialize the standard layouts. */
776
777 static void
778 initialize_layouts ()
779 {
780 tui_layout_split *layout;
781
782 layout = new tui_layout_split ();
783 layout->add_window ("src", 2);
784 layout->add_window ("status", 0);
785 layout->add_window ("cmd", 1);
786 add_layout_command ("src", layout);
787
788 layout = new tui_layout_split ();
789 layout->add_window ("asm", 2);
790 layout->add_window ("status", 0);
791 layout->add_window ("cmd", 1);
792 add_layout_command ("asm", layout);
793
794 layout = new tui_layout_split ();
795 layout->add_window ("src", 1);
796 layout->add_window ("asm", 1);
797 layout->add_window ("status", 0);
798 layout->add_window ("cmd", 1);
799 add_layout_command ("split", layout);
800
801 layout = new tui_layout_split ();
802 layout->add_window ("regs", 1);
803 layout->add_window ("src", 1);
804 layout->add_window ("status", 0);
805 layout->add_window ("cmd", 1);
806 layouts.emplace_back (layout);
807 src_regs_layout = layout;
808
809 layout = new tui_layout_split ();
810 layout->add_window ("regs", 1);
811 layout->add_window ("asm", 1);
812 layout->add_window ("status", 0);
813 layout->add_window ("cmd", 1);
814 layouts.emplace_back (layout);
815 asm_regs_layout = layout;
816 }
817
818 \f
819
820 /* A helper function that returns true if NAME is the name of an
821 available window. */
822
823 static bool
824 validate_window_name (const std::string &name)
825 {
826 return (name == "src" || name == "cmd"
827 || name == "regs" || name == "asm"
828 || name == "status");
829 }
830
831 /* Implementation of the "tui new-layout" command. */
832
833 static void
834 tui_new_layout_command (const char *spec, int from_tty)
835 {
836 std::string new_name = extract_arg (&spec);
837 if (new_name.empty ())
838 error (_("No layout name specified"));
839 if (new_name[0] == '-')
840 error (_("Layout name cannot start with '-'"));
841
842 std::vector<std::unique_ptr<tui_layout_split>> splits;
843 splits.emplace_back (new tui_layout_split);
844 std::unordered_set<std::string> seen_windows;
845 while (true)
846 {
847 spec = skip_spaces (spec);
848 if (spec[0] == '\0')
849 break;
850
851 if (spec[0] == '{')
852 {
853 splits.emplace_back (new tui_layout_split);
854 ++spec;
855 continue;
856 }
857
858 bool is_close = false;
859 std::string name;
860 if (spec[0] == '}')
861 {
862 is_close = true;
863 ++spec;
864 if (splits.size () == 1)
865 error (_("Extra '}' in layout specification"));
866 }
867 else
868 {
869 name = extract_arg (&spec);
870 if (name.empty ())
871 break;
872 if (!validate_window_name (name))
873 error (_("Unknown window \"%s\""), name.c_str ());
874 if (seen_windows.find (name) != seen_windows.end ())
875 error (_("Window \"%s\" seen twice in layout"), name.c_str ());
876 }
877
878 ULONGEST weight = get_ulongest (&spec, '}');
879 if ((int) weight != weight)
880 error (_("Weight out of range: %s"), pulongest (weight));
881 if (is_close)
882 {
883 std::unique_ptr<tui_layout_split> last_split
884 = std::move (splits.back ());
885 splits.pop_back ();
886 splits.back ()->add_split (std::move (last_split), weight);
887 }
888 else
889 {
890 splits.back ()->add_window (name.c_str (), weight);
891 seen_windows.insert (name);
892 }
893 }
894 if (splits.size () > 1)
895 error (_("Missing '}' in layout specification"));
896 if (seen_windows.empty ())
897 error (_("New layout does not contain any windows"));
898 if (seen_windows.find ("cmd") == seen_windows.end ())
899 error (_("New layout does not contain the \"cmd\" window"));
900
901 gdb::unique_xmalloc_ptr<char> cmd_name
902 = make_unique_xstrdup (new_name.c_str ());
903 std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
904 struct cmd_list_element *cmd
905 = add_layout_command (cmd_name.get (), new_layout.get ());
906 cmd->name_allocated = 1;
907 cmd_name.release ();
908 new_layout.release ();
909 }
910
911 /* Base command for "layout". */
912
913 static void
914 tui_layout_command (const char *layout_name, int from_tty)
915 {
916 help_list (layout_list, "layout ", all_commands, gdb_stdout);
917 }
918
919 /* Function to initialize gdb commands, for tui window layout
920 manipulation. */
921
922 void _initialize_tui_layout ();
923 void
924 _initialize_tui_layout ()
925 {
926 add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
927 Change the layout of windows.\n\
928 Usage: layout prev | next | LAYOUT-NAME"),
929 &layout_list, "layout ", 0, &cmdlist);
930
931 add_cmd ("next", class_tui, tui_next_layout_command,
932 _("Apply the next TUI layout"),
933 &layout_list);
934 add_cmd ("prev", class_tui, tui_prev_layout_command,
935 _("Apply the previous TUI layout"),
936 &layout_list);
937 add_cmd ("regs", class_tui, tui_regs_layout_command,
938 _("Apply the TUI register layout"),
939 &layout_list);
940
941 add_cmd ("new-layout", class_tui, tui_new_layout_command,
942 _("Create a new TUI layout.\n\
943 Usage: tui new-layout NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
944 Create a new TUI layout. The new layout will be named NAME,\n\
945 and can be accessed using \"layout NAME\".\n\
946 The windows will be displayed in the specified order.\n\
947 A WINDOW can also be of the form:\n\
948 { NAME WEIGHT [NAME WEIGHT]... }\n\
949 This form indicates a sub-frame.\n\
950 Each WEIGHT is an integer, which holds the relative size\n\
951 to be allocated to the window."),
952 tui_get_cmd_list ());
953
954 initialize_layouts ();
955 }
This page took 0.048803 seconds and 5 git commands to generate.