1 /* TUI display source/assembly window.
2 
3    Copyright (C) 1998-2024 Free Software Foundation, Inc.
4 
5    Contributed by Hewlett-Packard Company.
6 
7    This file is part of GDB.
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
21 
22 #ifndef TUI_TUI_WINSOURCE_H
23 #define TUI_TUI_WINSOURCE_H
24 
25 #include "tui/tui-data.h"
26 #include "symtab.h"
27 
28 enum tui_line_or_address_kind
29 {
30   LOA_LINE,
31   LOA_ADDRESS
32 };
33 
34 /* Structure describing source line or line address.  */
35 struct tui_line_or_address
36 {
37   enum tui_line_or_address_kind loa;
38   union
39     {
40       int line_no;
41       CORE_ADDR addr;
42     } u;
43 };
44 
45 /* Flags to tell what kind of breakpoint is at current line.  */
46 enum tui_bp_flag
47 {
48   TUI_BP_ENABLED = 0x01,
49   TUI_BP_DISABLED = 0x02,
50   TUI_BP_HIT = 0x04,
51   TUI_BP_CONDITIONAL = 0x08,
52   TUI_BP_HARDWARE = 0x10
53 };
54 
55 DEF_ENUM_FLAGS_TYPE (enum tui_bp_flag, tui_bp_flags);
56 
57 /* Position of breakpoint markers in the exec info string.  */
58 #define TUI_BP_HIT_POS      0
59 #define TUI_BP_BREAK_POS    1
60 #define TUI_EXEC_POS        2
61 #define TUI_EXECINFO_SIZE   3
62 
63 /* Elements in the Source/Disassembly Window.  */
64 struct tui_source_element
65 {
tui_source_elementtui_source_element66   tui_source_element ()
67   {
68     line_or_addr.loa = LOA_LINE;
69     line_or_addr.u.line_no = 0;
70   }
71 
72   DISABLE_COPY_AND_ASSIGN (tui_source_element);
73 
tui_source_elementtui_source_element74   tui_source_element (tui_source_element &&other)
75     : line (std::move (other.line)),
76       line_or_addr (other.line_or_addr),
77       is_exec_point (other.is_exec_point),
78       break_mode (other.break_mode)
79   {
80   }
81 
82   std::string line;
83   struct tui_line_or_address line_or_addr;
84   bool is_exec_point = false;
85   tui_bp_flags break_mode = 0;
86 };
87 
88 
89 /* The base class for all source-like windows, namely the source and
90    disassembly windows.  */
91 
92 struct tui_source_window_base : public tui_win_info
93 {
94 protected:
95   tui_source_window_base ();
96   ~tui_source_window_base ();
97 
98   DISABLE_COPY_AND_ASSIGN (tui_source_window_base);
99 
100   void do_scroll_horizontal (int num_to_scroll) override;
101 
102   /* Erase the content and display STRING.  */
103   void do_erase_source_content (const char *string);
104 
105   void rerender () override;
106 
107   virtual bool set_contents (struct gdbarch *gdbarch,
108                                    const struct symtab_and_line &sal) = 0;
109 
110   /* Return the number of extra margin characters needed by this
111      instance.  */
extra_margintui_source_window_base112   virtual int extra_margin () const
113   {
114     return 0;
115   }
116 
117   /* Display the line number in the window margin.  OFFSET indicates
118      which line to display; it is 0-based, with 0 meaning the line at
119      the top of the window.  */
show_line_numbertui_source_window_base120   virtual void show_line_number (int offset) const
121   {
122   }
123 
124   /* Redraw the complete line of a source or disassembly window.  */
125   void show_source_line (int lineno);
126 
127   /* Where to start generating content from.  */
128   struct tui_line_or_address m_start_line_or_addr;
129 
130   /* Architecture associated with code at this location.  */
131   struct gdbarch *m_gdbarch = nullptr;
132 
133   std::vector<tui_source_element> m_content;
134 
135   /* Length of longest line to be displayed.  */
136   int m_max_length;
137 
138 public:
139 
140   /* Refill the source window's source cache and update it.  If this
141      is a disassembly window, then just update it.  */
142   void refill ();
143 
144   /* Set the location of the execution point.  */
145   void set_is_exec_point_at (struct tui_line_or_address l);
146 
147   void update_tab_width () override;
148 
149   virtual bool location_matches_p (struct bp_location *loc, int line_no) = 0;
150 
151   /* Fill in the left margin of the current window with execution indicator
152      information, e.g. breakpoint indicators, and line numbers.  When
153      REFRESH_P is true this function will call refresh_window to ensure
154      updates are written to the screen, otherwise the refresh is skipped,
155      which will leave the on screen contents out of date.  When passing
156      false for REFRESH_P you should be planning to call refresh_window
157      yourself.  */
158   void update_exec_info (bool refresh_p = true);
159 
160   /* Update the window to display the given location.  Does nothing if
161      the location is already displayed.  */
162   virtual void maybe_update (const frame_info_ptr &fi, symtab_and_line sal) = 0;
163 
164   void update_source_window_as_is  (struct gdbarch *gdbarch,
165                                             const struct symtab_and_line &sal);
166   void update_source_window (struct gdbarch *gdbarch,
167                                    const struct symtab_and_line &sal);
168 
169   /* Scan the source window and the breakpoints to update the
170      break_mode information for each line.  Returns true if something
171      changed and the execution window must be refreshed.  See
172      tui_update_all_breakpoint_info for a description of
173      BEING_DELETED.  */
174   bool update_breakpoint_info (struct breakpoint *being_deleted,
175                                      bool current_only);
176 
177   /* Erase the source content.  */
178   virtual void erase_source_content () = 0;
179 
180   void refresh_window () override;
181 
182   /* Return the start address and gdbarch.  */
183   virtual void display_start_addr (struct gdbarch **gdbarch_p,
184                                            CORE_ADDR *addr_p) = 0;
185 
186   /* Function to ensure that the source or disassembly window
187      reflects the input address.  Single window variant of
188      update_source_windows_with_addr.  */
189   void update_source_window_with_addr (struct gdbarch *, CORE_ADDR);
190 
191 private:
192 
193   /* Used for horizontal scroll.  */
194   int m_horizontal_offset = 0;
195 
196   /* Check that the current values of M_HORIZONTAL_OFFSET and M_PAD_OFFSET
197      make sense given the current M_MAX_LENGTH (content width), WIDTH
198      (window size), and window margins.  After calling this function
199      M_HORIZONTAL_OFFSET and M_PAD_OFFSET might have been adjusted to
200      reduce unnecessary whitespace on the right side of the window.
201 
202      If M_PAD_OFFSET is adjusted then this function returns true
203      indicating that the pad contents need to be reloaded by calling
204      show_source_content.  If M_PAD_OFFSET is not adjusted then this
205      function returns false, the window contents might still need
206      redrawing if M_HORIZONTAL_OFFSET was adjusted, but right now, this
207      function is only called in contexts where the window is going to be
208      redrawn anyway.  */
209   bool validate_scroll_offsets ();
210 
211   /* Return the size of the left margin space, this is the space used to
212      display things like breakpoint markers.  */
left_margintui_source_window_base213   int left_margin () const
214   { return TUI_EXECINFO_SIZE + extra_margin (); }
215 
216   /* Return the width of the area that is available for window content.
217      This is the window width minus the borders and the left margin, which
218      is used for displaying things like breakpoint markers.  */
view_widthtui_source_window_base219   int view_width () const
220   { return width - left_margin () - box_size (); }
221 
222   void show_source_content ();
223 
224   /* Write STRING to the window M_PAD, but skip the first SKIP printable
225      characters.  Any escape sequences within the first SKIP characters are
226      still processed though.  This means if we have this string:
227 
228      "\033[31mABCDEFGHIJKLM\033[0m"
229 
230      and call this function with a skip value of 3, then we effectively
231      write this string to M_PAD:
232 
233      "\033[31mDEFGHIJKLM\033[0m"
234 
235      the initial escape that sets the color will still be applied.  */
236   void puts_to_pad_with_skip (const char *string, int skip);
237 
238   /* Called when the user "set style enabled" setting is changed.  */
239   void style_changed ();
240 
241   /* A token used to register and unregister an observer.  */
242   gdb::observers::token m_observable;
243 
244   /* Pad to hold some, or all, of the window contents.  Content is then
245      copied from this pad to the screen as the user scrolls horizontally,
246      this avoids the need to recalculate the screen contents each time the
247      user does a horizontal scroll.  */
248   std::unique_ptr<WINDOW, curses_deleter> m_pad;
249 
250   /* When M_PAD was allocated, this holds the width that was initially
251      asked for.  If we ask for a very large pad then the allocation may
252      fail, and we might instead allocate a narrower pad.  */
253   int m_pad_requested_width = 0;
254 
255   /* If M_PAD is not as wide as the content (so less than M_MAX_LENGTH)
256      then this value indicates the offset at which the pad contents begin.  */
257   int m_pad_offset = 0;
258 };
259 
260 
261 /* A wrapper for a TUI window iterator that only iterates over source
262    windows.  */
263 
264 struct tui_source_window_iterator
265 {
266 public:
267 
268   typedef std::vector<tui_win_info *>::iterator inner_iterator;
269 
270   typedef tui_source_window_iterator self_type;
271   typedef struct tui_source_window_base *value_type;
272   typedef struct tui_source_window_base *&reference;
273   typedef struct tui_source_window_base **pointer;
274   typedef std::forward_iterator_tag iterator_category;
275   typedef int difference_type;
276 
tui_source_window_iteratortui_source_window_iterator277   explicit tui_source_window_iterator (const inner_iterator &it,
278                                                const inner_iterator &end)
279     : m_iter (it),
280       m_end (end)
281   {
282     advance ();
283   }
284 
tui_source_window_iteratortui_source_window_iterator285   explicit tui_source_window_iterator (const inner_iterator &it)
286     : m_iter (it)
287   {
288   }
289 
290   bool operator!= (const self_type &other) const
291   {
292     return m_iter != other.m_iter;
293   }
294 
295   value_type operator* () const
296   {
297     return dynamic_cast<tui_source_window_base *> (*m_iter);
298   }
299 
300   self_type &operator++ ()
301   {
302     ++m_iter;
303     advance ();
304     return *this;
305   }
306 
307 private:
308 
advancetui_source_window_iterator309   void advance ()
310   {
311     while (m_iter != m_end
312              && dynamic_cast<tui_source_window_base *> (*m_iter) == nullptr)
313       ++m_iter;
314   }
315 
316   inner_iterator m_iter;
317   inner_iterator m_end;
318 };
319 
320 /* A range adapter for source windows.  */
321 
322 struct tui_source_windows
323 {
324   /* Work around Wmaybe-uninitialized warning with g++ 11.0.0, see also
325      PR gcc/96295.  Note that "tui_source_windows () = default" doesn't work
326      around the warning.  */
tui_source_windowstui_source_windows327   tui_source_windows () {}
328 
begintui_source_windows329   tui_source_window_iterator begin () const
330   {
331     return tui_source_window_iterator (tui_windows.begin (),
332                                                tui_windows.end ());
333   }
334 
endtui_source_windows335   tui_source_window_iterator end () const
336   {
337     return tui_source_window_iterator (tui_windows.end ());
338   }
339 };
340 
341 /* Update the execution windows to show the active breakpoints.  This
342    is called whenever a breakpoint is inserted, removed or has its
343    state changed.  Normally BEING_DELETED is nullptr; if not nullptr,
344    it indicates a breakpoint that is in the process of being deleted,
345    and which should therefore be ignored by the update.  This is done
346    because the relevant observer is notified before the breakpoint is
347    removed from the list of breakpoints.  */
348 extern void tui_update_all_breakpoint_info (struct breakpoint *being_deleted);
349 
350 /* Function to display the "main" routine.  */
351 extern void tui_display_main (void);
352 extern void tui_update_source_windows_with_addr (struct gdbarch *, CORE_ADDR);
353 extern void tui_update_source_windows_with_line (struct symtab_and_line sal);
354 
355 /* Extract some source text from PTR.  Returns a string holding the
356    desired text.  PTR is updated to point to the start of the next
357    line.  If LENGTH is non-NULL, then the length of the line is stored
358    there.  Escape sequences are not counted against the length.
359    Actually an approximation is used -- each byte of a multi-byte
360    sequence counts as a character here.  */
361 
362 extern std::string tui_copy_source_line (const char **ptr,
363                                                    int *length = nullptr);
364 
365 /* Constant definitions. */
366 #define SCROLL_THRESHOLD 2    /* Threshold for lazy scroll.  */
367 
368 #endif /* TUI_TUI_WINSOURCE_H */
369