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