aarch64: Normalize and sort feature bit macros
[deliverable/binutils-gdb.git] / gdb / tui / tui-layout.c
index fad4e374f57a50e9971678db879a8e9d7313eea6..8164b346370efa63815f63540e87ec2a2043c7dd 100644 (file)
@@ -1,7 +1,6 @@
 /* TUI layout window management.
 
-   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
-   Foundation, Inc.
+   Copyright (C) 1998-2020 Free Software Foundation, Inc.
 
    Contributed by Hewlett-Packard Company.
 
@@ -9,7 +8,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "arch-utils.h"
 #include "command.h"
 #include "symtab.h"
 #include "frame.h"
 #include "source.h"
+#include "cli/cli-cmds.h"
+#include "cli/cli-decode.h"
+#include "cli/cli-utils.h"
 #include <ctype.h>
+#include <unordered_map>
+#include <unordered_set>
 
 #include "tui/tui.h"
+#include "tui/tui-command.h"
 #include "tui/tui-data.h"
-#include "tui/tui-windata.h"
 #include "tui/tui-wingeneral.h"
 #include "tui/tui-stack.h"
 #include "tui/tui-regs.h"
 #include "tui/tui-win.h"
 #include "tui/tui-winsource.h"
 #include "tui/tui-disasm.h"
+#include "tui/tui-layout.h"
+#include "tui/tui-source.h"
+#include "gdb_curses.h"
 
-#ifdef HAVE_NCURSES_H       
-#include <ncurses.h>
-#else
-#ifdef HAVE_CURSES_H
-#include <curses.h>
-#endif
-#endif
+static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
 
-/*******************************
-** Static Local Decls
-********************************/
-static void showLayout (TuiLayoutType);
-static void _initGenWinInfo (TuiGenWinInfoPtr, TuiWinType, int, int, int, int);
-static void _initAndMakeWin (Opaque *, TuiWinType, int, int, int, int, int);
-static void _showSourceOrDisassemAndCommand (TuiLayoutType);
-static void _makeSourceOrDisassemWindow (TuiWinInfoPtr *, TuiWinType, int, int);
-static void _makeCommandWindow (TuiWinInfoPtr *, int, int);
-static void _makeSourceWindow (TuiWinInfoPtr *, int, int);
-static void _makeDisassemWindow (TuiWinInfoPtr *, int, int);
-static void _makeDataWindow (TuiWinInfoPtr *, int, int);
-static void _showSourceCommand (void);
-static void _showDisassemCommand (void);
-static void _showSourceDisassemCommand (void);
-static void _showData (TuiLayoutType);
-static TuiLayoutType _nextLayout (void);
-static TuiLayoutType _prevLayout (void);
-static void _tuiLayout_command (char *, int);
-static void _tuiToggleLayout_command (char *, int);
-static void _tuiToggleSplitLayout_command (char *, int);
-static CORE_ADDR _extractDisplayStartAddr (void);
-static void _tuiHandleXDBLayout (TuiLayoutDefPtr);
-
-
-/***************************************
-** DEFINITIONS
-***************************************/
-
-#define LAYOUT_USAGE     "Usage: layout prev | next | <layout_name> \n"
-
-/* Show the screen layout defined.  */
-static void
-showLayout (TuiLayoutType layout)
-{
-  TuiLayoutType curLayout = currentLayout ();
+/* The layouts.  */
+static std::vector<std::unique_ptr<tui_layout_split>> layouts;
 
-  if (layout != curLayout)
-    {
-      /*
-         ** Since the new layout may cause changes in window size, we
-         ** should free the content and reallocate on next display of
-         ** source/asm
-       */
-      freeAllSourceWinsContent ();
-      clearSourceWindows ();
-      if (layout == SRC_DATA_COMMAND || layout == DISASSEM_DATA_COMMAND)
-       {
-         _showData (layout);
-         refreshAll (winList);
-       }
-      else
-       {
-         /* First make the current layout be invisible */
-         m_allBeInvisible ();
-         m_beInvisible (locatorWinInfoPtr ());
-
-         switch (layout)
-           {
-             /* Now show the new layout */
-           case SRC_COMMAND:
-             _showSourceCommand ();
-             addToSourceWindows (srcWin);
-             break;
-           case DISASSEM_COMMAND:
-             _showDisassemCommand ();
-             addToSourceWindows (disassemWin);
-             break;
-           case SRC_DISASSEM_COMMAND:
-             _showSourceDisassemCommand ();
-             addToSourceWindows (srcWin);
-             addToSourceWindows (disassemWin);
-             break;
-           default:
-             break;
-           }
-       }
-    }
-}
+/* The layout that is currently applied.  */
+static std::unique_ptr<tui_layout_base> applied_layout;
+
+/* The "skeleton" version of the layout that is currently applied.  */
+static tui_layout_split *applied_skeleton;
 
+/* The two special "regs" layouts.  Note that these aren't registered
+   as commands and so can never be deleted.  */
+static tui_layout_split *src_regs_layout;
+static tui_layout_split *asm_regs_layout;
 
