Remove tui_gen_win_info
[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_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_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_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_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_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 tui_windows.push_back (m_window);
426 }
427
428 /* See tui-layout.h. */
429
430 void
431 tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
432 {
433 if (m_window == nullptr)
434 m_window = tui_get_window_by_name (m_contents);
435 if (height)
436 {
437 *min_value = m_window->min_height ();
438 *max_value = m_window->max_height ();
439 }
440 else
441 {
442 *min_value = m_window->min_width ();
443 *max_value = m_window->max_width ();
444 }
445 }
446
447 /* See tui-layout.h. */
448
449 bool
450 tui_layout_window::top_boxed_p () const
451 {
452 gdb_assert (m_window != nullptr);
453 return m_window->can_box ();
454 }
455
456 /* See tui-layout.h. */
457
458 bool
459 tui_layout_window::bottom_boxed_p () const
460 {
461 gdb_assert (m_window != nullptr);
462 return m_window->can_box ();
463 }
464
465 /* See tui-layout.h. */
466
467 void
468 tui_layout_window::replace_window (const char *name, const char *new_window)
469 {
470 if (m_contents == name)
471 {
472 m_contents = new_window;
473 if (m_window != nullptr)
474 {
475 m_window->make_visible (false);
476 m_window = tui_get_window_by_name (m_contents);
477 }
478 }
479 }
480
481 /* See tui-layout.h. */
482
483 void
484 tui_layout_window::specification (ui_file *output, int depth)
485 {
486 fputs_unfiltered (get_name (), output);
487 }
488
489 /* See tui-layout.h. */
490
491 void
492 tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
493 int weight)
494 {
495 split s = {weight, std::move (layout)};
496 m_splits.push_back (std::move (s));
497 }
498
499 /* See tui-layout.h. */
500
501 void
502 tui_layout_split::add_window (const char *name, int weight)
503 {
504 tui_layout_window *result = new tui_layout_window (name);
505 split s = {weight, std::unique_ptr<tui_layout_base> (result)};
506 m_splits.push_back (std::move (s));
507 }
508
509 /* See tui-layout.h. */
510
511 std::unique_ptr<tui_layout_base>
512 tui_layout_split::clone () const
513 {
514 tui_layout_split *result = new tui_layout_split (m_vertical);
515 for (const split &item : m_splits)
516 {
517 std::unique_ptr<tui_layout_base> next = item.layout->clone ();
518 split s = {item.weight, std::move (next)};
519 result->m_splits.push_back (std::move (s));
520 }
521 return std::unique_ptr<tui_layout_base> (result);
522 }
523
524 /* See tui-layout.h. */
525
526 void
527 tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
528 {
529 *min_value = 0;
530 *max_value = 0;
531 bool first_time = true;
532 for (const split &item : m_splits)
533 {
534 int new_min, new_max;
535 item.layout->get_sizes (height, &new_min, &new_max);
536 /* For the mismatch case, the first time through we want to set
537 the min and max to the computed values -- the "first_time"
538 check here is just a funny way of doing that. */
539 if (height == m_vertical || first_time)
540 {
541 *min_value += new_min;
542 *max_value += new_max;
543 }
544 else
545 {
546 *min_value = std::max (*min_value, new_min);
547 *max_value = std::min (*max_value, new_max);
548 }
549 first_time = false;
550 }
551 }
552
553 /* See tui-layout.h. */
554
555 bool
556 tui_layout_split::top_boxed_p () const
557 {
558 if (m_splits.empty ())
559 return false;
560 return m_splits[0].layout->top_boxed_p ();
561 }
562
563 /* See tui-layout.h. */
564
565 bool
566 tui_layout_split::bottom_boxed_p () const
567 {
568 if (m_splits.empty ())
569 return false;
570 return m_splits.back ().layout->top_boxed_p ();
571 }
572
573 /* See tui-layout.h. */
574
575 void
576 tui_layout_split::set_weights_from_heights ()
577 {
578 for (int i = 0; i < m_splits.size (); ++i)
579 m_splits[i].weight = m_splits[i].layout->height;
580 }
581
582 /* See tui-layout.h. */
583
584 tui_adjust_result
585 tui_layout_split::adjust_size (const char *name, int new_height)
586 {
587 /* Look through the children. If one is a layout holding the named
588 window, we're done; or if one actually is the named window,
589 update it. */
590 int found_index = -1;
591 for (int i = 0; i < m_splits.size (); ++i)
592 {
593 tui_adjust_result adjusted
594 = m_splits[i].layout->adjust_size (name, new_height);
595 if (adjusted == HANDLED)
596 return HANDLED;
597 if (adjusted == FOUND)
598 {
599 if (!m_vertical)
600 return FOUND;
601 found_index = i;
602 break;
603 }
604 }
605
606 if (found_index == -1)
607 return NOT_FOUND;
608 if (m_splits[found_index].layout->height == new_height)
609 return HANDLED;
610
611 set_weights_from_heights ();
612 int delta = m_splits[found_index].weight - new_height;
613 m_splits[found_index].weight = new_height;
614
615 /* Distribute the "delta" over the next window; but if the next
616 window cannot hold it all, keep going until we either find a
617 window that does, or until we loop all the way around. */
618 for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i)
619 {
620 int index = (found_index + 1 + i) % m_splits.size ();
621
622 int new_min, new_max;
623 m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
624
625 if (delta < 0)
626 {
627 /* The primary window grew, so we are trying to shrink other
628 windows. */
629 int available = m_splits[index].weight - new_min;
630 int shrink_by = std::min (available, -delta);
631 m_splits[index].weight -= shrink_by;
632 delta += shrink_by;
633 }
634 else
635 {
636 /* The primary window shrank, so we are trying to grow other
637 windows. */
638 int available = new_max - m_splits[index].weight;
639 int grow_by = std::min (available, delta);
640 m_splits[index].weight += grow_by;
641 delta -= grow_by;
642 }
643 }
644
645 if (delta != 0)
646 {
647 warning (_("Invalid window height specified"));
648 /* Effectively undo any modifications made here. */
649 set_weights_from_heights ();
650 }
651 else
652 {
653 /* Simply re-apply the updated layout. */
654 apply (x, y, width, height);
655 }
656
657 return HANDLED;
658 }
659
660 /* See tui-layout.h. */
661
662 void
663 tui_layout_split::apply (int x_, int y_, int width_, int height_)
664 {
665 x = x_;
666 y = y_;
667 width = width_;
668 height = height_;
669
670 struct size_info
671 {
672 int size;
673 int min_size;
674 int max_size;
675 /* True if this window will share a box border with the previous
676 window in the list. */
677 bool share_box;
678 };
679
680 std::vector<size_info> info (m_splits.size ());
681
682 /* Step 1: Find the min and max size of each sub-layout.
683 Fixed-sized layouts are given their desired size, and then the
684 remaining space is distributed among the remaining windows
685 according to the weights given. */
686 int available_size = m_vertical ? height : width;
687 int last_index = -1;
688 int total_weight = 0;
689 for (int i = 0; i < m_splits.size (); ++i)
690 {
691 bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
692
693 /* Always call get_sizes, to ensure that the window is
694 instantiated. This is a bit gross but less gross than adding
695 special cases for this in other places. */
696 m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
697 &info[i].max_size);
698
699 if (!m_applied
700 && cmd_win_already_exists
701 && m_splits[i].layout->get_name () != nullptr
702 && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
703 {
704 /* If this layout has never been applied, then it means the
705 user just changed the layout. In this situation, it's
706 desirable to keep the size of the command window the
707 same. Setting the min and max sizes this way ensures
708 that the resizing step, below, does the right thing with
709 this window. */
710 info[i].min_size = (m_vertical
711 ? TUI_CMD_WIN->height
712 : TUI_CMD_WIN->width);
713 info[i].max_size = info[i].min_size;
714 }
715
716 if (info[i].min_size == info[i].max_size)
717 available_size -= info[i].min_size;
718 else
719 {
720 last_index = i;
721 total_weight += m_splits[i].weight;
722 }
723
724 /* Two adjacent boxed windows will share a border, making a bit
725 more size available. */
726 if (i > 0
727 && m_splits[i - 1].layout->bottom_boxed_p ()
728 && m_splits[i].layout->top_boxed_p ())
729 info[i].share_box = true;
730 }
731
732 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
733 are given their fixed size, while others are resized according to
734 their weight. */
735 int used_size = 0;
736 for (int i = 0; i < m_splits.size (); ++i)
737 {
738 /* Compute the height and clamp to the allowable range. */
739 info[i].size = available_size * m_splits[i].weight / total_weight;
740 if (info[i].size > info[i].max_size)
741 info[i].size = info[i].max_size;
742 if (info[i].size < info[i].min_size)
743 info[i].size = info[i].min_size;
744 /* If there is any leftover size, just redistribute it to the
745 last resizeable window, by dropping it from the allocated
746 size. We could try to be fancier here perhaps, by
747 redistributing this size among all windows, not just the
748 last window. */
749 if (info[i].min_size != info[i].max_size)
750 {
751 used_size += info[i].size;
752 if (info[i].share_box)
753 --used_size;
754 }
755 }
756
757 /* Allocate any leftover size. */
758 if (available_size >= used_size && last_index != -1)
759 info[last_index].size += available_size - used_size;
760
761 /* Step 3: Resize. */
762 int size_accum = 0;
763 const int maximum = m_vertical ? height : width;
764 for (int i = 0; i < m_splits.size (); ++i)
765 {
766 /* If we fall off the bottom, just make allocations overlap.
767 GIGO. */
768 if (size_accum + info[i].size > maximum)
769 size_accum = maximum - info[i].size;
770 else if (info[i].share_box)
771 --size_accum;
772 if (m_vertical)
773 m_splits[i].layout->apply (x, y + size_accum, width, info[i].size);
774 else
775 m_splits[i].layout->apply (x + size_accum, y, info[i].size, height);
776 size_accum += info[i].size;
777 }
778
779 m_applied = true;
780 }
781
782 /* See tui-layout.h. */
783
784 void
785 tui_layout_split::remove_windows (const char *name)
786 {
787 for (int i = 0; i < m_splits.size (); ++i)
788 {
789 const char *this_name = m_splits[i].layout->get_name ();
790 if (this_name == nullptr)
791 m_splits[i].layout->remove_windows (name);
792 else if (strcmp (this_name, name) == 0
793 || strcmp (this_name, CMD_NAME) == 0
794 || strcmp (this_name, STATUS_NAME) == 0)
795 {
796 /* Keep. */
797 }
798 else
799 {
800 m_splits.erase (m_splits.begin () + i);
801 --i;
802 }
803 }
804 }
805
806 /* See tui-layout.h. */
807
808 void
809 tui_layout_split::replace_window (const char *name, const char *new_window)
810 {
811 for (auto &item : m_splits)
812 item.layout->replace_window (name, new_window);
813 }
814
815 /* See tui-layout.h. */
816
817 void
818 tui_layout_split::specification (ui_file *output, int depth)
819 {
820 if (depth > 0)
821 fputs_unfiltered ("{", output);
822
823 if (!m_vertical)
824 fputs_unfiltered ("-horizontal ", output);
825
826 bool first = true;
827 for (auto &item : m_splits)
828 {
829 if (!first)
830 fputs_unfiltered (" ", output);
831 first = false;
832 item.layout->specification (output, depth + 1);
833 fprintf_unfiltered (output, " %d", item.weight);
834 }
835
836 if (depth > 0)
837 fputs_unfiltered ("}", output);
838 }
839
840 /* Destroy the layout associated with SELF. */
841
842 static void
843 destroy_layout (struct cmd_list_element *self, void *context)
844 {
845 tui_layout_split *layout = (tui_layout_split *) context;
846 size_t index = find_layout (layout);
847 layouts.erase (layouts.begin () + index);
848 }
849
850 /* List holding the sub-commands of "layout". */
851
852 static struct cmd_list_element *layout_list;
853
854 /* Add a "layout" command with name NAME that switches to LAYOUT. */
855
856 static struct cmd_list_element *
857 add_layout_command (const char *name, tui_layout_split *layout)
858 {
859 struct cmd_list_element *cmd;
860
861 string_file spec;
862 layout->specification (&spec, 0);
863
864 gdb::unique_xmalloc_ptr<char> doc
865 (xstrprintf (_("Apply the \"%s\" layout.\n\
866 This layout was created using:\n\
867 tui new-layout %s %s"),
868 name, name, spec.c_str ()));
869
870 cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
871 set_cmd_context (cmd, layout);
872 /* There is no API to set this. */
873 cmd->func = tui_apply_layout;
874 cmd->destroyer = destroy_layout;
875 cmd->doc_allocated = 1;
876 doc.release ();
877 layouts.emplace_back (layout);
878
879 return cmd;
880 }
881
882 /* Initialize the standard layouts. */
883
884 static void
885 initialize_layouts ()
886 {
887 tui_layout_split *layout;
888
889 layout = new tui_layout_split ();
890 layout->add_window (SRC_NAME, 2);
891 layout->add_window (STATUS_NAME, 0);
892 layout->add_window (CMD_NAME, 1);
893 add_layout_command (SRC_NAME, layout);
894
895 layout = new tui_layout_split ();
896 layout->add_window (DISASSEM_NAME, 2);
897 layout->add_window (STATUS_NAME, 0);
898 layout->add_window (CMD_NAME, 1);
899 add_layout_command (DISASSEM_NAME, layout);
900
901 layout = new tui_layout_split ();
902 layout->add_window (SRC_NAME, 1);
903 layout->add_window (DISASSEM_NAME, 1);
904 layout->add_window (STATUS_NAME, 0);
905 layout->add_window (CMD_NAME, 1);
906 add_layout_command ("split", layout);
907
908 layout = new tui_layout_split ();
909 layout->add_window (DATA_NAME, 1);
910 layout->add_window (SRC_NAME, 1);
911 layout->add_window (STATUS_NAME, 0);
912 layout->add_window (CMD_NAME, 1);
913 layouts.emplace_back (layout);
914 src_regs_layout = layout;
915
916 layout = new tui_layout_split ();
917 layout->add_window (DATA_NAME, 1);
918 layout->add_window (DISASSEM_NAME, 1);
919 layout->add_window (STATUS_NAME, 0);
920 layout->add_window (CMD_NAME, 1);
921 layouts.emplace_back (layout);
922 asm_regs_layout = layout;
923 }
924
925 \f
926
927 /* A helper function that returns true if NAME is the name of an
928 available window. */
929
930 static bool
931 validate_window_name (const std::string &name)
932 {
933 auto iter = known_window_types->find (name);
934 return iter != known_window_types->end ();
935 }
936
937 /* Implementation of the "tui new-layout" command. */
938
939 static void
940 tui_new_layout_command (const char *spec, int from_tty)
941 {
942 std::string new_name = extract_arg (&spec);
943 if (new_name.empty ())
944 error (_("No layout name specified"));
945 if (new_name[0] == '-')
946 error (_("Layout name cannot start with '-'"));
947
948 bool is_vertical = true;
949 spec = skip_spaces (spec);
950 if (check_for_argument (&spec, "-horizontal"))
951 is_vertical = false;
952
953 std::vector<std::unique_ptr<tui_layout_split>> splits;
954 splits.emplace_back (new tui_layout_split (is_vertical));
955 std::unordered_set<std::string> seen_windows;
956 while (true)
957 {
958 spec = skip_spaces (spec);
959 if (spec[0] == '\0')
960 break;
961
962 if (spec[0] == '{')
963 {
964 is_vertical = true;
965 spec = skip_spaces (spec + 1);
966 if (check_for_argument (&spec, "-horizontal"))
967 is_vertical = false;
968 splits.emplace_back (new tui_layout_split (is_vertical));
969 continue;
970 }
971
972 bool is_close = false;
973 std::string name;
974 if (spec[0] == '}')
975 {
976 is_close = true;
977 ++spec;
978 if (splits.size () == 1)
979 error (_("Extra '}' in layout specification"));
980 }
981 else
982 {
983 name = extract_arg (&spec);
984 if (name.empty ())
985 break;
986 if (!validate_window_name (name))
987 error (_("Unknown window \"%s\""), name.c_str ());
988 if (seen_windows.find (name) != seen_windows.end ())
989 error (_("Window \"%s\" seen twice in layout"), name.c_str ());
990 }
991
992 ULONGEST weight = get_ulongest (&spec, '}');
993 if ((int) weight != weight)
994 error (_("Weight out of range: %s"), pulongest (weight));
995 if (is_close)
996 {
997 std::unique_ptr<tui_layout_split> last_split
998 = std::move (splits.back ());
999 splits.pop_back ();
1000 splits.back ()->add_split (std::move (last_split), weight);
1001 }
1002 else
1003 {
1004 splits.back ()->add_window (name.c_str (), weight);
1005 seen_windows.insert (name);
1006 }
1007 }
1008 if (splits.size () > 1)
1009 error (_("Missing '}' in layout specification"));
1010 if (seen_windows.empty ())
1011 error (_("New layout does not contain any windows"));
1012 if (seen_windows.find (CMD_NAME) == seen_windows.end ())
1013 error (_("New layout does not contain the \"" CMD_NAME "\" window"));
1014
1015 gdb::unique_xmalloc_ptr<char> cmd_name
1016 = make_unique_xstrdup (new_name.c_str ());
1017 std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
1018 struct cmd_list_element *cmd
1019 = add_layout_command (cmd_name.get (), new_layout.get ());
1020 cmd->name_allocated = 1;
1021 cmd_name.release ();
1022 new_layout.release ();
1023 }
1024
1025 /* Function to initialize gdb commands, for tui window layout
1026 manipulation. */
1027
1028 void _initialize_tui_layout ();
1029 void
1030 _initialize_tui_layout ()
1031 {
1032 add_basic_prefix_cmd ("layout", class_tui, _("\
1033 Change the layout of windows.\n\
1034 Usage: layout prev | next | LAYOUT-NAME"),
1035 &layout_list, "layout ", 0, &cmdlist);
1036
1037 add_cmd ("next", class_tui, tui_next_layout_command,
1038 _("Apply the next TUI layout."),
1039 &layout_list);
1040 add_cmd ("prev", class_tui, tui_prev_layout_command,
1041 _("Apply the previous TUI layout."),
1042 &layout_list);
1043 add_cmd ("regs", class_tui, tui_regs_layout_command,
1044 _("Apply the TUI register layout."),
1045 &layout_list);
1046
1047 add_cmd ("new-layout", class_tui, tui_new_layout_command,
1048 _("Create a new TUI layout.\n\
1049 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1050 Create a new TUI layout. The new layout will be named NAME,\n\
1051 and can be accessed using \"layout NAME\".\n\
1052 The windows will be displayed in the specified order.\n\
1053 A WINDOW can also be of the form:\n\
1054 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1055 This form indicates a sub-frame.\n\
1056 Each WEIGHT is an integer, which holds the relative size\n\
1057 to be allocated to the window."),
1058 tui_get_cmd_list ());
1059
1060 initialize_layouts ();
1061 initialize_known_windows ();
1062 }
This page took 0.051277 seconds and 4 git commands to generate.