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