xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/tui/tui-layout.c (revision 8b657b0747480f8989760d71343d6dd33f8d4cf9)
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 
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 
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 }
1334