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