1/* TUI window generic functions.
2
3   Copyright (C) 1998-2020 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 "defs.h"
28#include "command.h"
29#include "symtab.h"
30#include "breakpoint.h"
31#include "frame.h"
32#include "cli/cli-cmds.h"
33#include "cli/cli-style.h"
34#include "top.h"
35#include "source.h"
36#include "gdbsupport/event-loop.h"
37#include "gdbcmd.h"
38#include "async-event.h"
39
40#include "tui/tui.h"
41#include "tui/tui-io.h"
42#include "tui/tui-command.h"
43#include "tui/tui-data.h"
44#include "tui/tui-layout.h"
45#include "tui/tui-wingeneral.h"
46#include "tui/tui-stack.h"
47#include "tui/tui-regs.h"
48#include "tui/tui-disasm.h"
49#include "tui/tui-source.h"
50#include "tui/tui-winsource.h"
51#include "tui/tui-win.h"
52
53#include "gdb_curses.h"
54#include <ctype.h>
55#include "readline/readline.h"
56#include "gdbsupport/gdb_string_view.h"
57
58#include <signal.h>
59
60static void tui_set_tab_width_command (const char *, int);
61static void tui_refresh_all_command (const char *, int);
62static void tui_all_windows_info (const char *, int);
63static void tui_scroll_forward_command (const char *, int);
64static void tui_scroll_backward_command (const char *, int);
65static void tui_scroll_left_command (const char *, int);
66static void tui_scroll_right_command (const char *, int);
67static void parse_scrolling_args (const char *,
68				  struct tui_win_info **,
69				  int *);
70
71
72#ifndef ACS_LRCORNER
73#  define ACS_LRCORNER '+'
74#endif
75#ifndef ACS_LLCORNER
76#  define ACS_LLCORNER '+'
77#endif
78#ifndef ACS_ULCORNER
79#  define ACS_ULCORNER '+'
80#endif
81#ifndef ACS_URCORNER
82#  define ACS_URCORNER '+'
83#endif
84#ifndef ACS_HLINE
85#  define ACS_HLINE '-'
86#endif
87#ifndef ACS_VLINE
88#  define ACS_VLINE '|'
89#endif
90
91/* Possible values for tui-border-kind variable.  */
92static const char *const tui_border_kind_enums[] = {
93  "space",
94  "ascii",
95  "acs",
96  NULL
97};
98
99/* Possible values for tui-border-mode and tui-active-border-mode.  */
100static const char *const tui_border_mode_enums[] = {
101  "normal",
102  "standout",
103  "reverse",
104  "half",
105  "half-standout",
106  "bold",
107  "bold-standout",
108  NULL
109};
110
111struct tui_translate
112{
113  const char *name;
114  int value;
115};
116
117/* Translation table for border-mode variables.
118   The list of values must be terminated by a NULL.
119   After the NULL value, an entry defines the default.  */
120static struct tui_translate tui_border_mode_translate[] = {
121  { "normal",		A_NORMAL },
122  { "standout",		A_STANDOUT },
123  { "reverse",		A_REVERSE },
124  { "half",		A_DIM },
125  { "half-standout",	A_DIM | A_STANDOUT },
126  { "bold",		A_BOLD },
127  { "bold-standout",	A_BOLD | A_STANDOUT },
128  { 0, 0 },
129  { "normal",		A_NORMAL }
130};
131
132/* Translation tables for border-kind, one for each border
133   character (see wborder, border curses operations).
134   -1 is used to indicate the ACS because ACS characters
135   are determined at run time by curses (depends on terminal).  */
136static struct tui_translate tui_border_kind_translate_vline[] = {
137  { "space",    ' ' },
138  { "ascii",    '|' },
139  { "acs",      -1 },
140  { 0, 0 },
141  { "ascii",    '|' }
142};
143
144static struct tui_translate tui_border_kind_translate_hline[] = {
145  { "space",    ' ' },
146  { "ascii",    '-' },
147  { "acs",      -1 },
148  { 0, 0 },
149  { "ascii",    '-' }
150};
151
152static struct tui_translate tui_border_kind_translate_ulcorner[] = {
153  { "space",    ' ' },
154  { "ascii",    '+' },
155  { "acs",      -1 },
156  { 0, 0 },
157  { "ascii",    '+' }
158};
159
160static struct tui_translate tui_border_kind_translate_urcorner[] = {
161  { "space",    ' ' },
162  { "ascii",    '+' },
163  { "acs",      -1 },
164  { 0, 0 },
165  { "ascii",    '+' }
166};
167
168static struct tui_translate tui_border_kind_translate_llcorner[] = {
169  { "space",    ' ' },
170  { "ascii",    '+' },
171  { "acs",      -1 },
172  { 0, 0 },
173  { "ascii",    '+' }
174};
175
176static struct tui_translate tui_border_kind_translate_lrcorner[] = {
177  { "space",    ' ' },
178  { "ascii",    '+' },
179  { "acs",      -1 },
180  { 0, 0 },
181  { "ascii",    '+' }
182};
183
184
185/* Tui configuration variables controlled with set/show command.  */
186static const char *tui_active_border_mode = "bold-standout";
187static void
188show_tui_active_border_mode (struct ui_file *file,
189			     int from_tty,
190			     struct cmd_list_element *c,
191			     const char *value)
192{
193  fprintf_filtered (file, _("\
194The attribute mode to use for the active TUI window border is \"%s\".\n"),
195		    value);
196}
197
198static const char *tui_border_mode = "normal";
199static void
200show_tui_border_mode (struct ui_file *file,
201		      int from_tty,
202		      struct cmd_list_element *c,
203		      const char *value)
204{
205  fprintf_filtered (file, _("\
206The attribute mode to use for the TUI window borders is \"%s\".\n"),
207		    value);
208}
209
210static const char *tui_border_kind = "acs";
211static void
212show_tui_border_kind (struct ui_file *file,
213		      int from_tty,
214		      struct cmd_list_element *c,
215		      const char *value)
216{
217  fprintf_filtered (file, _("The kind of border for TUI windows is \"%s\".\n"),
218		    value);
219}
220
221
222/* Tui internal configuration variables.  These variables are updated
223   by tui_update_variables to reflect the tui configuration
224   variables.  */
225chtype tui_border_vline;
226chtype tui_border_hline;
227chtype tui_border_ulcorner;
228chtype tui_border_urcorner;
229chtype tui_border_llcorner;
230chtype tui_border_lrcorner;
231
232int tui_border_attrs;
233int tui_active_border_attrs;
234
235/* Identify the item in the translation table.
236   When the item is not recognized, use the default entry.  */
237static struct tui_translate *
238translate (const char *name, struct tui_translate *table)
239{
240  while (table->name)
241    {
242      if (name && strcmp (table->name, name) == 0)
243        return table;
244      table++;
245    }
246
247  /* Not found, return default entry.  */
248  table++;
249  return table;
250}
251
252/* Update the tui internal configuration according to gdb settings.
253   Returns 1 if the configuration has changed and the screen should
254   be redrawn.  */
255bool
256tui_update_variables ()
257{
258  bool need_redraw = false;
259  struct tui_translate *entry;
260
261  entry = translate (tui_border_mode, tui_border_mode_translate);
262  if (tui_border_attrs != entry->value)
263    {
264      tui_border_attrs = entry->value;
265      need_redraw = true;
266    }
267  entry = translate (tui_active_border_mode, tui_border_mode_translate);
268  if (tui_active_border_attrs != entry->value)
269    {
270      tui_active_border_attrs = entry->value;
271      need_redraw = true;
272    }
273
274  /* If one corner changes, all characters are changed.
275     Only check the first one.  The ACS characters are determined at
276     run time by curses terminal management.  */
277  entry = translate (tui_border_kind, tui_border_kind_translate_lrcorner);
278  if (tui_border_lrcorner != (chtype) entry->value)
279    {
280      tui_border_lrcorner = (entry->value < 0) ? ACS_LRCORNER : entry->value;
281      need_redraw = true;
282    }
283  entry = translate (tui_border_kind, tui_border_kind_translate_llcorner);
284  tui_border_llcorner = (entry->value < 0) ? ACS_LLCORNER : entry->value;
285
286  entry = translate (tui_border_kind, tui_border_kind_translate_ulcorner);
287  tui_border_ulcorner = (entry->value < 0) ? ACS_ULCORNER : entry->value;
288
289  entry = translate (tui_border_kind, tui_border_kind_translate_urcorner);
290  tui_border_urcorner = (entry->value < 0) ? ACS_URCORNER : entry->value;
291
292  entry = translate (tui_border_kind, tui_border_kind_translate_hline);
293  tui_border_hline = (entry->value < 0) ? ACS_HLINE : entry->value;
294
295  entry = translate (tui_border_kind, tui_border_kind_translate_vline);
296  tui_border_vline = (entry->value < 0) ? ACS_VLINE : entry->value;
297
298  return need_redraw;
299}
300
301static struct cmd_list_element *tuilist;
302
303struct cmd_list_element **
304tui_get_cmd_list (void)
305{
306  if (tuilist == 0)
307    add_basic_prefix_cmd ("tui", class_tui,
308			  _("Text User Interface commands."),
309			  &tuilist, "tui ", 0, &cmdlist);
310  return &tuilist;
311}
312
313/* The set_func hook of "set tui ..." commands that affect the window
314   borders on the TUI display.  */
315
316static void
317tui_set_var_cmd (const char *null_args,
318		 int from_tty, struct cmd_list_element *c)
319{
320  if (tui_update_variables () && tui_active)
321    tui_rehighlight_all ();
322}
323
324
325
326/* True if TUI resizes should print a message.  This is used by the
327   test suite.  */
328
329static bool resize_message;
330
331static void
332show_tui_resize_message (struct ui_file *file, int from_tty,
333			 struct cmd_list_element *c, const char *value)
334{
335  fprintf_filtered (file, _("TUI resize messaging is %s.\n"), value);
336}
337
338
339
340/* Generic window name completion function.  Complete window name pointed
341   to by TEXT and WORD.  If INCLUDE_NEXT_PREV_P is true then the special
342   window names 'next' and 'prev' will also be considered as possible
343   completions of the window name.  */
344
345static void
346window_name_completer (completion_tracker &tracker,
347		       int include_next_prev_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      /* We can't focus on an invisible window.  */
357      if (!win_info->is_visible ())
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
389static void
390focus_completer (struct cmd_list_element *ignore,
391		 completion_tracker &tracker,
392		 const char *text, const char *word)
393{
394  window_name_completer (tracker, 1, 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
401static void
402winheight_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, 0, text, word);
412}
413
414/* Update gdb's knowledge of the terminal size.  */
415void
416tui_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
435void
436tui_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
444void
445tui_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
454void
455tui_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
464void
465tui_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
474void
475tui_refresh_all_win (void)
476{
477  clearok (curscr, TRUE);
478  tui_refresh_all ();
479}
480
481void
482tui_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.  */
490void
491tui_resize_all (void)
492{
493  int height_diff, width_diff;
494  int screenheight, screenwidth;
495
496  rl_get_screen_size (&screenheight, &screenwidth);
497  width_diff = screenwidth - tui_term_width ();
498  height_diff = screenheight - tui_term_height ();
499  if (height_diff || width_diff)
500    {
501      struct tui_win_info *win_with_focus = tui_win_with_focus ();
502
503#ifdef HAVE_RESIZE_TERM
504      resize_term (screenheight, screenwidth);
505#endif
506      /* Turn keypad off while we resize.  */
507      if (win_with_focus != TUI_CMD_WIN)
508	keypad (TUI_CMD_WIN->handle.get (), FALSE);
509      tui_update_gdb_sizes ();
510      tui_set_term_height_to (screenheight);
511      tui_set_term_width_to (screenwidth);
512
513      /* erase + clearok are used instead of a straightforward clear as
514         AIX 5.3 does not define clear.  */
515      erase ();
516      clearok (curscr, TRUE);
517      tui_apply_current_layout ();
518      /* Turn keypad back on, unless focus is in the command
519	 window.  */
520      if (win_with_focus != TUI_CMD_WIN)
521	keypad (TUI_CMD_WIN->handle.get (), TRUE);
522    }
523}
524
525#ifdef SIGWINCH
526/* Token for use by TUI's asynchronous SIGWINCH handler.  */
527static struct async_signal_handler *tui_sigwinch_token;
528
529/* TUI's SIGWINCH signal handler.  */
530static void
531tui_sigwinch_handler (int signal)
532{
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.  */
538static void
539tui_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      set_screen_width_and_height (screen_width, screen_height);
549
550      /* win_resized is left set so that the next call to tui_enable()
551	 resizes the TUI windows.  */
552    }
553  else
554    {
555      tui_set_win_resized_to (false);
556      tui_resize_all ();
557      tui_refresh_all_win ();
558      tui_update_gdb_sizes ();
559      if (resize_message)
560	{
561	  static int count;
562	  printf_unfiltered ("@@ resize done %d, size = %dx%d\n", count,
563			     tui_term_width (), tui_term_height ());
564	  ++count;
565	}
566      tui_redisplay_readline ();
567    }
568}
569#endif
570
571/* Initialize TUI's SIGWINCH signal handler.  Note that the handler is not
572   uninstalled when we exit TUI, so the handler should not assume that TUI is
573   always active.  */
574void
575tui_initialize_win (void)
576{
577#ifdef SIGWINCH
578  tui_sigwinch_token
579    = create_async_signal_handler (tui_async_resize_screen, NULL);
580
581  {
582#ifdef HAVE_SIGACTION
583    struct sigaction old_winch;
584
585    memset (&old_winch, 0, sizeof (old_winch));
586    old_winch.sa_handler = &tui_sigwinch_handler;
587#ifdef SA_RESTART
588    old_winch.sa_flags = SA_RESTART;
589#endif
590    sigaction (SIGWINCH, &old_winch, NULL);
591#else
592    signal (SIGWINCH, &tui_sigwinch_handler);
593#endif
594  }
595#endif
596}
597
598
599static void
600tui_scroll_forward_command (const char *arg, int from_tty)
601{
602  int num_to_scroll = 1;
603  struct tui_win_info *win_to_scroll;
604
605  /* Make sure the curses mode is enabled.  */
606  tui_enable ();
607  if (arg == NULL)
608    parse_scrolling_args (arg, &win_to_scroll, NULL);
609  else
610    parse_scrolling_args (arg, &win_to_scroll, &num_to_scroll);
611  win_to_scroll->forward_scroll (num_to_scroll);
612}
613
614
615static void
616tui_scroll_backward_command (const char *arg, int from_tty)
617{
618  int num_to_scroll = 1;
619  struct tui_win_info *win_to_scroll;
620
621  /* Make sure the curses mode is enabled.  */
622  tui_enable ();
623  if (arg == NULL)
624    parse_scrolling_args (arg, &win_to_scroll, NULL);
625  else
626    parse_scrolling_args (arg, &win_to_scroll, &num_to_scroll);
627  win_to_scroll->backward_scroll (num_to_scroll);
628}
629
630
631static void
632tui_scroll_left_command (const char *arg, int from_tty)
633{
634  int num_to_scroll;
635  struct tui_win_info *win_to_scroll;
636
637  /* Make sure the curses mode is enabled.  */
638  tui_enable ();
639  parse_scrolling_args (arg, &win_to_scroll, &num_to_scroll);
640  win_to_scroll->left_scroll (num_to_scroll);
641}
642
643
644static void
645tui_scroll_right_command (const char *arg, int from_tty)
646{
647  int num_to_scroll;
648  struct tui_win_info *win_to_scroll;
649
650  /* Make sure the curses mode is enabled.  */
651  tui_enable ();
652  parse_scrolling_args (arg, &win_to_scroll, &num_to_scroll);
653  win_to_scroll->right_scroll (num_to_scroll);
654}
655
656
657/* Answer the window represented by name.  */
658static struct tui_win_info *
659tui_partial_win_by_name (gdb::string_view name)
660{
661  struct tui_win_info *best = nullptr;
662
663  for (tui_win_info *item : all_tui_windows ())
664    {
665      const char *cur_name = item->name ();
666
667      if (name == cur_name)
668	return item;
669      if (startswith (cur_name, name))
670	{
671	  if (best != nullptr)
672	    error (_("Window name \"%*s\" is ambiguous"),
673		   (int) name.size (), name.data ());
674	  best = item;
675	}
676    }
677
678  return best;
679}
680
681/* Set focus to the window named by 'arg'.  */
682static void
683tui_set_focus_command (const char *arg, int from_tty)
684{
685  tui_enable ();
686
687  if (arg == NULL)
688    error_no_arg (_("name of window to focus"));
689
690  struct tui_win_info *win_info = NULL;
691
692  if (subset_compare (arg, "next"))
693    win_info = tui_next_win (tui_win_with_focus ());
694  else if (subset_compare (arg, "prev"))
695    win_info = tui_prev_win (tui_win_with_focus ());
696  else
697    win_info = tui_partial_win_by_name (arg);
698
699  if (win_info == NULL)
700    error (_("Unrecognized window name \"%s\""), arg);
701  if (!win_info->is_visible ())
702    error (_("Window \"%s\" is not visible"), arg);
703
704  tui_set_win_focus_to (win_info);
705  keypad (TUI_CMD_WIN->handle.get (), win_info != TUI_CMD_WIN);
706  printf_filtered (_("Focus set to %s window.\n"),
707		   tui_win_with_focus ()->name ());
708}
709
710static void
711tui_all_windows_info (const char *arg, int from_tty)
712{
713  if (!tui_active)
714    {
715      printf_filtered (_("The TUI is not active.\n"));
716      return;
717    }
718
719  struct tui_win_info *win_with_focus = tui_win_with_focus ();
720  struct ui_out *uiout = current_uiout;
721
722  ui_out_emit_table table_emitter (uiout, 3, -1, "tui-windows");
723  uiout->table_header (10, ui_left, "name", "Name");
724  uiout->table_header (5, ui_right, "lines", "Lines");
725  uiout->table_header (10, ui_left, "focus", "Focus");
726  uiout->table_body ();
727
728  for (tui_win_info *win_info : all_tui_windows ())
729    if (win_info->is_visible ())
730      {
731	ui_out_emit_tuple tuple_emitter (uiout, nullptr);
732
733	uiout->field_string ("name", win_info->name ());
734	uiout->field_signed ("lines", win_info->height);
735	if (win_with_focus == win_info)
736	  uiout->field_string ("focus", _("(has focus)"));
737	else
738	  uiout->field_skip ("focus");
739	uiout->text ("\n");
740      }
741}
742
743
744static void
745tui_refresh_all_command (const char *arg, int from_tty)
746{
747  /* Make sure the curses mode is enabled.  */
748  tui_enable ();
749
750  tui_refresh_all_win ();
751}
752
753#define DEFAULT_TAB_LEN         8
754
755/* The tab width that should be used by the TUI.  */
756
757unsigned int tui_tab_width = DEFAULT_TAB_LEN;
758
759/* The tab width as set by the user.  */
760
761static unsigned int internal_tab_width = DEFAULT_TAB_LEN;
762
763/* After the tab width is set, call this to update the relevant
764   windows.  */
765
766static void
767update_tab_width ()
768{
769  for (tui_win_info *win_info : all_tui_windows ())
770    {
771      if (win_info->is_visible ())
772	win_info->update_tab_width ();
773    }
774}
775
776/* Callback for "set tui tab-width".  */
777
778static void
779tui_set_tab_width (const char *ignore,
780		   int from_tty, struct cmd_list_element *c)
781{
782  if (internal_tab_width == 0)
783    {
784      internal_tab_width = tui_tab_width;
785      error (_("Tab width must not be 0"));
786    }
787
788  tui_tab_width = internal_tab_width;
789  update_tab_width ();
790}
791
792/* Callback for "show tui tab-width".  */
793
794static void
795tui_show_tab_width (struct ui_file *file, int from_tty,
796		    struct cmd_list_element *c, const char *value)
797{
798  fprintf_filtered (gdb_stdout, _("TUI tab width is %s spaces.\n"), value);
799
800}
801
802/* See tui-win.h.  */
803
804bool compact_source = false;
805
806/* Callback for "set tui compact-source".  */
807
808static void
809tui_set_compact_source (const char *ignore, int from_tty,
810			struct cmd_list_element *c)
811{
812  if (TUI_SRC_WIN != nullptr)
813    TUI_SRC_WIN->refill ();
814}
815
816/* Callback for "show tui compact-source".  */
817
818static void
819tui_show_compact_source (struct ui_file *file, int from_tty,
820			 struct cmd_list_element *c, const char *value)
821{
822  printf_filtered (_("TUI source window compactness is %s.\n"), value);
823}
824
825/* Set the tab width of the specified window.  */
826static void
827tui_set_tab_width_command (const char *arg, int from_tty)
828{
829  /* Make sure the curses mode is enabled.  */
830  tui_enable ();
831  if (arg != NULL)
832    {
833      int ts;
834
835      ts = atoi (arg);
836      if (ts <= 0)
837	warning (_("Tab widths greater than 0 must be specified."));
838      else
839	{
840	  internal_tab_width = ts;
841	  tui_tab_width = ts;
842
843	  update_tab_width ();
844	}
845    }
846}
847
848
849/* Set the height of the specified window.  */
850static void
851tui_set_win_height_command (const char *arg, int from_tty)
852{
853  /* Make sure the curses mode is enabled.  */
854  tui_enable ();
855  if (arg == NULL)
856    error_no_arg (_("name of window"));
857
858  const char *buf = arg;
859  const char *buf_ptr = buf;
860  int new_height;
861  struct tui_win_info *win_info;
862
863  buf_ptr = skip_to_space (buf_ptr);
864
865  /* Validate the window name.  */
866  gdb::string_view wname (buf, buf_ptr - buf);
867  win_info = tui_partial_win_by_name (wname);
868
869  if (win_info == NULL)
870    error (_("Unrecognized window name \"%s\""), arg);
871  if (!win_info->is_visible ())
872    error (_("Window \"%s\" is not visible"), arg);
873
874  /* Process the size.  */
875  buf_ptr = skip_spaces (buf_ptr);
876
877  if (*buf_ptr != '\0')
878    {
879      bool negate = false;
880      bool fixed_size = true;
881      int input_no;;
882
883      if (*buf_ptr == '+' || *buf_ptr == '-')
884	{
885	  if (*buf_ptr == '-')
886	    negate = true;
887	  fixed_size = false;
888	  buf_ptr++;
889	}
890      input_no = atoi (buf_ptr);
891      if (input_no > 0)
892	{
893	  if (negate)
894	    input_no *= (-1);
895	  if (fixed_size)
896	    new_height = input_no;
897	  else
898	    new_height = win_info->height + input_no;
899
900	  /* Now change the window's height, and adjust
901	     all other windows around it.  */
902	  tui_adjust_window_height (win_info, new_height);
903	  tui_update_gdb_sizes ();
904	}
905      else
906	error (_("Invalid window height specified"));
907    }
908}
909
910/* See tui-data.h.  */
911
912int
913tui_win_info::max_height () const
914{
915  return tui_term_height () - 2;
916}
917
918/* See tui-data.h.  */
919
920int
921tui_win_info::max_width () const
922{
923  return tui_term_width () - 2;
924}
925
926static void
927parse_scrolling_args (const char *arg,
928		      struct tui_win_info **win_to_scroll,
929		      int *num_to_scroll)
930{
931  if (num_to_scroll)
932    *num_to_scroll = 0;
933  *win_to_scroll = tui_win_with_focus ();
934
935  /* First set up the default window to scroll, in case there is no
936     window name arg.  */
937  if (arg != NULL)
938    {
939      char *buf_ptr;
940
941      /* Process the number of lines to scroll.  */
942      std::string copy = arg;
943      buf_ptr = &copy[0];
944      if (isdigit (*buf_ptr))
945	{
946	  char *num_str;
947
948	  num_str = buf_ptr;
949	  buf_ptr = strchr (buf_ptr, ' ');
950	  if (buf_ptr != NULL)
951	    {
952	      *buf_ptr = '\0';
953	      if (num_to_scroll)
954		*num_to_scroll = atoi (num_str);
955	      buf_ptr++;
956	    }
957	  else if (num_to_scroll)
958	    *num_to_scroll = atoi (num_str);
959	}
960
961      /* Process the window name if one is specified.  */
962      if (buf_ptr != NULL)
963	{
964	  const char *wname;
965
966	  wname = skip_spaces (buf_ptr);
967
968	  if (*wname != '\0')
969	    {
970	      *win_to_scroll = tui_partial_win_by_name (wname);
971
972	      if (*win_to_scroll == NULL)
973		error (_("Unrecognized window `%s'"), wname);
974	      if (!(*win_to_scroll)->is_visible ())
975		error (_("Window is not visible"));
976	      else if (*win_to_scroll == TUI_CMD_WIN)
977		*win_to_scroll = *(tui_source_windows ().begin ());
978	    }
979	}
980    }
981}
982
983/* Function to initialize gdb commands, for tui window
984   manipulation.  */
985
986void _initialize_tui_win ();
987void
988_initialize_tui_win ()
989{
990  static struct cmd_list_element *tui_setlist;
991  static struct cmd_list_element *tui_showlist;
992  struct cmd_list_element *cmd;
993
994  /* Define the classes of commands.
995     They will appear in the help list in the reverse of this order.  */
996  add_basic_prefix_cmd ("tui", class_tui,
997			_("TUI configuration variables."),
998			&tui_setlist, "set tui ",
999			0 /* allow-unknown */, &setlist);
1000  add_show_prefix_cmd ("tui", class_tui,
1001		       _("TUI configuration variables."),
1002		       &tui_showlist, "show tui ",
1003		       0 /* allow-unknown */, &showlist);
1004
1005  add_com ("refresh", class_tui, tui_refresh_all_command,
1006           _("Refresh the terminal display."));
1007
1008  cmd = add_com ("tabset", class_tui, tui_set_tab_width_command, _("\
1009Set the width (in characters) of tab stops.\n\
1010Usage: tabset N"));
1011  deprecate_cmd (cmd, "set tui tab-width");
1012
1013  cmd = add_com ("winheight", class_tui, tui_set_win_height_command, _("\
1014Set or modify the height of a specified window.\n\
1015Usage: winheight WINDOW-NAME [+ | -] NUM-LINES\n\
1016Use \"info win\" to see the names of the windows currently being displayed."));
1017  add_com_alias ("wh", "winheight", class_tui, 0);
1018  set_cmd_completer (cmd, winheight_completer);
1019  add_info ("win", tui_all_windows_info,
1020	    _("List of all displayed windows.\n\
1021Usage: info win"));
1022  cmd = add_com ("focus", class_tui, tui_set_focus_command, _("\
1023Set focus to named window or next/prev window.\n\
1024Usage: focus [WINDOW-NAME | next | prev]\n\
1025Use \"info win\" to see the names of the windows currently being displayed."));
1026  add_com_alias ("fs", "focus", class_tui, 0);
1027  set_cmd_completer (cmd, focus_completer);
1028  add_com ("+", class_tui, tui_scroll_forward_command, _("\
1029Scroll window forward.\n\
1030Usage: + [N] [WIN]\n\
1031Scroll window WIN N lines forwards.  Both WIN and N are optional, N\n\
1032defaults to 1, and WIN defaults to the currently focused window."));
1033  add_com ("-", class_tui, tui_scroll_backward_command, _("\
1034Scroll window backward.\n\
1035Usage: - [N] [WIN]\n\
1036Scroll window WIN N lines backwards.  Both WIN and N are optional, N\n\
1037defaults to 1, and WIN defaults to the currently focused window."));
1038  add_com ("<", class_tui, tui_scroll_left_command, _("\
1039Scroll window text to the left.\n\
1040Usage: < [N] [WIN]\n\
1041Scroll window WIN N characters left.  Both WIN and N are optional, N\n\
1042defaults to 1, and WIN defaults to the currently focused window."));
1043  add_com (">", class_tui, tui_scroll_right_command, _("\
1044Scroll window text to the right.\n\
1045Usage: > [N] [WIN]\n\
1046Scroll window WIN N characters right.  Both WIN and N are optional, N\n\
1047defaults to 1, and WIN defaults to the currently focused window."));
1048
1049  /* Define the tui control variables.  */
1050  add_setshow_enum_cmd ("border-kind", no_class, tui_border_kind_enums,
1051			&tui_border_kind, _("\
1052Set the kind of border for TUI windows."), _("\
1053Show the kind of border for TUI windows."), _("\
1054This variable controls the border of TUI windows:\n\
1055   space           use a white space\n\
1056   ascii           use ascii characters + - | for the border\n\
1057   acs             use the Alternate Character Set"),
1058			tui_set_var_cmd,
1059			show_tui_border_kind,
1060			&tui_setlist, &tui_showlist);
1061
1062  add_setshow_enum_cmd ("border-mode", no_class, tui_border_mode_enums,
1063			&tui_border_mode, _("\
1064Set the attribute mode to use for the TUI window borders."), _("\
1065Show the attribute mode to use for the TUI window borders."), _("\
1066This variable controls the attributes to use for the window borders:\n\
1067   normal          normal display\n\
1068   standout        use highlight mode of terminal\n\
1069   reverse         use reverse video mode\n\
1070   half            use half bright\n\
1071   half-standout   use half bright and standout mode\n\
1072   bold            use extra bright or bold\n\
1073   bold-standout   use extra bright or bold with standout mode"),
1074			tui_set_var_cmd,
1075			show_tui_border_mode,
1076			&tui_setlist, &tui_showlist);
1077
1078  add_setshow_enum_cmd ("active-border-mode", no_class, tui_border_mode_enums,
1079			&tui_active_border_mode, _("\
1080Set the attribute mode to use for the active TUI window border."), _("\
1081Show the attribute mode to use for the active TUI window border."), _("\
1082This variable controls the attributes to use for the active window border:\n\
1083   normal          normal display\n\
1084   standout        use highlight mode of terminal\n\
1085   reverse         use reverse video mode\n\
1086   half            use half bright\n\
1087   half-standout   use half bright and standout mode\n\
1088   bold            use extra bright or bold\n\
1089   bold-standout   use extra bright or bold with standout mode"),
1090			tui_set_var_cmd,
1091			show_tui_active_border_mode,
1092			&tui_setlist, &tui_showlist);
1093
1094  add_setshow_zuinteger_cmd ("tab-width", no_class,
1095			     &internal_tab_width, _("\
1096Set the tab width, in characters, for the TUI."), _("\
1097Show the tab witdh, in characters, for the TUI."), _("\
1098This variable controls how many spaces are used to display a tab character."),
1099			     tui_set_tab_width, tui_show_tab_width,
1100			     &tui_setlist, &tui_showlist);
1101
1102  add_setshow_boolean_cmd ("tui-resize-message", class_maintenance,
1103			   &resize_message, _("\
1104Set TUI resize messaging."), _("\
1105Show TUI resize messaging."), _("\
1106When enabled GDB will print a message when the terminal is resized."),
1107			   nullptr,
1108			   show_tui_resize_message,
1109			   &maintenance_set_cmdlist,
1110			   &maintenance_show_cmdlist);
1111
1112  add_setshow_boolean_cmd ("compact-source", class_tui,
1113			   &compact_source, _("\
1114Set whether the TUI source window is compact."), _("\
1115Show whether the TUI source window is compact."), _("\
1116This variable controls whether the TUI source window is shown\n\
1117in a compact form.  The compact form puts the source closer to\n\
1118the line numbers and uses less horizontal space."),
1119			   tui_set_compact_source, tui_show_compact_source,
1120			   &tui_setlist, &tui_showlist);
1121
1122  tui_border_style.changed.attach (tui_rehighlight_all);
1123  tui_active_border_style.changed.attach (tui_rehighlight_all);
1124}
1125