/* TUI support I/O functions. Copyright 1998, 1999, 2000, 2001 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "defs.h" #include "terminal.h" #include "tui.h" #include "tuiData.h" #include "tuiIO.h" #include "tuiCommand.h" #include "tuiWin.h" #include /* The Solaris header files seem to provide no declaration for this at all when __STDC__ is defined. This shouldn't conflict with anything. */ extern char *tgoto (); int insert_mode = 0; /******************************************** ** LOCAL STATIC FORWARD DECLS ** ********************************************/ static void _updateCommandInfo (int); static unsigned int _tuiHandleResizeDuringIO (unsigned int); /********************************************************************************* ** PUBLIC FUNCTIONS ** *********************************************************************************/ /* ** tuiPuts_unfiltered(). ** Function to put a string to the command window ** When running in TUI mode, this is the "hook" ** for fputs_unfiltered(). That is, all debugger ** output eventually makes it's way to the bottom-level ** routine fputs_unfiltered (main.c), which (in TUI ** mode), calls tuiPuts_unfiltered(). */ void tuiPuts_unfiltered (const char *string, struct ui_file * stream) { int len = strlen (string); int i, linech; for (i = 0; i < len; i++) { if (string[i] == '\n' || string[i] == '\r') m_tuiStartNewLine; else { if ((cmdWin->detail.commandInfo.curch + 1) > cmdWin->generic.width) m_tuiStartNewLine; if (insert_mode) { mvwinsch (cmdWin->generic.handle, cmdWin->detail.commandInfo.curLine, cmdWin->detail.commandInfo.curch++, string[i]); wmove (cmdWin->generic.handle, cmdWin->detail.commandInfo.curLine, cmdWin->detail.commandInfo.curch); } else mvwaddch (cmdWin->generic.handle, cmdWin->detail.commandInfo.curLine, cmdWin->detail.commandInfo.curch++, string[i]); } } tuiRefreshWin (&cmdWin->generic); return; } /* tuiPuts_unfiltered */ /* A cover routine for tputs(). * tputs() is called from the readline package to put * out strings representing cursor positioning. * In TUI mode (non-XDB-style), tui_tputs() is called instead. * * The reason we need to hook tputs() is: * Since the output is going to curses and not to * a raw terminal, we need to intercept these special * sequences, and handle them them here. * * This function seems to be correctly handling all sequences * aimed at hpterm's, but there is additional work to do * for xterm's and dtterm's. I abandoned further work on this * in favor of "XDB style". In "XDB style", the command region * looks like terminal, not a curses window, and this routine * is not called. - RT */ void tui_tputs (str, affcnt, putfunc) char *str; int affcnt; int (*putfunc) (int); { extern char *rl_prompt; /* the prompt string */ /* This set of globals are defined and initialized * by the readline package. * * Note we're assuming tui_tputs() is being called * by the readline package. That's because we're recognizing * that a given string is being passed by * matching the string address against readline's * term_ global. To make this more general, * we'd have to actually recognize the termcap sequence * inside the string (more work than I want to do). - RT * * We don't see or need to handle every one of these here; * this is just the full list defined in readline/readline.c */ extern char *term_backspace; extern char *term_clreol; extern char *term_clrpag; extern char *term_cr; extern char *term_dc; extern char *term_ei; extern char *term_goto; extern char *term_ic; extern char *term_im; extern char *term_mm; extern char *term_mo; extern char *term_up; extern char *term_scroll_region; extern char *term_memory_lock; extern char *term_memory_unlock; extern char *term_cursor_move; extern char *visible_bell; /* Sanity check - if not TUI, just call tputs() */ if (!tui_version) tputs (str, affcnt, putfunc); /* The strings we special-case are handled first */ if (str == term_backspace) { /* Backspace. */ /* We see this on an emacs control-B. * I.e., it's like the left-arrow key (not like the backspace key). * The effect that readline wants when it transmits this * character to us is simply to back up one character * (but not to write a space over the old character). */ _updateCommandInfo (-1); wmove (cmdWin->generic.handle, cmdWin->detail.commandInfo.curLine, cmdWin->detail.commandInfo.curch); wrefresh (cmdWin->generic.handle); } else if (str == term_clreol) { /* Clear to end of line. */ wclrtoeol (cmdWin->generic.handle); wrefresh (cmdWin->generic.handle); } else if (str == term_cr) { /* Carriage return */ _updateCommandInfo (-cmdWin->detail.commandInfo.curch); wmove (cmdWin->generic.handle, cmdWin->detail.commandInfo.curLine, 0 /* readline will rewrite the prompt from 0 */ ); wrefresh (cmdWin->generic.handle); } else if (str == term_goto) { /* This is actually a tgoto() specifying a character position, * followed by either a term_IC/term_DC which [I think] means * insert/delete one character at that position. * There are complications with this one - need to either * extract the position from the string, or have a backdoor * means of communicating it from ../readline/display.c. * So this one is not yet implemented. * Not doing it seems to have no ill effects on command-line-editing * that I've noticed so far. - RT */ } else if (str == term_dc) { /* Delete character at current cursor position */ wdelch (cmdWin->generic.handle); wrefresh (cmdWin->generic.handle); } else if (str == term_im) { /* Turn on insert mode. */ insert_mode = 1; } else if (str == term_ei) { /* Turn off insert mode. */ insert_mode = 0; /* Strings we know about but don't handle * specially here are just passed along to tputs(). * * These are not handled because (as far as I can tell) * they are not actually emitted by the readline package * in the course of doing command-line editing. Some of them * theoretically could be used in the future, in which case we'd * need to handle them. */ } else if (str == term_ic || /* insert character */ str == term_cursor_move || /* cursor move */ str == term_clrpag || /* clear page */ str == term_mm || /* turn on meta key */ str == term_mo || /* turn off meta key */ str == term_up || /* up one line (not expected) */ str == term_scroll_region || /* set scroll region */ str == term_memory_lock || /* lock screen above cursor */ str == term_memory_unlock || /* unlock screen above cursor */ str == visible_bell) { /* flash screen */ tputs (str, affcnt, putfunc); } else { /* something else */ tputs (str, affcnt, putfunc); } } /* tui_tputs */ /* ** tui_vwgetch() ** Wrapper around wgetch with the window in a va_list */ unsigned int tui_vwgetch (va_list args) { unsigned int ch; WINDOW *window; window = va_arg (args, WINDOW *); return ((unsigned int) wgetch (window)); } /* tui_vwgetch */ /* ** tuiGetc(). ** Get a character from the command window. ** This is called from the readline package, ** that is, we have: ** tuiGetc() [here], called from ** readline code [in ../readline/], called from ** command_line_input() in top.c */ unsigned int tuiGetc (void) { unsigned int ch; extern char *rl_prompt; extern char *rl_line_buffer; extern int rl_point; /* Call the curses routine that reads one character */ #ifndef COMMENT ch = (unsigned int) vcatch_errors ((OpaqueFuncPtr) tui_vwgetch, cmdWin->generic.handle); #else ch = wgetch (cmdWin->generic.handle); #endif ch = _tuiHandleResizeDuringIO (ch); if (m_isCommandChar (ch)) { /* Handle prev/next/up/down here */ tuiTermSetup (0); ch = tuiDispatchCtrlChar (ch); cmdWin->detail.commandInfo.curch = strlen (rl_prompt) + rl_point; tuiTermUnsetup (0, cmdWin->detail.commandInfo.curch); } if (ch == '\n' || ch == '\r' || ch == '\f') cmdWin->detail.commandInfo.curch = 0; else tuiIncrCommandCharCountBy (1); return ch; } /* tuiGetc */ /* ** tuiBufferGetc(). */ /*elz: this function reads a line of input from the user and puts it in a static buffer. Subsequent calls to this same function obtain one char at the time, providing the caller with a behavior similar to fgetc. When the input is buffered, the backspaces have the needed effect, i.e. ignore the last char active in the buffer */ /* so far this function is called only from the query function in utils.c */ unsigned int tuiBufferGetc (void) { unsigned int ch; static unsigned char _ibuffer[512]; static int index_read = -1; static int length_of_answer = -1; int pos = 0; if (length_of_answer == -1) { /* this is the first time through, need to read the answer */ do { /* Call the curses routine that reads one character */ ch = (unsigned int) wgetch (cmdWin->generic.handle); if (ch != '\b') { _ibuffer[pos] = ch; pos++; } else pos--; } while (ch != '\r' && ch != '\n'); length_of_answer = pos; index_read = 0; } ch = _ibuffer[index_read]; index_read++; if (index_read == length_of_answer) { /*this is the last time through, reset for next query */ index_read = -1; length_of_answer = -1; } wrefresh (cmdWin->generic.handle); return (ch); } /* tuiBufferGetc */ /* ** tuiStartNewLines(). */ void tuiStartNewLines (int numLines) { if (numLines > 0) { if (cmdWin->generic.viewportHeight > 1 && cmdWin->detail.commandInfo.curLine < cmdWin->generic.viewportHeight) cmdWin->detail.commandInfo.curLine += numLines; else scroll (cmdWin->generic.handle); cmdWin->detail.commandInfo.curch = 0; wmove (cmdWin->generic.handle, cmdWin->detail.commandInfo.curLine, cmdWin->detail.commandInfo.curch); tuiRefreshWin (&cmdWin->generic); } return; } /* tuiStartNewLines */ /* ** tui_vStartNewLines(). ** With numLines in a va_list */ void tui_vStartNewLines (va_list args) { int numLines = va_arg (args, int); tuiStartNewLines (numLines); return; } /* tui_vStartNewLines */ /**************************************************************************** ** LOCAL STATIC FUNCTIONS ** *****************************************************************************/ /* ** _tuiHandleResizeDuringIO ** This function manages the cleanup when a resize has occured ** From within a call to getch() or read. Returns the character ** to return from getc or read. */ static unsigned int _tuiHandleResizeDuringIO (unsigned int originalCh) /* the char just read */ { if (tuiWinResized ()) { tuiRefreshAll (); dont_repeat (); tuiSetWinResizedTo (FALSE); rl_reset (); return '\n'; } else return originalCh; } /* _tuiHandleResizeDuringIO */ /* ** _updateCommandInfo(). ** Function to update the command window information. */ static void _updateCommandInfo (int sizeOfString) { if ((sizeOfString + cmdWin->detail.commandInfo.curch) > cmdWin->generic.width) { int newCurch = sizeOfString + cmdWin->detail.commandInfo.curch; tuiStartNewLines (1); cmdWin->detail.commandInfo.curch = newCurch - cmdWin->generic.width; } else cmdWin->detail.commandInfo.curch += sizeOfString; return; } /* _updateCommandInfo */ /* Looked at in main.c, fputs_unfiltered(), to decide * if it's safe to do standard output to the command window. */ int tui_owns_terminal = 0; /* Called to set up the terminal for TUI (curses) I/O. * We do this either on our way "in" to GDB after target * program execution, or else within tuiDo just before * going off to TUI routines. */ void tuiTermSetup (int turn_off_echo) { char *buffer; int start; int end; int endcol; extern char *term_scroll_region; extern char *term_cursor_move; extern char *term_memory_lock; extern char *term_memory_unlock; /* Turn off echoing, since the TUI does not * expect echoing. Below I only put in the TERMIOS * case, since that is what applies on HP-UX. turn_off_echo * is 1 except for the case where we're being called * on a "quit", in which case we want to leave echo on. */ if (turn_off_echo) { #ifdef HAVE_TERMIOS struct termios tio; tcgetattr (0, &tio); tio.c_lflag &= ~(ECHO); tcsetattr (0, TCSANOW, &tio); #endif } /* Compute the start and end lines of the command * region. (Actually we only use end here) */ start = winList[CMD_WIN]->generic.origin.y; end = start + winList[CMD_WIN]->generic.height - 1; endcol = winList[CMD_WIN]->generic.width - 1; if (term_memory_unlock) { /* Un-do the effect of the memory lock in terminal_inferior() */ tputs (term_memory_unlock, 1, (int (*) (int)) putchar); fflush (stdout); } else if (term_scroll_region) { /* Un-do the effect of setting scroll region in terminal_inferior() */ /* I'm actually not sure how to do this (we don't know for * sure what the scroll region was *before* we changed it), * but I'll guess that setting it to the whole screen is * the right thing. So, ... */ /* Set scroll region to be 0..end */ buffer = (char *) tgoto (term_scroll_region, end, 0); tputs (buffer, 1, (int (*) (int)) putchar); } /* else we're out of luck */ /* This is an attempt to keep the logical & physical * cursor in synch, going into curses. Without this, * curses seems to be confused by the fact that * GDB has physically moved the curser on it. One * visible effect of removing this code is that the * locator window fails to get updated and the line * of text that *should* go into the locator window * often goes to the wrong place. */ /* What's done here is to tell curses to write a ' ' * at the bottom right corner of the screen. * The idea is to wind up with the cursor in a known * place. * Note I'm relying on refresh() * only writing what changed (the space), * not the whole screen. */ standend (); move (end, endcol - 1); addch (' '); refresh (); tui_owns_terminal = 1; } /* tuiTermSetup */ /* Called to set up the terminal for target program I/O, meaning I/O * is confined to the command-window area. We also call this on our * way out of tuiDo, thus setting up the terminal this way for * debugger command I/O. */ void tuiTermUnsetup (int turn_on_echo, int to_column) { int start; int end; int curline; char *buffer; /* The next bunch of things are from readline */ extern char *term_scroll_region; extern char *term_cursor_move; extern char *term_memory_lock; extern char *term_memory_unlock; extern char *term_se; /* We need to turn on echoing, since the TUI turns it off */ /* Below I only put in the TERMIOS case, since that * is what applies on HP-UX. */ if (turn_on_echo) { #ifdef HAVE_TERMIOS struct termios tio; tcgetattr (0, &tio); tio.c_lflag |= (ECHO); tcsetattr (0, TCSANOW, &tio); #endif } /* Compute the start and end lines of the command * region, as well as the last "real" line of * the region (normally same as end, except when * we're first populating the region) */ start = winList[CMD_WIN]->generic.origin.y; end = start + winList[CMD_WIN]->generic.height - 1; curline = start + winList[CMD_WIN]->detail.commandInfo.curLine; /* We want to confine target I/O to the command region. * In order to do so, we must either have "memory lock" * (hpterm's) or "scroll regions" (xterm's). */ if (term_cursor_move && term_memory_lock) { /* Memory lock means lock region above cursor. * So first position the cursor, then call memory lock. */ buffer = tgoto (term_cursor_move, 0, start); tputs (buffer, 1, (int (*) (int)) putchar); tputs (term_memory_lock, 1, (int (*) (int)) putchar); } else if (term_scroll_region) { /* Set the scroll region to the command window */ buffer = tgoto (term_scroll_region, end, start); tputs (buffer, 1, (int (*) (int)) putchar); } /* else we can't do anything about target I/O */ /* Also turn off standout mode, in case it is on */ if (term_se != NULL) tputs (term_se, 1, (int (*) (int)) putchar); /* Now go to the appropriate spot on the end line */ buffer = tgoto (term_cursor_move, to_column, end); tputs (buffer, 1, (int (*) (int)) putchar); fflush (stdout); tui_owns_terminal = 0; } /* tuiTermUnsetup */