ODR warnings for "struct coff_symbol"
[binutils-gdb.git] / gdb / tui / tui-layout.c
1 /* TUI layout window management.
2
3 Copyright (C) 1998-2022 Free Software Foundation, Inc.
4
5 Contributed by Hewlett-Packard Company.
6
7 This file is part of GDB.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21
22 #include "defs.h"
23 #include "arch-utils.h"
24 #include "command.h"
25 #include "symtab.h"
26 #include "frame.h"
27 #include "source.h"
28 #include "cli/cli-cmds.h"
29 #include "cli/cli-decode.h"
30 #include "cli/cli-utils.h"
31 #include <ctype.h>
32 #include <unordered_map>
33 #include <unordered_set>
34
35 #include "tui/tui.h"
36 #include "tui/tui-command.h"
37 #include "tui/tui-data.h"
38 #include "tui/tui-wingeneral.h"
39 #include "tui/tui-stack.h"
40 #include "tui/tui-regs.h"
41 #include "tui/tui-win.h"
42 #include "tui/tui-winsource.h"
43 #include "tui/tui-disasm.h"
44 #include "tui/tui-layout.h"
45 #include "tui/tui-source.h"
46 #include "gdb_curses.h"
47
48 static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
49
50 /* The layouts. */
51 static std::vector<std::unique_ptr<tui_layout_split>> layouts;
52
53 /* The layout that is currently applied. */
54 static std::unique_ptr<tui_layout_base> applied_layout;
55
56 /* The "skeleton" version of the layout that is currently applied. */
57 static tui_layout_split *applied_skeleton;
58
59 /* The two special "regs" layouts. Note that these aren't registered
60 as commands and so can never be deleted. */
61 static tui_layout_split *src_regs_layout;
62 static tui_layout_split *asm_regs_layout;
63
64 /* See tui-data.h. */
65 std::vector<tui_win_info *> tui_windows;
66
67 /* See tui-layout.h. */
68
69 void
70 tui_apply_current_layout (bool preserve_cmd_win_size_p)
71 {
72 struct gdbarch *gdbarch;
73 CORE_ADDR addr;
74
75 extract_display_start_addr (&gdbarch, &addr);
76
77 for (tui_win_info *win_info : tui_windows)
78 win_info->make_visible (false);
79
80 applied_layout->apply (0, 0, tui_term_width (), tui_term_height (),
81 preserve_cmd_win_size_p);
82
83 /* Keep the list of internal windows up-to-date. */
84 for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
85 if (tui_win_list[win_type] != nullptr
86 && !tui_win_list[win_type]->is_visible ())
87 tui_win_list[win_type] = nullptr;
88
89 /* This should always be made visible by a layout. */
90 gdb_assert (TUI_CMD_WIN != nullptr);
91 gdb_assert (TUI_CMD_WIN->is_visible ());
92
93 /* Get the new list of currently visible windows. */
94 std::vector<tui_win_info *> new_tui_windows;
95 applied_layout->get_windows (&new_tui_windows);
96
97 /* Now delete any window that was not re-applied. */
98 tui_win_info *focus = tui_win_with_focus ();
99 for (tui_win_info *win_info : tui_windows)
100 {
101 if (!win_info->is_visible ())
102 {
103 if (focus == win_info)
104 tui_set_win_focus_to (new_tui_windows[0]);
105 delete win_info;
106 }
107 }
108
109 /* Replace the global list of active windows. */
110 tui_windows = std::move (new_tui_windows);
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
117 /* See tui-layout. */
118
119 void
120 tui_adjust_window_height (struct tui_win_info *win, int new_height)
121 {
122 applied_layout->set_height (win->name (), new_height);
123 }
124
125 /* See tui-layout. */
126
127 void
128 tui_adjust_window_width (struct tui_win_info *win, int new_width)
129 {
130 applied_layout->set_width (win->name (), new_width);
131 }
132
133 /* Set the current layout to LAYOUT. */
134
135 static void
136 tui_set_layout (tui_layout_split *layout)
137 {
138 std::string old_fingerprint;
139 if (applied_layout != nullptr)
140 old_fingerprint = applied_layout->layout_fingerprint ();
141
142 applied_skeleton = layout;
143 applied_layout = layout->clone ();
144
145 std::string new_fingerprint = applied_layout->layout_fingerprint ();
146 bool preserve_command_window_size
147 = (TUI_CMD_WIN != nullptr && old_fingerprint == new_fingerprint);
148
149 tui_apply_current_layout (preserve_command_window_size);
150 }
151
152 /* See tui-layout.h. */
153
154 void
155 tui_add_win_to_layout (enum tui_win_type type)
156 {
157 gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
158
159 /* If the window already exists, no need to add it. */
160 if (tui_win_list[type] != nullptr)
161 return;
162
163 /* If the window we are trying to replace doesn't exist, we're
164 done. */
165 enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
166 if (tui_win_list[other] == nullptr)
167 return;
168
169 const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
170 applied_layout->replace_window (tui_win_list[other]->name (), name);
171 tui_apply_current_layout (true);
172 }
173
174 /* Find LAYOUT in the "layouts" global and return its index. */
175
176 static size_t
177 find_layout (tui_layout_split *layout)
178 {
179 for (size_t i = 0; i < layouts.size (); ++i)
180 {
181 if (layout == layouts[i].get ())
182 return i;
183 }
184 gdb_assert_not_reached ("layout not found!?");
185 }
186
187 /* Function to set the layout. */
188
189 static void
190 tui_apply_layout (const char *args, int from_tty, cmd_list_element *command)
191 {
192 tui_layout_split *layout = (tui_layout_split *) command->context ();
193
194 /* Make sure the curses mode is enabled. */
195 tui_enable ();
196 tui_set_layout (layout);
197 }
198
199 /* See tui-layout.h. */
200
201 void
202 tui_next_layout ()
203 {
204 size_t index = find_layout (applied_skeleton);
205 ++index;
206 if (index == layouts.size ())
207 index = 0;
208 tui_set_layout (layouts[index].get ());
209 }
210
211 /* Implement the "layout next" command. */
212
213 static void
214 tui_next_layout_command (const char *arg, int from_tty)
215 {
216 tui_enable ();
217 tui_next_layout ();
218 }
219
220 /* See tui-layout.h. */
221
222 void
223 tui_set_initial_layout ()
224 {
225 tui_set_layout (layouts[0].get ());
226 }
227
228 /* Implement the "layout prev" command. */
229
230 static void
231 tui_prev_layout_command (const char *arg, int from_tty)
232 {
233 tui_enable ();
234 size_t index = find_layout (applied_skeleton);
235 if (index == 0)
236 index = layouts.size ();
237 --index;
238 tui_set_layout (layouts[index].get ());
239 }
240
241
242 /* See tui-layout.h. */
243
244 void
245 tui_regs_layout ()
246 {
247 /* If there's already a register window, we're done. */
248 if (TUI_DATA_WIN != nullptr)
249 return;
250
251 tui_set_layout (TUI_DISASM_WIN != nullptr
252 ? asm_regs_layout
253 : src_regs_layout);
254 }
255
256 /* Implement the "layout regs" command. */
257
258 static void
259 tui_regs_layout_command (const char *arg, int from_tty)
260 {
261 tui_enable ();
262 tui_regs_layout ();
263 }
264
265 /* See tui-layout.h. */
266
267 void
268 tui_remove_some_windows ()
269 {
270 tui_win_info *focus = tui_win_with_focus ();
271
272 if (strcmp (focus->name (), CMD_NAME) == 0)
273 {
274 /* Try leaving the source or disassembly window. If neither
275 exists, just do nothing. */
276 focus = TUI_SRC_WIN;
277 if (focus == nullptr)
278 focus = TUI_DISASM_WIN;
279 if (focus == nullptr)
280 return;
281 }
282
283 applied_layout->remove_windows (focus->name ());
284 tui_apply_current_layout (true);
285 }
286
287 static void
288 extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
289 {
290 if (TUI_SRC_WIN != nullptr)
291 TUI_SRC_WIN->display_start_addr (gdbarch_p, addr_p);
292 else if (TUI_DISASM_WIN != nullptr)
293 TUI_DISASM_WIN->display_start_addr (gdbarch_p, addr_p);
294 else
295 {
296 *gdbarch_p = nullptr;
297 *addr_p = 0;
298 }
299 }
300
301 void
302 tui_win_info::resize (int height_, int width_,
303 int origin_x_, int origin_y_)
304 {
305 if (width == width_ && height == height_
306 && x == origin_x_ && y == origin_y_
307 && handle != nullptr)
308 return;
309
310 width = width_;
311 height = height_;
312 x = origin_x_;
313 y = origin_y_;
314
315 if (handle != nullptr)
316 {
317 #ifdef HAVE_WRESIZE
318 wresize (handle.get (), height, width);
319 mvwin (handle.get (), y, x);
320 wmove (handle.get (), 0, 0);
321 #else
322 handle.reset (nullptr);
323 #endif
324 }
325
326 if (handle == nullptr)
327 make_window ();
328
329 rerender ();
330 }
331
332 \f
333
334 /* Helper function to create one of the built-in (non-locator)
335 windows. */
336
337 template<enum tui_win_type V, class T>
338 static tui_win_info *
339 make_standard_window (const char *)
340 {
341 if (tui_win_list[V] == nullptr)
342 tui_win_list[V] = new T ();
343 return tui_win_list[V];
344 }
345
346 /* A map holding all the known window types, keyed by name. Note that
347 this is heap-allocated and "leaked" at gdb exit. This avoids
348 ordering issues with destroying elements in the map at shutdown.
349 In particular, destroying this map can occur after Python has been
350 shut down, causing crashes if any window destruction requires
351 running Python code. */
352
353 static std::unordered_map<std::string, window_factory> *known_window_types;
354
355 /* Helper function that returns a TUI window, given its name. */
356
357 static tui_win_info *
358 tui_get_window_by_name (const std::string &name)
359 {
360 for (tui_win_info *window : tui_windows)
361 if (name == window->name ())
362 return window;
363
364 auto iter = known_window_types->find (name);
365 if (iter == known_window_types->end ())
366 error (_("Unknown window type \"%s\""), name.c_str ());
367
368 tui_win_info *result = iter->second (name.c_str ());
369 if (result == nullptr)
370 error (_("Could not create window \"%s\""), name.c_str ());
371 return result;
372 }
373
374 /* Initialize the known window types. */
375
376 static void
377 initialize_known_windows ()
378 {
379 known_window_types = new std::unordered_map<std::string, window_factory>;
380
381 known_window_types->emplace (SRC_NAME,
382 make_standard_window<SRC_WIN,
383 tui_source_window>);
384 known_window_types->emplace (CMD_NAME,
385 make_standard_window<CMD_WIN, tui_cmd_window>);
386 known_window_types->emplace (DATA_NAME,
387 make_standard_window<DATA_WIN,
388 tui_data_window>);
389 known_window_types->emplace (DISASSEM_NAME,
390 make_standard_window<DISASSEM_WIN,
391 tui_disasm_window>);
392 known_window_types->emplace (STATUS_NAME,
393 make_standard_window<STATUS_WIN,
394 tui_locator_window>);
395 }
396
397 /* See tui-layout.h. */
398
399 void
400 tui_register_window (const char *name, window_factory &&factory)
401 {
402 std::string name_copy = name;
403
404 if (name_copy == SRC_NAME || name_copy == CMD_NAME || name_copy == DATA_NAME
405 || name_copy == DISASSEM_NAME || name_copy == STATUS_NAME)
406 error (_("Window type \"%s\" is built-in"), name);
407
408 known_window_types->emplace (std::move (name_copy),
409 std::move (factory));
410 }
411
412 /* See tui-layout.h. */
413
414 std::unique_ptr<tui_layout_base>
415 tui_layout_window::clone () const
416 {
417 tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
418 return std::unique_ptr<tui_layout_base> (result);
419 }
420
421 /* See tui-layout.h. */
422
423 void
424 tui_layout_window::apply (int x_, int y_, int width_, int height_,
425 bool preserve_cmd_win_size_p)
426 {
427 x = x_;
428 y = y_;
429 width = width_;
430 height = height_;
431 gdb_assert (m_window != nullptr);
432 m_window->resize (height, width, x, y);
433 }
434
435 /* See tui-layout.h. */
436
437 void
438 tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
439 {
440 TUI_SCOPED_DEBUG_ENTER_EXIT;
441
442 if (m_window == nullptr)
443 m_window = tui_get_window_by_name (m_contents);
444
445 tui_debug_printf ("window = %s, getting %s",
446 m_window->name (), (height ? "height" : "width"));
447
448 if (height)
449 {
450 *min_value = m_window->min_height ();
451 *max_value = m_window->max_height ();
452 }
453 else
454 {
455 *min_value = m_window->min_width ();
456 *max_value = m_window->max_width ();
457 }
458
459 tui_debug_printf ("min = %d, max = %d", *min_value, *max_value);
460 }
461
462 /* See tui-layout.h. */
463
464 bool
465 tui_layout_window::first_edge_has_border_p () const
466 {
467 gdb_assert (m_window != nullptr);
468 return m_window->can_box ();
469 }
470
471 /* See tui-layout.h. */
472
473 bool
474 tui_layout_window::last_edge_has_border_p () const
475 {
476 gdb_assert (m_window != nullptr);
477 return m_window->can_box ();
478 }
479
480 /* See tui-layout.h. */
481
482 void
483 tui_layout_window::replace_window (const char *name, const char *new_window)
484 {
485 if (m_contents == name)
486 {
487 m_contents = new_window;
488 if (m_window != nullptr)
489 {
490 m_window->make_visible (false);
491 m_window = tui_get_window_by_name (m_contents);
492 }
493 }
494 }
495
496 /* See tui-layout.h. */
497
498 void
499 tui_layout_window::specification (ui_file *output, int depth)
500 {
501 gdb_puts (get_name (), output);
502 }
503
504 /* See tui-layout.h. */
505
506 std::string
507 tui_layout_window::layout_fingerprint () const
508 {
509 if (strcmp (get_name (), "cmd") == 0)
510 return "C";
511 else
512 return "";
513 }
514
515 /* See tui-layout.h. */
516
517 void
518 tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
519 int weight)
520 {
521 split s = {weight, std::move (layout)};
522 m_splits.push_back (std::move (s));
523 }
524
525 /* See tui-layout.h. */
526
527 void
528 tui_layout_split::add_window (const char *name, int weight)
529 {
530 tui_layout_window *result = new tui_layout_window (name);
531 split s = {weight, std::unique_ptr<tui_layout_base> (result)};
532 m_splits.push_back (std::move (s));
533 }
534
535 /* See tui-layout.h. */
536
537 std::unique_ptr<tui_layout_base>
538 tui_layout_split::clone () const
539 {
540 tui_layout_split *result = new tui_layout_split (m_vertical);
541 for (const split &item : m_splits)
542 {
543 std::unique_ptr<tui_layout_base> next = item.layout->clone ();
544 split s = {item.weight, std::move (next)};
545 result->m_splits.push_back (std::move (s));
546 }
547 return std::unique_ptr<tui_layout_base> (result);
548 }
549
550 /* See tui-layout.h. */
551
552 void
553 tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
554 {
555 TUI_SCOPED_DEBUG_ENTER_EXIT;
556
557 *min_value = 0;
558 *max_value = 0;
559 bool first_time = true;
560 for (const split &item : m_splits)
561 {
562 int new_min, new_max;
563 item.layout->get_sizes (height, &new_min, &new_max);
564 /* For the mismatch case, the first time through we want to set
565 the min and max to the computed values -- the "first_time"
566 check here is just a funny way of doing that. */
567 if (height == m_vertical || first_time)
568 {
569 *min_value += new_min;
570 *max_value += new_max;
571 }
572 else
573 {
574 *min_value = std::max (*min_value, new_min);
575 *max_value = std::min (*max_value, new_max);
576 }
577 first_time = false;
578 }
579
580 tui_debug_printf ("min_value = %d, max_value = %d", *min_value, *max_value);
581 }
582
583 /* See tui-layout.h. */
584
585 bool
586 tui_layout_split::first_edge_has_border_p () const
587 {
588 if (m_splits.empty ())
589 return false;
590 return m_splits[0].layout->first_edge_has_border_p ();
591 }
592
593 /* See tui-layout.h. */
594
595 bool
596 tui_layout_split::last_edge_has_border_p () const
597 {
598 if (m_splits.empty ())
599 return false;
600 return m_splits.back ().layout->last_edge_has_border_p ();
601 }
602
603 /* See tui-layout.h. */
604
605 void
606 tui_layout_split::set_weights_from_sizes ()
607 {
608 for (int i = 0; i < m_splits.size (); ++i)
609 m_splits[i].weight
610 = m_vertical ? m_splits[i].layout->height : m_splits[i].layout->width;
611 }
612
613 /* See tui-layout.h. */
614
615 std::string
616 tui_layout_split::tui_debug_weights_to_string () const
617 {
618 std::string str;
619
620 for (int i = 0; i < m_splits.size (); ++i)
621 {
622 if (i > 0)
623 str += ", ";
624 str += string_printf ("[%d] %d", i, m_splits[i].weight);
625 }
626
627 return str;
628 }
629
630 /* See tui-layout.h. */
631
632 void
633 tui_layout_split::tui_debug_print_size_info
634 (const std::vector<tui_layout_split::size_info> &info)
635 {
636 gdb_assert (debug_tui);
637
638 tui_debug_printf ("current size info data:");
639 for (int i = 0; i < info.size (); ++i)
640 tui_debug_printf (" [%d] { size = %d, min = %d, max = %d, share_box = %d }",
641 i, info[i].size, info[i].min_size,
642 info[i].max_size, info[i].share_box);
643 }
644
645 /* See tui-layout.h. */
646
647 tui_adjust_result
648 tui_layout_split::set_size (const char *name, int new_size, bool set_width_p)
649 {
650 TUI_SCOPED_DEBUG_ENTER_EXIT;
651
652 tui_debug_printf ("this = %p, name = %s, new_size = %d",
653 this, name, new_size);
654
655 /* Look through the children. If one is a layout holding the named
656 window, we're done; or if one actually is the named window,
657 update it. */
658 int found_index = -1;
659 for (int i = 0; i < m_splits.size (); ++i)
660 {
661 tui_adjust_result adjusted;
662 if (set_width_p)
663 adjusted = m_splits[i].layout->set_width (name, new_size);
664 else
665 adjusted = m_splits[i].layout->set_height (name, new_size);
666 if (adjusted == HANDLED)
667 return HANDLED;
668 if (adjusted == FOUND)
669 {
670 if (set_width_p ? m_vertical : !m_vertical)
671 return FOUND;
672 found_index = i;
673 break;
674 }
675 }
676
677 if (found_index == -1)
678 return NOT_FOUND;
679 int curr_size = (set_width_p
680 ? m_splits[found_index].layout->width
681 : m_splits[found_index].layout->height);
682 if (curr_size == new_size)
683 return HANDLED;
684
685 tui_debug_printf ("found window %s at index %d", name, found_index);
686
687 set_weights_from_sizes ();
688 int delta = m_splits[found_index].weight - new_size;
689 m_splits[found_index].weight = new_size;
690
691 tui_debug_printf ("before delta (%d) distribution, weights: %s",
692 delta, tui_debug_weights_to_string ().c_str ());
693
694 /* Distribute the "delta" over all other windows, while respecting their
695 min/max sizes. We grow each window by 1 line at a time continually
696 looping over all the windows. However, skip the window that the user
697 just resized, obviously we don't want to readjust that window. */
698 bool found_window_that_can_grow_p = true;
699 for (int i = 0; delta != 0; i = (i + 1) % m_splits.size ())
700 {
701 int index = (found_index + 1 + i) % m_splits.size ();
702 if (index == found_index)
703 {
704 if (!found_window_that_can_grow_p)
705 break;
706 found_window_that_can_grow_p = false;
707 continue;
708 }
709
710 int new_min, new_max;
711 m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
712
713 if (delta < 0)
714 {
715 /* The primary window grew, so we are trying to shrink other
716 windows. */
717 if (m_splits[index].weight > new_min)
718 {
719 m_splits[index].weight -= 1;
720 delta += 1;
721 found_window_that_can_grow_p = true;
722 }
723 }
724 else
725 {
726 /* The primary window shrank, so we are trying to grow other
727 windows. */
728 if (m_splits[index].weight < new_max)
729 {
730 m_splits[index].weight += 1;
731 delta -= 1;
732 found_window_that_can_grow_p = true;
733 }
734 }
735
736 tui_debug_printf ("index = %d, weight now: %d",
737 index, m_splits[index].weight);
738 }
739
740 tui_debug_printf ("after delta (%d) distribution, weights: %s",
741 delta, tui_debug_weights_to_string ().c_str ());
742
743 if (delta != 0)
744 {
745 if (set_width_p)
746 warning (_("Invalid window width specified"));
747 else
748 warning (_("Invalid window height specified"));
749 /* Effectively undo any modifications made here. */
750 set_weights_from_sizes ();
751 }
752 else
753 {
754 /* Simply re-apply the updated layout. We pass false here so that
755 the cmd window can be resized. However, we should have already
756 resized everything above to be "just right", so the apply call
757 here should not end up changing the sizes at all. */
758 apply (x, y, width, height, false);
759 }
760
761 return HANDLED;
762 }
763
764 /* See tui-layout.h. */
765
766 void
767 tui_layout_split::apply (int x_, int y_, int width_, int height_,
768 bool preserve_cmd_win_size_p)
769 {
770 TUI_SCOPED_DEBUG_ENTER_EXIT;
771
772 x = x_;
773 y = y_;
774 width = width_;
775 height = height_;
776
777 /* In some situations we fix the size of the cmd window. However,
778 occasionally this turns out to be a mistake. This struct is used to
779 hold the original information about the cmd window, so we can restore
780 it if needed. */
781 struct old_size_info
782 {
783 /* Constructor. */
784 old_size_info (int index_, int min_size_, int max_size_)
785 : index (index_),
786 min_size (min_size_),
787 max_size (max_size_)
788 { /* Nothing. */ }
789
790 /* The index in m_splits where the cmd window was found. */
791 int index;
792
793 /* The previous min/max size. */
794 int min_size;
795 int max_size;
796 };
797
798 /* This is given a value only if we fix the size of the cmd window. */
799 gdb::optional<old_size_info> old_cmd_info;
800
801 std::vector<size_info> info (m_splits.size ());
802
803 tui_debug_printf ("weights are: %s",
804 tui_debug_weights_to_string ().c_str ());
805
806 /* Step 1: Find the min and max size of each sub-layout.
807 Fixed-sized layouts are given their desired size, and then the
808 remaining space is distributed among the remaining windows
809 according to the weights given. */
810 int available_size = m_vertical ? height : width;
811 int last_index = -1;
812 int total_weight = 0;
813 for (int i = 0; i < m_splits.size (); ++i)
814 {
815 bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
816
817 /* Always call get_sizes, to ensure that the window is
818 instantiated. This is a bit gross but less gross than adding
819 special cases for this in other places. */
820 m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
821 &info[i].max_size);
822
823 if (preserve_cmd_win_size_p
824 && cmd_win_already_exists
825 && m_splits[i].layout->get_name () != nullptr
826 && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
827 {
828 /* Save the old cmd window information, in case we need to
829 restore it later. */
830 old_cmd_info.emplace (i, info[i].min_size, info[i].max_size);
831
832 /* If this layout has never been applied, then it means the
833 user just changed the layout. In this situation, it's
834 desirable to keep the size of the command window the
835 same. Setting the min and max sizes this way ensures
836 that the resizing step, below, does the right thing with
837 this window. */
838 info[i].min_size = (m_vertical
839 ? TUI_CMD_WIN->height
840 : TUI_CMD_WIN->width);
841 info[i].max_size = info[i].min_size;
842 }
843
844 if (info[i].min_size == info[i].max_size)
845 available_size -= info[i].min_size;
846 else
847 {
848 last_index = i;
849 total_weight += m_splits[i].weight;
850 }
851
852 /* Two adjacent boxed windows will share a border, making a bit
853 more size available. */
854 if (i > 0
855 && m_splits[i - 1].layout->last_edge_has_border_p ()
856 && m_splits[i].layout->first_edge_has_border_p ())
857 info[i].share_box = true;
858 }
859
860 /* If last_index is set then we have a window that is not of a fixed
861 size. This window will have its size calculated below, which requires
862 that the total_weight not be zero (we divide by total_weight, so don't
863 want a floating-point exception). */
864 gdb_assert (last_index == -1 || total_weight > 0);
865
866 /* Step 2: Compute the size of each sub-layout. Fixed-sized items
867 are given their fixed size, while others are resized according to
868 their weight. */
869 int used_size = 0;
870 for (int i = 0; i < m_splits.size (); ++i)
871 {
872 if (info[i].min_size != info[i].max_size)
873 {
874 /* Compute the height and clamp to the allowable range. */
875 info[i].size = available_size * m_splits[i].weight / total_weight;
876 if (info[i].size > info[i].max_size)
877 info[i].size = info[i].max_size;
878 if (info[i].size < info[i].min_size)
879 info[i].size = info[i].min_size;
880 /* Keep a total of all the size we've used so far (we gain some
881 size back if this window can share a border with a preceding
882 window). Any unused space will be distributed between all of
883 the other windows (while respecting min/max sizes) later in
884 this function. */
885 used_size += info[i].size;
886 if (info[i].share_box)
887 --used_size;
888 }
889 else
890 info[i].size = info[i].min_size;
891 }
892
893 if (debug_tui)
894 {
895 tui_debug_printf ("after initial size calculation");
896 tui_debug_printf ("available_size = %d, used_size = %d",
897 available_size, used_size);
898 tui_debug_printf ("total_weight = %d, last_index = %d",
899 total_weight, last_index);
900 tui_debug_print_size_info (info);
901 }
902
903 /* If we didn't find any sub-layouts that were of a non-fixed size, but
904 we did find the cmd window, then we can consider that a sort-of
905 non-fixed size sub-layout.
906
907 The cmd window might, initially, be of a fixed size (see above), but,
908 we are willing to relax this constraint if required to correctly apply
909 this layout (see below). */
910 if (last_index == -1 && old_cmd_info.has_value ())
911 last_index = old_cmd_info->index;
912
913 /* Allocate any leftover size. */
914 if (available_size != used_size && last_index != -1)
915 {
916 /* Loop over all windows until the amount of used space is equal to
917 the amount of available space. There's an escape hatch within
918 the loop in case we can't find any sub-layouts to resize. */
919 bool found_window_that_can_grow_p = true;
920 for (int idx = last_index;
921 available_size != used_size;
922 idx = (idx + 1) % m_splits.size ())
923 {
924 /* Every time we get back to last_index, which is where the loop
925 started, we check to make sure that we did assign some space
926 to a window, bringing used_size closer to available_size.
927
928 If we didn't, but the cmd window is of a fixed size, then we
929 can make the console window non-fixed-size, and continue
930 around the loop, hopefully, this will allow the layout to be
931 applied correctly.
932
933 If we still make it around the loop without moving used_size
934 closer to available_size, then there's nothing more we can do,
935 and we break out of the loop. */
936 if (idx == last_index)
937 {
938 /* If the used_size is greater than the available_size then
939 this indicates that the fixed-sized sub-layouts claimed
940 more space than is available. This layout is not going to
941 work. Our only hope at this point is to make the cmd
942 window non-fixed-size (if possible), and hope we can
943 shrink this enough to fit the rest of the sub-layouts in.
944
945 Alternatively, we've made it around the loop without
946 adjusting any window's size. This likely means all
947 windows have hit their min or max size. Again, our only
948 hope is to make the cmd window non-fixed-size, and hope
949 this fixes all our problems. */
950 if (old_cmd_info.has_value ()
951 && ((available_size < used_size)
952 || !found_window_that_can_grow_p))
953 {
954 info[old_cmd_info->index].min_size = old_cmd_info->min_size;
955 info[old_cmd_info->index].max_size = old_cmd_info->max_size;
956 tui_debug_printf
957 ("restoring index %d (cmd) size limits, min = %d, max = %d",
958 old_cmd_info->index, old_cmd_info->min_size,
959 old_cmd_info->max_size);
960 old_cmd_info.reset ();
961 }
962 else if (!found_window_that_can_grow_p)
963 break;
964 found_window_that_can_grow_p = false;
965 }
966
967 if (available_size > used_size
968 && info[idx].size < info[idx].max_size)
969 {
970 found_window_that_can_grow_p = true;
971 info[idx].size += 1;
972 used_size += 1;
973 }
974 else if (available_size < used_size
975 && info[idx].size > info[idx].min_size)
976 {
977 found_window_that_can_grow_p = true;
978 info[idx].size -= 1;
979 used_size -= 1;
980 }
981 }
982
983 if (debug_tui)
984 {
985 tui_debug_printf ("after final size calculation");
986 tui_debug_printf ("available_size = %d, used_size = %d",
987 available_size, used_size);
988 tui_debug_printf ("total_weight = %d, last_index = %d",
989 total_weight, last_index);
990 tui_debug_print_size_info (info);
991 }
992 }
993
994 /* Step 3: Resize. */
995 int size_accum = 0;
996 const int maximum = m_vertical ? height : width;
997 for (int i = 0; i < m_splits.size (); ++i)
998 {
999 /* If we fall off the bottom, just make allocations overlap.
1000 GIGO. */
1001 if (size_accum + info[i].size > maximum)
1002 size_accum = maximum - info[i].size;
1003 else if (info[i].share_box)
1004 --size_accum;
1005 if (m_vertical)
1006 m_splits[i].layout->apply (x, y + size_accum, width, info[i].size,
1007 preserve_cmd_win_size_p);
1008 else
1009 m_splits[i].layout->apply (x + size_accum, y, info[i].size, height,
1010 preserve_cmd_win_size_p);
1011 size_accum += info[i].size;
1012 }
1013 }
1014
1015 /* See tui-layout.h. */
1016
1017 void
1018 tui_layout_split::remove_windows (const char *name)
1019 {
1020 for (int i = 0; i < m_splits.size (); ++i)
1021 {
1022 const char *this_name = m_splits[i].layout->get_name ();
1023 if (this_name == nullptr)
1024 m_splits[i].layout->remove_windows (name);
1025 else if (strcmp (this_name, name) == 0
1026 || strcmp (this_name, CMD_NAME) == 0
1027 || strcmp (this_name, STATUS_NAME) == 0)
1028 {
1029 /* Keep. */
1030 }
1031 else
1032 {
1033 m_splits.erase (m_splits.begin () + i);
1034 --i;
1035 }
1036 }
1037 }
1038
1039 /* See tui-layout.h. */
1040
1041 void
1042 tui_layout_split::replace_window (const char *name, const char *new_window)
1043 {
1044 for (auto &item : m_splits)
1045 item.layout->replace_window (name, new_window);
1046 }
1047
1048 /* See tui-layout.h. */
1049
1050 void
1051 tui_layout_split::specification (ui_file *output, int depth)
1052 {
1053 if (depth > 0)
1054 gdb_puts ("{", output);
1055
1056 if (!m_vertical)
1057 gdb_puts ("-horizontal ", output);
1058
1059 bool first = true;
1060 for (auto &item : m_splits)
1061 {
1062 if (!first)
1063 gdb_puts (" ", output);
1064 first = false;
1065 item.layout->specification (output, depth + 1);
1066 gdb_printf (output, " %d", item.weight);
1067 }
1068
1069 if (depth > 0)
1070 gdb_puts ("}", output);
1071 }
1072
1073 /* See tui-layout.h. */
1074
1075 std::string
1076 tui_layout_split::layout_fingerprint () const
1077 {
1078 for (auto &item : m_splits)
1079 {
1080 std::string fp = item.layout->layout_fingerprint ();
1081 if (!fp.empty ())
1082 return std::string (m_vertical ? "V" : "H") + fp;
1083 }
1084
1085 return "";
1086 }
1087
1088 /* Destroy the layout associated with SELF. */
1089
1090 static void
1091 destroy_layout (struct cmd_list_element *self, void *context)
1092 {
1093 tui_layout_split *layout = (tui_layout_split *) context;
1094 size_t index = find_layout (layout);
1095 layouts.erase (layouts.begin () + index);
1096 }
1097
1098 /* List holding the sub-commands of "layout". */
1099
1100 static struct cmd_list_element *layout_list;
1101
1102 /* Called to implement 'tui layout'. */
1103
1104 static void
1105 tui_layout_command (const char *args, int from_tty)
1106 {
1107 help_list (layout_list, "tui layout ", all_commands, gdb_stdout);
1108 }
1109
1110 /* Add a "layout" command with name NAME that switches to LAYOUT. */
1111
1112 static struct cmd_list_element *
1113 add_layout_command (const char *name, tui_layout_split *layout)
1114 {
1115 struct cmd_list_element *cmd;
1116
1117 string_file spec;
1118 layout->specification (&spec, 0);
1119
1120 gdb::unique_xmalloc_ptr<char> doc
1121 = xstrprintf (_("Apply the \"%s\" layout.\n\
1122 This layout was created using:\n\
1123 tui new-layout %s %s"),
1124 name, name, spec.c_str ());
1125
1126 cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
1127 cmd->set_context (layout);
1128 /* There is no API to set this. */
1129 cmd->func = tui_apply_layout;
1130 cmd->destroyer = destroy_layout;
1131 cmd->doc_allocated = 1;
1132 doc.release ();
1133 layouts.emplace_back (layout);
1134
1135 return cmd;
1136 }
1137
1138 /* Initialize the standard layouts. */
1139
1140 static void
1141 initialize_layouts ()
1142 {
1143 tui_layout_split *layout;
1144
1145 layout = new tui_layout_split ();
1146 layout->add_window (SRC_NAME, 2);
1147 layout->add_window (STATUS_NAME, 0);
1148 layout->add_window (CMD_NAME, 1);
1149 add_layout_command (SRC_NAME, layout);
1150
1151 layout = new tui_layout_split ();
1152 layout->add_window (DISASSEM_NAME, 2);
1153 layout->add_window (STATUS_NAME, 0);
1154 layout->add_window (CMD_NAME, 1);
1155 add_layout_command (DISASSEM_NAME, layout);
1156
1157 layout = new tui_layout_split ();
1158 layout->add_window (SRC_NAME, 1);
1159 layout->add_window (DISASSEM_NAME, 1);
1160 layout->add_window (STATUS_NAME, 0);
1161 layout->add_window (CMD_NAME, 1);
1162 add_layout_command ("split", layout);
1163
1164 layout = new tui_layout_split ();
1165 layout->add_window (DATA_NAME, 1);
1166 layout->add_window (SRC_NAME, 1);
1167 layout->add_window (STATUS_NAME, 0);
1168 layout->add_window (CMD_NAME, 1);
1169 layouts.emplace_back (layout);
1170 src_regs_layout = layout;
1171
1172 layout = new tui_layout_split ();
1173 layout->add_window (DATA_NAME, 1);
1174 layout->add_window (DISASSEM_NAME, 1);
1175 layout->add_window (STATUS_NAME, 0);
1176 layout->add_window (CMD_NAME, 1);
1177 layouts.emplace_back (layout);
1178 asm_regs_layout = layout;
1179 }
1180
1181 \f
1182
1183 /* A helper function that returns true if NAME is the name of an
1184 available window. */
1185
1186 static bool
1187 validate_window_name (const std::string &name)
1188 {
1189 auto iter = known_window_types->find (name);
1190 return iter != known_window_types->end ();
1191 }
1192
1193 /* Implementation of the "tui new-layout" command. */
1194
1195 static void
1196 tui_new_layout_command (const char *spec, int from_tty)
1197 {
1198 std::string new_name = extract_arg (&spec);
1199 if (new_name.empty ())
1200 error (_("No layout name specified"));
1201 if (new_name[0] == '-')
1202 error (_("Layout name cannot start with '-'"));
1203
1204 bool is_vertical = true;
1205 spec = skip_spaces (spec);
1206 if (check_for_argument (&spec, "-horizontal"))
1207 is_vertical = false;
1208
1209 std::vector<std::unique_ptr<tui_layout_split>> splits;
1210 splits.emplace_back (new tui_layout_split (is_vertical));
1211 std::unordered_set<std::string> seen_windows;
1212 while (true)
1213 {
1214 spec = skip_spaces (spec);
1215 if (spec[0] == '\0')
1216 break;
1217
1218 if (spec[0] == '{')
1219 {
1220 is_vertical = true;
1221 spec = skip_spaces (spec + 1);
1222 if (check_for_argument (&spec, "-horizontal"))
1223 is_vertical = false;
1224 splits.emplace_back (new tui_layout_split (is_vertical));
1225 continue;
1226 }
1227
1228 bool is_close = false;
1229 std::string name;
1230 if (spec[0] == '}')
1231 {
1232 is_close = true;
1233 ++spec;
1234 if (splits.size () == 1)
1235 error (_("Extra '}' in layout specification"));
1236 }
1237 else
1238 {
1239 name = extract_arg (&spec);
1240 if (name.empty ())
1241 break;
1242 if (!validate_window_name (name))
1243 error (_("Unknown window \"%s\""), name.c_str ());
1244 if (seen_windows.find (name) != seen_windows.end ())
1245 error (_("Window \"%s\" seen twice in layout"), name.c_str ());
1246 }
1247
1248 ULONGEST weight = get_ulongest (&spec, '}');
1249 if ((int) weight != weight)
1250 error (_("Weight out of range: %s"), pulongest (weight));
1251 if (is_close)
1252 {
1253 std::unique_ptr<tui_layout_split> last_split
1254 = std::move (splits.back ());
1255 splits.pop_back ();
1256 splits.back ()->add_split (std::move (last_split), weight);
1257 }
1258 else
1259 {
1260 splits.back ()->add_window (name.c_str (), weight);
1261 seen_windows.insert (name);
1262 }
1263 }
1264 if (splits.size () > 1)
1265 error (_("Missing '}' in layout specification"));
1266 if (seen_windows.empty ())
1267 error (_("New layout does not contain any windows"));
1268 if (seen_windows.find (CMD_NAME) == seen_windows.end ())
1269 error (_("New layout does not contain the \"" CMD_NAME "\" window"));
1270
1271 gdb::unique_xmalloc_ptr<char> cmd_name
1272 = make_unique_xstrdup (new_name.c_str ());
1273 std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
1274 struct cmd_list_element *cmd
1275 = add_layout_command (cmd_name.get (), new_layout.get ());
1276 cmd->name_allocated = 1;
1277 cmd_name.release ();
1278 new_layout.release ();
1279 }
1280
1281 /* Function to initialize gdb commands, for tui window layout
1282 manipulation. */
1283
1284 void _initialize_tui_layout ();
1285 void
1286 _initialize_tui_layout ()
1287 {
1288 struct cmd_list_element *layout_cmd
1289 = add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
1290 Change the layout of windows.\n\
1291 Usage: tui layout prev | next | LAYOUT-NAME"),
1292 &layout_list, 0, tui_get_cmd_list ());
1293 add_com_alias ("layout", layout_cmd, class_tui, 0);
1294
1295 add_cmd ("next", class_tui, tui_next_layout_command,
1296 _("Apply the next TUI layout."),
1297 &layout_list);
1298 add_cmd ("prev", class_tui, tui_prev_layout_command,
1299 _("Apply the previous TUI layout."),
1300 &layout_list);
1301 add_cmd ("regs", class_tui, tui_regs_layout_command,
1302 _("Apply the TUI register layout."),
1303 &layout_list);
1304
1305 add_cmd ("new-layout", class_tui, tui_new_layout_command,
1306 _("Create a new TUI layout.\n\
1307 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1308 Create a new TUI layout. The new layout will be named NAME,\n\
1309 and can be accessed using \"layout NAME\".\n\
1310 The windows will be displayed in the specified order.\n\
1311 A WINDOW can also be of the form:\n\
1312 { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1313 This form indicates a sub-frame.\n\
1314 Each WEIGHT is an integer, which holds the relative size\n\
1315 to be allocated to the window."),
1316 tui_get_cmd_list ());
1317
1318 initialize_layouts ();
1319 initialize_known_windows ();
1320 }