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