-/* Function to set the layout to SRC_COMMAND, DISASSEM_COMMAND,
-   SRC_DISASSEM_COMMAND, SRC_DATA_COMMAND, or DISASSEM_DATA_COMMAND.
-   If the layout is SRC_DATA_COMMAND, DISASSEM_DATA_COMMAND, or
-   UNDEFINED_LAYOUT, then the data window is populated according to
-   regsDisplayType.  */
-enum tui_status
-tui_set_layout (enum tui_layout_type layoutType,
-               enum tui_register_display_type regsDisplayType)
+/* See tui-data.h.  */
+std::vector<tui_win_info *> tui_windows;
+
+/* When applying a layout, this is the list of all windows that were
+   in the previous layout.  This is used to re-use windows when
+   changing a layout.  */
+static std::vector<tui_win_info *> saved_tui_windows;
+
+/* See tui-layout.h.  */
+
+void
+tui_apply_current_layout ()
 {
-  TuiStatus status = TUI_SUCCESS;
+  struct gdbarch *gdbarch;
+  CORE_ADDR addr;
 
-  if (layoutType != UNDEFINED_LAYOUT || regsDisplayType != TUI_UNDEFINED_REGS)
-    {
-      TuiLayoutType curLayout = currentLayout (), newLayout = UNDEFINED_LAYOUT;
-      int regsPopulate = FALSE;
-      CORE_ADDR addr = _extractDisplayStartAddr ();
-      TuiWinInfoPtr newWinWithFocus = (TuiWinInfoPtr) NULL, winWithFocus = tuiWinWithFocus ();
-      TuiLayoutDefPtr layoutDef = tuiLayoutDef ();
+  extract_display_start_addr (&gdbarch, &addr);
 
+  saved_tui_windows = std::move (tui_windows);
+  tui_windows.clear ();
 
-      if (layoutType == UNDEFINED_LAYOUT &&
-         regsDisplayType != TUI_UNDEFINED_REGS)
-       {
-         if (curLayout == SRC_DISASSEM_COMMAND)
-           newLayout = DISASSEM_DATA_COMMAND;
-         else if (curLayout == SRC_COMMAND || curLayout == SRC_DATA_COMMAND)
-           newLayout = SRC_DATA_COMMAND;
-         else if (curLayout == DISASSEM_COMMAND ||
-                  curLayout == DISASSEM_DATA_COMMAND)
-           newLayout = DISASSEM_DATA_COMMAND;
-       }
-      else
-       newLayout = layoutType;
+  for (tui_win_info *win_info : saved_tui_windows)
+    win_info->make_visible (false);
 
-      regsPopulate = (newLayout == SRC_DATA_COMMAND ||
-                     newLayout == DISASSEM_DATA_COMMAND ||
-                     regsDisplayType != TUI_UNDEFINED_REGS);
-      if (newLayout != curLayout || regsDisplayType != TUI_UNDEFINED_REGS)
+  applied_layout->apply (0, 0, tui_term_width (), tui_term_height ());
+
+  /* Keep the list of internal windows up-to-date.  */
+  for (int win_type = SRC_WIN; (win_type < MAX_MAJOR_WINDOWS); win_type++)
+    if (tui_win_list[win_type] != nullptr
+       && !tui_win_list[win_type]->is_visible ())
+      tui_win_list[win_type] = nullptr;
+
+  /* This should always be made visible by a layout.  */
+  gdb_assert (TUI_CMD_WIN->is_visible ());
+
+  /* Now delete any window that was not re-applied.  */
+  tui_win_info *focus = tui_win_with_focus ();
+  for (tui_win_info *win_info : saved_tui_windows)
+    {
+      if (!win_info->is_visible ())
        {
-         if (newLayout != curLayout)
-           {
-             showLayout (newLayout);
-             /*
-                ** Now determine where focus should be
-              */
-             if (winWithFocus != cmdWin)
-               {
-                 switch (newLayout)
-                   {
-                   case SRC_COMMAND:
-                     tuiSetWinFocusTo (srcWin);
-                     layoutDef->displayMode = SRC_WIN;
-                     layoutDef->split = FALSE;
-                     break;
-                   case DISASSEM_COMMAND:
-                     /* the previous layout was not showing
-                        ** code. this can happen if there is no
-                        ** source available:
-                        ** 1. if the source file is in another dir OR
-                        ** 2. if target was compiled without -g
-                        ** We still want to show the assembly though!
-                      */
-                     addr = tui_get_begin_asm_address ();
-                     tuiSetWinFocusTo (disassemWin);
-                     layoutDef->displayMode = DISASSEM_WIN;
-                     layoutDef->split = FALSE;
-                     break;
-                   case SRC_DISASSEM_COMMAND:
-                     /* the previous layout was not showing
-                        ** code. this can happen if there is no
-                        ** source available:
-                        ** 1. if the source file is in another dir OR
-                        ** 2. if target was compiled without -g
-                        ** We still want to show the assembly though!
-                      */
-                     addr = tui_get_begin_asm_address ();
-                     if (winWithFocus == srcWin)
-                       tuiSetWinFocusTo (srcWin);
-                     else
-                       tuiSetWinFocusTo (disassemWin);
-                     layoutDef->split = TRUE;
-                     break;
-                   case SRC_DATA_COMMAND:
-                     if (winWithFocus != dataWin)
-                       tuiSetWinFocusTo (srcWin);
-                     else
-                       tuiSetWinFocusTo (dataWin);
-                     layoutDef->displayMode = SRC_WIN;
-                     layoutDef->split = FALSE;
-                     break;
-                   case DISASSEM_DATA_COMMAND:
-                     /* the previous layout was not showing
-                        ** code. this can happen if there is no
-                        ** source available:
-                        ** 1. if the source file is in another dir OR
-                        ** 2. if target was compiled without -g
-                        ** We still want to show the assembly though!
-                      */
-                     addr = tui_get_begin_asm_address ();
-                     if (winWithFocus != dataWin)
-                       tuiSetWinFocusTo (disassemWin);
-                     else
-                       tuiSetWinFocusTo (dataWin);
-                     layoutDef->displayMode = DISASSEM_WIN;
-                     layoutDef->split = FALSE;
-                     break;
-                   default:
-                     break;
-                   }
-               }
-             if (newWinWithFocus != (TuiWinInfoPtr) NULL)
-               tuiSetWinFocusTo (newWinWithFocus);
-             /*
-                ** Now update the window content
-              */
-             if (!regsPopulate &&
-                 (newLayout == SRC_DATA_COMMAND ||
-                  newLayout == DISASSEM_DATA_COMMAND))
-               tuiDisplayAllData ();
-
-             tui_update_source_windows_with_addr (addr);
-           }
-         if (regsPopulate)
-           {
-             layoutDef->regsDisplayType =
-               (regsDisplayType == TUI_UNDEFINED_REGS ?
-                TUI_GENERAL_REGS : regsDisplayType);
-             tui_show_registers (layoutDef->regsDisplayType);
-           }
+         if (focus == win_info)
+           tui_set_win_focus_to (tui_windows[0]);
+         delete win_info;
        }
     }
-  else
-    status = TUI_FAILURE;
 
-  return status;
+  if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
+    tui_get_begin_asm_address (&gdbarch, &addr);
+  tui_update_source_windows_with_addr (gdbarch, addr);
+
+  saved_tui_windows.clear ();
 }
 
-/* Add the specified window to the layout in a logical way.  This
-   means setting up the most logical layout given the window to be
-   added.  */
+/* See tui-layout.  */
+
 void
-tui_add_win_to_layout (enum tui_win_type type)
+tui_adjust_window_height (struct tui_win_info *win, int new_height)
 {
-  TuiLayoutType curLayout = currentLayout ();
+  applied_layout->adjust_size (win->name (), new_height);
+}
 
-  switch (type)
-    {
-    case SRC_WIN:
-      if (curLayout != SRC_COMMAND &&
-         curLayout != SRC_DISASSEM_COMMAND &&
-         curLayout != SRC_DATA_COMMAND)
-       {
-         clearSourceWindowsDetail ();
-         if (curLayout == DISASSEM_DATA_COMMAND)
-           showLayout (SRC_DATA_COMMAND);
-         else
-           showLayout (SRC_COMMAND);
-       }
-      break;
-    case DISASSEM_WIN:
-      if (curLayout != DISASSEM_COMMAND &&
-         curLayout != SRC_DISASSEM_COMMAND &&
-         curLayout != DISASSEM_DATA_COMMAND)
-       {
-         clearSourceWindowsDetail ();
-         if (curLayout == SRC_DATA_COMMAND)
-           showLayout (DISASSEM_DATA_COMMAND);
-         else
-           showLayout (DISASSEM_COMMAND);
-       }
-      break;
-    case DATA_WIN:
-      if (curLayout != SRC_DATA_COMMAND &&
-         curLayout != DISASSEM_DATA_COMMAND)
-       {
-         if (curLayout == DISASSEM_COMMAND)
-           showLayout (DISASSEM_DATA_COMMAND);
-         else
-           showLayout (SRC_DATA_COMMAND);
-       }
-      break;
-    default:
-      break;
-    }
+/* Set the current layout to LAYOUT.  */
 
-  return;
-}                              /* tuiAddWinToLayout */
+static void
+tui_set_layout (tui_layout_split *layout)
+{
+  applied_skeleton = layout;
+  applied_layout = layout->clone ();
+  tui_apply_current_layout ();
+}
 
+/* See tui-layout.h.  */
 
-/*
-   ** tuiDefaultWinHeight().
-   **        Answer the height of a window.  If it hasn't been created yet,
-   **        answer what the height of a window would be based upon its
-   **        type and the layout.
- */
-int
-tuiDefaultWinHeight (TuiWinType type, TuiLayoutType layout)
+void
+tui_add_win_to_layout (enum tui_win_type type)
 {
-  int h;
+  gdb_assert (type == SRC_WIN || type == DISASSEM_WIN);
 
-  if (winList[type] != (TuiWinInfoPtr) NULL)
-    h = winList[type]->generic.height;
-  else
+  /* If the window already exists, no need to add it.  */
+  if (tui_win_list[type] != nullptr)
+    return;
+
+  /* If the window we are trying to replace doesn't exist, we're
+     done.  */
+  enum tui_win_type other = type == SRC_WIN ? DISASSEM_WIN : SRC_WIN;
+  if (tui_win_list[other] == nullptr)
+    return;
+
+  const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
+  applied_layout->replace_window (tui_win_list[other]->name (), name);
+  tui_apply_current_layout ();
+}
+
+/* Find LAYOUT in the "layouts" global and return its index.  */
+
+static size_t
+find_layout (tui_layout_split *layout)
+{
+  for (size_t i = 0; i < layouts.size (); ++i)
     {
-      switch (layout)
-       {
-       case SRC_COMMAND:
-       case DISASSEM_COMMAND:
-         if (m_winPtrIsNull (cmdWin))
-           h = termHeight () / 2;
-         else
-           h = termHeight () - cmdWin->generic.height;
-         break;
-       case SRC_DISASSEM_COMMAND:
-       case SRC_DATA_COMMAND:
-       case DISASSEM_DATA_COMMAND:
-         if (m_winPtrIsNull (cmdWin))
-           h = termHeight () / 3;
-         else
-           h = (termHeight () - cmdWin->generic.height) / 2;
-         break;
-       default:
-         h = 0;
-         break;
-       }
+      if (layout == layouts[i].get ())
+       return i;
     }
+  gdb_assert_not_reached (_("layout not found!?"));
+}
 
-  return h;
-}                              /* tuiDefaultWinHeight */
-
+/* Function to set the layout. */
 
-/* Answer the height of a window.  If it hasn't been created yet,
-   answer what the height of a window would be based upon its type and
-   the layout.  */
-int
-tui_default_win_viewport_height (enum tui_win_type type,
-                                enum tui_layout_type layout)
+static void
+tui_apply_layout (struct cmd_list_element *command,
+                 const char *args, int from_tty)
 {
-  int h;
+  tui_layout_split *layout
+    = (tui_layout_split *) get_cmd_context (command);
 
-  h = tuiDefaultWinHeight (type, layout);
+  /* Make sure the curses mode is enabled.  */
+  tui_enable ();
+  tui_set_layout (layout);
+}
 
