xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/tui/tui-layout.c (revision 782713e6c126f1866c6d9cfdee4ceb49483b5828)
1 /* TUI layout window management.
2 
3    Copyright (C) 1998-2020 Free Software Foundation, Inc.
4 
5    Contributed by Hewlett-Packard Company.
6 
7    This file is part of GDB.
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
21 
22 #include "defs.h"
23 #include "arch-utils.h"
24 #include "command.h"
25 #include "symtab.h"
26 #include "frame.h"
27 #include "source.h"
28 #include "cli/cli-cmds.h"
29 #include "cli/cli-decode.h"
30 #include "cli/cli-utils.h"
31 #include <ctype.h>
32 #include <unordered_map>
33 #include <unordered_set>
34 
35 #include "tui/tui.h"
36 #include "tui/tui-command.h"
37 #include "tui/tui-data.h"
38 #include "tui/tui-wingeneral.h"
39 #include "tui/tui-stack.h"
40 #include "tui/tui-regs.h"
41 #include "tui/tui-win.h"
42 #include "tui/tui-winsource.h"
43 #include "tui/tui-disasm.h"
44 #include "tui/tui-layout.h"
45 #include "tui/tui-source.h"
46 #include "gdb_curses.h"
47 
48 static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
49 
50 /* The layouts.  */
51 static std::vector<std::unique_ptr<tui_layout_split>> layouts;
52 
53 /* The layout that is currently applied.  */
54 static std::unique_ptr<tui_layout_base> applied_layout;
55 
56 /* The "skeleton" version of the layout that is currently applied.  */
57 static tui_layout_split *applied_skeleton;
58 
59 /* The two special "regs" layouts.  Note that these aren't registered
60    as commands and so can never be deleted.  */
61 static tui_layout_split *src_regs_layout;
62 static tui_layout_split *asm_regs_layout;
63 
64 /* See tui-data.h.  */
65 std::vector<tui_win_info *> tui_windows;
66 
67 /* When applying a layout, this is the list of all windows that were
68    in the previous layout.  This is used to re-use windows when
69    changing a layout.  */
70 static std::vector<tui_win_info *> saved_tui_windows;
71 
72 /* See tui-layout.h.  */
73 
74 void
75 tui_apply_current_layout ()
76 {
77   struct gdbarch *gdbarch;
78   CORE_ADDR addr;
79 
80   extract_display_start_addr (&gdbarch, &addr);
81 
82   saved_tui_windows = std::move (tui_windows);
83   tui_windows.clear ();
84 
85   for (tui_win_info *win_info : saved_tui_windows)
86     win_info->make_visible (false);
87 
88   applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
89 
90   /* Keep the list of internal windows up-to-date.  */
91   for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
92     if (tui_win_list[win_type] != nullptr
93 	&& !tui_win_list[win_type]->is_visible ())
94       tui_win_list[win_type] = nullptr;
95 
96   /* This should always be made visible by a layout.  */
97   gdb_assert (TUI_CMD_WIN->is_visible ());
98 
99   /* Now delete any window that was not re-applied.  */
100   tui_win_info *focus = tui_win_with_focus ();
101   for (tui_win_info *win_info : saved_tui_windows)
102     {
103       if (!win_info->is_visible ())
104 	{
105 	  if (focus == win_info)
106 	    tui_set_win_focus_to (tui_windows[0]);
107 	  delete win_info;
108 	}
109     }
110 
111   if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
112     tui_get_begin_asm_address (&gdbarch, &addr);
113   tui_update_source_windows_with_addr (gdbarch, addr);
114 
115   saved_tui_windows.clear ();
116 }
117 
118 /* See tui-layout.  */
119 
120 void
121 tui_adjust_window_height (struct tui_win_info *win, int new_height)
122 {
123   applied_layout->adjust_size (win->name (), new_height);
124 }
125 
126 /* Set the current layout to LAYOUT.  */
127 
128 static void
129 tui_set_layout (tui_layout_split *layout)
130 {
131   applied_skeleton = layout;
132   applied_layout = layout->clone ();
133   tui_apply_current_layout ();
134 }
135 
136 /* See tui-layout.h.  */
137 
138 void
139 tui_add_win_to_layout (enum tui_win_type type)
140 {
141   gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
142 
143   /* If the window already exists, no need to add it.  */
144   if (tui_win_list[type] != nullptr)
145     return;
146 
147   /* If the window we are trying to replace doesn't exist, we're
148      done.  */
149   enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
150   if (tui_win_list[other] == nullptr)
151     return;
152 
153   const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
154   applied_layout->replace_window (tui_win_list[other]->name (), name);
155   tui_apply_current_layout ();
156 }
157 
158 /* Find LAYOUT in the "layouts" global and return its index.  */
159 
160 static size_t
161 find_layout (tui_layout_split *layout)
162 {
163   for (size_t i = 0; i < layouts.size (); ++i)
164     {
165       if (layout == layouts[i].get ())
166 	return i;
167     }
168   gdb_assert_not_reached (_("layout not found!?"));
169 }
170 
171 /* Function to set the layout. */
172 
173 static void
174 tui_apply_layout (struct cmd_list_element *command,
175 		  const char *args, int from_tty)
176 {
177   tui_layout_split *layout
178     = (tui_layout_split *) get_cmd_context (command);
179 
180   /* Make sure the curses mode is enabled.  */
181   tui_enable ();
182   tui_set_layout (layout);
183 }
184 
185 /* See tui-layout.h.  */
186 
187 void
188 tui_next_layout ()
189 {
190   size_t index = find_layout (applied_skeleton);
191   ++index;
192   if (index == layouts.size ())
193     index = 0;
194   tui_set_layout (layouts[index].get ());
195 }
196 
197 /* Implement the "layout next" command.  */
198 
199 static void
200 tui_next_layout_command (const char *arg, int from_tty)
201 {
202   tui_enable ();
203   tui_next_layout ();
204 }
205 
206 /* See tui-layout.h.  */
207 
208 void
209 tui_set_initial_layout ()
210 {
211   tui_set_layout (layouts[0].get ());
212 }
213 
214 /* Implement the "layout prev" command.  */
215 
216 static void
217 tui_prev_layout_command (const char *arg, int from_tty)
218 {
219   tui_enable ();
220   size_t index = find_layout (applied_skeleton);
221   if (index == 0)
222     index = layouts.size ();
223   --index;
224   tui_set_layout (layouts[index].get ());
225 }
226 
227 
228 /* See tui-layout.h.  */
229 
230 void
231 tui_regs_layout ()
232 {
233   /* If there's already a register window, we're done.  */
234   if (TUI_DATA_WIN != nullptr)
235     return;
236 
237   tui_set_layout (TUI_DISASM_WIN != nullptr
238 		  ? asm_regs_layout
239 		  : src_regs_layout);
240 }
241 
242 /* Implement the "layout regs" command.  */
243 
244 static void
245 tui_regs_layout_command (const char *arg, int from_tty)
246 {
247   tui_enable ();
248   tui_regs_layout ();
249 }
250 
251 /* See tui-layout.h.  */
252 
253 void
254 tui_remove_some_windows ()
255 {
256   tui_win_info *focus = tui_win_with_focus ();
257 
258   if (strcmp (focus->name (), CMD_NAME) == 0)
259     {
260       /* Try leaving the source or disassembly window.  If neither
261 	 exists, just do nothing.  */
262       focus = TUI_SRC_WIN;
263       if (focus == nullptr)
264 	focus = TUI_DISASM_WIN;
265       if (focus == nullptr)
266 	return;
267     }
268 
269   applied_layout->remove_windows (focus->name ());
270   tui_apply_current_layout ();
271 }
272 
273 static void
274 extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
275 {
276   if (TUI_SRC_WIN != nullptr)
277     TUI_SRC_WIN->display_start_addr (gdbarch_p, addr_p);
278   else if (TUI_DISASM_WIN != nullptr)
279     TUI_DISASM_WIN->display_start_addr (gdbarch_p, addr_p);
280   else
281     {
282       *gdbarch_p = nullptr;
283       *addr_p = 0;
284     }
285 }
286 
287 void
288 tui_win_info::resize (int height_, int width_,
289 		      int origin_x_, int origin_y_)
290 {
291   if (width == width_ && height == height_
292       && x == origin_x_ && y == origin_y_
293       && handle != nullptr)
294     return;
295 
296   width = width_;
297   height = height_;
298   x = origin_x_;
299   y = origin_y_;
300 
301   if (handle != nullptr)
302     {
303 #ifdef HAVE_WRESIZE
304       wresize (handle.get (), height, width);
305       mvwin (handle.get (), y, x);
306       wmove (handle.get (), 0, 0);
307 #else
308       handle.reset (nullptr);
309 #endif
310     }
311 
312   if (handle == nullptr)
313     make_window ();
314 
315   rerender ();
316 }
317 
318 
319 
320 /* Helper function to create one of the built-in (non-locator)
321    windows.  */
322 
323 template<enum tui_win_type V, class T>
324 static tui_win_info *
325 make_standard_window (const char *)
326 {
327   if (tui_win_list[V] == nullptr)
328     tui_win_list[V] = new T ();
329   return tui_win_list[V];
330 }
331 
332 /* Helper function to wrap tui_locator_win_info_ptr for
333    tui_get_window_by_name.  */
334 
335 static tui_win_info *
336 get_locator_window (const char *)
337 {
338   return tui_locator_win_info_ptr ();
339 }
340 
341 /* A map holding all the known window types, keyed by name.  Note that
342    this is heap-allocated and "leaked" at gdb exit.  This avoids
343    ordering issues with destroying elements in the map at shutdown.
344    In particular, destroying this map can occur after Python has been
345    shut down, causing crashes if any window destruction requires
346    running Python code.  */
347 
348 static std::unordered_map<std::string, window_factory> *known_window_types;
349 
350 /* Helper function that returns a TUI window, given its name.  */
351 
352 static tui_win_info *
353 tui_get_window_by_name (const std::string &name)
354 {
355   for (tui_win_info *window : saved_tui_windows)
356     if (name == window->name ())
357       return window;
358 
359   auto iter = known_window_types->find (name);
360   if (iter == known_window_types->end ())
361     error (_("Unknown window type \"%s\""), name.c_str ());
362 
363   tui_win_info *result = iter->second (name.c_str ());
364   if (result == nullptr)
365     error (_("Could not create window \"%s\""), name.c_str ());
366   return result;
367 }
368 
369 /* Initialize the known window types.  */
370 
371 static void
372 initialize_known_windows ()
373 {
374   known_window_types = new std::unordered_map<std::string, window_factory>;
375 
376   known_window_types->emplace (SRC_NAME,
377 			       make_standard_window<SRC_WIN,
378 						    tui_source_window>);
379   known_window_types->emplace (CMD_NAME,
380 			       make_standard_window<CMD_WIN, tui_cmd_window>);
381   known_window_types->emplace (DATA_NAME,
382 			       make_standard_window<DATA_WIN,
383 						    tui_data_window>);
384   known_window_types->emplace (DISASSEM_NAME,
385 			       make_standard_window<DISASSEM_WIN,
386 						    tui_disasm_window>);
387   known_window_types->emplace (STATUS_NAME, get_locator_window);
388 }
389 
390 /* See tui-layout.h.  */
391 
392 void
393 tui_register_window (const char *name, window_factory &&factory)
394 {
395   std::string name_copy = name;
396 
397   if (name_copy == SRC_NAME || name_copy == CMD_NAME || name_copy == DATA_NAME
398       || name_copy == DISASSEM_NAME || name_copy == STATUS_NAME)
399     error (_("Window type \"%s\" is built-in"), name);
400 
401   known_window_types->emplace (std::move (name_copy),
402 			       std::move (factory));
403 }
404 
405 /* See tui-layout.h.  */
406 
407 std::unique_ptr<tui_layout_base>
408 tui_layout_window::clone () const
409 {
410   tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
411   return std::unique_ptr<tui_layout_base> (result);
412 }
413 
414 /* See tui-layout.h.  */
415 
416 void
417 tui_layout_window::apply (int x_, int y_, int width_, int height_)
418 {
419   x = x_;
420   y = y_;
421   width = width_;
422   height = height_;
423   gdb_assert (m_window != nullptr);
424   m_window->resize (height, width, x, y);
425   tui_windows.push_back (m_window);
426 }
427 
428 /* See tui-layout.h.  */
429 
430 void
431 tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
432 {
433   if (m_window == nullptr)
434     m_window = tui_get_window_by_name (m_contents);
435   if (height)
436     {
437       *min_value = m_window->min_height ();
438       *max_value = m_window->max_height ();
439     }
440   else
441     {
442       *min_value = m_window->min_width ();
443       *max_value = m_window->max_width ();
444     }
445 }
446 
447 /* See tui-layout.h.  */
448 
449 bool
450 tui_layout_window::top_boxed_p () const
451 {
452   gdb_assert (m_window != nullptr);
453   return m_window->can_box ();
454 }
455 
456 /* See tui-layout.h.  */
457 
458 bool
459 tui_layout_window::bottom_boxed_p () const
460 {
461   gdb_assert (m_window != nullptr);
462   return m_window->can_box ();
463 }
464 
465 /* See tui-layout.h.  */
466 
467 void
468 tui_layout_window::replace_window (const char *name, const char *new_window)
469 {
470   if (m_contents == name)
471     {
472       m_contents = new_window;
473       if (m_window != nullptr)
474 	{
475 	  m_window->make_visible (false);
476 	  m_window = tui_get_window_by_name (m_contents);
477 	}
478     }
479 }
480 
481 /* See tui-layout.h.  */
482 
483 void
484 tui_layout_window::specification (ui_file *output, int depth)
485 {
486   fputs_unfiltered (get_name (), output);
487 }
488 
489 /* See tui-layout.h.  */
490 
491 void
492 tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
493 			     int weight)
494 {
495   split s = {weight, std::move (layout)};
496   m_splits.push_back (std::move (s));
497 }
498 
499 /* See tui-layout.h.  */
500 
501 void
502 tui_layout_split::add_window (const char *name, int weight)
503 {
504   tui_layout_window *result = new tui_layout_window (name);
505   split s = {weight, std::unique_ptr<tui_layout_base> (result)};
506   m_splits.push_back (std::move (s));
507 }
508 
509 /* See tui-layout.h.  */
510 
511 std::unique_ptr<tui_layout_base>
512 tui_layout_split::clone () const
513 {
514   tui_layout_split *result = new tui_layout_split (m_vertical);
515   for (const split &item : m_splits)
516     {
517       std::unique_ptr<tui_layout_base> next = item.layout->clone ();
518       split s = {item.weight, std::move (next)};
519       result->m_splits.push_back (std::move (s));
520     }
521   return std::unique_ptr<tui_layout_base> (result);
522 }
523 
524 /* See tui-layout.h.  */
525 
526 void
527 tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
528 {
529   *min_value = 0;
530   *max_value = 0;
531   bool first_time = true;
532   for (const split &item : m_splits)
533     {
534       int new_min, new_max;
535       item.layout->get_sizes (height, &new_min, &new_max);
536       /* For the mismatch case, the first time through we want to set
537 	 the min and max to the computed values -- the "first_time"
538 	 check here is just a funny way of doing that.  */
539       if (height == m_vertical || first_time)
540 	{
541 	  *min_value += new_min;
542 	  *max_value += new_max;
543 	}
544       else
545 	{
546 	  *min_value = std::max (*min_value, new_min);
547 	  *max_value = std::min (*max_value, new_max);
548 	}
549       first_time = false;
550     }
551 }
552 
553 /* See tui-layout.h.  */
554 
555 bool
556 tui_layout_split::top_boxed_p () const
557 {
558   if (m_splits.empty ())
559     return false;
560   return m_splits[0].layout->top_boxed_p ();
561 }
562 
563 /* See tui-layout.h.  */
564 
565 bool
566 tui_layout_split::bottom_boxed_p () const
567 {
568   if (m_splits.empty ())
569     return false;
570   return m_splits.back ().layout->top_boxed_p ();
571 }
572 
573 /* See tui-layout.h.  */
574 
575 void
576 tui_layout_split::set_weights_from_heights ()
577 {
578   for (int i = 0; i < m_splits.size (); ++i)
579     m_splits[i].weight = m_splits[i].layout->height;
580 }
581 
582 /* See tui-layout.h.  */
583 
584 tui_adjust_result
585 tui_layout_split::adjust_size (const char *name, int new_height)
586 {
587   /* Look through the children.  If one is a layout holding the named
588      window, we're done; or if one actually is the named window,
589      update it.  */
590   int found_index = -1;
591   for (int i = 0; i < m_splits.size (); ++i)
592     {
593       tui_adjust_result adjusted
594 	= m_splits[i].layout->adjust_size (name, new_height);
595       if (adjusted == HANDLED)
596 	return HANDLED;
597       if (adjusted == FOUND)
598 	{
599 	  if (!m_vertical)
600 	    return FOUND;
601 	  found_index = i;
602 	  break;
603 	}
604     }
605 
606   if (found_index == -1)
607     return NOT_FOUND;
608   if (m_splits[found_index].layout->height == new_height)
609     return HANDLED;
610 
611   set_weights_from_heights ();
612   int delta = m_splits[found_index].weight - new_height;
613   m_splits[found_index].weight = new_height;
614 
615   /* Distribute the "delta" over the next window; but if the next
616      window cannot hold it all, keep going until we either find a
617      window that does, or until we loop all the way around.  */
618   for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i)
619     {
620       int index = (found_index + 1 + i) % m_splits.size ();
621 
622       int new_min, new_max;
623       m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
624 
625       if (delta < 0)
626 	{
627 	  /* The primary window grew, so we are trying to shrink other
628 	     windows.  */
629 	  int available = m_splits[index].weight - new_min;
630 	  int shrink_by = std::min (available, -delta);
631 	  m_splits[index].weight -= shrink_by;
632 	  delta += shrink_by;
633 	}
634       else
635 	{
636 	  /* The primary window shrank, so we are trying to grow other
637 	     windows.  */
638 	  int available = new_max - m_splits[index].weight;
639 	  int grow_by = std::min (available, delta);
640 	  m_splits[index].weight += grow_by;
641 	  delta -= grow_by;
642 	}
643     }
644 
645   if (delta != 0)
646     {
647       warning (_("Invalid window height specified"));
648       /* Effectively undo any modifications made here.  */
649       set_weights_from_heights ();
650     }
651   else
652     {
653       /* Simply re-apply the updated layout.  */
654       apply (x, y, width, height);
655     }
656 
657   return HANDLED;
658 }
659 
660 /* See tui-layout.h.  */
661 
662 void
663 tui_layout_split::apply (int x_, int y_, int width_, int height_)
664 {
665   x = x_;
666   y = y_;
667   width = width_;
668   height = height_;
669 
670   struct size_info
671   {
672     int size;
673     int min_size;
674     int max_size;
675     /* True if this window will share a box border with the previous
676        window in the list.  */
677     bool share_box;
678   };
679 
680   std::vector<size_info> info (m_splits.size ());
681 
682   /* Step 1: Find the min and max size of each sub-layout.
683      Fixed-sized layouts are given their desired size, and then the
684      remaining space is distributed among the remaining windows
685      according to the weights given.  */
686   int available_size = m_vertical ? height : width;
687   int last_index = -1;
688   int total_weight = 0;
689   for (int i = 0; i < m_splits.size (); ++i)
690     {
691       bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
692 
693       /* Always call get_sizes, to ensure that the window is
694 	 instantiated.  This is a bit gross but less gross than adding
695 	 special cases for this in other places.  */
696       m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
697 				     &info[i].max_size);
698 
699       if (!m_applied
700 	  && cmd_win_already_exists
701 	  && m_splits[i].layout->get_name () != nullptr
702 	  && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
703 	{
704 	  /* If this layout has never been applied, then it means the
705 	     user just changed the layout.  In this situation, it's
706 	     desirable to keep the size of the command window the
707 	     same.  Setting the min and max sizes this way ensures
708 	     that the resizing step, below, does the right thing with
709 	     this window.  */
710 	  info[i].min_size = (m_vertical
711 			      ? TUI_CMD_WIN->height
712 			      : TUI_CMD_WIN->width);
713 	  info[i].max_size = info[i].min_size;
714 	}
715 
716       if (info[i].min_size == info[i].max_size)
717 	available_size -= info[i].min_size;
718       else
719 	{
720 	  last_index = i;
721 	  total_weight += m_splits[i].weight;
722 	}
723 
724       /* Two adjacent boxed windows will share a border, making a bit
725 	 more size available.  */
726       if (i > 0
727 	  && m_splits[i - 1].layout->bottom_boxed_p ()
728 	  && m_splits[i].layout->top_boxed_p ())
729 	info[i].share_box = true;
730     }
731 
732   /* Step 2: Compute the size of each sub-layout.  Fixed-sized items
733      are given their fixed size, while others are resized according to
734      their weight.  */
735   int used_size = 0;
736   for (int i = 0; i < m_splits.size (); ++i)
737     {
738       /* Compute the height and clamp to the allowable range.  */
739       info[i].size = available_size * m_splits[i].weight / total_weight;
740       if (info[i].size > info[i].max_size)
741 	info[i].size = info[i].max_size;
742       if (info[i].size < info[i].min_size)
743 	info[i].size = info[i].min_size;
744       /* If there is any leftover size, just redistribute it to the
745 	 last resizeable window, by dropping it from the allocated
746 	 size.  We could try to be fancier here perhaps, by
747 	 redistributing this size among all windows, not just the
748 	 last window.  */
749       if (info[i].min_size != info[i].max_size)
750 	{
751 	  used_size += info[i].size;
752 	  if (info[i].share_box)
753 	    --used_size;
754 	}
755     }
756 
757   /* Allocate any leftover size.  */
758   if (available_size >= used_size && last_index != -1)
759     info[last_index].size += available_size - used_size;
760 
761   /* Step 3: Resize.  */
762   int size_accum = 0;
763   const int maximum = m_vertical ? height : width;
764   for (int i = 0; i < m_splits.size (); ++i)
765     {
766       /* If we fall off the bottom, just make allocations overlap.
767 	 GIGO.  */
768       if (size_accum + info[i].size > maximum)
769 	size_accum = maximum - info[i].size;
770       else if (info[i].share_box)
771 	--size_accum;
772       if (m_vertical)
773 	m_splits[i].layout->apply (x, y + size_accum, width, info[i].size);
774       else
775 	m_splits[i].layout->apply (x + size_accum, y, info[i].size, height);
776       size_accum += info[i].size;
777     }
778 
779   m_applied = true;
780 }
781 
782 /* See tui-layout.h.  */
783 
784 void
785 tui_layout_split::remove_windows (const char *name)
786 {
787   for (int i = 0; i < m_splits.size (); ++i)
788     {
789       const char *this_name = m_splits[i].layout->get_name ();
790       if (this_name == nullptr)
791 	m_splits[i].layout->remove_windows (name);
792       else if (strcmp (this_name, name) == 0
793 	       || strcmp (this_name, CMD_NAME) == 0
794 	       || strcmp (this_name, STATUS_NAME) == 0)
795 	{
796 	  /* Keep.  */
797 	}
798       else
799 	{
800 	  m_splits.erase (m_splits.begin () + i);
801 	  --i;
802 	}
803     }
804 }
805 
806 /* See tui-layout.h.  */
807 
808 void
809 tui_layout_split::replace_window (const char *name, const char *new_window)
810 {
811   for (auto &item : m_splits)
812     item.layout->replace_window (name, new_window);
813 }
814 
815 /* See tui-layout.h.  */
816 
817 void
818 tui_layout_split::specification (ui_file *output, int depth)
819 {
820   if (depth > 0)
821     fputs_unfiltered ("{", output);
822 
823   if (!m_vertical)
824     fputs_unfiltered ("-horizontal ", output);
825 
826   bool first = true;
827   for (auto &item : m_splits)
828     {
829       if (!first)
830 	fputs_unfiltered (" ", output);
831       first = false;
832       item.layout->specification (output, depth + 1);
833       fprintf_unfiltered (output, " %d", item.weight);
834     }
835 
836   if (depth > 0)
837     fputs_unfiltered ("}", output);
838 }
839 
840 /* Destroy the layout associated with SELF.  */
841 
842 static void
843 destroy_layout (struct cmd_list_element *self, void *context)
844 {
845   tui_layout_split *layout = (tui_layout_split *) context;
846   size_t index = find_layout (layout);
847   layouts.erase (layouts.begin () + index);
848 }
849 
850 /* List holding the sub-commands of "layout".  */
851 
852 static struct cmd_list_element *layout_list;
853 
854 /* Add a "layout" command with name NAME that switches to LAYOUT.  */
855 
856 static struct cmd_list_element *
857 add_layout_command (const char *name, tui_layout_split *layout)
858 {
859   struct cmd_list_element *cmd;
860 
861   string_file spec;
862   layout->specification (&spec, 0);
863 
864   gdb::unique_xmalloc_ptr<char> doc
865     (xstrprintf (_("Apply the \"%s\" layout.\n\
866 This layout was created using:\n\
867   tui new-layout %s %s"),
868 		 name, name, spec.c_str ()));
869 
870   cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
871   set_cmd_context (cmd, layout);
872   /* There is no API to set this.  */
873   cmd->func = tui_apply_layout;
874   cmd->destroyer = destroy_layout;
875   cmd->doc_allocated = 1;
876   doc.release ();
877   layouts.emplace_back (layout);
878 
879   return cmd;
880 }
881 
882 /* Initialize the standard layouts.  */
883 
884 static void
885 initialize_layouts ()
886 {
887   tui_layout_split *layout;
888 
889   layout = new tui_layout_split ();
890   layout->add_window (SRC_NAME, 2);
891   layout->add_window (STATUS_NAME, 0);
892   layout->add_window (CMD_NAME, 1);
893   add_layout_command (SRC_NAME, layout);
894 
895   layout = new tui_layout_split ();
896   layout->add_window (DISASSEM_NAME, 2);
897   layout->add_window (STATUS_NAME, 0);
898   layout->add_window (CMD_NAME, 1);
899   add_layout_command (DISASSEM_NAME, layout);
900 
901   layout = new tui_layout_split ();
902   layout->add_window (SRC_NAME, 1);
903   layout->add_window (DISASSEM_NAME, 1);
904   layout->add_window (STATUS_NAME, 0);
905   layout->add_window (CMD_NAME, 1);
906   add_layout_command ("split", layout);
907 
908   layout = new tui_layout_split ();
909   layout->add_window (DATA_NAME, 1);
910   layout->add_window (SRC_NAME, 1);
911   layout->add_window (STATUS_NAME, 0);
912   layout->add_window (CMD_NAME, 1);
913   layouts.emplace_back (layout);
914   src_regs_layout = layout;
915 
916   layout = new tui_layout_split ();
917   layout->add_window (DATA_NAME, 1);
918   layout->add_window (DISASSEM_NAME, 1);
919   layout->add_window (STATUS_NAME, 0);
920   layout->add_window (CMD_NAME, 1);
921   layouts.emplace_back (layout);
922   asm_regs_layout = layout;
923 }
924 
925 
926 
927 /* A helper function that returns true if NAME is the name of an
928    available window.  */
929 
930 static bool
931 validate_window_name (const std::string &name)
932 {
933   auto iter = known_window_types->find (name);
934   return iter != known_window_types->end ();
935 }
936 
937 /* Implementation of the "tui new-layout" command.  */
938 
939 static void
940 tui_new_layout_command (const char *spec, int from_tty)
941 {
942   std::string new_name = extract_arg (&spec);
943   if (new_name.empty ())
944     error (_("No layout name specified"));
945   if (new_name[0] == '-')
946     error (_("Layout name cannot start with '-'"));
947 
948   bool is_vertical = true;
949   spec = skip_spaces (spec);
950   if (check_for_argument (&spec, "-horizontal"))
951     is_vertical = false;
952 
953   std::vector<std::unique_ptr<tui_layout_split>> splits;
954   splits.emplace_back (new tui_layout_split (is_vertical));
955   std::unordered_set<std::string> seen_windows;
956   while (true)
957     {
958       spec = skip_spaces (spec);
959       if (spec[0] == '\0')
960 	break;
961 
962       if (spec[0] == '{')
963 	{
964 	  is_vertical = true;
965 	  spec = skip_spaces (spec + 1);
966 	  if (check_for_argument (&spec, "-horizontal"))
967 	    is_vertical = false;
968 	  splits.emplace_back (new tui_layout_split (is_vertical));
969 	  continue;
970 	}
971 
972       bool is_close = false;
973       std::string name;
974       if (spec[0] == '}')
975 	{
976 	  is_close = true;
977 	  ++spec;
978 	  if (splits.size () == 1)
979 	    error (_("Extra '}' in layout specification"));
980 	}
981       else
982 	{
983 	  name = extract_arg (&spec);
984 	  if (name.empty ())
985 	    break;
986 	  if (!validate_window_name (name))
987 	    error (_("Unknown window \"%s\""), name.c_str ());
988 	  if (seen_windows.find (name) != seen_windows.end ())
989 	    error (_("Window \"%s\" seen twice in layout"), name.c_str ());
990 	}
991 
992       ULONGEST weight = get_ulongest (&spec, '}');
993       if ((int) weight != weight)
994 	error (_("Weight out of range: %s"), pulongest (weight));
995       if (is_close)
996 	{
997 	  std::unique_ptr<tui_layout_split> last_split
998 	    = std::move (splits.back ());
999 	  splits.pop_back ();
1000 	  splits.back ()->add_split (std::move (last_split), weight);
1001 	}
1002       else
1003 	{
1004 	  splits.back ()->add_window (name.c_str (), weight);
1005 	  seen_windows.insert (name);
1006 	}
1007     }
1008   if (splits.size () > 1)
1009     error (_("Missing '}' in layout specification"));
1010   if (seen_windows.empty ())
1011     error (_("New layout does not contain any windows"));
1012   if (seen_windows.find (CMD_NAME) == seen_windows.end ())
1013     error (_("New layout does not contain the \"" CMD_NAME "\" window"));
1014 
1015   gdb::unique_xmalloc_ptr<char> cmd_name
1016     = make_unique_xstrdup (new_name.c_str ());
1017   std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
1018   struct cmd_list_element *cmd
1019     = add_layout_command (cmd_name.get (), new_layout.get ());
1020   cmd->name_allocated = 1;
1021   cmd_name.release ();
1022   new_layout.release ();
1023 }
1024 
1025 /* Function to initialize gdb commands, for tui window layout
1026    manipulation.  */
1027 
1028 void _initialize_tui_layout ();
1029 void
1030 _initialize_tui_layout ()
1031 {
1032   add_basic_prefix_cmd ("layout", class_tui, _("\
1033 Change the layout of windows.\n\
1034 Usage: layout prev | next | LAYOUT-NAME"),
1035 			&layout_list, "layout ", 0, &cmdlist);
1036 
1037   add_cmd ("next", class_tui, tui_next_layout_command,
1038 	   _("Apply the next TUI layout."),
1039 	   &layout_list);
1040   add_cmd ("prev", class_tui, tui_prev_layout_command,
1041 	   _("Apply the previous TUI layout."),
1042 	   &layout_list);
1043   add_cmd ("regs", class_tui, tui_regs_layout_command,
1044 	   _("Apply the TUI register layout."),
1045 	   &layout_list);
1046 
1047   add_cmd ("new-layout", class_tui, tui_new_layout_command,
1048 	   _("Create a new TUI layout.\n\
1049 Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
1050 Create a new TUI layout.  The new layout will be named NAME,\n\
1051 and can be accessed using \"layout NAME\".\n\
1052 The windows will be displayed in the specified order.\n\
1053 A WINDOW can also be of the form:\n\
1054   { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
1055 This form indicates a sub-frame.\n\
1056 Each WEIGHT is an integer, which holds the relative size\n\
1057 to be allocated to the window."),
1058 	   tui_get_cmd_list ());
1059 
1060   initialize_layouts ();
1061   initialize_known_windows ();
1062 }
1063