123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278 |
- /* TUI support I/O functions.
- Copyright (C) 1998-2022 Free Software Foundation, Inc.
- Contributed by Hewlett-Packard Company.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "defs.h"
- #include "target.h"
- #include "gdbsupport/event-loop.h"
- #include "event-top.h"
- #include "command.h"
- #include "top.h"
- #include "tui/tui.h"
- #include "tui/tui-data.h"
- #include "tui/tui-io.h"
- #include "tui/tui-command.h"
- #include "tui/tui-win.h"
- #include "tui/tui-wingeneral.h"
- #include "tui/tui-file.h"
- #include "tui/tui-out.h"
- #include "ui-out.h"
- #include "cli-out.h"
- #include <fcntl.h>
- #include <signal.h>
- #ifdef __MINGW32__
- #include <windows.h>
- #endif
- #include "gdbsupport/filestuff.h"
- #include "completer.h"
- #include "gdb_curses.h"
- #include <map>
- #include "pager.h"
- /* This redefines CTRL if it is not already defined, so it must come
- after terminal state releated include files like <term.h> and
- "gdb_curses.h". */
- #include "readline/readline.h"
- #ifdef __MINGW32__
- static SHORT ncurses_norm_attr;
- #endif
- static int tui_getc (FILE *fp);
- static int
- key_is_start_sequence (int ch)
- {
- return (ch == 27);
- }
- /* Use definition from readline 4.3. */
- #undef CTRL_CHAR
- #define CTRL_CHAR(c) \
- ((c) < control_character_threshold && (((c) & 0x80) == 0))
- /* This file controls the IO interactions between gdb and curses.
- When the TUI is enabled, gdb has two modes a curses and a standard
- mode.
- In curses mode, the gdb outputs are made in a curses command
- window. For this, the gdb_stdout and gdb_stderr are redirected to
- the specific ui_file implemented by TUI. The output is handled by
- tui_puts(). The input is also controlled by curses with
- tui_getc(). The readline library uses this function to get its
- input. Several readline hooks are installed to redirect readline
- output to the TUI (see also the note below).
- In normal mode, the gdb outputs are restored to their origin, that
- is as if TUI is not used. Readline also uses its original getc()
- function with stdin.
- Note SCz/2001-07-21: the current readline is not clean in its
- management of the output. Even if we install a redisplay handler,
- it sometimes writes on a stdout file. It is important to redirect
- every output produced by readline, otherwise the curses window will
- be garbled. This is implemented with a pipe that TUI reads and
- readline writes to. A gdb input handler is created so that reading
- the pipe is handled automatically. This will probably not work on
- non-Unix platforms. The best fix is to make readline clean enough
- so that is never write on stdout.
- Note SCz/2002-09-01: we now use more readline hooks and it seems
- that with them we don't need the pipe anymore (verified by creating
- the pipe and closing its end so that write causes a SIGPIPE). The
- old pipe code is still there and can be conditionally removed by
- #undef TUI_USE_PIPE_FOR_READLINE. */
- /* For gdb 5.3, prefer to continue the pipe hack as a backup wheel. */
- #ifdef HAVE_PIPE
- #define TUI_USE_PIPE_FOR_READLINE
- #endif
- /* #undef TUI_USE_PIPE_FOR_READLINE */
- /* TUI output files. */
- static struct ui_file *tui_stdout;
- static struct ui_file *tui_stderr;
- static struct ui_file *tui_stdlog;
- struct ui_out *tui_out;
- /* GDB output files in non-curses mode. */
- static struct ui_file *tui_old_stdout;
- static struct ui_file *tui_old_stderr;
- static struct ui_file *tui_old_stdlog;
- cli_ui_out *tui_old_uiout;
- /* Readline previous hooks. */
- static rl_getc_func_t *tui_old_rl_getc_function;
- static rl_voidfunc_t *tui_old_rl_redisplay_function;
- static rl_vintfunc_t *tui_old_rl_prep_terminal;
- static rl_voidfunc_t *tui_old_rl_deprep_terminal;
- static rl_compdisp_func_t *tui_old_rl_display_matches_hook;
- static int tui_old_rl_echoing_p;
- /* Readline output stream.
- Should be removed when readline is clean. */
- static FILE *tui_rl_outstream;
- static FILE *tui_old_rl_outstream;
- #ifdef TUI_USE_PIPE_FOR_READLINE
- static int tui_readline_pipe[2];
- #endif
- /* Print a character in the curses command window. The output is
- buffered. It is up to the caller to refresh the screen if
- necessary. */
- static void
- do_tui_putc (WINDOW *w, char c)
- {
- /* Expand TABs, since ncurses on MS-Windows doesn't. */
- if (c == '\t')
- {
- int col;
- col = getcurx (w);
- do
- {
- waddch (w, ' ');
- col++;
- }
- while ((col % 8) != 0);
- }
- else
- waddch (w, c);
- }
- /* Update the cached value of the command window's start line based on
- the window's current Y coordinate. */
- static void
- update_cmdwin_start_line ()
- {
- TUI_CMD_WIN->start_line = getcury (TUI_CMD_WIN->handle.get ());
- }
- /* Print a character in the curses command window. The output is
- buffered. It is up to the caller to refresh the screen if
- necessary. */
- static void
- tui_putc (char c)
- {
- do_tui_putc (TUI_CMD_WIN->handle.get (), c);
- update_cmdwin_start_line ();
- }
- /* This maps colors to their corresponding color index. */
- static std::map<ui_file_style::color, int> color_map;
- /* This holds a pair of colors and is used to track the mapping
- between a color pair index and the actual colors. */
- struct color_pair
- {
- int fg;
- int bg;
- bool operator< (const color_pair &o) const
- {
- return fg < o.fg || (fg == o.fg && bg < o.bg);
- }
- };
- /* This maps pairs of colors to their corresponding color pair
- index. */
- static std::map<color_pair, int> color_pair_map;
- /* This is indexed by ANSI color offset from the base color, and holds
- the corresponding curses color constant. */
- static const int curses_colors[] = {
- COLOR_BLACK,
- COLOR_RED,
- COLOR_GREEN,
- COLOR_YELLOW,
- COLOR_BLUE,
- COLOR_MAGENTA,
- COLOR_CYAN,
- COLOR_WHITE
- };
- /* Given a color, find its index. */
- static bool
- get_color (const ui_file_style::color &color, int *result)
- {
- if (color.is_none ())
- *result = -1;
- else if (color.is_basic ())
- *result = curses_colors[color.get_value ()];
- else
- {
- auto it = color_map.find (color);
- if (it == color_map.end ())
- {
- /* The first 8 colors are standard. */
- int next = color_map.size () + 8;
- if (next >= COLORS)
- return false;
- uint8_t rgb[3];
- color.get_rgb (rgb);
- /* We store RGB as 0..255, but curses wants 0..1000. */
- if (init_color (next, rgb[0] * 1000 / 255, rgb[1] * 1000 / 255,
- rgb[2] * 1000 / 255) == ERR)
- return false;
- color_map[color] = next;
- *result = next;
- }
- else
- *result = it->second;
- }
- return true;
- }
- /* The most recently emitted color pair. */
- static int last_color_pair = -1;
- /* The most recently applied style. */
- static ui_file_style last_style;
- /* If true, we're highlighting the current source line in reverse
- video mode. */
- static bool reverse_mode_p = false;
- /* The background/foreground colors before we entered reverse
- mode. */
- static ui_file_style::color reverse_save_bg (ui_file_style::NONE);
- static ui_file_style::color reverse_save_fg (ui_file_style::NONE);
- /* Given two colors, return their color pair index; making a new one
- if necessary. */
- static int
- get_color_pair (int fg, int bg)
- {
- color_pair c = { fg, bg };
- auto it = color_pair_map.find (c);
- if (it == color_pair_map.end ())
- {
- /* Color pair 0 is our default color, so new colors start at
- 1. */
- int next = color_pair_map.size () + 1;
- /* Curses has a limited number of available color pairs. Fall
- back to the default if we've used too many. */
- if (next >= COLOR_PAIRS)
- return 0;
- init_pair (next, fg, bg);
- color_pair_map[c] = next;
- return next;
- }
- return it->second;
- }
- /* Apply STYLE to W. */
- void
- tui_apply_style (WINDOW *w, ui_file_style style)
- {
- /* Reset. */
- wattron (w, A_NORMAL);
- wattroff (w, A_BOLD);
- wattroff (w, A_DIM);
- wattroff (w, A_REVERSE);
- if (last_color_pair != -1)
- wattroff (w, COLOR_PAIR (last_color_pair));
- wattron (w, COLOR_PAIR (0));
- const ui_file_style::color &fg = style.get_foreground ();
- const ui_file_style::color &bg = style.get_background ();
- if (!fg.is_none () || !bg.is_none ())
- {
- int fgi, bgi;
- if (get_color (fg, &fgi) && get_color (bg, &bgi))
- {
- #ifdef __MINGW32__
- /* MS-Windows port of ncurses doesn't support implicit
- default foreground and background colors, so we must
- specify them explicitly when needed, using the colors we
- saw at startup. */
- if (fgi == -1)
- fgi = ncurses_norm_attr & 15;
- if (bgi == -1)
- bgi = (ncurses_norm_attr >> 4) & 15;
- #endif
- int pair = get_color_pair (fgi, bgi);
- if (last_color_pair != -1)
- wattroff (w, COLOR_PAIR (last_color_pair));
- wattron (w, COLOR_PAIR (pair));
- last_color_pair = pair;
- }
- }
- switch (style.get_intensity ())
- {
- case ui_file_style::NORMAL:
- break;
- case ui_file_style::BOLD:
- wattron (w, A_BOLD);
- break;
- case ui_file_style::DIM:
- wattron (w, A_DIM);
- break;
- default:
- gdb_assert_not_reached ("invalid intensity");
- }
- if (style.is_reverse ())
- wattron (w, A_REVERSE);
- last_style = style;
- }
- /* Apply an ANSI escape sequence from BUF to W. BUF must start with
- the ESC character. If BUF does not start with an ANSI escape,
- return 0. Otherwise, apply the sequence if it is recognized, or
- simply ignore it if not. In this case, the number of bytes read
- from BUF is returned. */
- static size_t
- apply_ansi_escape (WINDOW *w, const char *buf)
- {
- ui_file_style style = last_style;
- size_t n_read;
- if (!style.parse (buf, &n_read))
- return n_read;
- if (reverse_mode_p)
- {
- /* We want to reverse _only_ the default foreground/background
- colors. If the foreground color is not the default (because
- the text was styled), we want to leave it as is. If e.g.,
- the terminal is fg=BLACK, and bg=WHITE, and the style wants
- to print text in RED, we want to reverse the background color
- (print in BLACK), but still print the text in RED. To do
- that, we enable the A_REVERSE attribute, and re-reverse the
- parsed-style's fb/bg colors.
- Notes on the approach:
- - there's no portable way to know which colors the default
- fb/bg colors map to.
- - this approach does the right thing even if you change the
- terminal colors while GDB is running -- the reversed
- colors automatically adapt.
- */
- if (!style.is_default ())
- {
- ui_file_style::color bg = style.get_background ();
- ui_file_style::color fg = style.get_foreground ();
- style.set_fg (bg);
- style.set_bg (fg);
- }
- /* Enable A_REVERSE. */
- style.set_reverse (true);
- }
- tui_apply_style (w, style);
- return n_read;
- }
- /* See tui.io.h. */
- void
- tui_set_reverse_mode (WINDOW *w, bool reverse)
- {
- ui_file_style style = last_style;
- reverse_mode_p = reverse;
- style.set_reverse (reverse);
- if (reverse)
- {
- reverse_save_bg = style.get_background ();
- reverse_save_fg = style.get_foreground ();
- }
- else
- {
- style.set_bg (reverse_save_bg);
- style.set_fg (reverse_save_fg);
- }
- tui_apply_style (w, style);
- }
- /* Print LENGTH characters from the buffer pointed to by BUF to the
- curses command window. The output is buffered. It is up to the
- caller to refresh the screen if necessary. */
- void
- tui_write (const char *buf, size_t length)
- {
- /* We need this to be \0-terminated for the regexp matching. */
- std::string copy (buf, length);
- tui_puts (copy.c_str ());
- }
- /* Print a string in the curses command window. The output is
- buffered. It is up to the caller to refresh the screen if
- necessary. */
- void
- tui_puts (const char *string, WINDOW *w)
- {
- if (w == nullptr)
- w = TUI_CMD_WIN->handle.get ();
- while (true)
- {
- const char *next = strpbrk (string, "\n\1\2\033\t");
- /* Print the plain text prefix. */
- size_t n_chars = next == nullptr ? strlen (string) : next - string;
- if (n_chars > 0)
- waddnstr (w, string, n_chars);
- /* We finished. */
- if (next == nullptr)
- break;
- char c = *next;
- switch (c)
- {
- case '\1':
- case '\2':
- /* Ignore these, they are readline escape-marking
- sequences. */
- ++next;
- break;
- case '\n':
- case '\t':
- do_tui_putc (w, c);
- ++next;
- break;
- case '\033':
- {
- size_t bytes_read = apply_ansi_escape (w, next);
- if (bytes_read > 0)
- next += bytes_read;
- else
- {
- /* Just drop the escape. */
- ++next;
- }
- }
- break;
- default:
- gdb_assert_not_reached ("missing case in tui_puts");
- }
- string = next;
- }
- if (TUI_CMD_WIN != nullptr && w == TUI_CMD_WIN->handle.get ())
- update_cmdwin_start_line ();
- }
- static void
- tui_puts_internal (WINDOW *w, const char *string, int *height)
- {
- char c;
- int prev_col = 0;
- bool saw_nl = false;
- while ((c = *string++) != 0)
- {
- if (c == '\n')
- saw_nl = true;
- if (c == '\1' || c == '\2')
- {
- /* Ignore these, they are readline escape-marking
- sequences. */
- }
- else
- {
- if (c == '\033')
- {
- size_t bytes_read = apply_ansi_escape (w, string - 1);
- if (bytes_read > 0)
- {
- string = string + bytes_read - 1;
- continue;
- }
- }
- do_tui_putc (w, c);
- if (height != nullptr)
- {
- int col = getcurx (w);
- if (col <= prev_col)
- ++*height;
- prev_col = col;
- }
- }
- }
- if (TUI_CMD_WIN != nullptr && w == TUI_CMD_WIN->handle.get ())
- update_cmdwin_start_line ();
- if (saw_nl)
- wrefresh (w);
- }
- /* Readline callback.
- Redisplay the command line with its prompt after readline has
- changed the edited text. */
- void
- tui_redisplay_readline (void)
- {
- int prev_col;
- int height;
- int col;
- int c_pos;
- int c_line;
- int in;
- WINDOW *w;
- const char *prompt;
- int start_line;
- /* Detect when we temporarily left SingleKey and now the readline
- edit buffer is empty, automatically restore the SingleKey
- mode. The restore must only be done if the command has finished.
- The command could call prompt_for_continue and we must not
- restore SingleKey so that the prompt and normal keymap are used. */
- if (tui_current_key_mode == TUI_ONE_COMMAND_MODE && rl_end == 0
- && !gdb_in_secondary_prompt_p (current_ui))
- tui_set_key_mode (TUI_SINGLE_KEY_MODE);
- if (tui_current_key_mode == TUI_SINGLE_KEY_MODE)
- prompt = "";
- else
- prompt = rl_display_prompt;
-
- c_pos = -1;
- c_line = -1;
- w = TUI_CMD_WIN->handle.get ();
- start_line = TUI_CMD_WIN->start_line;
- wmove (w, start_line, 0);
- prev_col = 0;
- height = 1;
- if (prompt != nullptr)
- tui_puts_internal (w, prompt, &height);
- prev_col = getcurx (w);
- for (in = 0; in <= rl_end; in++)
- {
- unsigned char c;
-
- if (in == rl_point)
- {
- getyx (w, c_line, c_pos);
- }
- if (in == rl_end)
- break;
- c = (unsigned char) rl_line_buffer[in];
- if (CTRL_CHAR (c) || c == RUBOUT)
- {
- waddch (w, '^');
- waddch (w, CTRL_CHAR (c) ? UNCTRL (c) : '?');
- }
- else if (c == '\t')
- {
- /* Expand TABs, since ncurses on MS-Windows doesn't. */
- col = getcurx (w);
- do
- {
- waddch (w, ' ');
- col++;
- } while ((col % 8) != 0);
- }
- else
- {
- waddch (w, c);
- }
- if (c == '\n')
- TUI_CMD_WIN->start_line = getcury (w);
- col = getcurx (w);
- if (col < prev_col)
- height++;
- prev_col = col;
- }
- wclrtobot (w);
- TUI_CMD_WIN->start_line = getcury (w);
- if (c_line >= 0)
- wmove (w, c_line, c_pos);
- TUI_CMD_WIN->start_line -= height - 1;
- wrefresh (w);
- fflush(stdout);
- }
- /* Readline callback to prepare the terminal. It is called once each
- time we enter readline. Terminal is already setup in curses
- mode. */
- static void
- tui_prep_terminal (int notused1)
- {
- #ifdef NCURSES_MOUSE_VERSION
- mousemask (ALL_MOUSE_EVENTS, NULL);
- #endif
- }
- /* Readline callback to restore the terminal. It is called once each
- time we leave readline. There is nothing to do in curses mode. */
- static void
- tui_deprep_terminal (void)
- {
- #ifdef NCURSES_MOUSE_VERSION
- mousemask (0, NULL);
- #endif
- }
- #ifdef TUI_USE_PIPE_FOR_READLINE
- /* Read readline output pipe and feed the command window with it.
- Should be removed when readline is clean. */
- static void
- tui_readline_output (int error, gdb_client_data data)
- {
- int size;
- char buf[256];
- size = read (tui_readline_pipe[0], buf, sizeof (buf) - 1);
- if (size > 0 && tui_active)
- {
- buf[size] = 0;
- tui_puts (buf);
- }
- }
- #endif
- /* TUI version of displayer.crlf. */
- static void
- tui_mld_crlf (const struct match_list_displayer *displayer)
- {
- tui_putc ('\n');
- }
- /* TUI version of displayer.putch. */
- static void
- tui_mld_putch (const struct match_list_displayer *displayer, int ch)
- {
- tui_putc (ch);
- }
- /* TUI version of displayer.puts. */
- static void
- tui_mld_puts (const struct match_list_displayer *displayer, const char *s)
- {
- tui_puts (s);
- }
- /* TUI version of displayer.flush. */
- static void
- tui_mld_flush (const struct match_list_displayer *displayer)
- {
- wrefresh (TUI_CMD_WIN->handle.get ());
- }
- /* TUI version of displayer.erase_entire_line. */
- static void
- tui_mld_erase_entire_line (const struct match_list_displayer *displayer)
- {
- WINDOW *w = TUI_CMD_WIN->handle.get ();
- int cur_y = getcury (w);
- wmove (w, cur_y, 0);
- wclrtoeol (w);
- wmove (w, cur_y, 0);
- }
- /* TUI version of displayer.beep. */
- static void
- tui_mld_beep (const struct match_list_displayer *displayer)
- {
- beep ();
- }
- /* A wrapper for wgetch that enters nonl mode. We We normally want
- curses' "nl" mode, but when reading from the user, we'd like to
- differentiate between C-j and C-m, because some users bind these
- keys differently in their .inputrc. So, put curses into nonl mode
- just when reading from the user. See PR tui/20819. */
- static int
- gdb_wgetch (WINDOW *win)
- {
- nonl ();
- int r = wgetch (win);
- nl ();
- return r;
- }
- /* Helper function for tui_mld_read_key.
- This temporarily replaces tui_getc for use during tab-completion
- match list display. */
- static int
- tui_mld_getc (FILE *fp)
- {
- WINDOW *w = TUI_CMD_WIN->handle.get ();
- int c = gdb_wgetch (w);
- return c;
- }
- /* TUI version of displayer.read_key. */
- static int
- tui_mld_read_key (const struct match_list_displayer *displayer)
- {
- rl_getc_func_t *prev = rl_getc_function;
- int c;
- /* We can't use tui_getc as we need NEWLINE to not get emitted. */
- rl_getc_function = tui_mld_getc;
- c = rl_read_key ();
- rl_getc_function = prev;
- return c;
- }
- /* TUI version of rl_completion_display_matches_hook.
- See gdb_display_match_list for a description of the arguments. */
- static void
- tui_rl_display_match_list (char **matches, int len, int max)
- {
- struct match_list_displayer displayer;
- rl_get_screen_size (&displayer.height, &displayer.width);
- displayer.crlf = tui_mld_crlf;
- displayer.putch = tui_mld_putch;
- displayer.puts = tui_mld_puts;
- displayer.flush = tui_mld_flush;
- displayer.erase_entire_line = tui_mld_erase_entire_line;
- displayer.beep = tui_mld_beep;
- displayer.read_key = tui_mld_read_key;
- gdb_display_match_list (matches, len, max, &displayer);
- }
- /* Setup the IO for curses or non-curses mode.
- - In non-curses mode, readline and gdb use the standard input and
- standard output/error directly.
- - In curses mode, the standard output/error is controlled by TUI
- with the tui_stdout and tui_stderr. The output is redirected in
- the curses command window. Several readline callbacks are installed
- so that readline asks for its input to the curses command window
- with wgetch(). */
- void
- tui_setup_io (int mode)
- {
- extern int _rl_echoing_p;
- if (mode)
- {
- /* Ensure that readline has been initialized before saving any
- of its variables. */
- tui_ensure_readline_initialized ();
- /* Redirect readline to TUI. */
- tui_old_rl_redisplay_function = rl_redisplay_function;
- tui_old_rl_deprep_terminal = rl_deprep_term_function;
- tui_old_rl_prep_terminal = rl_prep_term_function;
- tui_old_rl_getc_function = rl_getc_function;
- tui_old_rl_display_matches_hook = rl_completion_display_matches_hook;
- tui_old_rl_outstream = rl_outstream;
- tui_old_rl_echoing_p = _rl_echoing_p;
- rl_redisplay_function = tui_redisplay_readline;
- rl_deprep_term_function = tui_deprep_terminal;
- rl_prep_term_function = tui_prep_terminal;
- rl_getc_function = tui_getc;
- _rl_echoing_p = 0;
- rl_outstream = tui_rl_outstream;
- rl_prompt = 0;
- rl_completion_display_matches_hook = tui_rl_display_match_list;
- rl_already_prompted = 0;
- /* Keep track of previous gdb output. */
- tui_old_stdout = gdb_stdout;
- tui_old_stderr = gdb_stderr;
- tui_old_stdlog = gdb_stdlog;
- tui_old_uiout = dynamic_cast<cli_ui_out *> (current_uiout);
- gdb_assert (tui_old_uiout != nullptr);
- /* Reconfigure gdb output. */
- gdb_stdout = tui_stdout;
- gdb_stderr = tui_stderr;
- gdb_stdlog = tui_stdlog;
- gdb_stdtarg = gdb_stderr; /* for moment */
- gdb_stdtargerr = gdb_stderr; /* for moment */
- current_uiout = tui_out;
- /* Save tty for SIGCONT. */
- savetty ();
- }
- else
- {
- /* Restore gdb output. */
- gdb_stdout = tui_old_stdout;
- gdb_stderr = tui_old_stderr;
- gdb_stdlog = tui_old_stdlog;
- gdb_stdtarg = gdb_stderr; /* for moment */
- gdb_stdtargerr = gdb_stderr; /* for moment */
- current_uiout = tui_old_uiout;
- /* Restore readline. */
- rl_redisplay_function = tui_old_rl_redisplay_function;
- rl_deprep_term_function = tui_old_rl_deprep_terminal;
- rl_prep_term_function = tui_old_rl_prep_terminal;
- rl_getc_function = tui_old_rl_getc_function;
- rl_completion_display_matches_hook = tui_old_rl_display_matches_hook;
- rl_outstream = tui_old_rl_outstream;
- _rl_echoing_p = tui_old_rl_echoing_p;
- rl_already_prompted = 0;
- /* Save tty for SIGCONT. */
- savetty ();
- /* Clean up color information. */
- last_color_pair = -1;
- last_style = ui_file_style ();
- color_map.clear ();
- color_pair_map.clear ();
- }
- }
- #ifdef SIGCONT
- /* Catch SIGCONT to restore the terminal and refresh the screen. */
- static void
- tui_cont_sig (int sig)
- {
- if (tui_active)
- {
- /* Restore the terminal setting because another process (shell)
- might have changed it. */
- resetty ();
- /* Force a refresh of the screen. */
- tui_refresh_all_win ();
- }
- signal (sig, tui_cont_sig);
- }
- #endif
- /* Initialize the IO for gdb in curses mode. */
- void
- tui_initialize_io (void)
- {
- #ifdef SIGCONT
- signal (SIGCONT, tui_cont_sig);
- #endif
- /* Create tui output streams. */
- tui_stdout = new pager_file (new tui_file (stdout));
- tui_stderr = new tui_file (stderr);
- tui_stdlog = new timestamped_file (tui_stderr);
- tui_out = tui_out_new (tui_stdout);
- /* Create the default UI. */
- tui_old_uiout = cli_out_new (gdb_stdout);
- #ifdef TUI_USE_PIPE_FOR_READLINE
- /* Temporary solution for readline writing to stdout: redirect
- readline output in a pipe, read that pipe and output the content
- in the curses command window. */
- if (gdb_pipe_cloexec (tui_readline_pipe) != 0)
- error (_("Cannot create pipe for readline"));
- tui_rl_outstream = fdopen (tui_readline_pipe[1], "w");
- if (tui_rl_outstream == 0)
- error (_("Cannot redirect readline output"));
- setvbuf (tui_rl_outstream, NULL, _IOLBF, 0);
- #ifdef O_NONBLOCK
- (void) fcntl (tui_readline_pipe[0], F_SETFL, O_NONBLOCK);
- #else
- #ifdef O_NDELAY
- (void) fcntl (tui_readline_pipe[0], F_SETFL, O_NDELAY);
- #endif
- #endif
- add_file_handler (tui_readline_pipe[0], tui_readline_output, 0, "tui");
- #else
- tui_rl_outstream = stdout;
- #endif
- #ifdef __MINGW32__
- /* MS-Windows port of ncurses doesn't support default foreground and
- background colors, so we must record the default colors at startup. */
- HANDLE hstdout = (HANDLE)_get_osfhandle (fileno (stdout));
- DWORD cmode;
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- if (hstdout != INVALID_HANDLE_VALUE
- && GetConsoleMode (hstdout, &cmode) != 0
- && GetConsoleScreenBufferInfo (hstdout, &csbi))
- ncurses_norm_attr = csbi.wAttributes;
- #endif
- }
- /* Dispatch the correct tui function based upon the mouse event. */
- #ifdef NCURSES_MOUSE_VERSION
- static void
- tui_dispatch_mouse_event ()
- {
- MEVENT mev;
- if (getmouse (&mev) != OK)
- return;
- for (tui_win_info *wi : all_tui_windows ())
- if (mev.x > wi->x && mev.x < wi->x + wi->width - 1
- && mev.y > wi->y && mev.y < wi->y + wi->height - 1)
- {
- if ((mev.bstate & BUTTON1_CLICKED) != 0
- || (mev.bstate & BUTTON2_CLICKED) != 0
- || (mev.bstate & BUTTON3_CLICKED) != 0)
- {
- int button = (mev.bstate & BUTTON1_CLICKED) != 0 ? 1
- : ((mev.bstate & BUTTON2_CLICKED) != 0 ? 2
- : 3);
- wi->click (mev.x - wi->x - 1, mev.y - wi->y - 1, button);
- }
- #ifdef BUTTON5_PRESSED
- else if ((mev.bstate & BUTTON4_PRESSED) != 0)
- wi->backward_scroll (3);
- else if ((mev.bstate & BUTTON5_PRESSED) != 0)
- wi->forward_scroll (3);
- #endif
- break;
- }
- }
- #endif
- /* Dispatch the correct tui function based upon the control
- character. */
- static unsigned int
- tui_dispatch_ctrl_char (unsigned int ch)
- {
- struct tui_win_info *win_info = tui_win_with_focus ();
- /* If no window has the focus, or if the focus window can't scroll,
- just pass the character through. */
- if (win_info == NULL || !win_info->can_scroll ())
- return ch;
- switch (ch)
- {
- case KEY_NPAGE:
- win_info->forward_scroll (0);
- break;
- case KEY_PPAGE:
- win_info->backward_scroll (0);
- break;
- case KEY_DOWN:
- case KEY_SF:
- win_info->forward_scroll (1);
- break;
- case KEY_UP:
- case KEY_SR:
- win_info->backward_scroll (1);
- break;
- case KEY_RIGHT:
- win_info->left_scroll (1);
- break;
- case KEY_LEFT:
- win_info->right_scroll (1);
- break;
- default:
- /* We didn't recognize the character as a control character, so pass it
- through. */
- return ch;
- }
- /* We intercepted the control character, so return 0 (which readline
- will interpret as a no-op). */
- return 0;
- }
- /* See tui-io.h. */
- void
- tui_inject_newline_into_command_window ()
- {
- gdb_assert (tui_active);
- WINDOW *w = TUI_CMD_WIN->handle.get ();
- /* When hitting return with an empty input, gdb executes the last
- command. If we emit a newline, this fills up the command window
- with empty lines with gdb prompt at beginning. Instead of that,
- stay on the same line but provide a visual effect to show the
- user we recognized the command. */
- if (rl_end == 0 && !gdb_in_secondary_prompt_p (current_ui))
- {
- wmove (w, getcury (w), 0);
- /* Clear the line. This will blink the gdb prompt since
- it will be redrawn at the same line. */
- wclrtoeol (w);
- wrefresh (w);
- napms (20);
- }
- else
- {
- /* Move cursor to the end of the command line before emitting the
- newline. We need to do so because when ncurses outputs a newline
- it truncates any text that appears past the end of the cursor. */
- int px, py;
- getyx (w, py, px);
- px += rl_end - rl_point;
- py += px / TUI_CMD_WIN->width;
- px %= TUI_CMD_WIN->width;
- wmove (w, py, px);
- tui_putc ('\n');
- }
- }
- /* If we're passing an escape sequence to readline, this points to a
- string holding the remaining characters of the sequence to pass.
- We advance the pointer one character at a time until '\0' is
- reached. */
- static const char *cur_seq = nullptr;
- /* Set CUR_SEQ to point at the current sequence to pass to readline,
- setup to call the input handler again so we complete the sequence
- shortly, and return the first character to start the sequence. */
- static int
- start_sequence (const char *seq)
- {
- call_stdin_event_handler_again_p = 1;
- cur_seq = seq + 1;
- return seq[0];
- }
- /* Main worker for tui_getc. Get a character from the command window.
- This is called from the readline package, but wrapped in a
- try/catch by tui_getc. */
- static int
- tui_getc_1 (FILE *fp)
- {
- int ch;
- WINDOW *w;
- w = TUI_CMD_WIN->handle.get ();
- #ifdef TUI_USE_PIPE_FOR_READLINE
- /* Flush readline output. */
- tui_readline_output (0, 0);
- #endif
- /* We enable keypad mode so that curses's wgetch processes mouse
- escape sequences. In keypad mode, wgetch also processes the
- escape sequences for keys such as up/down etc. and returns KEY_UP
- / KEY_DOWN etc. When we have the focus on the command window
- though, we want to pass the raw up/down etc. escape codes to
- readline so readline understands them. */
- if (cur_seq != nullptr)
- {
- ch = *cur_seq++;
- /* If we've reached the end of the string, we're done with the
- sequence. Otherwise, setup to get back here again for
- another character. */
- if (*cur_seq == '\0')
- cur_seq = nullptr;
- else
- call_stdin_event_handler_again_p = 1;
- return ch;
- }
- else
- ch = gdb_wgetch (w);
- /* Handle prev/next/up/down here. */
- ch = tui_dispatch_ctrl_char (ch);
- #ifdef NCURSES_MOUSE_VERSION
- if (ch == KEY_MOUSE)
- {
- tui_dispatch_mouse_event ();
- return 0;
- }
- #endif
- /* Translate curses keys back to escape sequences so that readline
- can understand them. We do this irrespective of which window has
- the focus. If e.g., we're focused on a non-command window, then
- the up/down keys will already have been filtered by
- tui_dispatch_ctrl_char. Keys that haven't been intercepted will
- be passed down to readline. */
- if (current_ui->command_editing)
- {
- /* For the standard arrow keys + home/end, hardcode sequences
- readline understands. See bind_arrow_keys_internal in
- readline/readline.c. */
- switch (ch)
- {
- case KEY_UP:
- return start_sequence ("\033[A");
- case KEY_DOWN:
- return start_sequence ("\033[B");
- case KEY_RIGHT:
- return start_sequence ("\033[C");
- case KEY_LEFT:
- return start_sequence ("\033[D");
- case KEY_HOME:
- return start_sequence ("\033[H");
- case KEY_END:
- return start_sequence ("\033[F");
- /* del and ins are unfortunately not hardcoded in readline for
- all systems. */
- case KEY_DC: /* del */
- #ifdef __MINGW32__
- return start_sequence ("\340S");
- #else
- return start_sequence ("\033[3~");
- #endif
- case KEY_IC: /* ins */
- #if defined __MINGW32__
- return start_sequence ("\340R");
- #else
- return start_sequence ("\033[2~");
- #endif
- }
- /* Keycodes above KEY_MAX are not garanteed to be stable.
- Compare keyname instead. */
- if (ch >= KEY_MAX)
- {
- auto name = gdb::string_view (keyname (ch));
- /* The following sequences are hardcoded in readline as
- well. */
- /* ctrl-arrow keys */
- if (name == "kLFT5") /* ctrl-left */
- return start_sequence ("\033[1;5D");
- else if (name == "kRIT5") /* ctrl-right */
- return start_sequence ("\033[1;5C");
- else if (name == "kDC5") /* ctrl-del */
- return start_sequence ("\033[3;5~");
- /* alt-arrow keys */
- else if (name == "kLFT3") /* alt-left */
- return start_sequence ("\033[1;3D");
- else if (name == "kRIT3") /* alt-right */
- return start_sequence ("\033[1;3C");
- }
- }
- /* Handle the CTRL-L refresh for each window. */
- if (ch == '\f')
- {
- tui_refresh_all_win ();
- return ch;
- }
- if (ch == KEY_BACKSPACE)
- return '\b';
- if (current_ui->command_editing && key_is_start_sequence (ch))
- {
- int ch_pending;
- nodelay (w, TRUE);
- ch_pending = gdb_wgetch (w);
- nodelay (w, FALSE);
- /* If we have pending input following a start sequence, call the stdin
- event handler again because ncurses may have already read and stored
- the input into its internal buffer, meaning that we won't get an stdin
- event for it. If we don't compensate for this missed stdin event, key
- sequences as Alt_F (^[f) will not behave promptly.
- (We only compensates for the missed 2nd byte of a key sequence because
- 2-byte sequences are by far the most commonly used. ncurses may have
- buffered a larger, 3+-byte key sequence though it remains to be seen
- whether it is useful to compensate for all the bytes of such
- sequences.) */
- if (ch_pending != ERR)
- {
- ungetch (ch_pending);
- call_stdin_event_handler_again_p = 1;
- }
- }
- if (ch > 0xff)
- {
- /* Readline doesn't understand non-8-bit curses keys, filter
- them out. */
- return 0;
- }
- return ch;
- }
- /* Get a character from the command window. This is called from the
- readline package. */
- static int
- tui_getc (FILE *fp)
- {
- try
- {
- return tui_getc_1 (fp);
- }
- catch (const gdb_exception &ex)
- {
- /* Just in case, don't ever let an exception escape to readline.
- This shouldn't ever happen, but if it does, print the
- exception instead of just crashing GDB. */
- exception_print (gdb_stderr, ex);
- /* If we threw an exception, it's because we recognized the
- character. */
- return 0;
- }
- }
|