-  if (winList[type] == cmdWin)
-    h -= 1;
-  else
-    h -= 2;
+/* See tui-layout.h.  */
 
-  return h;
-}                              /* tuiDefaultWinViewportHeight */
+void
+tui_next_layout ()
+{
+  size_t index = find_layout (applied_skeleton);
+  ++index;
+  if (index == layouts.size ())
+    index = 0;
+  tui_set_layout (layouts[index].get ());
+}
 
+/* Implement the "layout next" command.  */
+
+static void
+tui_next_layout_command (const char *arg, int from_tty)
+{
+  tui_enable ();
+  tui_next_layout ();
+}
+
+/* See tui-layout.h.  */
 
-/*
-   ** _initialize_tuiLayout().
-   **        Function to initialize gdb commands, for tui window layout
-   **        manipulation.
- */
 void
-_initialize_tuiLayout (void)
+tui_set_initial_layout ()
 {
-  add_com ("layout", class_tui, _tuiLayout_command,
-           "Change the layout of windows.\n\
-Usage: layout prev | next | <layout_name> \n\
-Layout names are:\n\
-   src   : Displays source and command windows.\n\
-   asm   : Displays disassembly and command windows.\n\
-   split : Displays source, disassembly and command windows.\n\
-   regs  : Displays register window. If existing layout\n\
-           is source/command or assembly/command, the \n\
-           register window is displayed. If the\n\
-           source/assembly/command (split) is displayed, \n\
-           the register window is displayed with \n\
-           the window that has current logical focus.\n");
-  if (xdb_commands)
-    {
-      add_com ("td", class_tui, _tuiToggleLayout_command,
-               "Toggle between Source/Command and Disassembly/Command layouts.\n");
-      add_com ("ts", class_tui, _tuiToggleSplitLayout_command,
-               "Toggle between Source/Command or Disassembly/Command and \n\
-Source/Disassembly/Command layouts.\n");
-    }
+  tui_set_layout (layouts[0].get ());
 }
 
+/* Implement the "layout prev" command.  */
+
+static void
+tui_prev_layout_command (const char *arg, int from_tty)
+{
+  tui_enable ();
+  size_t index = find_layout (applied_skeleton);
+  if (index == 0)
+    index = layouts.size ();
+  --index;
+  tui_set_layout (layouts[index].get ());
+}
 
-/*************************
-** STATIC LOCAL FUNCTIONS
-**************************/
 
+/* See tui-layout.h.  */
 
-/*
-   ** _tuiSetLayoutTo()
-   **    Function to set the layout to SRC, ASM, SPLIT, NEXT, PREV, DATA, REGS,
-   **        $REGS, $GREGS, $FREGS, $SREGS.
- */
-TuiStatus
-tui_set_layout_for_display_command (const char *layoutName)
+void
+tui_regs_layout ()
 {
-  TuiStatus status = TUI_SUCCESS;
+  /* If there's already a register window, we're done.  */
+  if (TUI_DATA_WIN != nullptr)
+    return;
 
-  if (layoutName != (char *) NULL)
-    {
-      register int i;
-      register char *bufPtr;
-      TuiLayoutType newLayout = UNDEFINED_LAYOUT;
-      TuiRegisterDisplayType dpyType = TUI_UNDEFINED_REGS;
-      TuiLayoutType curLayout = currentLayout ();
-
-      bufPtr = (char *) xstrdup (layoutName);
-      for (i = 0; (i < strlen (layoutName)); i++)
-       bufPtr[i] = toupper (bufPtr[i]);
-
-      /* First check for ambiguous input */
-      if (strlen (bufPtr) <= 1 && (*bufPtr == 'S' || *bufPtr == '$'))
-       {
-         warning ("Ambiguous command input.\n");
-         status = TUI_FAILURE;
-       }
-      else
-       {
-         if (subset_compare (bufPtr, "SRC"))
-           newLayout = SRC_COMMAND;
-         else if (subset_compare (bufPtr, "ASM"))
-           newLayout = DISASSEM_COMMAND;
-         else if (subset_compare (bufPtr, "SPLIT"))
-           newLayout = SRC_DISASSEM_COMMAND;
-         else if (subset_compare (bufPtr, "REGS") ||
-                  subset_compare (bufPtr, TUI_GENERAL_SPECIAL_REGS_NAME) ||
-                  subset_compare (bufPtr, TUI_GENERAL_REGS_NAME) ||
-                  subset_compare (bufPtr, TUI_FLOAT_REGS_NAME) ||
-                  subset_compare (bufPtr, TUI_SPECIAL_REGS_NAME))
-           {
-             if (curLayout == SRC_COMMAND || curLayout == SRC_DATA_COMMAND)
-               newLayout = SRC_DATA_COMMAND;
-             else
-               newLayout = DISASSEM_DATA_COMMAND;
-
-/* could ifdef out the following code. when compile with -z, there are null 
-   pointer references that cause a core dump if 'layout regs' is the first 
-   layout command issued by the user. HP has asked us to hook up this code 
-   - edie epstein
- */
-             if (subset_compare (bufPtr, TUI_FLOAT_REGS_NAME))
-               {
-                 if (dataWin->detail.dataDisplayInfo.regsDisplayType !=
-                     TUI_SFLOAT_REGS &&
-                     dataWin->detail.dataDisplayInfo.regsDisplayType !=
-                     TUI_DFLOAT_REGS)
-                   dpyType = TUI_SFLOAT_REGS;
-                 else
-                   dpyType =
-                     dataWin->detail.dataDisplayInfo.regsDisplayType;
-               }
-             else if (subset_compare (bufPtr,
-                                     TUI_GENERAL_SPECIAL_REGS_NAME))
-               dpyType = TUI_GENERAL_AND_SPECIAL_REGS;
-             else if (subset_compare (bufPtr, TUI_GENERAL_REGS_NAME))
-               dpyType = TUI_GENERAL_REGS;
-             else if (subset_compare (bufPtr, TUI_SPECIAL_REGS_NAME))
-               dpyType = TUI_SPECIAL_REGS;
-             else if (dataWin)
-               {
-                 if (dataWin->detail.dataDisplayInfo.regsDisplayType !=
-                     TUI_UNDEFINED_REGS)
-                   dpyType =
-                     dataWin->detail.dataDisplayInfo.regsDisplayType;
-                 else
-                   dpyType = TUI_GENERAL_REGS;
-               }
-
-/* end of potential ifdef 
- */
-
-/* if ifdefed out code above, then assume that the user wishes to display the 
-   general purpose registers 
- */
-
-/*              dpyType = TUI_GENERAL_REGS; 
- */
-           }
-         else if (subset_compare (bufPtr, "NEXT"))
-           newLayout = _nextLayout ();
-         else if (subset_compare (bufPtr, "PREV"))
-           newLayout = _prevLayout ();
-         else
-           status = TUI_FAILURE;
-         xfree (bufPtr);
-
-         tui_set_layout (newLayout, dpyType);
-       }
-    }
-  else
-    status = TUI_FAILURE;
+  tui_set_layout (TUI_DISASM_WIN != nullptr
+                 ? asm_regs_layout
+                 : src_regs_layout);
+}
+
+/* Implement the "layout regs" command.  */
 
-  return status;
+static void
+tui_regs_layout_command (const char *arg, int from_tty)
+{
+  tui_enable ();
+  tui_regs_layout ();
 }
 
+/* See tui-layout.h.  */
 
-static CORE_ADDR
-_extractDisplayStartAddr (void)
+void
+tui_remove_some_windows ()
 {
-  TuiLayoutType curLayout = currentLayout ();
-  CORE_ADDR addr;
-  CORE_ADDR pc;
-  struct symtab_and_line cursal = get_current_source_symtab_and_line ();
+  tui_win_info *focus = tui_win_with_focus ();
 
-  switch (curLayout)
+  if (strcmp (focus->name (), CMD_NAME) == 0)
     {
-    case SRC_COMMAND:
-    case SRC_DATA_COMMAND:
-      find_line_pc (cursal.symtab,
-                   srcWin->detail.sourceInfo.startLineOrAddr.lineNo,
-                   &pc);
-      addr = pc;
-      break;
-    case DISASSEM_COMMAND:
-    case SRC_DISASSEM_COMMAND:
-    case DISASSEM_DATA_COMMAND:
-      addr = disassemWin->detail.sourceInfo.startLineOrAddr.addr;
-      break;
-    default:
-      addr = 0;
-      break;
+      /* Try leaving the source or disassembly window.  If neither
+        exists, just do nothing.  */
+      focus = TUI_SRC_WIN;
+      if (focus == nullptr)
+       focus = TUI_DISASM_WIN;
+      if (focus == nullptr)
+       return;
     }
 
-  return addr;
-}                              /* _extractDisplayStartAddr */
-
+  applied_layout->remove_windows (focus->name ());
+  tui_apply_current_layout ();
+}
 
 static void
