* Copyright (c) 1988 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)termout.c 4.3 (Berkeley) 4/26/91";
/* Some version of this OS has a bad definition for nonl() */
#define nl() (_tty.sg_flags |= CRMOD,_pfast = _rawmode,stty(_tty_ch, &_tty))
#define nonl() (_tty.sg_flags &= ~CRMOD, _pfast = TRUE, stty(_tty_ch, &_tty))
#endif /* defined(ultrix) */
#include "../general/general.h"
#include "../api/disp_asc.h"
#include "../ctlr/hostctlr.h"
#include "../ctlr/externs.h"
#include "../ctlr/declare.h"
#include "../ctlr/screen.h"
#include "../ctlr/scrnctlr.h"
#include "../general/globals.h"
#define CorrectTerminalCursor() ((TransparentClock == OutputClock)? \
CursorAddress:UnLocked? CursorAddress: HighestScreen())
static int terminalCursorAddress
; /* where the cursor is on term */
static int screenInitd
; /* the screen has been initialized */
static int screenStopped
; /* the screen has been stopped */
static int max_changes_before_poll
; /* how many characters before looking */
/* at terminal and net again */
static int needToRing
; /* need to ring terinal bell */
static char *bellSequence
= "\07"; /* bell sequence (may be replaced by
* VB during initialization)
static WINDOW
*bellwin
= 0; /* The window the bell message is in */
int bellwinup
= 0; /* Are we up with it or not */
static char *myKS
, *myKE
;
#endif /* defined(unix) */
static int inHighlightMode
= 0;
ScreenImage Terminal
[MAXSCREENSIZE
];
/* Variables for transparent mode */
static int tcflag
= -1; /* transparent mode command flag */
static int savefd
[2]; /* for storing fds during transcom */
extern int tin
, tout
; /* file descriptors */
#endif /* defined(unix) */
* Initialize variables used by screen.
/* OurExitString - designed to keep us from going through infinite recursion */
OurExitString(string
, value
)
static int recursion
= 0;
ExitString(string
, value
);
OurExitString("ERR from refresh\n", 1);
char *from
; /* routine that gave error */
int where
; /* cursor address */
sprintf(foo
, "ERR from %s at %d (%d, %d)\n",
from
, where
, ScreenLine(where
), ScreenLineOffset(where
));
/* What is the screen address of the attribute byte for the terminal */
if (TermIsStartField(i
)) {
return(LowestScreen()); /* unformatted screen... */
* There are two algorithms for updating the screen.
* The first, SlowScreen() optimizes the line between the
* computer and the screen (say a 9600 baud line). To do
* this, we break out of the loop every so often to look
* at any pending input from the network (so that successive
* screens will only partially print until the final screen,
* the one the user possibly wants to see, is displayed
* The second algorithm tries to optimize CPU time (by
* being simpler) at the cost of the bandwidth to the
* Of course, curses(3X) gets in here also.
#else /* defined(NOT43) */
#endif /* defined(NOT43) */
register int is
, shouldbe
, isattr
, shouldattr
;
register int fieldattr
, termattr
;
register int columnsleft
;
#define HIGHLIGHT 1 /* Mask bits */
#define NONDISPLAY 4 /* Mask bits */
#define UNDETERMINED 8 /* Mask bits */
#define DoAttributes(x) \
switch (x&ATTR_DSPD_MASK) { \
case ATTR_DSPD_NONDISPLAY: \
# define SetHighlightMode(x) \
if (!inHighlightMode) { \
inHighlightMode = HIGHLIGHT; \
# define DoCharacterAt(c,p) { \
if (p != HighestScreen()) { \
if (terminalCursorAddress != p) { \
if (ERR == mvaddch(ScreenLine(p), \
ScreenLineOffset(p), c)) {\
terminalCursorAddress = ScreenInc(p); \
/* run through screen, printing out non-null lines */
/* There are two separate reasons for wanting to terminate this
* loop early. One is to respond to new input (either from
* the terminal or from the network [host]). For this reason,
* we expect to see 'HaveInput' come true when new input comes in.
* The second reason is a bit more difficult (for me) to understand.
* Basically, we don't want to get too far ahead of the characters that
* appear on the screen. Ideally, we would type out a few characters,
* wait until they appeared on the screen, then type out a few more.
* The reason for this is that the user, on seeing some characters
* appear on the screen may then start to type something. We would
* like to look at what the user types at about the same 'time'
* (measured by characters being sent to the terminal) that the
* user types them. For this reason, what we would like to do
* is update a bit, then call curses to do a refresh, flush the
* output to the terminal, then wait until the terminal data
* Note that curses is useful for, among other things, deciding whether
* or not to send :ce: (clear to end of line), so we should call curses
* at end of lines (beginning of next lines).
* The problems here are the following: If we do lots of write(2)s,
* we will be doing lots of context switches, thus lots of overhead
* (which we have already). Second, if we do a select to wait for
* the output to drain, we have to contend with the fact that NOW
* we are scheduled to run, but who knows what the scheduler will
* decide when the output has caught up.
if (Highest
>= HighestScreen()) { /* Could be > if screen shrunk... */
Highest
= ScreenDec(Highest
); /* else, while loop will never end */
if (Lowest
< LowestScreen()) {
Lowest
= LowestScreen(); /* could be -1 in some cases with
if (Highest
>= (pointer
= Lowest
)) {
/* if there is anything to do, do it. We won't terminate
* the loop until we've gone at least to Highest.
while ((pointer
<= Highest
) && !HaveInput
) {
/* point at the next place of disagreement */
pointer
+= (bunequal(Host
+pointer
, Terminal
+pointer
,
(Highest
-pointer
+1)*sizeof Host
[0])/sizeof Host
[0]);
* How many characters to change until the end of the
columnsleft
= NumberColumns
- ScreenLineOffset(pointer
);
* Make sure we are where we think we are.
move(ScreenLine(pointer
), ScreenLineOffset(pointer
));
/* what is the field attribute of the current position */
fieldattr
= FieldAttributes(pointer
);
if (TerminalFormattedScreen()) {
termattr
= TermAttributes(pointer
);
SetHighlightMode(fieldattr
);
* The following will terminate at least when we get back
* to the original 'pointer' location (since we force
if (IsStartField(pointer
)) {
fieldattr
= GetHost(pointer
);
if (fieldattr
&NONDISPLAY
) {
shouldbe
= GetHost(pointer
);
if (TermIsStartField(pointer
)) {
termattr
= UNDETERMINED
; /* Need to find out AFTER update */
if (termattr
&NONDISPLAY
) {
is
= GetTerminal(pointer
);
if ((shouldbe
== is
) && (shouldattr
== isattr
)
&& (GetHost(pointer
) == GetTerminal(pointer
))
&& (GetHost(ScreenInc(pointer
))
== GetTerminal(ScreenInc(pointer
)))) {
if (shouldattr
^inHighlightMode
) {
SetHighlightMode(shouldattr
);
DoCharacterAt(shouldbe
, pointer
);
if (IsStartField(pointer
)) {
TermNewField(pointer
, FieldAttributes(pointer
));
termattr
= GetTerminal(pointer
);
SetTerminal(pointer
, GetHost(pointer
));
* If this USED to be a start field location,
* recompute the terminal attributes.
if (termattr
== UNDETERMINED
) {
termattr
= WhereTermAttrByte(pointer
);
if ((termattr
!= 0) || TermIsStartField(0)) {
termattr
= GetTerminal(termattr
);
} else { /* Unformatted screen */
pointer
= ScreenInc(pointer
);
if (HaveInput
) { /* if input came in, take it */
* We need to start a new terminal field
* at this location iff the terminal attributes
* of this location are not what we have had
* them as (ie: we've overwritten the terminal
* start field, a the previous field had different
* display characteristics).
isattr
= TermAttributes(pointer
);
if ((!TermIsStartField(pointer
)) &&
* Since we are going to leave a new field
* at this terminal position, we
* need to make sure that we get an actual
* non-highlighted blank on the screen.
if ((is
!= DISP_BLANK
) || (termattr
&HIGHLIGHT
)) {
SetHighlightMode(0); /* Turn off highlight */
if (termattr
&HIGHLIGHT
) {
termattr
= ATTR_DSPD_HIGH
;
} else if (termattr
&NONDISPLAY
) {
termattr
= ATTR_DSPD_NONDISPLAY
;
TermNewField(pointer
, termattr
);
move(ScreenLine(pointer
), 0);
columnsleft
= NumberColumns
;
} /* end of while (...) */
if (Lowest
> Highest
) { /* if we finished input... */
Lowest
= HighestScreen()+1;
Highest
= LowestScreen()-1;
terminalCursorAddress
= CorrectTerminalCursor();
if (ERR
== move(ScreenLine(terminalCursorAddress
),
ScreenLineOffset(terminalCursorAddress
))) {
GoAway("move", terminalCursorAddress
);
StringToTerminal(bellSequence
);
EmptyTerminal(); /* move data along */
#else /* defined(NOT43) */
#endif /* defined(NOT43) */
#else /* defined(MSDOS) */
#endif /* defined(MSDOS) */
#define DoAttribute(a) if (IsHighlightedAttr(a)) { \
if (IsNonDisplayAttr(a)) { \
a = 0; /* zero == don't display */ \
if (!FormattedScreen()) { \
a = 1; /* one ==> do display on unformatted */\
int fieldattr
; /* spends most of its time == 0 or 1 */
/* OK. We want to do this a quickly as possible. So, we assume we
* only need to go from Lowest to Highest. However, if we find a
* field in the middle, we do the whole screen.
* In particular, we separate out the two cases from the beginning.
if ((Highest
!= HighestScreen()) || (Lowest
!= LowestScreen())) {
register int columnsleft
;
move(ScreenLine(Lowest
), ScreenLineOffset(Lowest
));
if (Highest
== HighestScreen()) {
Highest
= ScreenDec(Highest
);
#endif /* !defined(MSDOS) */
fieldattr
= FieldAttributes(Lowest
);
DoAttribute(fieldattr
); /* Set standout, non-display status */
columnsleft
= NumberColumns
-ScreenLineOffset(p
-Host
);
if (IsStartFieldPointer(p
)) { /* New field? */
Highest
= HighestScreen();
FastScreen(); /* Recurse */
} else if (fieldattr
) { /* Should we display? */
/* Display translated data */
addch((char)disp_asc
[GetTerminalPointer(p
)]);
addch(' '); /* Display a blank */
/* If the physical screen is larger than what we
* are using, we need to make sure that each line
* starts at the beginning of the line. Otherwise,
* we will just string all the lines together.
if (--columnsleft
== 0) {
columnsleft
= NumberColumns
;
} else { /* Going from Lowest to Highest */
unsigned char tmpbuf
[MAXNUMBERCOLUMNS
+1];
ScreenImage
*End
= &Host
[ScreenSize
]-1-SaveCorner
;
register unsigned char *tmp
= tmpbuf
, *tmpend
= tmpbuf
+NumberColumns
;
*tmpend
= 0; /* terminate from the beginning */
fieldattr
= FieldAttributes(LowestScreen());
DoAttribute(fieldattr
); /* Set standout, non-display status */
if (IsStartFieldPointer(p
)) { /* New field? */
*tmp
++ = 0; /* close out */
tmpend
= tmpbuf
+NumberColumns
-ScreenLineOffset(p
-Host
)-1;
fieldattr
= FieldAttributesPointer(p
); /* Get attributes */
DoAttribute(fieldattr
); /* Set standout, non-display */
if (fieldattr
) { /* Should we display? */
/* Display translated data */
*tmp
++ = disp_asc
[GetTerminalPointer(p
)];
/* If the physical screen is larger than what we
* are using, we need to make sure that each line
* starts at the beginning of the line. Otherwise,
* we will just string all the lines together.
int i
= p
-Host
; /* Be sure the "p++" happened first! */
tmpend
= tmpbuf
+ NumberColumns
;
Lowest
= HighestScreen()+1;
Highest
= LowestScreen()-1;
terminalCursorAddress
= CorrectTerminalCursor();
if (ERR
== move(ScreenLine(terminalCursorAddress
),
ScreenLineOffset(terminalCursorAddress
))) {
GoAway("move", terminalCursorAddress
);
StringToTerminal(bellSequence
);
EmptyTerminal(); /* move data along */
/* TryToSend - send data out to user's terminal */
#else /* defined(NOT43) */
#endif /* defined(NOT43) */
(*TryToSend
)() = FastScreen
;
/* InitTerminal - called to initialize the screen, etc. */
static int speeds
[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800,
extern void InitMapping();
InitMapping(); /* Go do mapping file (MAP3270) first */
if (!screenInitd
) { /* not initialized */
char *lotsofspace
= KSEbuffer
;
#endif /* defined(unix) */
if (initscr() == ERR
) { /* Initialize curses to get line size */
ExitString("InitTerminal: Error initializing curses", 1);
terminalCursorAddress
= SetBufferAddress(0,0);
ioctl(1, TIOCGETP
, (char *) &ourttyb
);
if ((ourttyb
.sg_ospeed
< 0) || (ourttyb
.sg_ospeed
> B9600
)) {
max_changes_before_poll
= 1920;
max_changes_before_poll
= speeds
[ourttyb
.sg_ospeed
]/10;
if (max_changes_before_poll
< 40) {
max_changes_before_poll
= 40;
HaveInput
= 1; /* get signals going */
#endif /* defined(unix) */
* By now, initscr() (in curses) has been called (from telnet.c),
* and the screen has been initialized.
/* the problem is that curses catches SIGTSTP to
* be nice, but it messes us up.
signal(SIGTSTP
, SIG_DFL
);
if ((myKS
= tgetstr("ks", &lotsofspace
)) != 0) {
if ((myKE
= tgetstr("ke", &lotsofspace
)) != 0) {
if (tgetstr("md", &lotsofspace
) && tgetstr("me", &lotsofspace
)) {
SO
= strsave(tgetstr("md", &lotsofspace
));
SE
= strsave(tgetstr("me", &lotsofspace
));
bellSequence
= VB
; /* use visual bell */
screenStopped
= 0; /* Not stopped */
/* StopScreen - called when we are going away... */
if (screenInitd
&& !screenStopped
) {
#endif /* defined(unix) */
StringToTerminal("\r\n");
screenStopped
= 1; /* This is stopped */
/* RefreshScreen - called to cause the screen to be refreshed */
/* ConnectScreen - called to reconnect to the screen */
#endif /* defined(unix) */
/* LocalClearScreen() - clear the whole ball of wax, cheaply */
outputPurge(); /* flush all data to terminal */
clear(); /* clear in curses */
Lowest
= HighestScreen()+1; /* everything in sync... */
Highest
= LowestScreen()+1;
if ((bellwin
= newwin(3, len
+2, LINES
/2, 0)) == NULL
) {
OurExitString("Error from newwin in RingBell", 1);
if (wmove(bellwin
, 1, 1) == ERR
) {
OurExitString("Error from wmove in RingBell", 1);
if (waddch(bellwin
, *s
++) == ERR
) {
OurExitString("Error from waddch in RingBell", 1);
if (wrefresh(bellwin
) == ERR
) {
OurExitString("Error from wrefresh in RingBell", 1);
/* returns a 1 if no more output available (so, go ahead and block),
or a 0 if there is more output available (so, just poll the other
sources/destinations, don't block).
/* called just before a select to conserve IO to terminal */
if (!(screenInitd
||screenStopped
)) {
return 1; /* No output if not initialized */
if ((Lowest
<= Highest
) || needToRing
||
(terminalCursorAddress
!= CorrectTerminalCursor())) {
return 1; /* no more output now */
return 0; /* more output for future */
* The following are defined to handle transparent data.
(void) signal(SIGCHLD
, SIG_DFL
);
(void) signal(SIGCHLD
, SIG_DFL
);
#endif /* defined(unix) */
TransOut(buffer
, count
, kind
, control
)
int control
; /* To see if we are done */
int inpipefd
[2], outpipefd
[2];
#endif /* defined(unix) */
while (DoTerminalOutput() == 0) {
#endif /* defined(unix) */
if (transcom
&& tcflag
== -1) {
while (1) { /* go thru once */
if (pipe(outpipefd
) < 0) {
if (pipe(inpipefd
) < 0) {
if ((tcflag
= fork()) == 0) {
(void) close(outpipefd
[1]);
if (dup(outpipefd
[0]) < 0) {
(void) close(outpipefd
[0]);
(void) close(inpipefd
[0]);
if (dup(inpipefd
[1]) < 0) {
(void) close(inpipefd
[1]);
if (execl("/bin/csh", "csh", "-c", transcom
, (char *) 0)) {
(void) close(inpipefd
[1]);
(void) close(outpipefd
[0]);
(void) signal(SIGCHLD
, (int (*)())aborttc
);
#endif /* defined(unix) */
(void) DataToTerminal((char *)buffer
, count
);
if (control
&& (kind
== 0)) { /* Send in AID byte */
extern void TransInput();
TransInput(1, kind
); /* Go get some data */
#endif /* defined(unix) */