This is term.c in view mode; [Download] [Up]
/* Terminal control based on terminfo capabilities. Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc. Originally part of GNU Emacs. Vastly edited and modified for use within ne. Copyright (C) 1993 Sebastiano Vigna This file is part of ne, the nice editor. 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. In other words, you are welcome to use, share and improve this program. You are forbidden to forbid anyone else to use, share and improve what you give them. Help stamp out software-hoarding! */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #ifndef TERMCAP #include <curses.h> #include <term.h> #else #include "info2cap.h" #endif #include "termchar.h" #include "cm.h" /* When displaying errors about the terminal database, we try to print the correct name. */ #ifdef TERMCAP #define DATABASE_NAME "termcap" #else #define DATABASE_NAME "terminfo" #endif /* This is the real instantiation of the cm structure used by cm.c to hold the cursor motion strings. */ struct cm Wcm; /* These are the functions called from cm.c. */ void losecursor (void); int Wcm_init (void); void cmcostinit (void); void cmgoto (int row, int col); /* PORTABILITY PROBLEM: this macro is responsible for filtering nonprintable characters. On systems with a wider system character set, it could be redefined, for instance, in order to allow characters between 128 and 160 to be printed. */ #define DECONTROL(c) { if ((unsigned char)(c) >= 127 && (unsigned char)(c) < 160) c = '?'; if ((unsigned char)(c) < ' ') (c) = '@'+(unsigned char)(c); } #define OUTPUT(a) tputs (a, lines - curY, cmputc) #define OUTPUT1(a) tputs (a, 1, cmputc) #define OUTPUTL(a, lines) tputs (a, lines, cmputc) #define OUTPUT_IF(a) { if (a) tputs (a, lines - curY, cmputc); } #define OUTPUT1_IF(a) { if (a) tputs (a, 1, cmputc); } /* Terminal charateristics that higher levels want to look at. These are all extern'd in termchar.h */ int line_ins_del_ok; /* Terminal can insert and delete lines */ int char_ins_del_ok; /* Terminal can insert and delete chars */ int scroll_region_ok; /* Terminal supports setting the scroll window */ int standout_ok; /* Terminal supports standout without magic cookies */ int cursor_on_off_ok; /* Terminal can make the cursor visible or invisible */ static int RPov; /* Least number of chars to start a TS_repeat. Less wouldn't be worth. */ static int delete_in_insert_mode; /* delete mode == insert mode */ static int save_cursor_ok; /* Terminal supports save and restore of cursor position */ static int se_is_so; /* 1 if same string both enters and leaves standout mode */ static int standout_requested; /* Nonzero when supposed to write text in standout mode. */ static int insert_mode; /* Nonzero when in insert mode. */ static int standout_mode; /* Nonzero when in standout mode. */ /* Size of window specified by higher levels. This is the number of lines, starting from top of screen, to participate in ins/del line operations. Effectively it excludes the bottom lines - specified_window_size lines from those operations. */ int specified_window; /* These functions ring a bell or flash the screen. If the service is not available, the other one is tried. */ void ring_bell(void) { OUTPUT1_IF (bell ? bell : flash_screen); } void do_flash(void) { OUTPUT1_IF (flash_screen ? flash_screen : bell); } /* This function sets correctly the scroll region. Note that the cursor is lost. This function assumes scroll_region_ok == TRUE. The cursor position is lost, and from the terminfo specs. */ static void set_scroll_region (int start, int stop) { char *buf; assert(scroll_region_ok); if (save_cursor_ok) OUTPUT1(save_cursor); if (change_scroll_region) { buf = tparm (change_scroll_region, start, stop - 1); } else { buf = tparm (set_window, start, stop - 1, 0, columns - 1); } OUTPUT1(buf); if (save_cursor_ok) OUTPUT1(restore_cursor); else losecursor(); } static void turn_on_insert (void) { if (!insert_mode) OUTPUT1(enter_insert_mode); insert_mode = TRUE; } static void turn_off_insert (void) { if (insert_mode) OUTPUT1(exit_insert_mode); insert_mode = FALSE; } /* These functions are called on all terminals in order to handle highlighting, but do nothing on terminals with a magic cookie (or without standout). */ static void turn_off_highlight (void) { if (standout_ok) { if (standout_mode) OUTPUT1(exit_standout_mode); standout_mode = FALSE; } } static void turn_on_highlight (void) { if (standout_ok) { if (!standout_mode) OUTPUT1(enter_standout_mode); standout_mode = TRUE; } } /* This function prepare the terminal for interactive I/O. It initializes the terminal, prepares the cursor address mode, and activates the keypad and the meta key. */ void set_terminal_modes(void) { /* Note that presently we do not support if and iprog, the program and the file which should be used, if present, to initialize the terminal. */ OUTPUT1_IF(init_1string); OUTPUT1_IF(init_2string); OUTPUT1_IF(init_3string); OUTPUT1_IF(enter_ca_mode); OUTPUT1_IF(keypad_xmit); if (has_meta_key) OUTPUT1_IF(meta_on); losecursor(); } /* This function puts again the terminal in its normal state. */ void reset_terminal_modes (void) { turn_off_highlight (); turn_off_insert (); OUTPUT1_IF (keypad_local); OUTPUT1_IF (exit_ca_mode); } /* This function sets the variable specified_window. Following line insert/delete operations will be limited to lines 0 to (size-1). */ void set_terminal_window(int size) { specified_window = size ? size : lines; } /* These functions are the external interface to standout activation. */ void standout_on (void) { standout_requested = TRUE; } void standout_off (void) { standout_requested = FALSE; } /* These functions are the external interface to cursor on/off strings. */ void cursor_on (void) { if (cursor_on_off_ok) OUTPUT1(cursor_normal); } void cursor_off (void) { if (cursor_on_off_ok) OUTPUT1(cursor_invisible); } /* Set standout mode to the mode specified for the text to be output. */ static void highlight_if_desired (void) { if (standout_requested) turn_on_highlight (); else turn_off_highlight (); } /* Move to absolute position, specified origin 0 */ void move_cursor (int row, int col) { if (curY == row && curX == col) return; if (!move_standout_mode) turn_off_highlight (); if (!move_insert_mode) turn_off_insert (); cmgoto (row, col); } /* This function clears from the cursor position to the end of line. It assumes that the line is already clear starting at column first_unused_hpos. Note that the cursor may be moved, on terminals lacking a `ce' string. */ void clear_end_of_line(int first_unused_hpos) { int i; if (curX >= first_unused_hpos) return; if (clr_eol) { OUTPUT1 (clr_eol); } else { /* We have to do it the hard way. */ turn_off_insert (); for (i = curX; i < first_unused_hpos; i++) putchar (' '); cmplus (first_unused_hpos - curX); } } /* Shorthand; use this if you don't know anything about the state of the line. */ void clear_to_eol(void) { clear_end_of_line(columns); } /* This function clears from the cursor position to the end of screen */ void clear_to_end (void) { int i; if (clr_eos) { OUTPUT(clr_eos); } else { for (i = curY; i < lines; i++) { move_cursor (i, 0); clear_to_eol(); } } } /* This function clears the entire screen */ void clear_entire_screen (void) { if (clear_screen) { OUTPUTL(clear_screen, lines); cmat (0, 0); } else { move_cursor (0, 0); clear_to_end(); } } /* This function outputs len characters pointed at by string. The characters will be truncated to the end of the current line. Passing a NULL for string results in outputting spaces. A len of 0 causes no action. */ void output_chars (const char *string, int len) { const char *p; char *buf; int c; const char *first_check; if (len == 0) return; highlight_if_desired (); turn_off_insert (); if (curX + len > columns) len = columns - curX; /* Don't dare write in last column of bottom line, if AutoWrap, since that would scroll the whole screen on some terminals. */ if (AutoWrap && curY + 1 == lines && curX + len == columns) len--; cmplus (len); if (string == NULL) { if (len > RPov) { buf = tparm (repeat_char, ' ', len); OUTPUT1(buf); } else { while(--len >= 0) putchar(' '); } return; } first_check = string; if (RPov > len && !transparent_underline && !tilde_glitch) { while(--len >= 0) { c = *string++; DECONTROL(c); putchar(c); } } else while (--len >= 0) { c = *string; if (RPov + 1 < len && string >= first_check) { int repeat_count; p = string; /* Now, len is number of chars left starting at p */ while (++p - string <= len && *p == c); repeat_count = p - string; if (repeat_count > RPov) { buf = tparm (repeat_char, c, repeat_count); OUTPUT1(buf); string = p; len -= repeat_count - 1; continue; } else { /* If all N identical chars are too few, don't even consider the last N-1, the last N-2,... */ first_check = p; } } if (c == '_' && transparent_underline) { putchar (' '); OUTPUT1(Left); } if (tilde_glitch && c == '~') c = '`'; DECONTROL(c); putchar (c); string++; } } /* Same as output_chars(), but inserts instead. */ void insert_chars (const char *start, int len) { char *buf; int c; if (len == 0) return; highlight_if_desired (); if (parm_ich) { buf = tparm (parm_ich, len); OUTPUT1 (buf); if (start) output_chars (start, len); return; } if (curX + len > columns) len = columns - curX; /* Don't dare write in last column of bottom line, if AutoWrap, since that would scroll the whole screen on some terminals. */ if (AutoWrap && curY + 1 == lines && curX + len == columns) len--; turn_on_insert (); cmplus (len); if (!transparent_underline && !tilde_glitch && start && insert_padding == NULL && insert_character == NULL) { while(--len >= 0) { c = *start++; DECONTROL(c); putchar(c); } } else while (--len >= 0) { OUTPUT1_IF (insert_character); if (!start) c = ' '; else { c = *start++; if (tilde_glitch && c == '~') c = '`'; } DECONTROL(c); putchar (c); OUTPUT1_IF(insert_padding); } } /* This function deletes n characters at the current cursor position. */ void delete_chars (int n) { char *buf; if (delete_in_insert_mode) { turn_on_insert(); } else { turn_off_insert(); OUTPUT1_IF(enter_delete_mode); } if (parm_dch) { buf = tparm(parm_dch, n); OUTPUT1(buf); } else while(--n>=0) OUTPUT1(delete_character); if (!delete_in_insert_mode) OUTPUT_IF(exit_delete_mode); } /* This internal function will do an insertion or deletion for n lines, given a parametrized and/or a one-line capability for that purpose. */ static void do_multi_ins_del(char *multi, char *single, int n) { if (multi) { char *buf; buf = tparm(multi, n); OUTPUT(buf); } else { while(--n>=0) OUTPUT(single); } } /* This function inserts n lines at vertical position vpos. If n is negative, it deletes -n lines. specified_window is taken into account. This function assumes line_ins_del_ok == TRUE. */ void ins_del_lines (int vpos, int n) { int i = n > 0 ? n : -n; assert(line_ins_del_ok); assert(i != 0); assert(vpos < specified_window); if (scroll_region_ok && vpos + i >= specified_window) return; if (!memory_below && vpos + i >= lines) return; if (scroll_region_ok) { if (specified_window != lines) set_scroll_region(vpos, specified_window); if (n < 0) { move_cursor(specified_window - 1, 0); while (--i >= 0) OUTPUTL(scroll_forward, specified_window-vpos+1); } else { move_cursor(vpos, 0); while (--i >= 0) OUTPUTL(scroll_reverse, specified_window-vpos+1); } if (specified_window != lines) set_scroll_region(0, lines); } else { if (n > 0) { if (specified_window != lines) { move_cursor(specified_window - i, 0); do_multi_ins_del(parm_delete_line, delete_line, i); } move_cursor(vpos, 0); do_multi_ins_del(parm_insert_line, insert_line, i); } else { move_cursor(vpos, 0); do_multi_ins_del(parm_delete_line, delete_line, i); if (specified_window != lines) { move_cursor(specified_window-i, 0); do_multi_ins_del(parm_insert_line, insert_line, i); } else if (memory_below) { move_cursor(lines + n, 0); clear_to_end (); } } } } extern int cost; /* In cm.c */ extern int evalcost(int); /* This function performs the cursor motion cost setup, and sets the variable RPov to the number of characters (with padding) which are really output when repeating one character. */ static void calculate_costs (void) { if (repeat_char) { char *buf = tparm(repeat_char, ' ', 1); cost = 0; tputs(buf, 1, evalcost); RPov = cost+1; } else RPov = columns * 2; cmcostinit(); } /* This is the main terminal initialization function. It sets up Wcm, patches here and there the terminfo database, calculates the costs, and initializes the terminal characteristics variables. Note that this function can exit(). */ void term_init (void) { /* First of all we initialize the terminfo database. */ setupterm(0,1,0); ColPosition = column_address; RowPosition = row_address; AbsPosition = cursor_address; CR = carriage_return; Home = cursor_home; LastLine = cursor_to_ll; Right = cursor_right; Down = cursor_down; Left = cursor_left; Up = cursor_up; AutoWrap = auto_right_margin; MagicWrap = eat_newline_glitch; ScreenRows = lines; ScreenCols = columns; if (!bell) bell = "\07"; if (!scroll_forward) scroll_forward = Down; if (!scroll_reverse) scroll_reverse = Up; if (key_backspace && key_left && !strcmp(key_backspace, key_left)) { /* In case left and backspace produce the same sequence, we want to get key_left. */ key_backspace = NULL; } specified_window = lines; if (Wcm_init ()) { /* We can't do cursor motion */ if (generic_type) { printf("Your terminal type is a generic terminal, not a real\nterminal, and it lacks the ability to position the cursor.\nPlease check that the variable TERM is set correctly, and that\nyour " DATABASE_NAME "database is up to date.\n"); } else { printf("Your terminal type is not powerful enough to run ne:\nit lacks the ability to position the cursor.\nPlease check that the variable TERM is set correctly, and that\nyour " DATABASE_NAME "database is up to date.\n"); } resetterm(); exit(1); } calculate_costs(); delete_in_insert_mode = enter_delete_mode && enter_insert_mode && !strcmp (enter_delete_mode, enter_insert_mode); se_is_so = enter_standout_mode && exit_standout_mode && !strcmp (enter_standout_mode, exit_standout_mode); scroll_region_ok = set_window || change_scroll_region; line_ins_del_ok = (((insert_line || parm_insert_line) && (delete_line || parm_delete_line)) || (scroll_region_ok && scroll_forward && scroll_reverse)); char_ins_del_ok = ((insert_character || enter_insert_mode || insert_padding || parm_ich) && (delete_character || parm_dch)); save_cursor_ok = (save_cursor && restore_cursor); standout_ok = (enter_standout_mode && exit_standout_mode && magic_cookie_glitch < 0); cursor_on_off_ok = (cursor_invisible && cursor_normal); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.