-_tuiHandleXDBLayout (TuiLayoutDefPtr layoutDef)
+extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
 {
-  if (layoutDef->split)
+  if (TUI_SRC_WIN != nullptr)
+    TUI_SRC_WIN->display_start_addr (gdbarch_p, addr_p);
+  else if (TUI_DISASM_WIN != nullptr)
+    TUI_DISASM_WIN->display_start_addr (gdbarch_p, addr_p);
+  else
     {
-      tui_set_layout (SRC_DISASSEM_COMMAND, TUI_UNDEFINED_REGS);
-      tuiSetWinFocusTo (winList[layoutDef->displayMode]);
+      *gdbarch_p = nullptr;
+      *addr_p = 0;
     }
-  else
+}
+
+void
+tui_gen_win_info::resize (int height_, int width_,
+                         int origin_x_, int origin_y_)
+{
+  if (width == width_ && height == height_
+      && x == origin_x_ && y == origin_y_
+      && handle != nullptr)
+    return;
+
+  width = width_;
+  height = height_;
+  x = origin_x_;
+  y = origin_y_;
+
+  if (handle != nullptr)
     {
-      if (layoutDef->displayMode == SRC_WIN)
-       tui_set_layout (SRC_COMMAND, TUI_UNDEFINED_REGS);
-      else
-       tui_set_layout (DISASSEM_DATA_COMMAND, layoutDef->regsDisplayType);
+#ifdef HAVE_WRESIZE
+      wresize (handle.get (), height, width);
+      mvwin (handle.get (), y, x);
+      wmove (handle.get (), 0, 0);
+#else
+      handle.reset (nullptr);
+#endif
     }
 
+  if (handle == nullptr)
+    make_window ();
 
-  return;
-}                              /* _tuiHandleXDBLayout */
+  rerender ();
+}
 
+\f
 
-static void
-_tuiToggleLayout_command (char *arg, int fromTTY)
+/* Helper function to create one of the built-in (non-locator)
+   windows.  */
+
+template<enum tui_win_type V, class T>
+static tui_gen_win_info *
+make_standard_window (const char *)
 {
-  TuiLayoutDefPtr layoutDef = tuiLayoutDef ();
+  if (tui_win_list[V] == nullptr)
+    tui_win_list[V] = new T ();
+  return tui_win_list[V];
+}
 
-  /* Make sure the curses mode is enabled.  */
-  tui_enable ();
-  if (layoutDef->displayMode == SRC_WIN)
-    layoutDef->displayMode = DISASSEM_WIN;
-  else
-    layoutDef->displayMode = SRC_WIN;
+/* Helper function to wrap tui_locator_win_info_ptr for
+   tui_get_window_by_name.  */
+
+static tui_gen_win_info *
+get_locator_window (const char *)
+{
+  return tui_locator_win_info_ptr ();
+}
+
+/* A map holding all the known window types, keyed by name.  Note that
+   this is heap-allocated and "leaked" at gdb exit.  This avoids
+   ordering issues with destroying elements in the map at shutdown.
+   In particular, destroying this map can occur after Python has been
+   shut down, causing crashes if any window destruction requires
+   running Python code.  */
 
-  if (!layoutDef->split)
-    _tuiHandleXDBLayout (layoutDef);
+static std::unordered_map<std::string, window_factory> *known_window_types;
 
+/* Helper function that returns a TUI window, given its name.  */
+
+static tui_gen_win_info *
+tui_get_window_by_name (const std::string &name)
+{
+  for (tui_win_info *window : saved_tui_windows)
+    if (name == window->name ())
+      return window;
+
+  auto iter = known_window_types->find (name);
+  if (iter == known_window_types->end ())
+    error (_("Unknown window type \"%s\""), name.c_str ());
+
+  tui_gen_win_info *result = iter->second (name.c_str ());
+  if (result == nullptr)
+    error (_("Could not create window \"%s\""), name.c_str ());
+  return result;
 }
 
+/* Initialize the known window types.  */
 
 static void
-_tuiToggleSplitLayout_command (char *arg, int fromTTY)
+initialize_known_windows ()
 {
-  TuiLayoutDefPtr layoutDef = tuiLayoutDef ();
+  known_window_types = new std::unordered_map<std::string, window_factory>;
+
+  known_window_types->emplace (SRC_NAME,
+                              make_standard_window<SRC_WIN,
+                                                   tui_source_window>);
+  known_window_types->emplace (CMD_NAME,
+                              make_standard_window<CMD_WIN, tui_cmd_window>);
+  known_window_types->emplace (DATA_NAME,
+                              make_standard_window<DATA_WIN,
+                                                   tui_data_window>);
+  known_window_types->emplace (DISASSEM_NAME,
+                              make_standard_window<DISASSEM_WIN,
+                                                   tui_disasm_window>);
+  known_window_types->emplace (STATUS_NAME, get_locator_window);
+}
 
-  /* Make sure the curses mode is enabled.  */
-  tui_enable ();
-  layoutDef->split = (!layoutDef->split);
-  _tuiHandleXDBLayout (layoutDef);
+/* See tui-layout.h.  */
 
+void
+tui_register_window (const char *name, window_factory &&factory)
+{
+  std::string name_copy = name;
+
+  if (name_copy == SRC_NAME || name_copy == CMD_NAME || name_copy == DATA_NAME
+      || name_copy == DISASSEM_NAME || name_copy == STATUS_NAME)
+    error (_("Window type \"%s\" is built-in"), name);
+
+  known_window_types->emplace (std::move (name_copy),
+                              std::move (factory));
 }
 
+/* See tui-layout.h.  */
 
-static void
-_tuiLayout_command (char *arg, int fromTTY)
+std::unique_ptr<tui_layout_base>
+tui_layout_window::clone () const
 {
-  /* Make sure the curses mode is enabled.  */
-  tui_enable ();
+  tui_layout_window *result = new tui_layout_window (m_contents.c_str ());
+  return std::unique_ptr<tui_layout_base> (result);
+}
 
-  /* Switch to the selected layout.  */
-  if (tui_set_layout_for_display_command (arg) != TUI_SUCCESS)
-    warning ("Invalid layout specified.\n%s", LAYOUT_USAGE);
+/* See tui-layout.h.  */
 
+void
+tui_layout_window::apply (int x_, int y_, int width_, int height_)
+{
+  x = x_;
+  y = y_;
+  width = width_;
+  height = height_;
+  gdb_assert (m_window != nullptr);
+  m_window->resize (height, width, x, y);
+  if (dynamic_cast<tui_win_info *> (m_window) != nullptr)
+    tui_windows.push_back ((tui_win_info *) m_window);
 }
 
-/*
-   ** _nextLayout().
-   **        Answer the previous layout to cycle to.
- */
-static TuiLayoutType
-_nextLayout (void)
-{
-  TuiLayoutType newLayout;
+/* See tui-layout.h.  */
 
-  newLayout = currentLayout ();
-  if (newLayout == UNDEFINED_LAYOUT)
-    newLayout = SRC_COMMAND;
+void
+tui_layout_window::get_sizes (bool height, int *min_value, int *max_value)
+{
+  if (m_window == nullptr)
+    m_window = tui_get_window_by_name (m_contents);
+  if (height)
+    {
+      *min_value = m_window->min_height ();
+      *max_value = m_window->max_height ();
+    }
   else
     {
-      newLayout++;
-      if (newLayout == UNDEFINED_LAYOUT)
-       newLayout = SRC_COMMAND;
+      *min_value = m_window->min_width ();
+      *max_value = m_window->max_width ();
     }
+}
+
+/* See tui-layout.h.  */
 
-  return newLayout;
-}                              /* _nextLayout */
+bool
+tui_layout_window::top_boxed_p () const
+{
+  gdb_assert (m_window != nullptr);
+  return m_window->can_box ();
+}
 
+/* See tui-layout.h.  */
 
-/*
-   ** _prevLayout().
-   **        Answer the next layout to cycle to.
- */
-static TuiLayoutType
-_prevLayout (void)
+bool
+tui_layout_window::bottom_boxed_p () const
 {
-  TuiLayoutType newLayout;
+  gdb_assert (m_window != nullptr);
+  return m_window->can_box ();
+}
 
