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