/* GDB hooks for TUI.
- Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2001-2020 Free Software Foundation, Inc.
This file is part of GDB.
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 "symtab.h"
#include "objfiles.h"
#include "target.h"
#include "gdbcore.h"
-#include "event-loop.h"
+#include "gdbsupport/event-loop.h"
#include "event-top.h"
#include "frame.h"
#include "breakpoint.h"
-#include "gdb-events.h"
#include "ui-out.h"
#include "top.h"
-#include <readline/readline.h>
+#include "observable.h"
+#include "source.h"
#include <unistd.h>
#include <fcntl.h>
#include "tui/tui.h"
+#include "tui/tui-hooks.h"
#include "tui/tui-data.h"
#include "tui/tui-layout.h"
#include "tui/tui-io.h"
#include "tui/tui-regs.h"
#include "tui/tui-win.h"
#include "tui/tui-stack.h"
-#include "tui/tui-windata.h"
#include "tui/tui-winsource.h"
-#ifdef HAVE_NCURSES_H
-#include <ncurses.h>
-#else
-#ifdef HAVE_CURSES_H
-#include <curses.h>
-#endif
-#endif
-
-int tui_target_has_run = 0;
-
-static void (* tui_target_new_objfile_chain) (struct objfile*);
+#include "gdb_curses.h"
static void
tui_new_objfile_hook (struct objfile* objfile)
{
if (tui_active)
tui_display_main ();
-
- if (tui_target_new_objfile_chain)
- tui_target_new_objfile_chain (objfile);
}
-static int
-tui_query_hook (const char * msg, va_list argp)
-{
- int retval;
- int ans2;
- int answer;
+/* Prevent recursion of deprecated_register_changed_hook(). */
+static bool tui_refreshing_registers = false;
- /* Automatically answer "yes" if input is not from a terminal. */
- if (!input_from_terminal_p ())
- return 1;
-
- echo ();
- while (1)
- {
- wrap_here (""); /* Flush any buffered output */
- gdb_flush (gdb_stdout);
+/* Observer for the register_changed notification. */
- vfprintf_filtered (gdb_stdout, msg, argp);
- printf_filtered ("(y or n) ");
+static void
+tui_register_changed (struct frame_info *frame, int regno)
+{
+ struct frame_info *fi;
- wrap_here ("");
- gdb_flush (gdb_stdout);
+ if (!tui_is_window_visible (DATA_WIN))
+ return;
- answer = tui_getc (stdin);
- clearerr (stdin); /* in case of C-d */
- if (answer == EOF) /* C-d */
- {
- retval = 1;
- break;
- }
- /* Eat rest of input line, to EOF or newline */
- if (answer != '\n')
- do
- {
- ans2 = tui_getc (stdin);
- clearerr (stdin);
- }
- while (ans2 != EOF && ans2 != '\n' && ans2 != '\r');
-
- if (answer >= 'a')
- answer -= 040;
- if (answer == 'Y')
- {
- retval = 1;
- break;
- }
- if (answer == 'N')
- {
- retval = 0;
- break;
- }
- printf_filtered ("Please answer y or n.\n");
+ /* The frame of the register that was changed may differ from the selected
+ frame, but we only want to show the register values of the selected frame.
+ And even if the frames differ a register change made in one can still show
+ up in the other. So we always use the selected frame here, and ignore
+ FRAME. */
+ fi = get_selected_frame (NULL);
+ if (!tui_refreshing_registers)
+ {
+ tui_refreshing_registers = true;
+ TUI_DATA_WIN->check_register_values (fi);
+ tui_refreshing_registers = false;
}
- noecho ();
- return retval;
}
-/* Prevent recursion of registers_changed_hook(). */
-static int tui_refreshing_registers = 0;
+/* Breakpoint creation hook.
+ Update the screen to show the new breakpoint. */
+static void
+tui_event_create_breakpoint (struct breakpoint *b)
+{
+ tui_update_all_breakpoint_info (nullptr);
+}
+/* Breakpoint deletion hook.
+ Refresh the screen to update the breakpoint marks. */
static void
-tui_registers_changed_hook (void)
+tui_event_delete_breakpoint (struct breakpoint *b)
{
- struct frame_info *fi;
+ tui_update_all_breakpoint_info (b);
+}
- fi = deprecated_selected_frame;
- if (fi && tui_refreshing_registers == 0)
- {
- tui_refreshing_registers = 1;
-#if 0
- tuiCheckDataValues (fi);
-#endif
- tui_refreshing_registers = 0;
- }
+static void
+tui_event_modify_breakpoint (struct breakpoint *b)
+{
+ tui_update_all_breakpoint_info (nullptr);
}
+/* This is set to true if the next window refresh should come from the
+ current stack frame. */
+
+static bool from_stack;
+
+/* This is set to true if the next window refresh should come from the
+ current source symtab. */
+
+static bool from_source_symtab;
+
+/* Refresh TUI's frame and register information. This is a hook intended to be
+ used to update the screen after potential frame and register changes. */
+
static void
-tui_register_changed_hook (int regno)
+tui_refresh_frame_and_register_information ()
{
- struct frame_info *fi;
+ if (!from_stack && !from_source_symtab)
+ return;
+
+ target_terminal::scoped_restore_terminal_state term_state;
+ target_terminal::ours_for_output ();
- fi = deprecated_selected_frame;
- if (fi && tui_refreshing_registers == 0)
+ if (from_stack && has_stack_frames ())
{
- tui_refreshing_registers = 1;
- tui_check_data_values (fi);
- tui_refreshing_registers = 0;
+ struct frame_info *fi = get_selected_frame (NULL);
+
+ /* Display the frame position (even if there is no symbols or
+ the PC is not known). */
+ bool frame_info_changed_p = tui_show_frame_info (fi);
+
+ /* Refresh the register window if it's visible. */
+ if (tui_is_window_visible (DATA_WIN)
+ && (frame_info_changed_p || from_stack))
+ {
+ tui_refreshing_registers = true;
+ TUI_DATA_WIN->check_register_values (fi);
+ tui_refreshing_registers = false;
+ }
+ }
+ else if (!from_stack)
+ {
+ /* Make sure that the source window is displayed. */
+ tui_add_win_to_layout (SRC_WIN);
+
+ struct symtab_and_line sal = get_current_source_symtab_and_line ();
+ tui_update_source_windows_with_line (sal);
}
}
-/* Breakpoint creation hook.
- Update the screen to show the new breakpoint. */
+/* Dummy callback for deprecated_print_frame_info_listing_hook which is called
+ from print_frame_info. */
+
static void
-tui_event_create_breakpoint (int number)
+tui_dummy_print_frame_info_listing_hook (struct symtab *s,
+ int line,
+ int stopline,
+ int noerror)
{
- tui_update_all_breakpoint_info ();
}
-/* Breakpoint deletion hook.
- Refresh the screen to update the breakpoint marks. */
+/* Perform all necessary cleanups regarding our module's inferior data
+ that is required after the inferior INF just exited. */
+
static void
-tui_event_delete_breakpoint (int number)
+tui_inferior_exit (struct inferior *inf)
{
- tui_update_all_breakpoint_info ();
+ /* Leave the SingleKey mode to make sure the gdb prompt is visible. */
+ tui_set_key_mode (TUI_COMMAND_MODE);
+ tui_show_frame_info (0);
+ tui_display_main ();
}
+/* Observer for the before_prompt notification. */
+
static void
-tui_event_modify_breakpoint (int number)
+tui_before_prompt (const char *current_gdb_prompt)
{
- tui_update_all_breakpoint_info ();
+ tui_refresh_frame_and_register_information ();
+ from_stack = false;
+ from_source_symtab = false;
}
+/* Observer for the normal_stop notification. */
+
static void
-tui_event_default (int number)
+tui_normal_stop (struct bpstats *bs, int print_frame)
{
- ;
+ from_stack = true;
}
-static struct gdb_events *tui_old_event_hooks;
+/* Observer for user_selected_context_changed. */
-static struct gdb_events tui_event_hooks =
-{
- tui_event_create_breakpoint,
- tui_event_delete_breakpoint,
- tui_event_modify_breakpoint,
- tui_event_default,
- tui_event_default,
- tui_event_default
-};
-
-/* Called when going to wait for the target.
- Leave curses mode and setup program mode. */
-static ptid_t
-tui_target_wait_hook (ptid_t pid, struct target_waitstatus *status)
+static void
+tui_context_changed (user_selected_what ignore)
{
- ptid_t res;
-
- /* Leave tui mode (optional). */
-#if 0
- if (tui_active)
- {
- target_terminal_ours ();
- endwin ();
- target_terminal_inferior ();
- }
-#endif
- tui_target_has_run = 1;
- res = target_wait (pid, status);
-
- if (tui_active)
- {
- /* TODO: need to refresh (optional). */
- }
- return res;
+ from_stack = true;
}
-/* The selected frame has changed. This is happens after a target
- stop or when the user explicitly changes the frame (up/down/thread/...). */
+/* Observer for current_source_symtab_and_line_changed. */
+
static void
-tui_selected_frame_level_changed_hook (int level)
+tui_symtab_changed ()
{
- struct frame_info *fi;
+ from_source_symtab = true;
+}
- fi = deprecated_selected_frame;
- /* Ensure that symbols for this frame are read in. Also, determine the
- source language of this frame, and switch to it if desired. */
- if (fi)
- {
- struct symtab *s;
-
- s = find_pc_symtab (get_frame_pc (fi));
- /* elz: this if here fixes the problem with the pc not being displayed
- in the tui asm layout, with no debug symbols. The value of s
- would be 0 here, and select_source_symtab would abort the
- command by calling the 'error' function */
- if (s)
- select_source_symtab (s);
-
- /* Display the frame position (even if there is no symbols). */
- tui_show_frame_info (fi);
+/* Token associated with observers registered while TUI hooks are
+ installed. */
+static const gdb::observers::token tui_observers_token {};
- /* Refresh the register window if it's visible. */
- if (tui_is_window_visible (DATA_WIN))
- {
- tui_refreshing_registers = 1;
- tui_check_data_values (fi);
- tui_refreshing_registers = 0;
- }
- }
-}
+/* Attach or detach a single observer, according to ATTACH. */
-/* Called from print_frame_info to list the line we stopped in. */
+template<typename T>
static void
-tui_print_frame_info_listing_hook (struct symtab *s, int line,
- int stopline, int noerror)
+attach_or_detach (T &observable, typename T::func_type func, bool attach)
{
- select_source_symtab (s);
- tui_show_frame_info (deprecated_selected_frame);
+ if (attach)
+ observable.attach (func, tui_observers_token);
+ else
+ observable.detach (tui_observers_token);
}
-/* Called when the target process died or is detached.
- Update the status line. */
+/* Attach or detach TUI observers, according to ATTACH. */
+
static void
-tui_detach_hook (void)
+tui_attach_detach_observers (bool attach)
{
- tui_show_frame_info (0);
- tui_display_main ();
+ attach_or_detach (gdb::observers::breakpoint_created,
+ tui_event_create_breakpoint, attach);
+ attach_or_detach (gdb::observers::breakpoint_deleted,
+ tui_event_delete_breakpoint, attach);
+ attach_or_detach (gdb::observers::breakpoint_modified,
+ tui_event_modify_breakpoint, attach);
+ attach_or_detach (gdb::observers::inferior_exit,
+ tui_inferior_exit, attach);
+ attach_or_detach (gdb::observers::before_prompt,
+ tui_before_prompt, attach);
+ attach_or_detach (gdb::observers::normal_stop,
+ tui_normal_stop, attach);
+ attach_or_detach (gdb::observers::register_changed,
+ tui_register_changed, attach);
+ attach_or_detach (gdb::observers::user_selected_context_changed,
+ tui_context_changed, attach);
+ attach_or_detach (gdb::observers::current_source_symtab_and_line_changed,
+ tui_symtab_changed, attach);
}
/* Install the TUI specific hooks. */
void
tui_install_hooks (void)
{
- target_wait_hook = tui_target_wait_hook;
- selected_frame_level_changed_hook = tui_selected_frame_level_changed_hook;
- print_frame_info_listing_hook = tui_print_frame_info_listing_hook;
-
- query_hook = tui_query_hook;
+ /* If this hook is not set to something then print_frame_info will
+ assume that the CLI, not the TUI, is active, and will print the frame info
+ for us in such a way that we are not prepared to handle. This hook is
+ otherwise effectively obsolete. */
+ deprecated_print_frame_info_listing_hook
+ = tui_dummy_print_frame_info_listing_hook;
/* Install the event hooks. */
- tui_old_event_hooks = set_gdb_event_hooks (&tui_event_hooks);
-
- registers_changed_hook = tui_registers_changed_hook;
- register_changed_hook = tui_register_changed_hook;
- detach_hook = tui_detach_hook;
+ tui_attach_detach_observers (true);
}
/* Remove the TUI specific hooks. */
void
tui_remove_hooks (void)
{
- target_wait_hook = 0;
- selected_frame_level_changed_hook = 0;
- print_frame_info_listing_hook = 0;
- query_hook = 0;
- registers_changed_hook = 0;
- register_changed_hook = 0;
- detach_hook = 0;
-
- /* Restore the previous event hooks. */
- set_gdb_event_hooks (tui_old_event_hooks);
-}
+ deprecated_print_frame_info_listing_hook = 0;
-void _initialize_tui_hooks (void);
+ /* Remove our observers. */
+ tui_attach_detach_observers (false);
+}
+void _initialize_tui_hooks ();
void
-_initialize_tui_hooks (void)
+_initialize_tui_hooks ()
{
/* Install the permanent hooks. */
- tui_target_new_objfile_chain = target_new_objfile_hook;
- target_new_objfile_hook = tui_new_objfile_hook;
+ gdb::observers::new_objfile.attach (tui_new_objfile_hook);
}