-  newLayout = currentLayout ();
-  if (newLayout == SRC_COMMAND)
-    newLayout = DISASSEM_DATA_COMMAND;
-  else
+/* See tui-layout.h.  */
+
+void
+tui_layout_window::replace_window (const char *name, const char *new_window)
+{
+  if (m_contents == name)
     {
-      newLayout--;
-      if (newLayout == UNDEFINED_LAYOUT)
-       newLayout = DISASSEM_DATA_COMMAND;
+      m_contents = new_window;
+      if (m_window != nullptr)
+       {
+         m_window->make_visible (false);
+         m_window = tui_get_window_by_name (m_contents);
+       }
     }
+}
 
-  return newLayout;
-}                              /* _prevLayout */
+/* See tui-layout.h.  */
 
+void
+tui_layout_window::specification (ui_file *output, int depth)
+{
+  fputs_unfiltered (get_name (), output);
+}
 
+/* See tui-layout.h.  */
 
-/*
-   ** _makeCommandWindow().
- */
-static void
-_makeCommandWindow (TuiWinInfoPtr * winInfoPtr, int height, int originY)
+void
+tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
+                            int weight)
 {
-  _initAndMakeWin ((Opaque *) winInfoPtr,
-                  CMD_WIN,
-                  height,
-                  termWidth (),
-                  0,
-                  originY,
-                  DONT_BOX_WINDOW);
+  split s = {weight, std::move (layout)};
+  m_splits.push_back (std::move (s));
+}
 
-  (*winInfoPtr)->canHighlight = FALSE;
+/* See tui-layout.h.  */
 
-  return;
-}                              /* _makeCommandWindow */
+void
+tui_layout_split::add_window (const char *name, int weight)
+{
+  tui_layout_window *result = new tui_layout_window (name);
+  split s = {weight, std::unique_ptr<tui_layout_base> (result)};
+  m_splits.push_back (std::move (s));
+}
 
+/* See tui-layout.h.  */
 
-/*
-   ** _makeSourceWindow().
- */
-static void
-_makeSourceWindow (TuiWinInfoPtr * winInfoPtr, int height, int originY)
+std::unique_ptr<tui_layout_base>
+tui_layout_split::clone () const
 {
-  _makeSourceOrDisassemWindow (winInfoPtr, SRC_WIN, height, originY);
-
-  return;
-}                              /* _makeSourceWindow */
+  tui_layout_split *result = new tui_layout_split (m_vertical);
+  for (const split &item : m_splits)
+    {
+      std::unique_ptr<tui_layout_base> next = item.layout->clone ();
+      split s = {item.weight, std::move (next)};
+      result->m_splits.push_back (std::move (s));
+    }
+  return std::unique_ptr<tui_layout_base> (result);
+}
 
+/* See tui-layout.h.  */
 
-/*
-   ** _makeDisassemWindow().
- */
-static void
-_makeDisassemWindow (TuiWinInfoPtr * winInfoPtr, int height, int originY)
+void
+tui_layout_split::get_sizes (bool height, int *min_value, int *max_value)
 {
-  _makeSourceOrDisassemWindow (winInfoPtr, DISASSEM_WIN, height, originY);
-
-  return;
-}                              /* _makeDisassemWindow */
+  *min_value = 0;
+  *max_value = 0;
+  bool first_time = true;
+  for (const split &item : m_splits)
+    {
+      int new_min, new_max;
+      item.layout->get_sizes (height, &new_min, &new_max);
+      /* For the mismatch case, the first time through we want to set
+        the min and max to the computed values -- the "first_time"
+        check here is just a funny way of doing that.  */
+      if (height == m_vertical || first_time)
+       {
+         *min_value += new_min;
+         *max_value += new_max;
+       }
+      else
+       {
+         *min_value = std::max (*min_value, new_min);
+         *max_value = std::min (*max_value, new_max);
+       }
+      first_time = false;
+    }
+}
 
+/* See tui-layout.h.  */
 
-/*
-   ** _makeDataWindow().
- */
-static void
-_makeDataWindow (TuiWinInfoPtr * winInfoPtr, int height, int originY)
+bool
+tui_layout_split::top_boxed_p () const
 {
-  _initAndMakeWin ((Opaque *) winInfoPtr,
-                  DATA_WIN,
-                  height,
-                  termWidth (),
-                  0,
-                  originY,
-                  BOX_WINDOW);
+  if (m_splits.empty ())
+    return false;
+  return m_splits[0].layout->top_boxed_p ();
+}
 
-  return;
-}                              /* _makeDataWindow */
+/* See tui-layout.h.  */
 
+bool
+tui_layout_split::bottom_boxed_p () const
+{
+  if (m_splits.empty ())
+    return false;
+  return m_splits.back ().layout->top_boxed_p ();
+}
 
+/* See tui-layout.h.  */
 
-/*
-   **    _showSourceCommand().
-   **        Show the Source/Command layout
- */
-static void
-_showSourceCommand (void)
+void
+tui_layout_split::set_weights_from_heights ()
 {
-  _showSourceOrDisassemAndCommand (SRC_COMMAND);
-
-  return;
-}                              /* _showSourceCommand */
+  for (int i = 0; i < m_splits.size (); ++i)
+    m_splits[i].weight = m_splits[i].layout->height;
+}
 
+/* See tui-layout.h.  */
 
-/*
-   **    _showDisassemCommand().
-   **        Show the Dissassem/Command layout
- */
-static void
-_showDisassemCommand (void)
+tui_adjust_result
+tui_layout_split::adjust_size (const char *name, int new_height)
 {
-  _showSourceOrDisassemAndCommand (DISASSEM_COMMAND);
+  /* Look through the children.  If one is a layout holding the named
+     window, we're done; or if one actually is the named window,
+     update it.  */
+  int found_index = -1;
+  for (int i = 0; i < m_splits.size (); ++i)
+    {
+      tui_adjust_result adjusted
+       = m_splits[i].layout->adjust_size (name, new_height);
+      if (adjusted == HANDLED)
+       return HANDLED;
+      if (adjusted == FOUND)
+       {
+         if (!m_vertical)
+           return FOUND;
+         found_index = i;
+         break;
+       }
+    }
 
-  return;
-}                              /* _showDisassemCommand */
+  if (found_index == -1)
+    return NOT_FOUND;
+  if (m_splits[found_index].layout->height == new_height)
+    return HANDLED;
 
+  set_weights_from_heights ();
+  int delta = m_splits[found_index].weight - new_height;
+  m_splits[found_index].weight = new_height;
 
