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