1 /* TUI window generic functions.
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 /* This module contains procedures for handling tui window functions
23    like resize, scrolling, scrolling, changing focus, etc.
24 
25    Author: Susan B. Macchia  */
26 
27 #include "command.h"
28 #include "symtab.h"
29 #include "breakpoint.h"
30 #include "frame.h"
31 #include "cli/cli-cmds.h"
32 #include "cli/cli-style.h"
33 #include "top.h"
34 #include "source.h"
35 #include "gdbsupport/event-loop.h"
36 #include "async-event.h"
37 #include "utils.h"
38 
39 #include "tui/tui.h"
40 #include "tui/tui-io.h"
41 #include "tui/tui-command.h"
42 #include "tui/tui-data.h"
43 #include "tui/tui-layout.h"
44 #include "tui/tui-wingeneral.h"
45 #include "tui/tui-status.h"
46 #include "tui/tui-regs.h"
47 #include "tui/tui-disasm.h"
48 #include "tui/tui-source.h"
49 #include "tui/tui-winsource.h"
50 #include "tui/tui-win.h"
51 
52 #include "gdb_curses.h"
53 #include <ctype.h>
54 #include "readline/readline.h"
55 #include <string_view>
56 
57 #include <signal.h>
58 
59 static void tui_set_tab_width_command (const char *, int);
60 static void tui_refresh_all_command (const char *, int);
61 static void tui_all_windows_info (const char *, int);
62 static void tui_scroll_forward_command (const char *, int);
63 static void tui_scroll_backward_command (const char *, int);
64 static void tui_scroll_left_command (const char *, int);
65 static void tui_scroll_right_command (const char *, int);
66 static void parse_scrolling_args (const char *,
67                                           struct tui_win_info **,
68                                           int *);
69 
70 
71 #ifndef ACS_LRCORNER
72 #  define ACS_LRCORNER '+'
73 #endif
74 #ifndef ACS_LLCORNER
75 #  define ACS_LLCORNER '+'
76 #endif
77 #ifndef ACS_ULCORNER
78 #  define ACS_ULCORNER '+'
79 #endif
80 #ifndef ACS_URCORNER
81 #  define ACS_URCORNER '+'
82 #endif
83 #ifndef ACS_HLINE
84 #  define ACS_HLINE '-'
85 #endif
86 #ifndef ACS_VLINE
87 #  define ACS_VLINE '|'
88 #endif
89 
90 /* Possible values for tui-border-kind variable.  */
91 static const char *const tui_border_kind_enums[] = {
92   "space",
93   "ascii",
94   "acs",
95   NULL
96 };
97 
98 /* Possible values for tui-border-mode and tui-active-border-mode.  */
99 static const char *const tui_border_mode_enums[] = {
100   "normal",
101   "standout",
102   "reverse",
103   "half",
104   "half-standout",
105   "bold",
106   "bold-standout",
107   NULL
108 };
109 
110 struct tui_translate
111 {
112   const char *name;
113   int value;
114 };
115 
116 /* Translation table for border-mode variables.
117    The list of values must be terminated by a NULL.  */
118 static struct tui_translate tui_border_mode_translate[] = {
119   { "normal",                 A_NORMAL },
120   { "standout",               A_STANDOUT },
121   { "reverse",                A_REVERSE },
122   { "half",                   A_DIM },
123   { "half-standout",          A_DIM | A_STANDOUT },
124   { "bold",                   A_BOLD },
125   { "bold-standout",          A_BOLD | A_STANDOUT },
126   { 0, 0 }
127 };
128 
129 /* Translation tables for border-kind (acs excluded), one for vline, hline and
130    corners (see wborder, border curses operations).  */
131 static struct tui_translate tui_border_kind_translate_vline[] = {
132   { "space",    ' ' },
133   { "ascii",    '|' },
134   { 0, 0 }
135 };
136 
137 static struct tui_translate tui_border_kind_translate_hline[] = {
138   { "space",    ' ' },
139   { "ascii",    '-' },
140   { 0, 0 }
141 };
142 
143 static struct tui_translate tui_border_kind_translate_corner[] = {
144   { "space",    ' ' },
145   { "ascii",    '+' },
146   { 0, 0 }
147 };
148 
149 
150 /* Tui configuration variables controlled with set/show command.  */
151 static const char *tui_active_border_mode = "bold-standout";
152 static void
show_tui_active_border_mode(struct ui_file * file,int from_tty,struct cmd_list_element * c,const char * value)153 show_tui_active_border_mode (struct ui_file *file,
154                                    int from_tty,
155                                    struct cmd_list_element *c,
156                                    const char *value)
157 {
158   gdb_printf (file, _("\
159 The attribute mode to use for the active TUI window border is \"%s\".\n"),
160                 value);
161 }
162 
163 static const char *tui_border_mode = "normal";
164 static void
show_tui_border_mode(struct ui_file * file,int from_tty,struct cmd_list_element * c,const char * value)165 show_tui_border_mode (struct ui_file *file,
166                           int from_tty,
167                           struct cmd_list_element *c,
168                           const char *value)
169 {
170   gdb_printf (file, _("\
171 The attribute mode to use for the TUI window borders is \"%s\".\n"),
172                 value);
173 }
174 
175 static const char *tui_border_kind = "acs";
176 static void
show_tui_border_kind(struct ui_file * file,int from_tty,struct cmd_list_element * c,const char * value)177 show_tui_border_kind (struct ui_file *file,
178                           int from_tty,
179                           struct cmd_list_element *c,
180                           const char *value)
181 {
182   gdb_printf (file, _("The kind of border for TUI windows is \"%s\".\n"),
183                 value);
184 }
185 
186 /* Implementation of the "set/show style tui-current-position" commands.  */
187 
188 bool style_tui_current_position = false;
189 
190 static void
show_style_tui_current_position(ui_file * file,int from_tty,cmd_list_element * c,const char * value)191 show_style_tui_current_position (ui_file *file,
192                                          int from_tty,
193                                          cmd_list_element *c,
194                                          const char *value)
195 {
196   gdb_printf (file, _("\
197 Styling the text highlighted by the TUI's current position indicator is %s.\n"),
198                         value);
199 }
200 
201 static void
set_style_tui_current_position(const char * ignore,int from_tty,cmd_list_element * c)202 set_style_tui_current_position (const char *ignore, int from_tty,
203                                         cmd_list_element *c)
204 {
205   if (TUI_SRC_WIN != nullptr)
206     TUI_SRC_WIN->refill ();
207   if (TUI_DISASM_WIN != nullptr)
208     TUI_DISASM_WIN->refill ();
209 }
210 
211 /* Tui internal configuration variables.  These variables are updated
212    by tui_update_variables to reflect the tui configuration
213    variables.  */
214 chtype tui_border_vline;
215 chtype tui_border_hline;
216 chtype tui_border_ulcorner;
217 chtype tui_border_urcorner;
218 chtype tui_border_llcorner;
219 chtype tui_border_lrcorner;
220 
221 int tui_border_attrs;
222 int tui_active_border_attrs;
223 
224 /* Identify the item in the translation table, and return the corresponding value.  */
225 static int
translate(const char * name,struct tui_translate * table)226 translate (const char *name, struct tui_translate *table)
227 {
228   while (table->name)
229     {
230       if (name && strcmp (table->name, name) == 0)
231           return table->value;
232       table++;
233     }
234 
235   gdb_assert_not_reached ("");
236 }
237 
238 /* Translate NAME to a value.  If NAME is "acs", use ACS_CHAR.  Otherwise, use
239    translation table TABLE. */
240 static int
translate_acs(const char * name,struct tui_translate * table,int acs_char)241 translate_acs (const char *name, struct tui_translate *table, int acs_char)
242 {
243   /* The ACS characters are determined at run time by curses terminal
244      management.  */
245   if (strcmp (name, "acs") == 0)
246     return acs_char;
247 
248   return translate (name, table);
249 }
250 
251 /* Update the tui internal configuration according to gdb settings.
252    Returns 1 if the configuration has changed and the screen should
253    be redrawn.  */
254 bool
tui_update_variables()255 tui_update_variables ()
256 {
257   bool need_redraw = false;
258   int val;
259 
260   val = translate (tui_border_mode, tui_border_mode_translate);
261   need_redraw |= assign_return_if_changed<int> (tui_border_attrs, val);
262 
263   val = translate (tui_active_border_mode, tui_border_mode_translate);
264   need_redraw |= assign_return_if_changed<int> (tui_active_border_attrs, val);
265 
266   /* If one corner changes, all characters are changed.  Only check the first
267      one.  */
268   val = translate_acs (tui_border_kind, tui_border_kind_translate_corner,
269                            ACS_LRCORNER);
270   need_redraw |= assign_return_if_changed<chtype> (tui_border_lrcorner, val);
271 
272   tui_border_llcorner
273     = translate_acs (tui_border_kind, tui_border_kind_translate_corner,
274                          ACS_LLCORNER);
275 
276   tui_border_ulcorner
277     = translate_acs (tui_border_kind, tui_border_kind_translate_corner,
278                          ACS_ULCORNER);
279 
280   tui_border_urcorner =
281     translate_acs (tui_border_kind, tui_border_kind_translate_corner,
282                        ACS_URCORNER);
283 
284   tui_border_hline
285     = translate_acs (tui_border_kind, tui_border_kind_translate_hline,
286                          ACS_HLINE);
287 
288   tui_border_vline
289     = translate_acs (tui_border_kind, tui_border_kind_translate_vline,
290                          ACS_VLINE);
291 
292   return need_redraw;
293 }
294 
295 static struct cmd_list_element *tuilist;
296 
297 struct cmd_list_element **
tui_get_cmd_list(void)298 tui_get_cmd_list (void)
299 {
300   if (tuilist == 0)
301     add_basic_prefix_cmd ("tui", class_tui,
302                                 _("Text User Interface commands."),
303                                 &tuilist, 0, &cmdlist);
304   return &tuilist;
305 }
306 
307 /* The set_func hook of "set tui ..." commands that affect the window
308    borders on the TUI display.  */
309 
310 static void
tui_set_var_cmd(const char * null_args,int from_tty,struct cmd_list_element * c)311 tui_set_var_cmd (const char *null_args,
312                      int from_tty, struct cmd_list_element *c)
313 {
314   if (tui_update_variables () && tui_active)
315     tui_rehighlight_all ();
316 }
317 
318 
319 
320 /* True if TUI resizes should print a message.  This is used by the
321    test suite.  */
322 
323 static bool resize_message;
324 
325 static void
show_tui_resize_message(struct ui_file * file,int from_tty,struct cmd_list_element * c,const char * value)326 show_tui_resize_message (struct ui_file *file, int from_tty,
327                                struct cmd_list_element *c, const char *value)
328 {
329   gdb_printf (file, _("TUI resize messaging is %s.\n"), value);
330 }
331 
332 
333 
334 /* Generic window name completion function.  Complete window name pointed
335    to by TEXT and WORD.
336 
337    If EXCLUDE_CANNOT_FOCUS_P is true, then windows that can't take focus
338    will be excluded from the completions, otherwise they will be included.
339 
340    If INCLUDE_NEXT_PREV_P is true then the special window names 'next' and
341    'prev' will also be considered as possible completions of the window
342    name.  This is independent of EXCLUDE_CANNOT_FOCUS_P.  */
343 
344 static void
window_name_completer(completion_tracker & tracker,bool include_next_prev_p,bool exclude_cannot_focus_p,const char * text,const char * word)345 window_name_completer (completion_tracker &tracker,
346                            bool include_next_prev_p,
347                            bool exclude_cannot_focus_p,
348                            const char *text, const char *word)
349 {
350   std::vector<const char *> completion_name_vec;
351 
352   for (tui_win_info *win_info : all_tui_windows ())
353     {
354       const char *completion_name = NULL;
355 
356       /* Don't include an invisible window.  */
357       if (!win_info->is_visible ())
358           continue;
359 
360       /* If requested, exclude windows that can't be focused.  */
361       if (exclude_cannot_focus_p && !win_info->can_focus ())
362           continue;
363 
364       completion_name = win_info->name ();
365       gdb_assert (completion_name != NULL);
366       completion_name_vec.push_back (completion_name);
367     }
368 
369   /* If no windows are considered visible then the TUI has not yet been
370      initialized.  But still "focus src" and "focus cmd" will work because
371      invoking the focus command will entail initializing the TUI which sets the
372      default layout to "src".  */
373   if (completion_name_vec.empty ())
374     {
375       completion_name_vec.push_back (SRC_NAME);
376       completion_name_vec.push_back (CMD_NAME);
377     }
378 
379   if (include_next_prev_p)
380     {
381       completion_name_vec.push_back ("next");
382       completion_name_vec.push_back ("prev");
383     }
384 
385 
386   completion_name_vec.push_back (NULL);
387   complete_on_enum (tracker, completion_name_vec.data (), text, word);
388 }
389 
390 /* Complete possible window names to focus on.  TEXT is the complete text
391    entered so far, WORD is the word currently being completed.  */
392 
393 static void
focus_completer(struct cmd_list_element * ignore,completion_tracker & tracker,const char * text,const char * word)394 focus_completer (struct cmd_list_element *ignore,
395                      completion_tracker &tracker,
396                      const char *text, const char *word)
397 {
398   window_name_completer (tracker, true, true, text, word);
399 }
400 
401 /* Complete possible window names for winheight command.  TEXT is the
402    complete text entered so far, WORD is the word currently being
403    completed.  */
404 
405 static void
winheight_completer(struct cmd_list_element * ignore,completion_tracker & tracker,const char * text,const char * word)406 winheight_completer (struct cmd_list_element *ignore,
407                          completion_tracker &tracker,
408                          const char *text, const char *word)
409 {
410   /* The first word is the window name.  That we can complete.  Subsequent
411      words can't be completed.  */
412   if (word != text)
413     return;
414 
415   window_name_completer (tracker, false, false, text, word);
416 }
417 
418 /* Update gdb's knowledge of the terminal size.  */
419 void
tui_update_gdb_sizes(void)420 tui_update_gdb_sizes (void)
421 {
422   int width, height;
423 
424   if (tui_active)
425     {
426       width = TUI_CMD_WIN->width;
427       height = TUI_CMD_WIN->height;
428     }
429   else
430     {
431       width = tui_term_width ();
432       height = tui_term_height ();
433     }
434 
435   set_screen_width_and_height (width, height);
436 }
437 
438 
439 void
forward_scroll(int num_to_scroll)440 tui_win_info::forward_scroll (int num_to_scroll)
441 {
442   if (num_to_scroll == 0)
443     num_to_scroll = height - 3;
444 
445   do_scroll_vertical (num_to_scroll);
446 }
447 
448 void
backward_scroll(int num_to_scroll)449 tui_win_info::backward_scroll (int num_to_scroll)
450 {
451   if (num_to_scroll == 0)
452     num_to_scroll = height - 3;
453 
454   do_scroll_vertical (-num_to_scroll);
455 }
456 
457 
458 void
left_scroll(int num_to_scroll)459 tui_win_info::left_scroll (int num_to_scroll)
460 {
461   if (num_to_scroll == 0)
462     num_to_scroll = 1;
463 
464   do_scroll_horizontal (num_to_scroll);
465 }
466 
467 
468 void
right_scroll(int num_to_scroll)469 tui_win_info::right_scroll (int num_to_scroll)
470 {
471   if (num_to_scroll == 0)
472     num_to_scroll = 1;
473 
474   do_scroll_horizontal (-num_to_scroll);
475 }
476 
477 
478 void
tui_refresh_all_win(void)479 tui_refresh_all_win (void)
480 {
481   clearok (curscr, TRUE);
482   for (tui_win_info *win_info : all_tui_windows ())
483     {
484       if (win_info->is_visible ())
485           win_info->refresh_window ();
486     }
487 }
488 
489 void
tui_rehighlight_all(void)490 tui_rehighlight_all (void)
491 {
492   for (tui_win_info *win_info : all_tui_windows ())
493     win_info->check_and_display_highlight_if_needed ();
494 }
495 
496 /* Resize all the windows based on the terminal size.  This function
497    gets called from within the readline SIGWINCH handler.  */
498 void
tui_resize_all(void)499 tui_resize_all (void)
500 {
501   int height_diff, width_diff;
502   int screenheight, screenwidth;
503 
504   rl_get_screen_size (&screenheight, &screenwidth);
505   screenwidth += readline_hidden_cols;
506 
507   width_diff = screenwidth - tui_term_width ();
508   height_diff = screenheight - tui_term_height ();
509   if (height_diff || width_diff)
510     {
511 #ifdef HAVE_RESIZE_TERM
512       resize_term (screenheight, screenwidth);
513 #endif
514       /* Turn keypad off while we resize.  */
515       keypad (TUI_CMD_WIN->handle.get (), FALSE);
516       tui_update_gdb_sizes ();
517       tui_set_term_height_to (screenheight);
518       tui_set_term_width_to (screenwidth);
519 
520       /* erase + clearok are used instead of a straightforward clear as
521            AIX 5.3 does not define clear.  */
522       erase ();
523       clearok (curscr, TRUE);
524       /* Apply the current layout.  The 'false' here allows the command
525            window to resize proportionately with containing terminal, rather
526            than maintaining a fixed size.  */
527       tui_apply_current_layout (false); /* Turn keypad back on.  */
528       keypad (TUI_CMD_WIN->handle.get (), TRUE);
529     }
530 }
531 
532 #ifdef SIGWINCH
533 /* Token for use by TUI's asynchronous SIGWINCH handler.  */
534 static struct async_signal_handler *tui_sigwinch_token;
535 
536 /* TUI's SIGWINCH signal handler.  */
537 static void
tui_sigwinch_handler(int signal)538 tui_sigwinch_handler (int signal)
539 {
540   mark_async_signal_handler (tui_sigwinch_token);
541   tui_set_win_resized_to (true);
542 }
543 
544 /* Callback for asynchronously resizing TUI following a SIGWINCH signal.  */
545 static void
tui_async_resize_screen(gdb_client_data arg)546 tui_async_resize_screen (gdb_client_data arg)
547 {
548   rl_resize_terminal ();
549 
550   if (!tui_active)
551     {
552       int screen_height, screen_width;
553 
554       rl_get_screen_size (&screen_height, &screen_width);
555       screen_width += readline_hidden_cols;
556       set_screen_width_and_height (screen_width, screen_height);
557 
558       /* win_resized is left set so that the next call to tui_enable()
559            resizes the TUI windows.  */
560     }
561   else
562     {
563       tui_set_win_resized_to (false);
564       tui_resize_all ();
565       tui_refresh_all_win ();
566       tui_update_gdb_sizes ();
567       if (resize_message)
568           {
569             static int count;
570             printf_unfiltered ("@@ resize done %d, size = %dx%d\n", count,
571                                    tui_term_width (), tui_term_height ());
572             ++count;
573           }
574       tui_redisplay_readline ();
575     }
576 }
577 #endif
578 
579 /* Initialize TUI's SIGWINCH signal handler.  Note that the handler is not
580    uninstalled when we exit TUI, so the handler should not assume that TUI is
581    always active.  */
582 void
tui_initialize_win(void)583 tui_initialize_win (void)
584 {
585 #ifdef SIGWINCH
586   tui_sigwinch_token
587     = create_async_signal_handler (tui_async_resize_screen, NULL,
588                                            "tui-sigwinch");
589 
590   {
591 #ifdef HAVE_SIGACTION
592     struct sigaction old_winch;
593 
594     memset (&old_winch, 0, sizeof (old_winch));
595     old_winch.sa_handler = &tui_sigwinch_handler;
596 #ifdef SA_RESTART
597     old_winch.sa_flags = SA_RESTART;
598 #endif
599     sigaction (SIGWINCH, &old_winch, NULL);
600 #else
601     signal (SIGWINCH, &tui_sigwinch_handler);
602 #endif
603   }
604 #endif
605 }
606 
607 
608 static void
tui_scroll_forward_command(const char * arg,int from_tty)609 tui_scroll_forward_command (const char *arg, int from_tty)
610 {
611   int num_to_scroll = 1;
612   struct tui_win_info *win_to_scroll;
613 
614   /* Make sure the curses mode is enabled.  */
615   tui_enable ();
616   if (arg == NULL)
617     parse_scrolling_args (arg, &win_to_scroll, NULL);
618   else
619     parse_scrolling_args (arg, &win_to_scroll, &num_to_scroll);
620   win_to_scroll->forward_scroll (num_to_scroll);
621 }
622 
623 
624 static void
tui_scroll_backward_command(const char * arg,int from_tty)625 tui_scroll_backward_command (const char *arg, int from_tty)
626 {
627   int num_to_scroll = 1;
628   struct tui_win_info *win_to_scroll;
629 
630   /* Make sure the curses mode is enabled.  */
631   tui_enable ();
632   if (arg == NULL)
633     parse_scrolling_args (arg, &win_to_scroll, NULL);
634   else
635     parse_scrolling_args (arg, &win_to_scroll, &num_to_scroll);
636   win_to_scroll->backward_scroll (num_to_scroll);
637 }
638 
639 
640 static void
tui_scroll_left_command(const char * arg,int from_tty)641 tui_scroll_left_command (const char *arg, int from_tty)
642 {
643   int num_to_scroll;
644   struct tui_win_info *win_to_scroll;
645 
646   /* Make sure the curses mode is enabled.  */
647   tui_enable ();
648   parse_scrolling_args (arg, &win_to_scroll, &num_to_scroll);
649   win_to_scroll->left_scroll (num_to_scroll);
650 }
651 
652 
653 static void
tui_scroll_right_command(const char * arg,int from_tty)654 tui_scroll_right_command (const char *arg, int from_tty)
655 {
656   int num_to_scroll;
657   struct tui_win_info *win_to_scroll;
658 
659   /* Make sure the curses mode is enabled.  */
660   tui_enable ();
661   parse_scrolling_args (arg, &win_to_scroll, &num_to_scroll);
662   win_to_scroll->right_scroll (num_to_scroll);
663 }
664 
665 
666 /* Answer the window represented by name.  */
667 static struct tui_win_info *
tui_partial_win_by_name(std::string_view name)668 tui_partial_win_by_name (std::string_view name)
669 {
670   struct tui_win_info *best = nullptr;
671 
672   for (tui_win_info *item : all_tui_windows ())
673     {
674       const char *cur_name = item->name ();
675 
676       if (name == cur_name)
677           return item;
678       if (startswith (cur_name, name))
679           {
680             if (best != nullptr)
681               error (_("Window name \"%*s\" is ambiguous"),
682                        (int) name.size (), name.data ());
683             best = item;
684           }
685     }
686 
687   return best;
688 }
689 
690 /* Set focus to the window named by 'arg'.  */
691 static void
tui_set_focus_command(const char * arg,int from_tty)692 tui_set_focus_command (const char *arg, int from_tty)
693 {
694   tui_enable ();
695 
696   if (arg == NULL)
697     error_no_arg (_("name of window to focus"));
698 
699   struct tui_win_info *win_info = NULL;
700 
701   if (startswith ("next", arg))
702     win_info = tui_next_win (tui_win_with_focus ());
703   else if (startswith ("prev", arg))
704     win_info = tui_prev_win (tui_win_with_focus ());
705   else
706     win_info = tui_partial_win_by_name (arg);
707 
708   if (win_info == nullptr)
709     {
710       /* When WIN_INFO is nullptr this can either mean that the window name
711            is unknown to GDB, or that the window is not in the current
712            layout.  To try and help the user, give a different error
713            depending on which of these is the case.  */
714       std::string matching_window_name;
715       bool is_ambiguous = false;
716 
717       for (const std::string &name : all_known_window_names ())
718           {
719             /* Look through all windows in the current layout, if the window
720                is in the current layout then we're not interested is it.  */
721             for (tui_win_info *item : all_tui_windows ())
722               if (item->name () == name)
723                 continue;
724 
725             if (startswith (name, arg))
726               {
727                 if (matching_window_name.empty ())
728                     matching_window_name = name;
729                 else
730                     is_ambiguous = true;
731               }
732           };
733 
734       if (!matching_window_name.empty ())
735           {
736             if (is_ambiguous)
737               error (_("No windows matching \"%s\" in the current layout"),
738                        arg);
739             else
740               error (_("Window \"%s\" is not in the current layout"),
741                        matching_window_name.c_str ());
742           }
743       else
744           error (_("Unrecognized window name \"%s\""), arg);
745     }
746 
747   /* If a window is part of the current layout then it will have a
748      tui_win_info associated with it and be visible, otherwise, there will
749      be no tui_win_info and the above error will have been raised.  */
750   gdb_assert (win_info->is_visible ());
751 
752   if (!win_info->can_focus ())
753     error (_("Window \"%s\" cannot be focused"), arg);
754 
755   tui_set_win_focus_to (win_info);
756   gdb_printf (_("Focus set to %s window.\n"),
757                 tui_win_with_focus ()->name ());
758 }
759 
760 static void
tui_all_windows_info(const char * arg,int from_tty)761 tui_all_windows_info (const char *arg, int from_tty)
762 {
763   if (!tui_active)
764     {
765       gdb_printf (_("The TUI is not active.\n"));
766       return;
767     }
768 
769   struct tui_win_info *win_with_focus = tui_win_with_focus ();
770   struct ui_out *uiout = current_uiout;
771 
772   ui_out_emit_table table_emitter (uiout, 4, -1, "tui-windows");
773   uiout->table_header (10, ui_left, "name", "Name");
774   uiout->table_header (5, ui_right, "lines", "Lines");
775   uiout->table_header (7, ui_right, "columns", "Columns");
776   uiout->table_header (10, ui_left, "focus", "Focus");
777   uiout->table_body ();
778 
779   for (tui_win_info *win_info : all_tui_windows ())
780     if (win_info->is_visible ())
781       {
782           ui_out_emit_tuple tuple_emitter (uiout, nullptr);
783 
784           uiout->field_string ("name", win_info->name ());
785           uiout->field_signed ("lines", win_info->height);
786           uiout->field_signed ("columns", win_info->width);
787           if (win_with_focus == win_info)
788             uiout->field_string ("focus", _("(has focus)"));
789           else
790             uiout->field_skip ("focus");
791           uiout->text ("\n");
792       }
793 }
794 
795 
796 static void
tui_refresh_all_command(const char * arg,int from_tty)797 tui_refresh_all_command (const char *arg, int from_tty)
798 {
799   /* Make sure the curses mode is enabled.  */
800   tui_enable ();
801 
802   tui_refresh_all_win ();
803 }
804 
805 #define DEFAULT_TAB_LEN         8
806 
807 /* The tab width that should be used by the TUI.  */
808 
809 unsigned int tui_tab_width = DEFAULT_TAB_LEN;
810 
811 /* The tab width as set by the user.  */
812 
813 static unsigned int internal_tab_width = DEFAULT_TAB_LEN;
814 
815 /* After the tab width is set, call this to update the relevant
816    windows.  */
817 
818 static void
update_tab_width()819 update_tab_width ()
820 {
821   for (tui_win_info *win_info : all_tui_windows ())
822     {
823       if (win_info->is_visible ())
824           win_info->update_tab_width ();
825     }
826 }
827 
828 /* Callback for "set tui tab-width".  */
829 
830 static void
tui_set_tab_width(const char * ignore,int from_tty,struct cmd_list_element * c)831 tui_set_tab_width (const char *ignore,
832                        int from_tty, struct cmd_list_element *c)
833 {
834   if (internal_tab_width == 0)
835     {
836       internal_tab_width = tui_tab_width;
837       error (_("Tab width must not be 0"));
838     }
839 
840   tui_tab_width = internal_tab_width;
841   update_tab_width ();
842 }
843 
844 /* Callback for "show tui tab-width".  */
845 
846 static void
tui_show_tab_width(struct ui_file * file,int from_tty,struct cmd_list_element * c,const char * value)847 tui_show_tab_width (struct ui_file *file, int from_tty,
848                         struct cmd_list_element *c, const char *value)
849 {
850   gdb_printf (file, _("TUI tab width is %s spaces.\n"), value);
851 
852 }
853 
854 /* See tui-win.h.  */
855 
856 bool compact_source = false;
857 
858 /* Callback for "set tui compact-source".  */
859 
860 static void
tui_set_compact_source(const char * ignore,int from_tty,struct cmd_list_element * c)861 tui_set_compact_source (const char *ignore, int from_tty,
862                               struct cmd_list_element *c)
863 {
864   if (TUI_SRC_WIN != nullptr)
865     TUI_SRC_WIN->refill ();
866 }
867 
868 /* Callback for "show tui compact-source".  */
869 
870 static void
tui_show_compact_source(struct ui_file * file,int from_tty,struct cmd_list_element * c,const char * value)871 tui_show_compact_source (struct ui_file *file, int from_tty,
872                                struct cmd_list_element *c, const char *value)
873 {
874   gdb_printf (file, _("TUI source window compactness is %s.\n"), value);
875 }
876 
877 bool tui_enable_mouse = true;
878 
879 /* Implement 'show tui mouse-events'.  */
880 
881 static void
show_tui_mouse_events(struct ui_file * file,int from_tty,struct cmd_list_element * c,const char * value)882 show_tui_mouse_events (struct ui_file *file, int from_tty,
883                            struct cmd_list_element *c, const char *value)
884 {
885   gdb_printf (file, _("TUI mouse events are %s.\n"), value);
886 }
887 
888 /* Set the tab width of the specified window.  */
889 static void
tui_set_tab_width_command(const char * arg,int from_tty)890 tui_set_tab_width_command (const char *arg, int from_tty)
891 {
892   /* Make sure the curses mode is enabled.  */
893   tui_enable ();
894   if (arg != NULL)
895     {
896       int ts;
897 
898       ts = atoi (arg);
899       if (ts <= 0)
900           warning (_("Tab widths greater than 0 must be specified."));
901       else
902           {
903             internal_tab_width = ts;
904             tui_tab_width = ts;
905 
906             update_tab_width ();
907           }
908     }
909 }
910 
911 /* Helper function for the user commands to adjust a window's width or
912    height.  The ARG string contains the command line arguments from the
913    user, which should give the name of a window, and how to adjust the
914    size.
915 
916    When SET_WIDTH_P is true the width of the window is adjusted based on
917    ARG, and when SET_WIDTH_P is false, the height of the window is adjusted
918    based on ARG.
919 
920    On invalid input, or if the size can't be adjusted as requested, then an
921    error is thrown, otherwise, the window sizes are adjusted, and the
922    windows redrawn.  */
923 
924 static void
tui_set_win_size(const char * arg,bool set_width_p)925 tui_set_win_size (const char *arg, bool set_width_p)
926 {
927   /* Make sure the curses mode is enabled.  */
928   tui_enable ();
929   if (arg == NULL)
930     error_no_arg (_("name of window"));
931 
932   const char *buf = arg;
933   const char *buf_ptr = buf;
934   int new_size;
935   struct tui_win_info *win_info;
936 
937   buf_ptr = skip_to_space (buf_ptr);
938 
939   /* Validate the window name.  */
940   std::string_view wname (buf, buf_ptr - buf);
941   win_info = tui_partial_win_by_name (wname);
942 
943   if (win_info == NULL)
944     error (_("Unrecognized window name \"%s\""), arg);
945   if (!win_info->is_visible ())
946     error (_("Window \"%s\" is not visible"), arg);
947 
948   /* Process the size.  */
949   buf_ptr = skip_spaces (buf_ptr);
950 
951   if (*buf_ptr != '\0')
952     {
953       bool negate = false;
954       bool fixed_size = true;
955       int input_no;;
956 
957       if (*buf_ptr == '+' || *buf_ptr == '-')
958           {
959             if (*buf_ptr == '-')
960               negate = true;
961             fixed_size = false;
962             buf_ptr++;
963           }
964       input_no = atoi (buf_ptr);
965       if (input_no > 0)
966           {
967             if (negate)
968               input_no *= (-1);
969             if (fixed_size)
970               new_size = input_no;
971             else
972               {
973                 int curr_size;
974                 if (set_width_p)
975                     curr_size = win_info->width;
976                 else
977                     curr_size = win_info->height;
978                 new_size = curr_size + input_no;
979               }
980 
981             /* Now change the window's height, and adjust
982                all other windows around it.  */
983             if (set_width_p)
984               tui_adjust_window_width (win_info, new_size);
985             else
986               tui_adjust_window_height (win_info, new_size);
987             tui_update_gdb_sizes ();
988           }
989       else
990           {
991             if (set_width_p)
992               error (_("Invalid window width specified"));
993             else
994               error (_("Invalid window height specified"));
995           }
996     }
997 }
998 
999 /* Implement the 'tui window height' command (alias 'winheight').  */
1000 
1001 static void
tui_set_win_height_command(const char * arg,int from_tty)1002 tui_set_win_height_command (const char *arg, int from_tty)
1003 {
1004   /* Pass false as the final argument to set the height.  */
1005   tui_set_win_size (arg, false);
1006 }
1007 
1008 /* Implement the 'tui window width' command (alias 'winwidth').  */
1009 
1010 static void
tui_set_win_width_command(const char * arg,int from_tty)1011 tui_set_win_width_command (const char *arg, int from_tty)
1012 {
1013   /* Pass true as the final argument to set the width.  */
1014   tui_set_win_size (arg, true);
1015 }
1016 
1017 /* See tui-data.h.  */
1018 
1019 int
max_height()1020 tui_win_info::max_height () const
1021 {
1022   return tui_term_height ();
1023 }
1024 
1025 /* See tui-data.h.  */
1026 
1027 int
max_width()1028 tui_win_info::max_width () const
1029 {
1030   return tui_term_width ();
1031 }
1032 
1033 static void
parse_scrolling_args(const char * arg,struct tui_win_info ** win_to_scroll,int * num_to_scroll)1034 parse_scrolling_args (const char *arg,
1035                           struct tui_win_info **win_to_scroll,
1036                           int *num_to_scroll)
1037 {
1038   if (num_to_scroll)
1039     *num_to_scroll = 0;
1040   *win_to_scroll = tui_win_with_focus ();
1041 
1042   /* First set up the default window to scroll, in case there is no
1043      window name arg.  */
1044   if (arg != NULL)
1045     {
1046       char *buf_ptr;
1047 
1048       /* Process the number of lines to scroll.  */
1049       std::string copy = arg;
1050       buf_ptr = &copy[0];
1051       if (isdigit (*buf_ptr))
1052           {
1053             char *num_str;
1054 
1055             num_str = buf_ptr;
1056             buf_ptr = strchr (buf_ptr, ' ');
1057             if (buf_ptr != NULL)
1058               {
1059                 *buf_ptr = '\0';
1060                 if (num_to_scroll)
1061                     *num_to_scroll = atoi (num_str);
1062                 buf_ptr++;
1063               }
1064             else if (num_to_scroll)
1065               *num_to_scroll = atoi (num_str);
1066           }
1067 
1068       /* Process the window name if one is specified.  */
1069       if (buf_ptr != NULL)
1070           {
1071             const char *wname;
1072 
1073             wname = skip_spaces (buf_ptr);
1074 
1075             if (*wname != '\0')
1076               {
1077                 *win_to_scroll = tui_partial_win_by_name (wname);
1078 
1079                 if (*win_to_scroll == NULL)
1080                     error (_("Unrecognized window `%s'"), wname);
1081                 if (!(*win_to_scroll)->is_visible ())
1082                     error (_("Window is not visible"));
1083                 else if (*win_to_scroll == TUI_CMD_WIN)
1084                     *win_to_scroll = *(tui_source_windows ().begin ());
1085               }
1086           }
1087     }
1088 }
1089 
1090 /* The list of 'tui window' sub-commands.  */
1091 
1092 static cmd_list_element *tui_window_cmds = nullptr;
1093 
1094 /* Called to implement 'tui window'.  */
1095 
1096 static void
tui_window_command(const char * args,int from_tty)1097 tui_window_command (const char *args, int from_tty)
1098 {
1099   help_list (tui_window_cmds, "tui window ", all_commands, gdb_stdout);
1100 }
1101 
1102 /* See tui-win.h.  */
1103 
1104 bool tui_left_margin_verbose = false;
1105 
1106 /* Function to initialize gdb commands, for tui window
1107    manipulation.  */
1108 
1109 void _initialize_tui_win ();
1110 void
_initialize_tui_win()1111 _initialize_tui_win ()
1112 {
1113   static struct cmd_list_element *tui_setlist;
1114   static struct cmd_list_element *tui_showlist;
1115 
1116   /* Define the classes of commands.
1117      They will appear in the help list in the reverse of this order.  */
1118   add_setshow_prefix_cmd ("tui", class_tui,
1119                                 _("TUI configuration variables."),
1120                                 _("TUI configuration variables."),
1121                                 &tui_setlist, &tui_showlist,
1122                                 &setlist, &showlist);
1123 
1124   cmd_list_element *refresh_cmd
1125     = add_cmd ("refresh", class_tui, tui_refresh_all_command,
1126                  _("Refresh the terminal display."),
1127                  tui_get_cmd_list ());
1128   add_com_alias ("refresh", refresh_cmd, class_tui, 0);
1129 
1130   cmd_list_element *tabset_cmd
1131     = add_com ("tabset", class_tui, tui_set_tab_width_command, _("\
1132 Set the width (in characters) of tab stops.\n\
1133 Usage: tabset N"));
1134   deprecate_cmd (tabset_cmd, "set tui tab-width");
1135 
1136   /* Setup the 'tui window' list of command.  */
1137   add_prefix_cmd ("window", class_tui, tui_window_command,
1138                       _("Text User Interface window commands."),
1139                       &tui_window_cmds, 1, tui_get_cmd_list ());
1140 
1141   cmd_list_element *winheight_cmd
1142     = add_cmd ("height", class_tui, tui_set_win_height_command, _("\
1143 Set or modify the height of a specified window.\n\
1144 Usage: tui window height WINDOW-NAME [+ | -] NUM-LINES\n\
1145 Use \"info win\" to see the names of the windows currently being displayed."),
1146                  &tui_window_cmds);
1147   add_com_alias ("winheight", winheight_cmd, class_tui, 0);
1148   add_com_alias ("wh", winheight_cmd, class_tui, 0);
1149   set_cmd_completer (winheight_cmd, winheight_completer);
1150 
1151   cmd_list_element *winwidth_cmd
1152     = add_cmd ("width", class_tui, tui_set_win_width_command, _("\
1153 Set or modify the width of a specified window.\n\
1154 Usage: tui window width WINDOW-NAME [+ | -] NUM-LINES\n\
1155 Use \"info win\" to see the names of the windows currently being displayed."),
1156                  &tui_window_cmds);
1157   add_com_alias ("winwidth", winwidth_cmd, class_tui, 0);
1158   set_cmd_completer (winwidth_cmd, winheight_completer);
1159 
1160   add_info ("win", tui_all_windows_info,
1161               _("List of all displayed windows.\n\
1162 Usage: info win"));
1163   cmd_list_element *focus_cmd
1164     = add_cmd ("focus", class_tui, tui_set_focus_command, _("\
1165 Set focus to named window or next/prev window.\n\
1166 Usage: tui focus [WINDOW-NAME | next | prev]\n\
1167 Use \"info win\" to see the names of the windows currently being displayed."),
1168                  tui_get_cmd_list ());
1169   add_com_alias ("focus", focus_cmd, class_tui, 0);
1170   add_com_alias ("fs", focus_cmd, class_tui, 0);
1171   set_cmd_completer (focus_cmd, focus_completer);
1172   add_com ("+", class_tui, tui_scroll_forward_command, _("\
1173 Scroll window forward.\n\
1174 Usage: + [N] [WIN]\n\
1175 Scroll window WIN N lines forwards.  Both WIN and N are optional, N\n\
1176 defaults to 1, and WIN defaults to the currently focused window."));
1177   add_com ("-", class_tui, tui_scroll_backward_command, _("\
1178 Scroll window backward.\n\
1179 Usage: - [N] [WIN]\n\
1180 Scroll window WIN N lines backwards.  Both WIN and N are optional, N\n\
1181 defaults to 1, and WIN defaults to the currently focused window."));
1182   add_com ("<", class_tui, tui_scroll_left_command, _("\
1183 Scroll window text to the left.\n\
1184 Usage: < [N] [WIN]\n\
1185 Scroll window WIN N characters left.  Both WIN and N are optional, N\n\
1186 defaults to 1, and WIN defaults to the currently focused window."));
1187   add_com (">", class_tui, tui_scroll_right_command, _("\
1188 Scroll window text to the right.\n\
1189 Usage: > [N] [WIN]\n\
1190 Scroll window WIN N characters right.  Both WIN and N are optional, N\n\
1191 defaults to 1, and WIN defaults to the currently focused window."));
1192 
1193   /* Define the tui control variables.  */
1194   add_setshow_enum_cmd ("border-kind", no_class, tui_border_kind_enums,
1195                               &tui_border_kind, _("\
1196 Set the kind of border for TUI windows."), _("\
1197 Show the kind of border for TUI windows."), _("\
1198 This variable controls the border of TUI windows:\n\
1199    space           use a white space\n\
1200    ascii           use ascii characters + - | for the border\n\
1201    acs             use the Alternate Character Set"),
1202                               tui_set_var_cmd,
1203                               show_tui_border_kind,
1204                               &tui_setlist, &tui_showlist);
1205 
1206   const std::string help_attribute_mode (_("\
1207    normal          normal display\n\
1208    standout        use highlight mode of terminal\n\
1209    reverse         use reverse video mode\n\
1210    half            use half bright\n\
1211    half-standout   use half bright and standout mode\n\
1212    bold            use extra bright or bold\n\
1213    bold-standout   use extra bright or bold with standout mode"));
1214 
1215   const std::string help_tui_border_mode
1216     = (_("\
1217 This variable controls the attributes to use for the window borders:\n")
1218        + help_attribute_mode);
1219 
1220   add_setshow_enum_cmd ("border-mode", no_class, tui_border_mode_enums,
1221                               &tui_border_mode, _("\
1222 Set the attribute mode to use for the TUI window borders."), _("\
1223 Show the attribute mode to use for the TUI window borders."),
1224                               help_tui_border_mode.c_str (),
1225                               tui_set_var_cmd,
1226                               show_tui_border_mode,
1227                               &tui_setlist, &tui_showlist);
1228 
1229   const std::string help_tui_active_border_mode
1230     = (_("\
1231 This variable controls the attributes to use for the active window borders:\n")
1232        + help_attribute_mode);
1233 
1234   add_setshow_enum_cmd ("active-border-mode", no_class, tui_border_mode_enums,
1235                               &tui_active_border_mode, _("\
1236 Set the attribute mode to use for the active TUI window border."), _("\
1237 Show the attribute mode to use for the active TUI window border."),
1238                               help_tui_active_border_mode.c_str (),
1239                               tui_set_var_cmd,
1240                               show_tui_active_border_mode,
1241                               &tui_setlist, &tui_showlist);
1242 
1243   add_setshow_zuinteger_cmd ("tab-width", no_class,
1244                                    &internal_tab_width, _("\
1245 Set the tab width, in characters, for the TUI."), _("\
1246 Show the tab width, in characters, for the TUI."), _("\
1247 This variable controls how many spaces are used to display a tab character."),
1248                                    tui_set_tab_width, tui_show_tab_width,
1249                                    &tui_setlist, &tui_showlist);
1250 
1251   add_setshow_boolean_cmd ("tui-resize-message", class_maintenance,
1252                                  &resize_message, _("\
1253 Set TUI resize messaging."), _("\
1254 Show TUI resize messaging."), _("\
1255 When enabled GDB will print a message when the terminal is resized."),
1256                                  nullptr,
1257                                  show_tui_resize_message,
1258                                  &maintenance_set_cmdlist,
1259                                  &maintenance_show_cmdlist);
1260 
1261   add_setshow_boolean_cmd ("compact-source", class_tui,
1262                                  &compact_source, _("\
1263 Set whether the TUI source window is compact."), _("\
1264 Show whether the TUI source window is compact."), _("\
1265 This variable controls whether the TUI source window is shown\n\
1266 in a compact form.  The compact form uses less horizontal space."),
1267                                  tui_set_compact_source, tui_show_compact_source,
1268                                  &tui_setlist, &tui_showlist);
1269 
1270   add_setshow_boolean_cmd ("mouse-events", class_tui,
1271                                  &tui_enable_mouse, _("\
1272 Set whether TUI mode handles mouse clicks."), _("\
1273 Show whether TUI mode handles mouse clicks."), _("\
1274 When on (default), mouse clicks control the TUI and can be accessed by Python\n\
1275 extensions.  When off, mouse clicks are handled by the terminal, enabling\n\
1276 terminal-native text selection."),
1277                                  nullptr,
1278                                  show_tui_mouse_events,
1279                                  &tui_setlist, &tui_showlist);
1280 
1281   add_setshow_boolean_cmd ("tui-current-position", class_maintenance,
1282                                  &style_tui_current_position, _("\
1283 Set whether to style text highlighted by the TUI's current position indicator."),
1284                                  _("\
1285 Show whether to style text highlighted by the TUI's current position indicator."),
1286                                  _("\
1287 When enabled, the source and assembly code highlighted by the TUI's current\n\
1288 position indicator is styled."),
1289                                  set_style_tui_current_position,
1290                                  show_style_tui_current_position,
1291                                  &style_set_list,
1292                                  &style_show_list);
1293 
1294   add_setshow_boolean_cmd ("tui-left-margin-verbose", class_maintenance,
1295                                  &tui_left_margin_verbose, _("\
1296 Set whether the left margin should use '_' and '0' instead of spaces."),
1297                                  _("\
1298 Show whether the left margin should use '_' and '0' instead of spaces."),
1299                                  _("\
1300 When enabled, the left margin will use '_' and '0' instead of spaces."),
1301                                  nullptr,
1302                                  nullptr,
1303                                  &maintenance_set_cmdlist,
1304                                  &maintenance_show_cmdlist);
1305 
1306   tui_border_style.changed.attach (tui_rehighlight_all, "tui-win");
1307   tui_active_border_style.changed.attach (tui_rehighlight_all, "tui-win");
1308 }
1309