-/*
-   **    _showSourceDisassemCommand().
-   **        Show the Source/Disassem/Command layout
- */
-static void
-_showSourceDisassemCommand (void)
-{
-  if (currentLayout () != SRC_DISASSEM_COMMAND)
+  /* Distribute the "delta" over the next window; but if the next
+     window cannot hold it all, keep going until we either find a
+     window that does, or until we loop all the way around.  */
+  for (int i = 0; delta != 0 && i < m_splits.size () - 1; ++i)
     {
-      int cmdHeight, srcHeight, asmHeight;
-
-      if (m_winPtrNotNull (cmdWin))
-       cmdHeight = cmdWin->generic.height;
-      else
-       cmdHeight = termHeight () / 3;
+      int index = (found_index + 1 + i) % m_splits.size ();
 
-      srcHeight = (termHeight () - cmdHeight) / 2;
-      asmHeight = termHeight () - (srcHeight + cmdHeight);
+      int new_min, new_max;
+      m_splits[index].layout->get_sizes (m_vertical, &new_min, &new_max);
 
-      if (m_winPtrIsNull (srcWin))
-       _makeSourceWindow (&srcWin, srcHeight, 0);
-      else
+      if (delta < 0)
        {
-         _initGenWinInfo (&srcWin->generic,
-                          srcWin->generic.type,
-                          srcHeight,
-                          srcWin->generic.width,
-                          srcWin->detail.sourceInfo.executionInfo->width,
-                          0);
-         srcWin->canHighlight = TRUE;
-         _initGenWinInfo (srcWin->detail.sourceInfo.executionInfo,
-                          EXEC_INFO_WIN,
-                          srcHeight,
-                          3,
-                          0,
-                          0);
-         m_beVisible (srcWin);
-         m_beVisible (srcWin->detail.sourceInfo.executionInfo);
-         srcWin->detail.sourceInfo.hasLocator = FALSE;;
+         /* The primary window grew, so we are trying to shrink other
+            windows.  */
+         int available = m_splits[index].weight - new_min;
+         int shrink_by = std::min (available, -delta);
+         m_splits[index].weight -= shrink_by;
+         delta += shrink_by;
        }
-      if (m_winPtrNotNull (srcWin))
+      else
        {
-         TuiGenWinInfoPtr locator = locatorWinInfoPtr ();
-
-         tui_show_source_content (srcWin);
-         if (m_winPtrIsNull (disassemWin))
-           {
-             _makeDisassemWindow (&disassemWin, asmHeight, srcHeight - 1);
-             _initAndMakeWin ((Opaque *) & locator,
-                              LOCATOR_WIN,
-                              2 /* 1 */ ,
-                              termWidth (),
-                              0,
-                              (srcHeight + asmHeight) - 1,
-                              DONT_BOX_WINDOW);
-           }
-         else
-           {
-             _initGenWinInfo (locator,
-                              LOCATOR_WIN,
-                              2 /* 1 */ ,
-                              termWidth (),
-                              0,
-                              (srcHeight + asmHeight) - 1);
-             disassemWin->detail.sourceInfo.hasLocator = TRUE;
-             _initGenWinInfo (
-                               &disassemWin->generic,
-                               disassemWin->generic.type,
-                               asmHeight,
-                               disassemWin->generic.width,
-                       disassemWin->detail.sourceInfo.executionInfo->width,
-                               srcHeight - 1);
-             _initGenWinInfo (disassemWin->detail.sourceInfo.executionInfo,
-                              EXEC_INFO_WIN,
-                              asmHeight,
-                              3,
-                              0,
-                              srcHeight - 1);
-             disassemWin->canHighlight = TRUE;
-             m_beVisible (disassemWin);
-             m_beVisible (disassemWin->detail.sourceInfo.executionInfo);
-           }
-         if (m_winPtrNotNull (disassemWin))
-           {
-             srcWin->detail.sourceInfo.hasLocator = FALSE;
-             disassemWin->detail.sourceInfo.hasLocator = TRUE;
-             m_beVisible (locator);
-             tui_show_locator_content ();
-             tui_show_source_content (disassemWin);
-
-             if (m_winPtrIsNull (cmdWin))
-               _makeCommandWindow (&cmdWin,
-                                   cmdHeight,
-                                   termHeight () - cmdHeight);
-             else
-               {
-                 _initGenWinInfo (&cmdWin->generic,
-                                  cmdWin->generic.type,
-                                  cmdWin->generic.height,
-                                  cmdWin->generic.width,
-                                  0,
-                                  cmdWin->generic.origin.y);
-                 cmdWin->canHighlight = FALSE;
-                 m_beVisible (cmdWin);
-               }
-             if (m_winPtrNotNull (cmdWin))
-               tuiRefreshWin (&cmdWin->generic);
-           }
+         /* The primary window shrank, so we are trying to grow other
+            windows.  */
+         int available = new_max - m_splits[index].weight;
+         int grow_by = std::min (available, delta);
+         m_splits[index].weight += grow_by;
+         delta -= grow_by;
        }
-      setCurrentLayoutTo (SRC_DISASSEM_COMMAND);
     }
 
-  return;
-}                              /* _showSourceDisassemCommand */
+  if (delta != 0)
+    {
+      warning (_("Invalid window height specified"));
+      /* Effectively undo any modifications made here.  */
+      set_weights_from_heights ();
+    }
+  else
+    {
+      /* Simply re-apply the updated layout.  */
+      apply (x, y, width, height);
+    }
+
+  return HANDLED;
+}
 
+/* See tui-layout.h.  */
 
-/*
-   **    _showData().
-   **        Show the Source/Data/Command or the Dissassembly/Data/Command layout
- */
-static void
-_showData (TuiLayoutType newLayout)
+void
+tui_layout_split::apply (int x_, int y_, int width_, int height_)
 {
-  int totalHeight = (termHeight () - cmdWin->generic.height);
-  int srcHeight, dataHeight;
-  TuiWinType winType;
-  TuiGenWinInfoPtr locator = locatorWinInfoPtr ();
-
-
-  dataHeight = totalHeight / 2;
-  srcHeight = totalHeight - dataHeight;
-  m_allBeInvisible ();
-  m_beInvisible (locator);
-  _makeDataWindow (&dataWin, dataHeight, 0);
-  dataWin->canHighlight = TRUE;
-  if (newLayout == SRC_DATA_COMMAND)
-    winType = SRC_WIN;
-  else
-    winType = DISASSEM_WIN;
-  if (m_winPtrIsNull (winList[winType]))
+  x = x_;
+  y = y_;
+  width = width_;
+  height = height_;
+
+  struct size_info
+  {
+    int size;
+    int min_size;
+    int max_size;
+    /* True if this window will share a box border with the previous
+       window in the list.  */
+    bool share_box;
+  };
+
+  std::vector<size_info> info (m_splits.size ());
+
+  /* Step 1: Find the min and max size of each sub-layout.
+     Fixed-sized layouts are given their desired size, and then the
+     remaining space is distributed among the remaining windows
+     according to the weights given.  */
+  int available_size = m_vertical ? height : width;
+  int last_index = -1;
+  int total_weight = 0;
+  for (int i = 0; i < m_splits.size (); ++i)
     {
-      if (winType == SRC_WIN)
-       _makeSourceWindow (&winList[winType], srcHeight, dataHeight - 1);
+      bool cmd_win_already_exists = TUI_CMD_WIN != nullptr;
+
+      /* Always call get_sizes, to ensure that the window is
+        instantiated.  This is a bit gross but less gross than adding
+        special cases for this in other places.  */
+      m_splits[i].layout->get_sizes (m_vertical, &info[i].min_size,
+                                    &info[i].max_size);
+
+      if (!m_applied
+         && cmd_win_already_exists
+         && m_splits[i].layout->get_name () != nullptr
+         && strcmp (m_splits[i].layout->get_name (), "cmd") == 0)
+       {
+         /* If this layout has never been applied, then it means the
+            user just changed the layout.  In this situation, it's
+            desirable to keep the size of the command window the
+            same.  Setting the min and max sizes this way ensures
+            that the resizing step, below, does the right thing with
+            this window.  */
+         info[i].min_size = (m_vertical
+                             ? TUI_CMD_WIN->height
+                             : TUI_CMD_WIN->width);
+         info[i].max_size = info[i].min_size;
+       }
+
+      if (info[i].min_size == info[i].max_size)
+       available_size -= info[i].min_size;
       else
-       _makeDisassemWindow (&winList[winType], srcHeight, dataHeight - 1);
-      _initAndMakeWin ((Opaque *) & locator,
-                      LOCATOR_WIN,
-                      2 /* 1 */ ,
-                      termWidth (),
-                      0,
-                      totalHeight - 1,
-                      DONT_BOX_WINDOW);
+       {
+         last_index = i;
+         total_weight += m_splits[i].weight;
+       }
+
+      /* Two adjacent boxed windows will share a border, making a bit
+        more size available.  */
+      if (i > 0
+         && m_splits[i - 1].layout->bottom_boxed_p ()
+         && m_splits[i].layout->top_boxed_p ())
+       info[i].share_box = true;
     }
-  else
+
+  /* Step 2: Compute the size of each sub-layout.  Fixed-sized items
+     are given their fixed size, while others are resized according to
+     their weight.  */
+  int used_size = 0;
+  for (int i = 0; i < m_splits.size (); ++i)
     {
-      _initGenWinInfo (&winList[winType]->generic,
-                      winList[winType]->generic.type,
-                      srcHeight,
-                      winList[winType]->generic.width,
-                  winList[winType]->detail.sourceInfo.executionInfo->width,
-                      dataHeight - 1);
-      _initGenWinInfo (winList[winType]->detail.sourceInfo.executionInfo,
-                      EXEC_INFO_WIN,
-                      srcHeight,
-                      3,
-                      0,
-                      dataHeight - 1);
-      m_beVisible (winList[winType]);
-      m_beVisible (winList[winType]->detail.sourceInfo.executionInfo);
-      _initGenWinInfo (locator,
-                      LOCATOR_WIN,
-                      2 /* 1 */ ,
-                      termWidth (),
-                      0,
-                      totalHeight - 1);
+      /* Compute the height and clamp to the allowable range.  */
+      info[i].size = available_size * m_splits[i].weight / total_weight;
+      if (info[i].size > info[i].max_size)
+       info[i].size = info[i].max_size;
+      if (info[i].size < info[i].min_size)
+       info[i].size = info[i].min_size;
+      /* If there is any leftover size, just redistribute it to the
+        last resizeable window, by dropping it from the allocated
+        size.  We could try to be fancier here perhaps, by
+        redistributing this size among all windows, not just the
+        last window.  */
+      if (info[i].min_size != info[i].max_size)
+       {
+         used_size += info[i].size;
+         if (info[i].share_box)
+           --used_size;
+       }
     }
-  winList[winType]->detail.sourceInfo.hasLocator = TRUE;
-  m_beVisible (locator);
-  tui_show_locator_content ();
-  addToSourceWindows (winList[winType]);
-  setCurrentLayoutTo (newLayout);
-
-  return;
-}                              /* _showData */
-
-/*
-   ** _initGenWinInfo().
- */
-static void
-_initGenWinInfo (TuiGenWinInfoPtr winInfo, TuiWinType type,
-                 int height, int width, int originX, int originY)
-{
-  int h = height;
 
-  winInfo->type = type;
-  winInfo->width = width;
-  winInfo->height = h;
-  if (h > 1)
+  /* Allocate any leftover size.  */
+  if (available_size >= used_size && last_index != -1)
+    info[last_index].size += available_size - used_size;
+
+  /* Step 3: Resize.  */
+  int size_accum = 0;
+  const int maximum = m_vertical ? height : width;
+  for (int i = 0; i < m_splits.size (); ++i)
     {
-      winInfo->viewportHeight = h - 1;
-      if (winInfo->type != CMD_WIN)
-       winInfo->viewportHeight--;
+      /* If we fall off the bottom, just make allocations overlap.
+        GIGO.  */
+      if (size_accum + info[i].size > maximum)
+       size_accum = maximum - info[i].size;
+      else if (info[i].share_box)
+       --size_accum;
+      if (m_vertical)
+       m_splits[i].layout->apply (x, y + size_accum, width, info[i].size);
+      else
+       m_splits[i].layout->apply (x + size_accum, y, info[i].size, height);
+      size_accum += info[i].size;
     }
-  else
-    winInfo->viewportHeight = 1;
-  winInfo->origin.x = originX;
-  winInfo->origin.y = originY;
 
-  return;
-}                              /* _initGenWinInfo */
+  m_applied = true;
+}
 
