Home | History | Annotate | Line # | Download | only in tui
tui.c revision 1.9
      1 /* General functions for the WDB TUI.
      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 #include "defs.h"
     23 #include "gdbcmd.h"
     24 #include "tui/tui.h"
     25 #include "tui/tui-hooks.h"
     26 #include "tui/tui-command.h"
     27 #include "tui/tui-data.h"
     28 #include "tui/tui-layout.h"
     29 #include "tui/tui-io.h"
     30 #include "tui/tui-regs.h"
     31 #include "tui/tui-stack.h"
     32 #include "tui/tui-win.h"
     33 #include "tui/tui-wingeneral.h"
     34 #include "tui/tui-winsource.h"
     35 #include "tui/tui-source.h"
     36 #include "target.h"
     37 #include "frame.h"
     38 #include "breakpoint.h"
     39 #include "inferior.h"
     40 #include "symtab.h"
     41 #include "source.h"
     42 #include "terminal.h"
     43 #include "top.h"
     44 
     45 #include <ctype.h>
     46 #include <signal.h>
     47 #include <fcntl.h>
     48 #include <setjmp.h>
     49 
     50 #include "gdb_curses.h"
     51 #include "interps.h"
     52 
     53 /* This redefines CTRL if it is not already defined, so it must come
     54    after terminal state releated include files like <term.h> and
     55    "gdb_curses.h".  */
     56 #include "readline/readline.h"
     57 
     58 /* Tells whether the TUI is active or not.  */
     59 bool tui_active = false;
     60 static bool tui_finish_init = true;
     61 
     62 enum tui_key_mode tui_current_key_mode = TUI_COMMAND_MODE;
     63 
     64 struct tui_char_command
     65 {
     66   unsigned char key;
     67   const char *cmd;
     68 };
     69 
     70 /* Key mapping to gdb commands when the TUI is using the single key
     71    mode.  */
     72 static const struct tui_char_command tui_commands[] = {
     73   { 'c', "continue" },
     74   { 'd', "down" },
     75   { 'f', "finish" },
     76   { 'n', "next" },
     77   { 'o', "nexti" },
     78   { 'r', "run" },
     79   { 's', "step" },
     80   { 'i', "stepi" },
     81   { 'u', "up" },
     82   { 'v', "info locals" },
     83   { 'w', "where" },
     84   { 0, 0 },
     85 };
     86 
     87 static Keymap tui_keymap;
     88 static Keymap tui_readline_standard_keymap;
     89 
     90 /* TUI readline command.
     91    Switch the output mode between TUI/standard gdb.  */
     92 static int
     93 tui_rl_switch_mode (int notused1, int notused2)
     94 {
     95 
     96   /* Don't let exceptions escape.  We're in the middle of a readline
     97      callback that isn't prepared for that.  */
     98   try
     99     {
    100       if (tui_active)
    101 	{
    102 	  tui_disable ();
    103 	  rl_prep_terminal (0);
    104 	}
    105       else
    106 	{
    107 	  /* If tui_enable throws, we'll re-prep below.  */
    108 	  rl_deprep_terminal ();
    109 	  tui_enable ();
    110 	}
    111     }
    112   catch (const gdb_exception &ex)
    113     {
    114       exception_print (gdb_stderr, ex);
    115 
    116       if (!tui_active)
    117 	rl_prep_terminal (0);
    118     }
    119 
    120   /* Clear the readline in case switching occurred in middle of
    121      something.  */
    122   if (rl_end)
    123     rl_kill_text (0, rl_end);
    124 
    125   /* Since we left the curses mode, the terminal mode is restored to
    126      some previous state.  That state may not be suitable for readline
    127      to work correctly (it may be restored in line mode).  We force an
    128      exit of the current readline so that readline is re-entered and
    129      it will be able to setup the terminal for its needs.  By
    130      re-entering in readline, we also redisplay its prompt in the
    131      non-curses mode.  */
    132   rl_newline (1, '\n');
    133 
    134   /* Make sure the \n we are returning does not repeat the last
    135      command.  */
    136   dont_repeat ();
    137   return 0;
    138 }
    139 
    140 /* TUI readline command.
    141    Change the TUI layout to show a next layout.
    142    This function is bound to CTRL-X 2.  It is intended to provide
    143    a functionality close to the Emacs split-window command.  */
    144 static int
    145 tui_rl_change_windows (int notused1, int notused2)
    146 {
    147   if (!tui_active)
    148     tui_rl_switch_mode (0 /* notused */, 0 /* notused */);
    149 
    150   if (tui_active)
    151     tui_next_layout ();
    152 
    153   return 0;
    154 }
    155 
    156 /* TUI readline command.
    157    Delete the second TUI window to only show one.  */
    158 static int
    159 tui_rl_delete_other_windows (int notused1, int notused2)
    160 {
    161   if (!tui_active)
    162     tui_rl_switch_mode (0 /* notused */, 0 /* notused */);
    163 
    164   if (tui_active)
    165     tui_remove_some_windows ();
    166 
    167   return 0;
    168 }
    169 
    170 /* TUI readline command.
    171    Switch the active window to give the focus to a next window.  */
    172 static int
    173 tui_rl_other_window (int count, int key)
    174 {
    175   struct tui_win_info *win_info;
    176 
    177   if (!tui_active)
    178     tui_rl_switch_mode (0 /* notused */, 0 /* notused */);
    179 
    180   win_info = tui_next_win (tui_win_with_focus ());
    181   if (win_info)
    182     {
    183       tui_set_win_focus_to (win_info);
    184       keypad (TUI_CMD_WIN->handle.get (), win_info != TUI_CMD_WIN);
    185     }
    186   return 0;
    187 }
    188 
    189 /* TUI readline command.
    190    Execute the gdb command bound to the specified key.  */
    191 static int
    192 tui_rl_command_key (int count, int key)
    193 {
    194   int i;
    195 
    196   reinitialize_more_filter ();
    197   for (i = 0; tui_commands[i].cmd; i++)
    198     {
    199       if (tui_commands[i].key == key)
    200         {
    201           /* Insert the command in the readline buffer.
    202              Avoid calling the gdb command here since it creates
    203              a possible recursion on readline if prompt_for_continue
    204              is called (See PR 9584).  The command will also appear
    205              in the readline history which turns out to be better.  */
    206           rl_insert_text (tui_commands[i].cmd);
    207           rl_newline (1, '\n');
    208 
    209           /* Switch to gdb command mode while executing the command.
    210              This way the gdb's continue prompty will be displayed.  */
    211           tui_set_key_mode (TUI_ONE_COMMAND_MODE);
    212           return 0;
    213         }
    214     }
    215   return 0;
    216 }
    217 
    218 /* TUI readline command.
    219    Temporarily leave the TUI SingleKey mode to allow editing
    220    a gdb command with the normal readline.  Once the command
    221    is executed, the TUI SingleKey mode is installed back.  */
    222 static int
    223 tui_rl_command_mode (int count, int key)
    224 {
    225   tui_set_key_mode (TUI_ONE_COMMAND_MODE);
    226   return rl_insert (count, key);
    227 }
    228 
    229 /* TUI readline command.
    230    Switch between TUI SingleKey mode and gdb readline editing.  */
    231 static int
    232 tui_rl_next_keymap (int notused1, int notused2)
    233 {
    234   if (!tui_active)
    235     tui_rl_switch_mode (0 /* notused */, 0 /* notused */);
    236 
    237   tui_set_key_mode (tui_current_key_mode == TUI_COMMAND_MODE
    238                     ? TUI_SINGLE_KEY_MODE : TUI_COMMAND_MODE);
    239   return 0;
    240 }
    241 
    242 /* Readline hook to redisplay ourself the gdb prompt.
    243    In the SingleKey mode, the prompt is not printed so that
    244    the command window is cleaner.  It will be displayed if
    245    we temporarily leave the SingleKey mode.  */
    246 static int
    247 tui_rl_startup_hook (void)
    248 {
    249   rl_already_prompted = 1;
    250   if (tui_current_key_mode != TUI_COMMAND_MODE
    251       && !gdb_in_secondary_prompt_p (current_ui))
    252     tui_set_key_mode (TUI_SINGLE_KEY_MODE);
    253   tui_redisplay_readline ();
    254   return 0;
    255 }
    256 
    257 /* Change the TUI key mode by installing the appropriate readline
    258    keymap.  */
    259 void
    260 tui_set_key_mode (enum tui_key_mode mode)
    261 {
    262   tui_current_key_mode = mode;
    263   rl_set_keymap (mode == TUI_SINGLE_KEY_MODE
    264                  ? tui_keymap : tui_readline_standard_keymap);
    265   tui_show_locator_content ();
    266 }
    267 
    268 /* Initialize readline and configure the keymap for the switching
    269    key shortcut.  */
    270 void
    271 tui_ensure_readline_initialized ()
    272 {
    273   static bool initialized;
    274 
    275   if (initialized)
    276     return;
    277   initialized = true;
    278 
    279   int i;
    280   Keymap tui_ctlx_keymap;
    281 
    282   rl_add_defun ("tui-switch-mode", tui_rl_switch_mode, -1);
    283   rl_add_defun ("next-keymap", tui_rl_next_keymap, -1);
    284   rl_add_defun ("tui-delete-other-windows", tui_rl_delete_other_windows, -1);
    285   rl_add_defun ("tui-change-windows", tui_rl_change_windows, -1);
    286   rl_add_defun ("tui-other-window", tui_rl_other_window, -1);
    287 
    288   tui_keymap = rl_make_bare_keymap ();
    289 
    290   /* The named keymap feature was added in Readline 8.0.  */
    291 #if RL_READLINE_VERSION >= 0x800
    292   rl_set_keymap_name ("SingleKey", tui_keymap);
    293 #endif
    294 
    295   tui_ctlx_keymap = rl_make_bare_keymap ();
    296   tui_readline_standard_keymap = rl_get_keymap ();
    297 
    298   for (i = 0; tui_commands[i].cmd; i++)
    299     rl_bind_key_in_map (tui_commands[i].key, tui_rl_command_key, tui_keymap);
    300 
    301   rl_generic_bind (ISKMAP, "\\C-x", (char*) tui_ctlx_keymap, tui_keymap);
    302 
    303   /* Bind all other keys to tui_rl_command_mode so that we switch
    304      temporarily from SingleKey mode and can enter a gdb command.  */
    305   for (i = ' '; i < 0x7f; i++)
    306     {
    307       int j;
    308 
    309       for (j = 0; tui_commands[j].cmd; j++)
    310         if (tui_commands[j].key == i)
    311           break;
    312 
    313       if (tui_commands[j].cmd)
    314         continue;
    315 
    316       rl_bind_key_in_map (i, tui_rl_command_mode, tui_keymap);
    317     }
    318 
    319   rl_bind_key_in_map ('a', tui_rl_switch_mode, emacs_ctlx_keymap);
    320   rl_bind_key_in_map ('a', tui_rl_switch_mode, tui_ctlx_keymap);
    321   rl_bind_key_in_map ('A', tui_rl_switch_mode, emacs_ctlx_keymap);
    322   rl_bind_key_in_map ('A', tui_rl_switch_mode, tui_ctlx_keymap);
    323   rl_bind_key_in_map (CTRL ('A'), tui_rl_switch_mode, emacs_ctlx_keymap);
    324   rl_bind_key_in_map (CTRL ('A'), tui_rl_switch_mode, tui_ctlx_keymap);
    325   rl_bind_key_in_map ('1', tui_rl_delete_other_windows, emacs_ctlx_keymap);
    326   rl_bind_key_in_map ('1', tui_rl_delete_other_windows, tui_ctlx_keymap);
    327   rl_bind_key_in_map ('2', tui_rl_change_windows, emacs_ctlx_keymap);
    328   rl_bind_key_in_map ('2', tui_rl_change_windows, tui_ctlx_keymap);
    329   rl_bind_key_in_map ('o', tui_rl_other_window, emacs_ctlx_keymap);
    330   rl_bind_key_in_map ('o', tui_rl_other_window, tui_ctlx_keymap);
    331   rl_bind_key_in_map ('q', tui_rl_next_keymap, tui_keymap);
    332   rl_bind_key_in_map ('s', tui_rl_next_keymap, emacs_ctlx_keymap);
    333   rl_bind_key_in_map ('s', tui_rl_next_keymap, tui_ctlx_keymap);
    334 
    335   /* Initialize readline after the above.  */
    336   rl_initialize ();
    337 }
    338 
    339 /* Return the TERM variable from the environment, or "<unset>"
    340    if not set.  */
    341 
    342 static const char *
    343 gdb_getenv_term (void)
    344 {
    345   const char *term;
    346 
    347   term = getenv ("TERM");
    348   if (term != NULL)
    349     return term;
    350   return "<unset>";
    351 }
    352 
    353 /* Enter in the tui mode (curses).
    354    When in normal mode, it installs the tui hooks in gdb, redirects
    355    the gdb output, configures the readline to work in tui mode.
    356    When in curses mode, it does nothing.  */
    357 void
    358 tui_enable (void)
    359 {
    360   if (tui_active)
    361     return;
    362 
    363   /* To avoid to initialize curses when gdb starts, there is a deferred
    364      curses initialization.  This initialization is made only once
    365      and the first time the curses mode is entered.  */
    366   if (tui_finish_init)
    367     {
    368       WINDOW *w;
    369       SCREEN *s;
    370 #ifndef __MINGW32__
    371        const char *cap;
    372 #endif
    373       const char *interp;
    374 
    375       /* If the top level interpreter is not the console/tui (e.g.,
    376 	 MI), enabling curses will certainly lose.  */
    377       interp = top_level_interpreter ()->name ();
    378       if (strcmp (interp, INTERP_TUI) != 0)
    379 	error (_("Cannot enable the TUI when the interpreter is '%s'"), interp);
    380 
    381       /* Don't try to setup curses (and print funny control
    382 	 characters) if we're not outputting to a terminal.  */
    383       if (!gdb_stderr->isatty ())
    384 	error (_("Cannot enable the TUI when output is not a terminal"));
    385 
    386       s = newterm (NULL, stdout, stdin);
    387 #ifdef __MINGW32__
    388       /* The MinGW port of ncurses requires $TERM to be unset in order
    389 	 to activate the Windows console driver.  */
    390       if (s == NULL)
    391 	s = newterm ((char *) "unknown", stdout, stdin);
    392 #endif
    393       if (s == NULL)
    394 	{
    395 	  error (_("Cannot enable the TUI: error opening terminal [TERM=%s]"),
    396 		 gdb_getenv_term ());
    397 	}
    398       w = stdscr;
    399       if (has_colors ())
    400 	{
    401 #ifdef HAVE_USE_DEFAULT_COLORS
    402 	  /* Ncurses extension to help with resetting to the default
    403 	     color.  */
    404 	  use_default_colors ();
    405 #endif
    406 	  start_color ();
    407 	}
    408 
    409       /* Check required terminal capabilities.  The MinGW port of
    410 	 ncurses does have them, but doesn't expose them through "cup".  */
    411 #ifndef __MINGW32__
    412       cap = tigetstr ((char *) "cup");
    413       if (cap == NULL || cap == (char *) -1 || *cap == '\0')
    414 	{
    415 	  endwin ();
    416 	  delscreen (s);
    417 	  error (_("Cannot enable the TUI: "
    418 		   "terminal doesn't support cursor addressing [TERM=%s]"),
    419 		 gdb_getenv_term ());
    420 	}
    421 #endif
    422 
    423       cbreak ();
    424       noecho ();
    425       /* timeout (1); */
    426       nodelay(w, FALSE);
    427       nl();
    428       keypad (w, TRUE);
    429       tui_set_term_height_to (LINES);
    430       tui_set_term_width_to (COLS);
    431       def_prog_mode ();
    432 
    433       tui_show_frame_info (0);
    434       tui_set_initial_layout ();
    435       tui_set_win_focus_to (TUI_SRC_WIN);
    436       keypad (TUI_CMD_WIN->handle.get (), TRUE);
    437       wrefresh (TUI_CMD_WIN->handle.get ());
    438       tui_finish_init = false;
    439     }
    440   else
    441     {
    442      /* Save the current gdb setting of the terminal.
    443         Curses will restore this state when endwin() is called.  */
    444      def_shell_mode ();
    445      clearok (stdscr, TRUE);
    446    }
    447 
    448   if (tui_update_variables ())
    449     tui_rehighlight_all ();
    450 
    451   tui_setup_io (1);
    452 
    453   tui_active = true;
    454 
    455   /* Resize windows before anything might display/refresh a
    456      window.  */
    457   if (tui_win_resized ())
    458     {
    459       tui_set_win_resized_to (false);
    460       tui_resize_all ();
    461     }
    462 
    463   if (deprecated_safe_get_selected_frame ())
    464     tui_show_frame_info (deprecated_safe_get_selected_frame ());
    465   else
    466     tui_display_main ();
    467 
    468   /* Install the TUI specific hooks.  This must be done after the call to
    469      tui_display_main so that we don't detect the symtab changed event it
    470      can cause.  */
    471   tui_install_hooks ();
    472   rl_startup_hook = tui_rl_startup_hook;
    473 
    474   /* Restore TUI keymap.  */
    475   tui_set_key_mode (tui_current_key_mode);
    476 
    477   /* Refresh the screen.  */
    478   tui_refresh_all_win ();
    479 
    480   /* Update gdb's knowledge of its terminal.  */
    481   gdb_save_tty_state ();
    482   tui_update_gdb_sizes ();
    483 }
    484 
    485 /* Leave the tui mode.
    486    Remove the tui hooks and configure the gdb output and readline
    487    back to their original state.  The curses mode is left so that
    488    the terminal setting is restored to the point when we entered.  */
    489 void
    490 tui_disable (void)
    491 {
    492   if (!tui_active)
    493     return;
    494 
    495   /* Restore initial readline keymap.  */
    496   rl_set_keymap (tui_readline_standard_keymap);
    497 
    498   /* Remove TUI hooks.  */
    499   tui_remove_hooks ();
    500   rl_startup_hook = 0;
    501   rl_already_prompted = 0;
    502 
    503   /* Leave curses and restore previous gdb terminal setting.  */
    504   endwin ();
    505 
    506   /* gdb terminal has changed, update gdb internal copy of it
    507      so that terminal management with the inferior works.  */
    508   tui_setup_io (0);
    509 
    510   /* Update gdb's knowledge of its terminal.  */
    511   gdb_save_tty_state ();
    512 
    513   tui_active = false;
    514   tui_update_gdb_sizes ();
    515 }
    516 
    517 /* Command wrapper for enabling tui mode.  */
    518 
    519 static void
    520 tui_enable_command (const char *args, int from_tty)
    521 {
    522   tui_enable ();
    523 }
    524 
    525 /* Command wrapper for leaving tui mode.  */
    526 
    527 static void
    528 tui_disable_command (const char *args, int from_tty)
    529 {
    530   tui_disable ();
    531 }
    532 
    533 void
    534 tui_show_assembly (struct gdbarch *gdbarch, CORE_ADDR addr)
    535 {
    536   tui_suppress_output suppress;
    537   tui_add_win_to_layout (DISASSEM_WIN);
    538   tui_update_source_windows_with_addr (gdbarch, addr);
    539 }
    540 
    541 bool
    542 tui_is_window_visible (enum tui_win_type type)
    543 {
    544   if (!tui_active)
    545     return false;
    546 
    547   if (tui_win_list[type] == 0)
    548     return false;
    549 
    550   return tui_win_list[type]->is_visible ();
    551 }
    552 
    553 bool
    554 tui_get_command_dimension (unsigned int *width,
    555 			   unsigned int *height)
    556 {
    557   if (!tui_active || (TUI_CMD_WIN == NULL))
    558     return false;
    559 
    560   *width = TUI_CMD_WIN->width;
    561   *height = TUI_CMD_WIN->height;
    562   return true;
    563 }
    564 
    565 void _initialize_tui ();
    566 void
    567 _initialize_tui ()
    568 {
    569   struct cmd_list_element **tuicmd;
    570 
    571   tuicmd = tui_get_cmd_list ();
    572 
    573   add_cmd ("enable", class_tui, tui_enable_command,
    574 	   _("Enable TUI display mode.\n\
    575 Usage: tui enable"),
    576 	   tuicmd);
    577   add_cmd ("disable", class_tui, tui_disable_command,
    578 	   _("Disable TUI display mode.\n\
    579 Usage: tui disable"),
    580 	   tuicmd);
    581 }
    582