1/* General functions for the WDB TUI.
2
3   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
4   Foundation, Inc.
5
6   Contributed by Hewlett-Packard Company.
7
8   This file is part of GDB.
9
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or
13   (at your option) any later version.
14
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 59 Temple Place - Suite 330,
23   Boston, MA 02111-1307, USA.  */
24
25#include "defs.h"
26#include "gdbcmd.h"
27#include "tui/tui.h"
28#include "tui/tui-hooks.h"
29#include "tui/tui-data.h"
30#include "tui/tui-layout.h"
31#include "tui/tui-io.h"
32#include "tui/tui-regs.h"
33#include "tui/tui-stack.h"
34#include "tui/tui-win.h"
35#include "tui/tui-winsource.h"
36#include "tui/tui-windata.h"
37#include "target.h"
38#include "frame.h"
39#include "breakpoint.h"
40#include "inferior.h"
41#include "symtab.h"
42#include "source.h"
43
44#include <stdio.h>
45#include <stdlib.h>
46#include <ctype.h>
47#ifdef HAVE_TERM_H
48#include <term.h>
49#endif
50#include <signal.h>
51#include <fcntl.h>
52#if 0
53#include <termio.h>
54#endif
55#include <setjmp.h>
56
57#include "gdb_curses.h"
58
59/* This redefines CTRL if it is not already defined, so it must come
60   after terminal state releated include files like <term.h> and
61   "gdb_ncurses.h".  */
62#include "readline/readline.h"
63
64/* Tells whether the TUI is active or not.  */
65int tui_active = 0;
66static int tui_finish_init = 1;
67
68enum tui_key_mode tui_current_key_mode = TUI_COMMAND_MODE;
69
70struct tui_char_command
71{
72  unsigned char key;
73  const char* cmd;
74};
75
76/* Key mapping to gdb commands when the TUI is using the single key mode.  */
77static const struct tui_char_command tui_commands[] = {
78  { 'c', "continue" },
79  { 'd', "down" },
80  { 'f', "finish" },
81  { 'n', "next" },
82  { 'r', "run" },
83  { 's', "step" },
84  { 'u', "up" },
85  { 'v', "info locals" },
86  { 'w', "where" },
87  { 0, 0 },
88};
89
90static Keymap tui_keymap;
91static Keymap tui_readline_standard_keymap;
92
93/* TUI readline command.
94   Switch the output mode between TUI/standard gdb.  */
95static int
96tui_rl_switch_mode (int notused1, int notused2)
97{
98  if (tui_active)
99    {
100      tui_disable ();
101      rl_prep_terminal (0);
102    }
103  else
104    {
105      rl_deprep_terminal ();
106      tui_enable ();
107    }
108
109  /* Clear the readline in case switching occurred in middle of something.  */
110  if (rl_end)
111    rl_kill_text (0, rl_end);
112
113  /* Since we left the curses mode, the terminal mode is restored to
114     some previous state.  That state may not be suitable for readline
115     to work correctly (it may be restored in line mode).  We force an
116     exit of the current readline so that readline is re-entered and it
117     will be able to setup the terminal for its needs.  By re-entering
118     in readline, we also redisplay its prompt in the non-curses mode.  */
119  rl_newline (1, '\n');
120
121  /* Make sure the \n we are returning does not repeat the last command.  */
122  dont_repeat ();
123  return 0;
124}
125
126/* TUI readline command.
127   Change the TUI layout to show a next layout.
128   This function is bound to CTRL-X 2.  It is intended to provide
129   a functionality close to the Emacs split-window command.  We always
130   show two windows (src+asm), (src+regs) or (asm+regs).  */
131static int
132tui_rl_change_windows (int notused1, int notused2)
133{
134  if (!tui_active)
135    tui_rl_switch_mode (0/*notused*/, 0/*notused*/);
136
137  if (tui_active)
138    {
139      enum tui_layout_type new_layout;
140      enum tui_register_display_type regs_type = TUI_UNDEFINED_REGS;
141
142      new_layout = tui_current_layout ();
143
144      /* Select a new layout to have a rolling layout behavior
145	 with always two windows (except when undefined).  */
146      switch (new_layout)
147	{
148	case SRC_COMMAND:
149	  new_layout = SRC_DISASSEM_COMMAND;
150	  break;
151
152	case DISASSEM_COMMAND:
153	  new_layout = SRC_DISASSEM_COMMAND;
154	  break;
155
156	case SRC_DATA_COMMAND:
157	  new_layout = SRC_DISASSEM_COMMAND;
158	  break;
159
160	case SRC_DISASSEM_COMMAND:
161	  new_layout = DISASSEM_DATA_COMMAND;
162	  break;
163
164	case DISASSEM_DATA_COMMAND:
165	  new_layout = SRC_DATA_COMMAND;
166	  break;
167
168	default:
169	  new_layout = SRC_COMMAND;
170	  break;
171	}
172      tui_set_layout (new_layout, regs_type);
173    }
174  return 0;
175}
176
177/* TUI readline command.
178   Delete the second TUI window to only show one.  */
179static int
180tui_rl_delete_other_windows (int notused1, int notused2)
181{
182  if (!tui_active)
183    tui_rl_switch_mode (0/*notused*/, 0/*notused*/);
184
185  if (tui_active)
186    {
187      enum tui_layout_type new_layout;
188      enum tui_register_display_type regs_type = TUI_UNDEFINED_REGS;
189
190      new_layout = tui_current_layout ();
191
192      /* Kill one window.  */
193      switch (new_layout)
194	{
195	case SRC_COMMAND:
196	case SRC_DATA_COMMAND:
197	case SRC_DISASSEM_COMMAND:
198	default:
199	  new_layout = SRC_COMMAND;
200	  break;
201
202	case DISASSEM_COMMAND:
203	case DISASSEM_DATA_COMMAND:
204	  new_layout = DISASSEM_COMMAND;
205	  break;
206	}
207      tui_set_layout (new_layout, regs_type);
208    }
209  return 0;
210}
211
212/* TUI readline command.
213   Switch the active window to give the focus to a next window.  */
214static int
215tui_rl_other_window (int count, int key)
216{
217  struct tui_win_info * win_info;
218
219  if (!tui_active)
220    tui_rl_switch_mode (0/*notused*/, 0/*notused*/);
221
222  win_info = tui_next_win (tui_win_with_focus ());
223  if (win_info)
224    {
225      tui_set_win_focus_to (win_info);
226      if (TUI_DATA_WIN && TUI_DATA_WIN->generic.is_visible)
227        tui_refresh_data_win ();
228      keypad (TUI_CMD_WIN->generic.handle, (win_info != TUI_CMD_WIN));
229    }
230  return 0;
231}
232
233/* TUI readline command.
234   Execute the gdb command bound to the specified key.  */
235static int
236tui_rl_command_key (int count, int key)
237{
238  int i;
239
240  reinitialize_more_filter ();
241  for (i = 0; tui_commands[i].cmd; i++)
242    {
243      if (tui_commands[i].key == key)
244        {
245          /* Must save the command because it can be modified
246             by execute_command.  */
247          char* cmd = alloca (strlen (tui_commands[i].cmd) + 1);
248          strcpy (cmd, tui_commands[i].cmd);
249          execute_command (cmd, TRUE);
250          return 0;
251        }
252    }
253  return 0;
254}
255
256/* TUI readline command.
257   Temporarily leave the TUI SingleKey mode to allow editing
258   a gdb command with the normal readline.  Once the command
259   is executed, the TUI SingleKey mode is installed back.  */
260static int
261tui_rl_command_mode (int count, int key)
262{
263  tui_set_key_mode (TUI_ONE_COMMAND_MODE);
264  return rl_insert (count, key);
265}
266
267/* TUI readline command.
268   Switch between TUI SingleKey mode and gdb readline editing.  */
269static int
270tui_rl_next_keymap (int notused1, int notused2)
271{
272  if (!tui_active)
273    tui_rl_switch_mode (0/*notused*/, 0/*notused*/);
274
275  tui_set_key_mode (tui_current_key_mode == TUI_COMMAND_MODE
276                    ? TUI_SINGLE_KEY_MODE : TUI_COMMAND_MODE);
277  return 0;
278}
279
280/* Readline hook to redisplay ourself the gdb prompt.
281   In the SingleKey mode, the prompt is not printed so that
282   the command window is cleaner.  It will be displayed if
283   we temporarily leave the SingleKey mode.  */
284static int
285tui_rl_startup_hook (void)
286{
287  rl_already_prompted = 1;
288  if (tui_current_key_mode != TUI_COMMAND_MODE)
289    tui_set_key_mode (TUI_SINGLE_KEY_MODE);
290  tui_redisplay_readline ();
291  return 0;
292}
293
294/* Change the TUI key mode by installing the appropriate readline keymap.  */
295void
296tui_set_key_mode (enum tui_key_mode mode)
297{
298  tui_current_key_mode = mode;
299  rl_set_keymap (mode == TUI_SINGLE_KEY_MODE
300                 ? tui_keymap : tui_readline_standard_keymap);
301  tui_show_locator_content ();
302}
303
304/* Initialize readline and configure the keymap for the switching
305   key shortcut.  */
306void
307tui_initialize_readline (void)
308{
309  int i;
310  Keymap tui_ctlx_keymap;
311
312  rl_initialize ();
313
314  rl_add_defun ("tui-switch-mode", tui_rl_switch_mode, -1);
315  rl_add_defun ("gdb-command", tui_rl_command_key, -1);
316  rl_add_defun ("next-keymap", tui_rl_next_keymap, -1);
317
318  tui_keymap = rl_make_bare_keymap ();
319  tui_ctlx_keymap = rl_make_bare_keymap ();
320  tui_readline_standard_keymap = rl_get_keymap ();
321
322  for (i = 0; tui_commands[i].cmd; i++)
323    rl_bind_key_in_map (tui_commands[i].key, tui_rl_command_key, tui_keymap);
324
325  rl_generic_bind (ISKMAP, "\\C-x", (char*) tui_ctlx_keymap, tui_keymap);
326
327  /* Bind all other keys to tui_rl_command_mode so that we switch
328     temporarily from SingleKey mode and can enter a gdb command.  */
329  for (i = ' '; i < 0x7f; i++)
330    {
331      int j;
332
333      for (j = 0; tui_commands[j].cmd; j++)
334        if (tui_commands[j].key == i)
335          break;
336
337      if (tui_commands[j].cmd)
338        continue;
339
340      rl_bind_key_in_map (i, tui_rl_command_mode, tui_keymap);
341    }
342
343  rl_bind_key_in_map ('a', tui_rl_switch_mode, emacs_ctlx_keymap);
344  rl_bind_key_in_map ('a', tui_rl_switch_mode, tui_ctlx_keymap);
345  rl_bind_key_in_map ('A', tui_rl_switch_mode, emacs_ctlx_keymap);
346  rl_bind_key_in_map ('A', tui_rl_switch_mode, tui_ctlx_keymap);
347  rl_bind_key_in_map (CTRL ('A'), tui_rl_switch_mode, emacs_ctlx_keymap);
348  rl_bind_key_in_map (CTRL ('A'), tui_rl_switch_mode, tui_ctlx_keymap);
349  rl_bind_key_in_map ('1', tui_rl_delete_other_windows, emacs_ctlx_keymap);
350  rl_bind_key_in_map ('1', tui_rl_delete_other_windows, tui_ctlx_keymap);
351  rl_bind_key_in_map ('2', tui_rl_change_windows, emacs_ctlx_keymap);
352  rl_bind_key_in_map ('2', tui_rl_change_windows, tui_ctlx_keymap);
353  rl_bind_key_in_map ('o', tui_rl_other_window, emacs_ctlx_keymap);
354  rl_bind_key_in_map ('o', tui_rl_other_window, tui_ctlx_keymap);
355  rl_bind_key_in_map ('q', tui_rl_next_keymap, tui_keymap);
356  rl_bind_key_in_map ('s', tui_rl_next_keymap, emacs_ctlx_keymap);
357  rl_bind_key_in_map ('s', tui_rl_next_keymap, tui_ctlx_keymap);
358}
359
360/* Enter in the tui mode (curses).
361   When in normal mode, it installs the tui hooks in gdb, redirects
362   the gdb output, configures the readline to work in tui mode.
363   When in curses mode, it does nothing.  */
364void
365tui_enable (void)
366{
367  if (tui_active)
368    return;
369
370  /* To avoid to initialize curses when gdb starts, there is a defered
371     curses initialization.  This initialization is made only once
372     and the first time the curses mode is entered.  */
373  if (tui_finish_init)
374    {
375      WINDOW *w;
376
377      w = initscr ();
378
379      cbreak ();
380      noecho ();
381      /*timeout (1);*/
382      nodelay(w, FALSE);
383      nl();
384      keypad (w, TRUE);
385      rl_initialize ();
386      tui_set_term_height_to (LINES);
387      tui_set_term_width_to (COLS);
388      def_prog_mode ();
389
390      tui_show_frame_info (0);
391      tui_set_layout (SRC_COMMAND, TUI_UNDEFINED_REGS);
392      tui_set_win_focus_to (TUI_SRC_WIN);
393      keypad (TUI_CMD_WIN->generic.handle, TRUE);
394      wrefresh (TUI_CMD_WIN->generic.handle);
395      tui_finish_init = 0;
396    }
397  else
398    {
399     /* Save the current gdb setting of the terminal.
400        Curses will restore this state when endwin() is called.  */
401     def_shell_mode ();
402     clearok (stdscr, TRUE);
403   }
404
405  /* Install the TUI specific hooks.  */
406  tui_install_hooks ();
407  rl_startup_hook = tui_rl_startup_hook;
408
409  tui_update_variables ();
410
411  tui_setup_io (1);
412
413  tui_active = 1;
414  if (deprecated_selected_frame)
415     tui_show_frame_info (deprecated_selected_frame);
416
417  /* Restore TUI keymap.  */
418  tui_set_key_mode (tui_current_key_mode);
419  tui_refresh_all_win ();
420
421  /* Update gdb's knowledge of its terminal.  */
422  target_terminal_save_ours ();
423  tui_update_gdb_sizes ();
424}
425
426/* Leave the tui mode.
427   Remove the tui hooks and configure the gdb output and readline
428   back to their original state.  The curses mode is left so that
429   the terminal setting is restored to the point when we entered.  */
430void
431tui_disable (void)
432{
433  if (!tui_active)
434    return;
435
436  /* Restore initial readline keymap.  */
437  rl_set_keymap (tui_readline_standard_keymap);
438
439  /* Remove TUI hooks.  */
440  tui_remove_hooks ();
441  rl_startup_hook = 0;
442  rl_already_prompted = 0;
443
444  /* Leave curses and restore previous gdb terminal setting.  */
445  endwin ();
446
447  /* gdb terminal has changed, update gdb internal copy of it
448     so that terminal management with the inferior works.  */
449  tui_setup_io (0);
450
451  /* Update gdb's knowledge of its terminal.  */
452  target_terminal_save_ours ();
453
454  tui_active = 0;
455  tui_update_gdb_sizes ();
456}
457
458void
459strcat_to_buf (char *buf, int buflen, const char *item_to_add)
460{
461  if (item_to_add != (char *) NULL && buf != (char *) NULL)
462    {
463      if ((strlen (buf) + strlen (item_to_add)) <= buflen)
464	strcat (buf, item_to_add);
465      else
466	strncat (buf, item_to_add, (buflen - strlen (buf)));
467    }
468}
469
470#if 0
471/* Solaris <sys/termios.h> defines CTRL. */
472#ifndef CTRL
473#define CTRL(x)         (x & ~0140)
474#endif
475
476#define FILEDES         2
477#define CHK(val, dft)   (val<=0 ? dft : val)
478
479static void
480tui_reset (void)
481{
482  struct termio mode;
483
484  /*
485     ** reset the teletype mode bits to a sensible state.
486     ** Copied tset.c
487   */
488#if ! defined (USG) && defined (TIOCGETC)
489  struct tchars tbuf;
490#endif /* !USG && TIOCGETC */
491#ifdef UCB_NTTY
492  struct ltchars ltc;
493
494  if (ldisc == NTTYDISC)
495    {
496      ioctl (FILEDES, TIOCGLTC, &ltc);
497      ltc.t_suspc = CHK (ltc.t_suspc, CTRL ('Z'));
498      ltc.t_dsuspc = CHK (ltc.t_dsuspc, CTRL ('Y'));
499      ltc.t_rprntc = CHK (ltc.t_rprntc, CTRL ('R'));
500      ltc.t_flushc = CHK (ltc.t_flushc, CTRL ('O'));
501      ltc.t_werasc = CHK (ltc.t_werasc, CTRL ('W'));
502      ltc.t_lnextc = CHK (ltc.t_lnextc, CTRL ('V'));
503      ioctl (FILEDES, TIOCSLTC, &ltc);
504    }
505#endif /* UCB_NTTY */
506#ifndef USG
507#ifdef TIOCGETC
508  ioctl (FILEDES, TIOCGETC, &tbuf);
509  tbuf.t_intrc = CHK (tbuf.t_intrc, CTRL ('?'));
510  tbuf.t_quitc = CHK (tbuf.t_quitc, CTRL ('\\'));
511  tbuf.t_startc = CHK (tbuf.t_startc, CTRL ('Q'));
512  tbuf.t_stopc = CHK (tbuf.t_stopc, CTRL ('S'));
513  tbuf.t_eofc = CHK (tbuf.t_eofc, CTRL ('D'));
514  /* brkc is left alone */
515  ioctl (FILEDES, TIOCSETC, &tbuf);
516#endif /* TIOCGETC */
517  mode.sg_flags &= ~(RAW
518#ifdef CBREAK
519		     | CBREAK
520#endif /* CBREAK */
521		     | VTDELAY | ALLDELAY);
522  mode.sg_flags |= XTABS | ECHO | CRMOD | ANYP;
523#else /*USG */
524  ioctl (FILEDES, TCGETA, &mode);
525  mode.c_cc[VINTR] = CHK (mode.c_cc[VINTR], CTRL ('?'));
526  mode.c_cc[VQUIT] = CHK (mode.c_cc[VQUIT], CTRL ('\\'));
527  mode.c_cc[VEOF] = CHK (mode.c_cc[VEOF], CTRL ('D'));
528
529  mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | INLCR | IGNCR | IUCLC | IXOFF);
530  mode.c_iflag |= (BRKINT | ISTRIP | ICRNL | IXON);
531  mode.c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | OFDEL |
532		    NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY);
533  mode.c_oflag |= (OPOST | ONLCR);
534  mode.c_cflag &= ~(CSIZE | PARODD | CLOCAL);
535#ifndef hp9000s800
536  mode.c_cflag |= (CS8 | CREAD);
537#else /*hp9000s800 */
538  mode.c_cflag |= (CS8 | CSTOPB | CREAD);
539#endif /* hp9000s800 */
540  mode.c_lflag &= ~(XCASE | ECHONL | NOFLSH);
541  mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOK);
542  ioctl (FILEDES, TCSETAW, &mode);
543#endif /* USG */
544
545  return;
546}
547#endif
548
549void
550tui_show_source (const char *file, int line)
551{
552  struct symtab_and_line cursal = get_current_source_symtab_and_line ();
553  /* make sure that the source window is displayed */
554  tui_add_win_to_layout (SRC_WIN);
555
556  tui_update_source_windows_with_line (cursal.symtab, line);
557  tui_update_locator_filename (file);
558}
559
560void
561tui_show_assembly (CORE_ADDR addr)
562{
563  tui_add_win_to_layout (DISASSEM_WIN);
564  tui_update_source_windows_with_addr (addr);
565}
566
567int
568tui_is_window_visible (enum tui_win_type type)
569{
570  if (tui_active == 0)
571    return 0;
572
573  if (tui_win_list[type] == 0)
574    return 0;
575
576  return tui_win_list[type]->generic.is_visible;
577}
578
579int
580tui_get_command_dimension (int *width, int *height)
581{
582  if (!tui_active || (TUI_CMD_WIN == NULL))
583    {
584      return 0;
585    }
586
587  *width = TUI_CMD_WIN->generic.width;
588  *height = TUI_CMD_WIN->generic.height;
589  return 1;
590}
591