-/*
-   ** _initAndMakeWin().
- */
-static void
-_initAndMakeWin (Opaque * winInfoPtr, TuiWinType winType,
-                 int height, int width, int originX, int originY, int boxIt)
-{
-  Opaque opaqueWinInfo = *winInfoPtr;
-  TuiGenWinInfoPtr generic;
+/* See tui-layout.h.  */
 
-  if (opaqueWinInfo == (Opaque) NULL)
+void
+tui_layout_split::remove_windows (const char *name)
+{
+  for (int i = 0; i < m_splits.size (); ++i)
     {
-      if (m_winIsAuxillary (winType))
-       opaqueWinInfo = (Opaque) allocGenericWinInfo ();
+      const char *this_name = m_splits[i].layout->get_name ();
+      if (this_name == nullptr)
+       m_splits[i].layout->remove_windows (name);
+      else if (strcmp (this_name, name) == 0
+              || strcmp (this_name, CMD_NAME) == 0
+              || strcmp (this_name, STATUS_NAME) == 0)
+       {
+         /* Keep.  */
+       }
       else
-       opaqueWinInfo = (Opaque) allocWinInfo (winType);
+       {
+         m_splits.erase (m_splits.begin () + i);
+         --i;
+       }
     }
-  if (m_winIsAuxillary (winType))
-    generic = (TuiGenWinInfoPtr) opaqueWinInfo;
-  else
-    generic = &((TuiWinInfoPtr) opaqueWinInfo)->generic;
+}
+
+/* See tui-layout.h.  */
 
-  if (opaqueWinInfo != (Opaque) NULL)
+void
+tui_layout_split::replace_window (const char *name, const char *new_window)
+{
+  for (auto &item : m_splits)
+    item.layout->replace_window (name, new_window);
+}
+
+/* See tui-layout.h.  */
+
+void
+tui_layout_split::specification (ui_file *output, int depth)
+{
+  if (depth > 0)
+    fputs_unfiltered ("{", output);
+
+  if (!m_vertical)
+    fputs_unfiltered ("-horizontal ", output);
+
+  bool first = true;
+  for (auto &item : m_splits)
     {
-      _initGenWinInfo (generic, winType, height, width, originX, originY);
-      if (!m_winIsAuxillary (winType))
-       {
-         if (generic->type == CMD_WIN)
-           ((TuiWinInfoPtr) opaqueWinInfo)->canHighlight = FALSE;
-         else
-           ((TuiWinInfoPtr) opaqueWinInfo)->canHighlight = TRUE;
-       }
-      makeWindow (generic, boxIt);
+      if (!first)
+       fputs_unfiltered (" ", output);
+      first = false;
+      item.layout->specification (output, depth + 1);
+      fprintf_unfiltered (output, " %d", item.weight);
     }
-  *winInfoPtr = opaqueWinInfo;
+
+  if (depth > 0)
+    fputs_unfiltered ("}", output);
 }
 
+/* Destroy the layout associated with SELF.  */
 
-/*
-   ** _makeSourceOrDisassemWindow().
- */
 static void
-_makeSourceOrDisassemWindow (TuiWinInfoPtr * winInfoPtr, TuiWinType type,
-                             int height, int originY)
+destroy_layout (struct cmd_list_element *self, void *context)
 {
-  TuiGenWinInfoPtr executionInfo = (TuiGenWinInfoPtr) NULL;
+  tui_layout_split *layout = (tui_layout_split *) context;
+  size_t index = find_layout (layout);
+  layouts.erase (layouts.begin () + index);
+}
+
+/* List holding the sub-commands of "layout".  */
+
+static struct cmd_list_element *layout_list;
+
+/* Add a "layout" command with name NAME that switches to LAYOUT.  */
+
+static struct cmd_list_element *
+add_layout_command (const char *name, tui_layout_split *layout)
+{
+  struct cmd_list_element *cmd;
+
+  string_file spec;
+  layout->specification (&spec, 0);
+
+  gdb::unique_xmalloc_ptr<char> doc
+    (xstrprintf (_("Apply the \"%s\" layout.\n\
+This layout was created using:\n\
+  tui new-layout %s %s"),
+                name, name, spec.c_str ()));
+
+  cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
+  set_cmd_context (cmd, layout);
+  /* There is no API to set this.  */
+  cmd->func = tui_apply_layout;
+  cmd->destroyer = destroy_layout;
+  cmd->doc_allocated = 1;
+  doc.release ();
+  layouts.emplace_back (layout);
+
+  return cmd;
+}
+
+/* Initialize the standard layouts.  */
 
-  /*
-     ** Create the exeuction info window.
-   */
-  if (type == SRC_WIN)
-    executionInfo = sourceExecInfoWinPtr ();
-  else
-    executionInfo = disassemExecInfoWinPtr ();
-  _initAndMakeWin ((Opaque *) & executionInfo,
-                  EXEC_INFO_WIN,
-                  height,
-                  3,
-                  0,
-                  originY,
-                  DONT_BOX_WINDOW);
-  /*
-     ** Now create the source window.
-   */
-  _initAndMakeWin ((Opaque *) winInfoPtr,
-                  type,
-                  height,
-                  termWidth () - executionInfo->width,
-                  executionInfo->width,
-                  originY,
-                  BOX_WINDOW);
-
-  (*winInfoPtr)->detail.sourceInfo.executionInfo = executionInfo;
-
-  return;
-}                              /* _makeSourceOrDisassemWindow */
-
-
-/*
-   **    _showSourceOrDisassemAndCommand().
-   **        Show the Source/Command or the Disassem layout
- */
 static void
