#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-source.h"
#include "gdb_curses.h"
-static void tui_layout_command (const char *, int);
static void extract_display_start_addr (struct gdbarch **, CORE_ADDR *);
/* The layouts. */
/* 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 ()
{
+ struct gdbarch *gdbarch;
+ CORE_ADDR addr;
+
+ extract_display_start_addr (&gdbarch, &addr);
+
+ saved_tui_windows = std::move (tui_windows);
tui_windows.clear ();
+
+ for (tui_win_info *win_info : saved_tui_windows)
+ win_info->make_visible (false);
+
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 (focus == win_info)
+ tui_set_win_focus_to (tui_windows[0]);
+ delete win_info;
+ }
+ }
+
+ 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 ();
}
/* See tui-layout. */
static void
tui_set_layout (tui_layout_split *layout)
{
- struct gdbarch *gdbarch;
- CORE_ADDR addr;
-
- extract_display_start_addr (&gdbarch, &addr);
- tui_make_all_invisible ();
applied_skeleton = layout;
applied_layout = layout->clone ();
tui_apply_current_layout ();
- tui_delete_invisible_windows ();
-
- if (gdbarch == nullptr && TUI_DISASM_WIN != nullptr)
- tui_get_begin_asm_address (&gdbarch, &addr);
- tui_update_source_windows_with_addr (gdbarch, addr);
}
/* See tui-layout.h. */
const char *name = type == SRC_WIN ? SRC_NAME : DISASSEM_NAME;
applied_layout->replace_window (tui_win_list[other]->name (), name);
tui_apply_current_layout ();
- tui_delete_invisible_windows ();
}
/* Find LAYOUT in the "layouts" global and return its index. */
{
tui_win_info *focus = tui_win_with_focus ();
- if (strcmp (focus->name (), "cmd") == 0)
+ if (strcmp (focus->name (), CMD_NAME) == 0)
{
/* Try leaving the source or disassembly window. If neither
exists, just do nothing. */
static void
extract_display_start_addr (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
{
- struct gdbarch *gdbarch = nullptr;
- CORE_ADDR addr = 0;
- CORE_ADDR pc;
- struct symtab_and_line cursal = get_current_source_symtab_and_line ();
-
if (TUI_SRC_WIN != nullptr)
- {
- gdbarch = TUI_SRC_WIN->gdbarch;
- find_line_pc (cursal.symtab,
- TUI_SRC_WIN->start_line_or_addr.u.line_no,
- &pc);
- addr = pc;
- }
+ 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
{
- gdbarch = TUI_DISASM_WIN->gdbarch;
- addr = TUI_DISASM_WIN->start_line_or_addr.u.addr;
+ *gdbarch_p = nullptr;
+ *addr_p = 0;
}
-
- *gdbarch_p = gdbarch;
- *addr_p = addr;
}
void
\f
+/* 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 *)
+{
+ if (tui_win_list[V] == nullptr)
+ tui_win_list[V] = new T ();
+ return tui_win_list[V];
+}
+
+/* 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. */
+
+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)
{
- if (name == "src")
- {
- if (tui_win_list[SRC_WIN] == nullptr)
- tui_win_list[SRC_WIN] = new tui_source_window ();
- return tui_win_list[SRC_WIN];
- }
- else if (name == "cmd")
- {
- if (tui_win_list[CMD_WIN] == nullptr)
- tui_win_list[CMD_WIN] = new tui_cmd_window ();
- return tui_win_list[CMD_WIN];
- }
- else if (name == "regs")
- {
- if (tui_win_list[DATA_WIN] == nullptr)
- tui_win_list[DATA_WIN] = new tui_data_window ();
- return tui_win_list[DATA_WIN];
- }
- else if (name == "asm")
- {
- if (tui_win_list[DISASSEM_WIN] == nullptr)
- tui_win_list[DISASSEM_WIN] = new tui_disasm_window ();
- return tui_win_list[DISASSEM_WIN];
- }
- else
- {
- gdb_assert (name == "status");
- return tui_locator_win_info_ptr ();
- }
+ 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
+initialize_known_windows ()
+{
+ 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);
+}
+
+/* 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. */
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
{
- if (strcmp (this_name, name) == 0
- || strcmp (this_name, "cmd") == 0)
- {
- /* Keep. */
- }
m_splits.erase (m_splits.begin () + i);
--i;
}
tui_layout_split *layout;
layout = new tui_layout_split ();
- layout->add_window ("src", 2);
- layout->add_window ("status", 0);
- layout->add_window ("cmd", 1);
- add_layout_command ("src", layout);
+ 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 ("asm", 2);
- layout->add_window ("status", 0);
- layout->add_window ("cmd", 1);
- add_layout_command ("asm", layout);
+ 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", 1);
- layout->add_window ("asm", 1);
- layout->add_window ("status", 0);
- layout->add_window ("cmd", 1);
+ 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 ("regs", 1);
- layout->add_window ("src", 1);
- layout->add_window ("status", 0);
- layout->add_window ("cmd", 1);
+ 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 ("regs", 1);
- layout->add_window ("asm", 1);
- layout->add_window ("status", 0);
- layout->add_window ("cmd", 1);
+ 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;
}
static bool
validate_window_name (const std::string &name)
{
- return (name == "src" || name == "cmd"
- || name == "regs" || name == "asm"
- || name == "status");
+ auto iter = known_window_types->find (name);
+ return iter != known_window_types->end ();
}
/* Implementation of the "tui new-layout" command. */
error (_("Missing '}' in layout specification"));
if (seen_windows.empty ())
error (_("New layout does not contain any windows"));
- if (seen_windows.find ("cmd") == seen_windows.end ())
- error (_("New layout does not contain the \"cmd\" window"));
+ 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 ());
new_layout.release ();
}
-/* Base command for "layout". */
-
-static void
-tui_layout_command (const char *layout_name, int from_tty)
-{
- help_list (layout_list, "layout ", all_commands, gdb_stdout);
-}
-
/* Function to initialize gdb commands, for tui window layout
manipulation. */
void
_initialize_tui_layout ()
{
- add_prefix_cmd ("layout", class_tui, tui_layout_command, _("\
+ add_basic_prefix_cmd ("layout", class_tui, _("\
Change the layout of windows.\n\
Usage: layout prev | next | LAYOUT-NAME"),
- &layout_list, "layout ", 0, &cmdlist);
+ &layout_list, "layout ", 0, &cmdlist);
add_cmd ("next", class_tui, tui_next_layout_command,
- _("Apply the next TUI layout"),
+ _("Apply the next TUI layout."),
&layout_list);
add_cmd ("prev", class_tui, tui_prev_layout_command,
- _("Apply the previous TUI layout"),
+ _("Apply the previous TUI layout."),
&layout_list);
add_cmd ("regs", class_tui, tui_regs_layout_command,
- _("Apply the TUI register layout"),
+ _("Apply the TUI register layout."),
&layout_list);
add_cmd ("new-layout", class_tui, tui_new_layout_command,
tui_get_cmd_list ());
initialize_layouts ();
+ initialize_known_windows ();
}