1 /* GDB hooks for TUI.
2
3 Copyright (C) 2001-2024 Free Software Foundation, Inc.
4
5 This file is part of GDB.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include "symtab.h"
21 #include "inferior.h"
22 #include "command.h"
23 #include "bfd.h"
24 #include "symfile.h"
25 #include "objfiles.h"
26 #include "target.h"
27 #include "gdbcore.h"
28 #include "gdbsupport/event-loop.h"
29 #include "event-top.h"
30 #include "frame.h"
31 #include "breakpoint.h"
32 #include "ui-out.h"
33 #include "top.h"
34 #include "observable.h"
35 #include "source.h"
36 #include <unistd.h>
37 #include <fcntl.h>
38
39 #include "tui/tui.h"
40 #include "tui/tui-hooks.h"
41 #include "tui/tui-data.h"
42 #include "tui/tui-layout.h"
43 #include "tui/tui-io.h"
44 #include "tui/tui-regs.h"
45 #include "tui/tui-win.h"
46 #include "tui/tui-status.h"
47 #include "tui/tui-winsource.h"
48
49 #include "gdb_curses.h"
50
51 static void
tui_new_objfile_hook(struct objfile * objfile)52 tui_new_objfile_hook (struct objfile* objfile)
53 {
54 if (tui_active)
55 tui_display_main ();
56 }
57
58 /* Observer for the register_changed notification. */
59
60 static void
tui_register_changed(const frame_info_ptr & frame,int regno)61 tui_register_changed (const frame_info_ptr &frame, int regno)
62 {
63 frame_info_ptr fi;
64
65 if (!tui_is_window_visible (DATA_WIN))
66 return;
67
68 /* The frame of the register that was changed may differ from the selected
69 frame, but we only want to show the register values of the selected frame.
70 And even if the frames differ a register change made in one can still show
71 up in the other. So we always use the selected frame here, and ignore
72 FRAME. */
73 fi = get_selected_frame (NULL);
74 TUI_DATA_WIN->check_register_values (fi);
75 }
76
77 /* Breakpoint creation hook.
78 Update the screen to show the new breakpoint. */
79 static void
tui_event_create_breakpoint(struct breakpoint * b)80 tui_event_create_breakpoint (struct breakpoint *b)
81 {
82 tui_update_all_breakpoint_info (nullptr);
83 }
84
85 /* Breakpoint deletion hook.
86 Refresh the screen to update the breakpoint marks. */
87 static void
tui_event_delete_breakpoint(struct breakpoint * b)88 tui_event_delete_breakpoint (struct breakpoint *b)
89 {
90 tui_update_all_breakpoint_info (b);
91 }
92
93 static void
tui_event_modify_breakpoint(struct breakpoint * b)94 tui_event_modify_breakpoint (struct breakpoint *b)
95 {
96 tui_update_all_breakpoint_info (nullptr);
97 }
98
99 /* This is set to true if the next window refresh should come from the
100 current stack frame. */
101
102 static bool from_stack;
103
104 /* This is set to true if the next window refresh should come from the
105 current source symtab. */
106
107 static bool from_source_symtab;
108
109 /* Refresh TUI's frame and register information. This is a hook intended to be
110 used to update the screen after potential frame and register changes. */
111
112 static void
tui_refresh_frame_and_register_information()113 tui_refresh_frame_and_register_information ()
114 {
115 if (!from_stack && !from_source_symtab)
116 return;
117
118 target_terminal::scoped_restore_terminal_state term_state;
119 target_terminal::ours_for_output ();
120
121 if (from_stack)
122 {
123 frame_info_ptr fi;
124 if (has_stack_frames ())
125 {
126 fi = get_selected_frame (NULL);
127
128 /* Display the frame position (even if there is no symbols or
129 the PC is not known). */
130 tui_show_frame_info (fi);
131 }
132
133 /* Refresh the register window if it's visible. */
134 if (tui_is_window_visible (DATA_WIN))
135 TUI_DATA_WIN->check_register_values (fi);
136 }
137 else
138 {
139 /* Make sure that the source window is displayed. */
140 tui_add_win_to_layout (SRC_WIN);
141
142 struct symtab_and_line sal = get_current_source_symtab_and_line ();
143 tui_update_source_windows_with_line (sal);
144 }
145 }
146
147 /* Dummy callback for deprecated_print_frame_info_listing_hook which is called
148 from print_frame_info. */
149
150 static void
tui_dummy_print_frame_info_listing_hook(struct symtab * s,int line,int stopline,int noerror)151 tui_dummy_print_frame_info_listing_hook (struct symtab *s,
152 int line,
153 int stopline,
154 int noerror)
155 {
156 }
157
158 /* Perform all necessary cleanups regarding our module's inferior data
159 that is required after the inferior INF just exited. */
160
161 static void
tui_inferior_exit(struct inferior * inf)162 tui_inferior_exit (struct inferior *inf)
163 {
164 /* Leave the SingleKey mode to make sure the gdb prompt is visible. */
165 tui_set_key_mode (TUI_COMMAND_MODE);
166 tui_show_frame_info (0);
167 tui_display_main ();
168 from_stack = true;
169 }
170
171 /* Observer for the before_prompt notification. */
172
173 static void
tui_before_prompt(const char * current_gdb_prompt)174 tui_before_prompt (const char *current_gdb_prompt)
175 {
176 tui_refresh_frame_and_register_information ();
177 from_stack = false;
178 from_source_symtab = false;
179 }
180
181 /* Observer for the normal_stop notification. */
182
183 static void
tui_normal_stop(struct bpstat * bs,int print_frame)184 tui_normal_stop (struct bpstat *bs, int print_frame)
185 {
186 from_stack = true;
187 }
188
189 /* Observer for user_selected_context_changed. */
190
191 static void
tui_context_changed(user_selected_what ignore)192 tui_context_changed (user_selected_what ignore)
193 {
194 from_stack = true;
195 }
196
197 /* Observer for current_source_symtab_and_line_changed. */
198
199 static void
tui_symtab_changed()200 tui_symtab_changed ()
201 {
202 from_source_symtab = true;
203 }
204
205 /* Token associated with observers registered while TUI hooks are
206 installed. */
207 static const gdb::observers::token tui_observers_token {};
208
209 /* Attach or detach a single observer, according to ATTACH. */
210
211 template<typename T>
212 static void
attach_or_detach(T & observable,typename T::func_type func,bool attach)213 attach_or_detach (T &observable, typename T::func_type func, bool attach)
214 {
215 if (attach)
216 observable.attach (func, tui_observers_token, "tui-hooks");
217 else
218 observable.detach (tui_observers_token);
219 }
220
221 /* Attach or detach TUI observers, according to ATTACH. */
222
223 static void
tui_attach_detach_observers(bool attach)224 tui_attach_detach_observers (bool attach)
225 {
226 attach_or_detach (gdb::observers::breakpoint_created,
227 tui_event_create_breakpoint, attach);
228 attach_or_detach (gdb::observers::breakpoint_deleted,
229 tui_event_delete_breakpoint, attach);
230 attach_or_detach (gdb::observers::breakpoint_modified,
231 tui_event_modify_breakpoint, attach);
232 attach_or_detach (gdb::observers::inferior_exit,
233 tui_inferior_exit, attach);
234 attach_or_detach (gdb::observers::before_prompt,
235 tui_before_prompt, attach);
236 attach_or_detach (gdb::observers::normal_stop,
237 tui_normal_stop, attach);
238 attach_or_detach (gdb::observers::register_changed,
239 tui_register_changed, attach);
240 attach_or_detach (gdb::observers::user_selected_context_changed,
241 tui_context_changed, attach);
242 attach_or_detach (gdb::observers::current_source_symtab_and_line_changed,
243 tui_symtab_changed, attach);
244 }
245
246 /* Install the TUI specific hooks. */
247 void
tui_install_hooks(void)248 tui_install_hooks (void)
249 {
250 /* If this hook is not set to something then print_frame_info will
251 assume that the CLI, not the TUI, is active, and will print the frame info
252 for us in such a way that we are not prepared to handle. This hook is
253 otherwise effectively obsolete. */
254 deprecated_print_frame_info_listing_hook
255 = tui_dummy_print_frame_info_listing_hook;
256
257 /* Install the event hooks. */
258 tui_attach_detach_observers (true);
259 }
260
261 /* Remove the TUI specific hooks. */
262 void
tui_remove_hooks(void)263 tui_remove_hooks (void)
264 {
265 deprecated_print_frame_info_listing_hook = 0;
266
267 /* Remove our observers. */
268 tui_attach_detach_observers (false);
269 }
270
271 void _initialize_tui_hooks ();
272 void
_initialize_tui_hooks()273 _initialize_tui_hooks ()
274 {
275 /* Install the permanent hooks. */
276 gdb::observers::new_objfile.attach (tui_new_objfile_hook, "tui-hooks");
277 }
278