/* @(#)tvcursor.c 17.1.1.1 (ES0-DMD) 01/25/02 17:36:55 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) 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., 675 Massachusetss Ave, Cambridge, MA 02139, USA. Corresponding concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .TYPE Module .IDENTIFICATION tvcursor.c .AUTHOR Francois Ochsenbein [ESO-IPG] .LANGUAGE C .KEYWORDS Terminal Independant i/o Package. .ENVIRONMENT TermWindows .COMMENTS This module includes functions to define and perform scrolling, and cursor movements. \begin{TeX} Used Capabilities: $$\begin{tabular}{|lp{30em}|} \hline {\tt cm} & Move cursor to specified position \\ {\tt ff} & Form-feed \\ {\tt ho} & Move cursor to home (\ie [0,0]) position\\ {\tt cs} & Define a scrolling region \\ {\tt sf} & Scroll forwards (up) \\ {\tt sr} & Scroll reverse (down) \\ \hline \end{tabular}$$ \end{TeX} .VERSION 1.0 07-Jul-1986: Creation .VERSION 1.1 23-Oct-1986 Flag the move to a newline, to ensure that correct attributes are set even with HP's (attributes end with end-of-line). Take care of cursor move if a scrolling region exists: reset first the scrolling region. (added tv_sr0 to do this) .VERSION 1.2 26-Nov-1986 Remove bug in tv_sr0 .VERSION 2.0 03-Dec-1986 New terminal-independant graphic characters with output buffering version. .VERSION 2.1 11-Jun-1987 Version '2'. Improvements for hard terminals. .VERSION 2.2 23-Jun-1987 Adapted to UNIX and Minimal Implementation. .VERSION 2.3 04-Dec-1987 Save and Get Cursor also save Attribute .VERSION 3.0 20-Mar-1988 Version '3' of TermWindows. .VERSION 3.1 06-Oct-1989 Be sure that tv_sr0 RESETS the scrolling region. .VERSION 3.2 23-May-1990 Hard terminals up movement does nothing... ----------------------------------------------------------------------------*/ #define DEBUG 0 /* For debugging only */ #define PM_LEVEL LEVEL_TV #define TW_MACROS 0 /* Don't use TermWindows Macros */ #define TW_import 0 #define TW_STRUCT 0 /* Do not use window Structures */ #include MID_EXTERN TERM *terms ; #define SEND(p) tv_send(p,1) #if DEBUG # define TERMTEST if(!terms->version) tv_gopen();\ tv_ed(terms) #else # define TERMTEST if(!terms->version) tv_gopen() #endif #define FINISH goto FIN #define ENTER_TV(x) static int state_buffer; \ ENTER(x); TERMTEST; \ state_buffer = tv_buffer(1); #define EXIT_TV(f) FIN: \ tv_buffer(state_buffer); \ EXIT(f) #if DEBUG MID_STATIC char ch=EOS; MID_STATIC int type=0; #endif /* Macros just to improve readibility */ #define newl new_pos[0] #define newc new_pos[1] #define oldl old_pos[0] #define oldc old_pos[1] #define curl (terms->pos[0]) #define curc (terms->pos[1]) #define diml (terms->dim[0]) #define dimc (terms->dim[1]) #define BUFS (terms->bufs) MONITOR (ZTCURSOR); /*========================================================================== * tv_cm(line, col) *==========================================================================*/ static int tv_cm(line, col) /*+++ .PURPOSE Position the cursor using direct cursor addressing .RETURNS OK / NOK (no direct addressing) .REMARKS NO DELAY IS SENT HERE (for optimization). NO TEST on validy of line and col. *** For internal use ***. Not Traced. ------------------*/ int line; /* IN: Line number to move to */ int col; /* IN: Column number to move to */ { short int new_pos[2]; newl = line; newc = col; status = tv_out(BUFS, tu_format(BUFS, terms->tt_cm, new_pos)); /* tv_delay(terms->tt_cm); */ return(status); } /*========================================================================== * tv_range *==========================================================================*/ int tv_range(coo, limits) /*++++++++++++++ .PURPOSE Verify and updates the cursor position versus limits. Lines are numbered from 0 at the top of the screen; columns are numbered from 0 at the left margin of the screen. .RETURNS OK / NOK (cursor position modified) .REMARKS Not traced. ----------------*/ short int coo[2]; /* MOD: Cursor position (line, col) to verify */ short int limits[2]; /* IN: Limits of the screen */ { register int s; s = OK; if(coo[0] < 0) { coo[0] = 0; s = NOK;} if(coo[1] < 0) { coo[1] = 0; s = NOK;} if(coo[0] >= limits[0]) { coo[0] = limits[0]-1; s = NOK;} if(coo[1] >= limits[1]) { coo[1] = limits[1]-1; s = NOK;} return(s); } /*========================================================================== * tv_pos *==========================================================================*/ int tv_pos() /*++++++++++++++ .PURPOSE Returns the cursor position, as a number of characters from the top left. .RETURNS Position, in range [0, lines*cols] .REMARKS Not traced. ----------------*/ { return (terms->pos[0]*terms->dim[1] + terms->pos[1]); } /*========================================================================== * tv_cur() *==========================================================================*/ int tv_cur(saved) /*+++++++++++ .PURPOSE Restore the cursor position. .RETURNS OK .REMARKS -----------------------------------------------------------------------*/ TVSAVE *saved; /* IN: Old position + attribute */ { ENTER_TV("tv_cur"); status = tv_goto(saved->pos[0], saved->pos[1]); tv_attr(saved->attr); FINISH; EXIT_TV(status); } /*========================================================================== * tv_cus() *==========================================================================*/ int tv_cus(saved) /*++++++++++++++ .PURPOSE Save the current cursor position. .RETURNS OK .REMARKS Not traced. ---------------*/ TVSAVE *saved; /* OUT: Current position + attribute */ { saved->pos[0] = terms->pos[0]; saved->pos[1] = terms->pos[1]; saved->attr = terms->attr; return(OK); } /*========================================================================== * tv_agoto *==========================================================================*/ int tv_agoto(pos) /*+++++++++++ .PURPOSE Position the cursor to a specified byte position. Position are numbered from 0 at the top left corner to N-1 at bottom right corner. .RETURNS Actual position .REMARKS ------------*/ int pos; /* IN: Position to move to */ { int J, N, n; J = terms->dim[1]; N = J * terms->dim[0]; n = MAX(pos, 0); if (n >= N) n = N-1; tv_goto(n/J, n%J); return(n); } /*========================================================================== * tv_goto *==========================================================================*/ int tv_goto(line, col) /*+++++++++++ .PURPOSE Position the cursor to a specified line and column position on the screen. Lines are numbered from 0 at the top of the screen; columns are numbered from 0 at the left margin of the screen. If full screen is not asked for, no test is performed on the validity of line and column. .RETURNS OK / NOK (not possible, e.g. up on a hard terminal) .REMARKS An attempt to position the cursor off the screen will place it on an edge. NO DELAY IS SENT HERE. (for optimization) ------------*/ int line; /* IN: Line number to move to */ int col; /* IN: Column number to move to */ { short int new_pos[2]; short int old_pos[2]; register int n; ENTER_TV("tv_goto"); status = OK; tv_where(old_pos); /* Save current position */ newl = line; newc = col; tv_range(new_pos, terms->dim); /* Verify cursor within screen */ #if DEBUG TRACE_ED_I("Old line ",oldl); TRACE_ED_I(" ... col ",oldc); TRACE_ED_I("Moving to line ",line); TRACE_ED_I(" ... col ",col); #endif /* Optimize for move on the same line */ if (newl == oldl) { n = newc - oldc; /* Displacement */ if ( n == 0 ) FINISH; /* No move */ if ( (n > -3) && (n < 3) ) { status = tv_mvc(_RIGHT_, n); FINISH; } } else terms->standout |= ChangedLine; if ( (newl < terms->scregion[0]) || (newl > terms->scregion[1]) ) tv_sr0(); /* Reset scrolling region */ /* Optimize for a move to the left margin */ if (newc == 0 ) { if (oldl == newl) { status = tv_out("\r",1); curc = 0; FINISH; } if (newl == (oldl+1)) { status = tv_nl(); FINISH; } } /* Cursor addressing capability MUST exist in Minimal Implementation */ #if (TW_LEVEL == 0) status = tv_cm(newl, newc); curl = newl; curc = newc; #else if (*terms->tt_cm) { status = tv_cm(newl, newc); curl = newl; curc = newc; FINISH; } /* Unknown terminal: use right-left, up-down mouvements --- * but Optimize for move on the same line to the right */ if (newl == oldl) { n = newc - oldc; /* Displacement */ if ( n > 0 ) { status = tv_mvc(_RIGHT_, n); FINISH; } } status = tv_out("\r",1); /* Go first to left margin */ curc = 0; oldc = 0; n = newl - oldl; /* ... then move down ... */ while(n>0) { tv_nl(); n--; } /* ... move up if required... */ if(n) status = tv_mvc(_UP_, -n); /* ... finally, move right */ status = tv_mvc(_RIGHT_, newc); #endif EXIT_TV(status); } /*====================================================================== * tv_home *======================================================================*/ int tv_home() /*+++ .PURPOSE Move the cursor to home position .RETURNS OK .REMARKS Minimal implementation: use goto(0,0) ---*/ { #if (TW_LEVEL > 0) MID_REGISTER char *p; #endif ENTER_TV("tv_home"); if (terms->scregion[0]) /* Reset scrolling region */ tv_sr0(); #if (TW_LEVEL == 0) /* Minimal Implementation */ status = tv_cm(0,0); curl = 0; curc = 0; #else /* --Full Implementation */ /* Look if "ho" possibility exists */ p = SearchCap("ho"); if (p) { status = SEND(p); curl = 0; curc = 0; FINISH; } /* If up movements not available, simply issue a newline */ if(*terms->tt_ptr[_UP_] == 0) tv_nl(); /* Use up movements . Note that zt_mvc takes care of the * existance or not of up movements. */ tv_out("\r",1); /* Go first to left margin */ curc = 0; status = tv_mvc(_UP_, curl); #endif EXIT_TV(status); } /*====================================================================== * tv_mvc *======================================================================*/ int tv_mvc(direction,times) /*+++ .PURPOSE Move the cursor n times in the specified direction: up, down, right, left or newline. .RETURNS OK / NOK (not possible, e.g. up on a hard terminal) .REMARKS A negative number of times asks for the opposite direction. If full screen is not asked for, no test on the number of of cursor mouvements is performed. --- Minimal Implementation: cursor movements ALWAYS exist. ---*/ int direction; /* IN: The direction of the displacement */ int times; /* IN: Number of times to move */ { short int old_pos[2]; MID_RSTATIC short int step[2] = {-1, 1}; int k, n, init_attr; ENTER_TV("tv_mvc"); #if DEBUG TRACE_ED_I("Moving to direction: ",direction); TRACE_ED_I(".... times: ",times); #endif status = OK; if (times == 0) FINISH; tv_where(old_pos); /* Save current position */ init_attr = terms->attr_init; tv_range(terms->pos, terms->dim); /* Test the cursor position */ k = direction >> 1; /* Lead to Horiz., Vert. or */ switch(k) { case _VERTICAL_: terms->standout |= ChangedLine; case _HORIZONTAL_: n = times * step[direction & 1]; terms->pos[k] += n; tv_range(terms->pos, terms->dim); if (k == _HORIZONTAL_) n = curc - oldc; break; default: /* _NEWLINE_ is assumed */ if ( (n = times) < 0) FINISH; while (--n>=0) tv_nl(); FINISH; } k <<= 1; /* Final Direction */ if (n < 0) n = -n; else k++; #if (TW_LEVEL > 0) /* ==> Full Implementation */ switch(k) { case _UP_ : /* New line for hard terminals: Nothing */ if(*terms->tt_ptr[_UP_] == 0) { /* tv_nl(); */ status = NOK; FINISH; } break; case _DOWN_ : /* Use newlines if cap' doesn't exist */ if (*terms->tt_ptr[_DOWN_] == 0) { curl = oldl; curc = oldc; while (--n >= 0) tv_nl(); n = oldc; curc = n; k = _RIGHT_; } break; case _LEFT_ : /* When capability doesn't exist, use \r * then move to the right */ if (*terms->tt_ptr[_LEFT_] == 0) { tv_out("\r", 1); n = curc; /* Blanks to move */ k = _RIGHT_; /* Next op. is RIGHT */ } else break; case _RIGHT_: /* Test if blank is used --- if so, don't * forget to use standard attribute ! */ if (*(terms->tt_ptr[_RIGHT_] + 4) == ' ') init_attr = terms->attr, tv_attr((int)terms->attr_init); break; } #endif /* Send n times the sequence */ status = tv_send(terms->tt_ptr[k], n); if (terms->attr_init != init_attr) /* If blanks used for _RIGHT_ */ tv_attr(init_attr); EXIT_TV(status); } /*========================================================================== * tv_nl *==========================================================================*/ int tv_nl() /*+++++++++++++++ .PURPOSE New line, i.e. position the cursor to the beginning of the next line. .RETURNS OK .REMARKS The cursor position is at the beginning of the next line. The insert mode is switched off. -----------*/ { register char *p; unsigned char old_attr; ENTER_TV("tv_nl"); status = OK; #if (TW_LEVEL > 0) /* First, switch off the insert mode if set */ tv_imode(0); #endif curc = 0; /* The beginning of a line */ p = terms->tt_ptr[_NEWLINE_]; old_attr = terms->attr; tv_attr(terms->attr_init); curl++; if (curl >= diml) curl = diml -1; status = SEND(p); tv_attr(old_attr); FINISH; EXIT_TV(status); } /*====================================================================== * tv_scroll *======================================================================*/ int tv_scroll(direction, times) /*+++ .PURPOSE Scroll n lines in the specified (_UP_ or _DOWN_) direction. .RETURNS OK / NOK (always NOK in minimal implementation) .REMARKS A negative number of times asks for the opposite direction. _UP_ is the default. ---*/ int direction; /* IN: The direction of the displacement */ int times; /* IN: Number of lines to scroll */ { #if (TW_LEVEL == 0) /* ===> Minimal Implementation */ return(NOK); #else /* ===> FULL Implementation */ int dir, n; char *p; TVSAVE saved; ENTER_TV("tv_scroll"); #if DEBUG TRACE_ED_I("Scrolling lines: ",times); tv_getc(&ch,&type); #endif status = OK; if (times == 0) FINISH; dir = direction & 1; n = times; if (times < 0) /* Change direction */ { dir = 1^direction; n = -times; } tv_cus(&saved); /* Save cursor position */ /* If no scrolling capability exist: move to top or bottom of screen, and use - down mouvements to scroll up - insert lines to scroll down */ if (dir == _DOWN_) { tv_home(); p = "sr"; p = SearchCap(p); if (p) status = tv_send(p,n); else status = tv_il(n); } else { tv_goto(terms->dim[0]-1, 0); p = "sf"; p = SearchCap(p); if (p) status = tv_send(p,n); else status = tv_send(terms->tt_ptr[_DOWN_], n); } tv_cur(&saved); /* Restore Cursor */ EXIT_TV(status); #endif } /*========================================================================== * tv_sr *==========================================================================*/ int tv_sr(l1, l2) /*+++++++++++++ .PURPOSE Defines a scrolling region on the terminal between lines l1 and l2. Lines are numbered from 0 at the top of the screen. .RETURNS OK, or NOK (capability does not exist) .REMARKS On return, the cursor is at the left margin within the scrolling region. It does not move if NOK is returned, or if the scrolling region did not change. ----------------*/ int l1; /* IN: Upper line of the scrolling region */ int l2; /* IN: Lower line of the scrolling region */ { register char *p; short int scroll_lim[2]; ENTER_TV("tv_sr"); status = OK; p = SearchCap("cs"); /* Null pointer if terminal doesn't allow to define scrolling regions */ if_not(p) { status = NOK; terms->scregion[0] = 0; terms->scregion[1] = diml-1; FINISH; } scroll_lim[0] = MIN(l1,l2); scroll_lim[1] = MAX(l1,l2); if(scroll_lim[0] < 0) scroll_lim[0] = 0; if(scroll_lim[1] >= diml) scroll_lim[1] = diml-1; if ( (terms->scregion[0] != scroll_lim[0]) || (terms->scregion[1] != scroll_lim[1]) ) { if_not(status = tv_out(BUFS,tu_format(BUFS,p,scroll_lim))) FINISH; if_not(status = tv_delay(p)) FINISH; terms->scregion[0] = scroll_lim[0]; terms->scregion[1] = scroll_lim[1]; if (curl < scroll_lim[0]) curl = scroll_lim[0]; if (curl > scroll_lim[1]) curl = scroll_lim[1]; curc = 0; if (*terms->tt_cm) /* If cursor addressing exists, use it */ tv_cm(curl, 0); } EXIT_TV(status); } /*========================================================================== * tv_sr0 *==========================================================================*/ int tv_sr0() /*+++++++++++++++ .PURPOSE Redefines the complete screen as the scrolling region. .RETURNS OK / NOK (no change in scrolling region) .REMARKS On return, cursor is at home position if OK returned, and remains if NOK. ----------------*/ { ENTER_TV("tv_sr0"); /* Set the current scrolling region to (0,0) to force a change in the tv_sr routine */ terms->scregion[0] = 0, terms->scregion[1] = 0; status = tv_sr(0, diml-1); FINISH; EXIT_TV(status); } /*========================================================================== * tv_where *==========================================================================*/ int tv_where(coo) /*+++++++++++ .PURPOSE Returns the current cursor position. .RETURNS OK .REMARKS The position is adjusted within the screen limits. Not traced. ------------*/ short int coo[2]; /* OUT: current position: line (0=top), col (0=left) */ { coo[0] = curl; coo[1] = curc; /* if (terms->flags & TERM_am); else tv_range(coo, terms->dim); */ tv_range(coo, terms->dim); return(OK); }