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