-_showSourceOrDisassemAndCommand (TuiLayoutType layoutType)
+initialize_layouts ()
 {
-  if (currentLayout () != layoutType)
-    {
-      TuiWinInfoPtr *winInfoPtr;
-      int srcHeight, cmdHeight;
-      TuiGenWinInfoPtr locator = locatorWinInfoPtr ();
+  tui_layout_split *layout;
+
+  layout = new tui_layout_split ();
+  layout->add_window (SRC_NAME, 2);
+  layout->add_window (STATUS_NAME, 0);
+  layout->add_window (CMD_NAME, 1);
+  add_layout_command (SRC_NAME, layout);
+
+  layout = new tui_layout_split ();
+  layout->add_window (DISASSEM_NAME, 2);
+  layout->add_window (STATUS_NAME, 0);
+  layout->add_window (CMD_NAME, 1);
+  add_layout_command (DISASSEM_NAME, layout);
+
+  layout = new tui_layout_split ();
+  layout->add_window (SRC_NAME, 1);
+  layout->add_window (DISASSEM_NAME, 1);
+  layout->add_window (STATUS_NAME, 0);
+  layout->add_window (CMD_NAME, 1);
+  add_layout_command ("split", layout);
+
+  layout = new tui_layout_split ();
+  layout->add_window (DATA_NAME, 1);
+  layout->add_window (SRC_NAME, 1);
+  layout->add_window (STATUS_NAME, 0);
+  layout->add_window (CMD_NAME, 1);
+  layouts.emplace_back (layout);
+  src_regs_layout = layout;
+
+  layout = new tui_layout_split ();
+  layout->add_window (DATA_NAME, 1);
+  layout->add_window (DISASSEM_NAME, 1);
+  layout->add_window (STATUS_NAME, 0);
+  layout->add_window (CMD_NAME, 1);
+  layouts.emplace_back (layout);
+  asm_regs_layout = layout;
+}
 
-      if (m_winPtrNotNull (cmdWin))
-       cmdHeight = cmdWin->generic.height;
-      else
-       cmdHeight = termHeight () / 3;
-      srcHeight = termHeight () - cmdHeight;
+\f
 
+/* A helper function that returns true if NAME is the name of an
+   available window.  */
 
-      if (layoutType == SRC_COMMAND)
-       winInfoPtr = &srcWin;
-      else
-       winInfoPtr = &disassemWin;
+static bool
+validate_window_name (const std::string &name)
+{
+  auto iter = known_window_types->find (name);
+  return iter != known_window_types->end ();
+}
 
-      if (m_winPtrIsNull (*winInfoPtr))
+/* Implementation of the "tui new-layout" command.  */
+
+static void
+tui_new_layout_command (const char *spec, int from_tty)
+{
+  std::string new_name = extract_arg (&spec);
+  if (new_name.empty ())
+    error (_("No layout name specified"));
+  if (new_name[0] == '-')
+    error (_("Layout name cannot start with '-'"));
+
+  bool is_vertical = true;
+  spec = skip_spaces (spec);
+  if (check_for_argument (&spec, "-horizontal"))
+    is_vertical = false;
+
+  std::vector<std::unique_ptr<tui_layout_split>> splits;
+  splits.emplace_back (new tui_layout_split (is_vertical));
+  std::unordered_set<std::string> seen_windows;
+  while (true)
+    {
+      spec = skip_spaces (spec);
+      if (spec[0] == '\0')
+       break;
+
+      if (spec[0] == '{')
        {
-         if (layoutType == SRC_COMMAND)
-           _makeSourceWindow (winInfoPtr, srcHeight - 1, 0);
-         else
-           _makeDisassemWindow (winInfoPtr, srcHeight - 1, 0);
-         _initAndMakeWin ((Opaque *) & locator,
-                          LOCATOR_WIN,
-                          2 /* 1 */ ,
-                          termWidth (),
-                          0,
-                          srcHeight - 1,
-                          DONT_BOX_WINDOW);
+         is_vertical = true;
+         spec = skip_spaces (spec + 1);
+         if (check_for_argument (&spec, "-horizontal"))
+           is_vertical = false;
+         splits.emplace_back (new tui_layout_split (is_vertical));
+         continue;
+       }
+
+      bool is_close = false;
+      std::string name;
+      if (spec[0] == '}')
+       {
+         is_close = true;
+         ++spec;
+         if (splits.size () == 1)
+           error (_("Extra '}' in layout specification"));
        }
       else
        {
-         _initGenWinInfo (locator,
-                          LOCATOR_WIN,
-                          2 /* 1 */ ,
-                          termWidth (),
-                          0,
-                          srcHeight - 1);
-         (*winInfoPtr)->detail.sourceInfo.hasLocator = TRUE;
-         _initGenWinInfo (
-                           &(*winInfoPtr)->generic,
-                           (*winInfoPtr)->generic.type,
-                           srcHeight - 1,
-                           (*winInfoPtr)->generic.width,
-                     (*winInfoPtr)->detail.sourceInfo.executionInfo->width,
-                           0);
-         _initGenWinInfo ((*winInfoPtr)->detail.sourceInfo.executionInfo,
-                          EXEC_INFO_WIN,
-                          srcHeight - 1,
-                          3,
-                          0,
-                          0);
-         (*winInfoPtr)->canHighlight = TRUE;
-         m_beVisible (*winInfoPtr);
-         m_beVisible ((*winInfoPtr)->detail.sourceInfo.executionInfo);
+         name = extract_arg (&spec);
+         if (name.empty ())
+           break;
+         if (!validate_window_name (name))
+           error (_("Unknown window \"%s\""), name.c_str ());
+         if (seen_windows.find (name) != seen_windows.end ())
+           error (_("Window \"%s\" seen twice in layout"), name.c_str ());
        }
-      if (m_winPtrNotNull (*winInfoPtr))
+
+      ULONGEST weight = get_ulongest (&spec, '}');
+      if ((int) weight != weight)
+       error (_("Weight out of range: %s"), pulongest (weight));
+      if (is_close)
        {
-         (*winInfoPtr)->detail.sourceInfo.hasLocator = TRUE;
-         m_beVisible (locator);
-         tui_show_locator_content ();
-         tui_show_source_content (*winInfoPtr);
-
-         if (m_winPtrIsNull (cmdWin))
-           {
-             _makeCommandWindow (&cmdWin, cmdHeight, srcHeight);
-             tuiRefreshWin (&cmdWin->generic);
-           }
-         else
-           {
-             _initGenWinInfo (&cmdWin->generic,
-                              cmdWin->generic.type,
-                              cmdWin->generic.height,
-                              cmdWin->generic.width,
-                              cmdWin->generic.origin.x,
-                              cmdWin->generic.origin.y);
-             cmdWin->canHighlight = FALSE;
-             m_beVisible (cmdWin);
-           }
+         std::unique_ptr<tui_layout_split> last_split
+           = std::move (splits.back ());
+         splits.pop_back ();
+         splits.back ()->add_split (std::move (last_split), weight);
+       }
+      else
+       {
+         splits.back ()->add_window (name.c_str (), weight);
+         seen_windows.insert (name);
        }
-      setCurrentLayoutTo (layoutType);
     }
+  if (splits.size () > 1)
+    error (_("Missing '}' in layout specification"));
+  if (seen_windows.empty ())
+    error (_("New layout does not contain any windows"));
+  if (seen_windows.find (CMD_NAME) == seen_windows.end ())
+    error (_("New layout does not contain the \"" CMD_NAME "\" window"));
+
+  gdb::unique_xmalloc_ptr<char> cmd_name
+    = make_unique_xstrdup (new_name.c_str ());
+  std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
+  struct cmd_list_element *cmd
+    = add_layout_command (cmd_name.get (), new_layout.get ());
+  cmd->name_allocated = 1;
+  cmd_name.release ();
+  new_layout.release ();
+}
 
-  return;
-}                              /* _showSourceOrDisassemAndCommand */
+/* Function to initialize gdb commands, for tui window layout
+   manipulation.  */
+
+void _initialize_tui_layout ();
+void
+_initialize_tui_layout ()
+{
+  add_basic_prefix_cmd ("layout", class_tui, _("\
+Change the layout of windows.\n\
+Usage: layout prev | next | LAYOUT-NAME"),
+                       &layout_list, "layout ", 0, &cmdlist);
+
+  add_cmd ("next", class_tui, tui_next_layout_command,
+          _("Apply the next TUI layout."),
+          &layout_list);
+  add_cmd ("prev", class_tui, tui_prev_layout_command,
+          _("Apply the previous TUI layout."),
+          &layout_list);
+  add_cmd ("regs", class_tui, tui_regs_layout_command,
+          _("Apply the TUI register layout."),
+          &layout_list);
+
+  add_cmd ("new-layout", class_tui, tui_new_layout_command,
+          _("Create a new TUI layout.\n\
+Usage: tui new-layout [-horizontal] NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
+Create a new TUI layout.  The new layout will be named NAME,\n\
+and can be accessed using \"layout NAME\".\n\
+The windows will be displayed in the specified order.\n\
+A WINDOW can also be of the form:\n\
+  { [-horizontal] NAME WEIGHT [NAME WEIGHT]... }\n\
+This form indicates a sub-frame.\n\
+Each WEIGHT is an integer, which holds the relative size\n\
+to be allocated to the window."),
+          tui_get_cmd_list ());
+
+  initialize_layouts ();
+  initialize_known_windows ();
+}
This page took 0.042165 seconds and 4 git commands to generate.