1 /* $NetBSD: termout.c,v 1.14 2003/08/07 11:16:37 agc Exp $ */
4 * Copyright (c) 1988 The Regents of the University of California.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)termout.c 4.3 (Berkeley) 4/26/91";
37 __RCSID("$NetBSD: termout.c,v 1.14 2003/08/07 11:16:37 agc Exp $");
48 extern char *tgetstr(char *, char **);
56 /* Some version of this OS has a bad definition for nonl() */
60 #define nl() (_tty.sg_flags |= CRMOD,_pfast = _rawmode,stty(_tty_ch, &_tty))
61 #define nonl() (_tty.sg_flags &= ~CRMOD, _pfast = TRUE, stty(_tty_ch, &_tty))
62 #endif /* defined(ultrix) */
64 #include "../general/general.h"
68 #include "../api/disp_asc.h"
70 #include "../ctlr/hostctlr.h"
71 #include "../ctlr/declare.h"
72 #include "../ctlr/oia.h"
73 #include "../ctlr/screen.h"
74 #include "../ctlr/scrnctlr.h"
76 #include "../ascii/state.h"
77 #include "../ascii/map3270.h"
79 #include "../general/globals.h"
84 extern int TransparentClock
, OutputClock
;
86 #define CorrectTerminalCursor() ((TransparentClock == OutputClock)? \
87 CursorAddress:UnLocked? CursorAddress: HighestScreen())
90 static int terminalCursorAddress
; /* where the cursor is on term */
91 static int screenInitd
; /* the screen has been initialized */
92 static int screenStopped
; /* the screen has been stopped */
93 static int max_changes_before_poll
; /* how many characters before looking */
94 /* at terminal and net again */
96 static int needToRing
; /* need to ring terinal bell */
97 static char bellSequence
[1024]; /* bell sequence */
98 static WINDOW
*bellwin
= 0; /* The window the bell message is in */
99 int bellwinup
= 0; /* Are we up with it or not */
102 static int inHighlightMode
= 0;
103 ScreenImage Terminal
[MAXSCREENSIZE
];
105 /* Variables for transparent mode */
107 static int tcflag
= -1; /* transparent mode command flag */
108 static int savefd
[2]; /* for storing fds during transcom */
109 extern int tin
, tout
; /* file descriptors */
111 static void aborttc(int);
112 #endif /* defined(unix) */
114 static void OurExitString(char *, int);
115 static void DoARefresh(void);
116 static void GoAway(char *, int);
117 static int WhereTermAttrByte(int);
118 static void SlowScreen(void);
119 static void FastScreen(void);
121 static void ScreenOIA(OIA
*);
128 * Initialize variables used by screen.
136 ClearArray(Terminal
);
140 /* OurExitString - designed to keep us from going through infinite recursion */
143 OurExitString(string
, value
)
147 static int recursion
= 0;
151 ExitString(string
, value
);
161 if (ERR
== refresh()) {
162 OurExitString("ERR from refresh\n", 1);
168 char *from
; /* routine that gave error */
169 int where
; /* cursor address */
173 sprintf(foo
, "ERR from %s at %d (%d, %d)\n",
174 from
, where
, ScreenLine(where
), ScreenLineOffset(where
));
175 OurExitString(foo
, 1);
179 /* What is the screen address of the attribute byte for the terminal */
190 if (TermIsStartField(i
)) {
196 return(LowestScreen()); /* unformatted screen... */
200 * There are two algorithms for updating the screen.
201 * The first, SlowScreen() optimizes the line between the
202 * computer and the screen (say a 9600 baud line). To do
203 * this, we break out of the loop every so often to look
204 * at any pending input from the network (so that successive
205 * screens will only partially print until the final screen,
206 * the one the user possibly wants to see, is displayed
209 * The second algorithm tries to optimize CPU time (by
210 * being simpler) at the cost of the bandwidth to the
213 * Of course, curses(3X) gets in here also.
219 #else /* defined(NOT43) */
221 #endif /* defined(NOT43) */
224 int is
, shouldbe
, isattr
, shouldattr
;
226 int fieldattr
, termattr
;
230 #define HIGHLIGHT 1 /* Mask bits */
231 #define NONDISPLAY 4 /* Mask bits */
232 #define UNDETERMINED 8 /* Mask bits */
234 #define DoAttributes(x) \
235 switch (x&ATTR_DSPD_MASK) { \
236 case ATTR_DSPD_NONDISPLAY: \
239 case ATTR_DSPD_HIGH: \
247 # define SetHighlightMode(x) \
249 if ((x)&HIGHLIGHT) { \
250 if (!inHighlightMode) { \
251 inHighlightMode = HIGHLIGHT; \
255 if (inHighlightMode) { \
256 inHighlightMode = 0; \
262 # define DoCharacterAt(c,p) { \
263 if (p != HighestScreen()) { \
264 c = disp_asc[c&0xff]; \
265 if (terminalCursorAddress != p) { \
266 if (ERR == mvaddch(ScreenLine(p), \
267 ScreenLineOffset(p), c)) {\
268 GoAway("mvaddch", p); \
271 if (ERR == addch(c)) {\
272 GoAway("addch", p); \
275 terminalCursorAddress = ScreenInc(p); \
280 /* run through screen, printing out non-null lines */
282 /* There are two separate reasons for wanting to terminate this
283 * loop early. One is to respond to new input (either from
284 * the terminal or from the network [host]). For this reason,
285 * we expect to see 'HaveInput' come true when new input comes in.
287 * The second reason is a bit more difficult (for me) to understand.
288 * Basically, we don't want to get too far ahead of the characters that
289 * appear on the screen. Ideally, we would type out a few characters,
290 * wait until they appeared on the screen, then type out a few more.
291 * The reason for this is that the user, on seeing some characters
292 * appear on the screen may then start to type something. We would
293 * like to look at what the user types at about the same 'time'
294 * (measured by characters being sent to the terminal) that the
295 * user types them. For this reason, what we would like to do
296 * is update a bit, then call curses to do a refresh, flush the
297 * output to the terminal, then wait until the terminal data
300 * Note that curses is useful for, among other things, deciding whether
301 * or not to send :ce: (clear to end of line), so we should call curses
302 * at end of lines (beginning of next lines).
304 * The problems here are the following: If we do lots of write(2)s,
305 * we will be doing lots of context switches, thus lots of overhead
306 * (which we have already). Second, if we do a select to wait for
307 * the output to drain, we have to contend with the fact that NOW
308 * we are scheduled to run, but who knows what the scheduler will
309 * decide when the output has caught up.
312 if (Highest
>= HighestScreen()) { /* Could be > if screen shrunk... */
313 Highest
= ScreenDec(Highest
); /* else, while loop will never end */
315 if (Lowest
< LowestScreen()) {
316 Lowest
= LowestScreen(); /* could be -1 in some cases with
317 * unformatted screens.
320 if (Highest
>= (pointer
= Lowest
)) {
321 /* if there is anything to do, do it. We won't terminate
322 * the loop until we've gone at least to Highest.
324 while ((pointer
<= Highest
) && !HaveInput
) {
326 /* point at the next place of disagreement */
327 pointer
+= (bunequal(Host
+pointer
, Terminal
+pointer
,
328 (Highest
-pointer
+1)*sizeof Host
[0])/sizeof Host
[0]);
331 * How many characters to change until the end of the
334 columnsleft
= NumberColumns
- ScreenLineOffset(pointer
);
336 * Make sure we are where we think we are.
338 move(ScreenLine(pointer
), ScreenLineOffset(pointer
));
340 /* what is the field attribute of the current position */
341 if (FormattedScreen()) {
342 fieldattr
= FieldAttributes(pointer
);
343 DoAttributes(fieldattr
);
347 if (TerminalFormattedScreen()) {
348 termattr
= TermAttributes(pointer
);
349 DoAttributes(termattr
);
354 SetHighlightMode(fieldattr
);
356 * The following will terminate at least when we get back
357 * to the original 'pointer' location (since we force
358 * things to be equal).
361 if (IsStartField(pointer
)) {
362 shouldbe
= DISP_BLANK
;
364 fieldattr
= GetHost(pointer
);
365 DoAttributes(fieldattr
);
367 if (fieldattr
&NONDISPLAY
) {
368 shouldbe
= DISP_BLANK
;
370 shouldbe
= GetHost(pointer
);
372 shouldattr
= fieldattr
;
374 if (TermIsStartField(pointer
)) {
377 termattr
= UNDETERMINED
; /* Need to find out AFTER update */
379 if (termattr
&NONDISPLAY
) {
382 is
= GetTerminal(pointer
);
386 if ((shouldbe
== is
) && (shouldattr
== isattr
)
387 && (GetHost(pointer
) == GetTerminal(pointer
))
388 && (GetHost(ScreenInc(pointer
))
389 == GetTerminal(ScreenInc(pointer
)))) {
393 if (shouldattr
^inHighlightMode
) {
394 SetHighlightMode(shouldattr
);
397 DoCharacterAt(shouldbe
, pointer
);
398 if (IsStartField(pointer
)) {
399 TermNewField(pointer
, FieldAttributes(pointer
));
400 termattr
= GetTerminal(pointer
);
401 DoAttributes(termattr
);
403 SetTerminal(pointer
, GetHost(pointer
));
405 * If this USED to be a start field location,
406 * recompute the terminal attributes.
408 if (termattr
== UNDETERMINED
) {
409 termattr
= WhereTermAttrByte(pointer
);
410 if ((termattr
!= 0) || TermIsStartField(0)) {
411 termattr
= GetTerminal(termattr
);
412 DoAttributes(termattr
);
413 } else { /* Unformatted screen */
418 pointer
= ScreenInc(pointer
);
419 if (!(--columnsleft
)) {
422 if (HaveInput
) { /* if input came in, take it */
426 * We need to start a new terminal field
427 * at this location iff the terminal attributes
428 * of this location are not what we have had
429 * them as (ie: we've overwritten the terminal
430 * start field, a the previous field had different
431 * display characteristics).
434 isattr
= TermAttributes(pointer
);
435 DoAttributes(isattr
);
436 if ((!TermIsStartField(pointer
)) &&
437 (isattr
!= termattr
)) {
439 * Since we are going to leave a new field
440 * at this terminal position, we
441 * need to make sure that we get an actual
442 * non-highlighted blank on the screen.
444 if ((is
!= DISP_BLANK
) || (termattr
&HIGHLIGHT
)) {
445 SetHighlightMode(0); /* Turn off highlight */
446 c
= ScreenInc(pointer
);
450 if (termattr
&HIGHLIGHT
) {
451 termattr
= ATTR_DSPD_HIGH
;
452 } else if (termattr
&NONDISPLAY
) {
453 termattr
= ATTR_DSPD_NONDISPLAY
;
457 TermNewField(pointer
, termattr
);
461 move(ScreenLine(pointer
), 0);
462 columnsleft
= NumberColumns
;
464 } /* end of for (;;) */
465 } /* end of while (...) */
469 if (Lowest
> Highest
) { /* if we finished input... */
470 Lowest
= HighestScreen()+1;
471 Highest
= LowestScreen()-1;
472 terminalCursorAddress
= CorrectTerminalCursor();
473 if (ERR
== move(ScreenLine(terminalCursorAddress
),
474 ScreenLineOffset(terminalCursorAddress
))) {
475 GoAway("move", terminalCursorAddress
);
479 StringToTerminal(bellSequence
);
483 EmptyTerminal(); /* move data along */
489 #else /* defined(NOT43) */
491 #endif /* defined(NOT43) */
496 #else /* defined(MSDOS) */
498 #endif /* defined(MSDOS) */
500 #define DoAttribute(a) if (IsHighlightedAttr(a)) { \
505 if (IsNonDisplayAttr(a)) { \
506 a = 0; /* zero == don't display */ \
508 if (!FormattedScreen()) { \
509 a = 1; /* one ==> do display on unformatted */\
511 ScreenImage
*p
, *upper
;
512 int fieldattr
; /* spends most of its time == 0 or 1 */
514 /* OK. We want to do this a quickly as possible. So, we assume we
515 * only need to go from Lowest to Highest. However, if we find a
516 * field in the middle, we do the whole screen.
518 * In particular, we separate out the two cases from the beginning.
520 if ((Highest
!= HighestScreen()) || (Lowest
!= LowestScreen())) {
523 move(ScreenLine(Lowest
), ScreenLineOffset(Lowest
));
526 if (Highest
== HighestScreen()) {
527 Highest
= ScreenDec(Highest
);
529 #endif /* !defined(MSDOS) */
530 upper
= &Host
[Highest
];
531 fieldattr
= FieldAttributes(Lowest
);
532 DoAttribute(fieldattr
); /* Set standout, non-display status */
533 columnsleft
= NumberColumns
-ScreenLineOffset(p
-Host
);
536 if (IsStartFieldPointer(p
)) { /* New field? */
537 Highest
= HighestScreen();
538 Lowest
= LowestScreen();
539 FastScreen(); /* Recurse */
541 } else if (fieldattr
) { /* Should we display? */
542 /* Display translated data */
543 addch((char)disp_asc
[GetTerminalPointer(p
)]);
545 addch(' '); /* Display a blank */
547 /* If the physical screen is larger than what we
548 * are using, we need to make sure that each line
549 * starts at the beginning of the line. Otherwise,
550 * we will just string all the lines together.
553 if (--columnsleft
== 0) {
556 move(ScreenLine(i
), 0);
557 columnsleft
= NumberColumns
;
560 } else { /* Going from Lowest to Highest */
561 unsigned char tmpbuf
[MAXNUMBERCOLUMNS
+1];
562 ScreenImage
*End
= &Host
[ScreenSize
]-1-SaveCorner
;
563 unsigned char *tmp
= tmpbuf
, *tmpend
= tmpbuf
+NumberColumns
;
565 *tmpend
= 0; /* terminate from the beginning */
568 fieldattr
= FieldAttributes(LowestScreen());
569 DoAttribute(fieldattr
); /* Set standout, non-display status */
572 if (IsStartFieldPointer(p
)) { /* New field? */
574 *tmp
++ = 0; /* close out */
575 addstr((char *)tmpbuf
);
577 tmpend
= tmpbuf
+NumberColumns
-ScreenLineOffset(p
-Host
)-1;
581 fieldattr
= FieldAttributesPointer(p
); /* Get attributes */
582 DoAttribute(fieldattr
); /* Set standout, non-display */
584 if (fieldattr
) { /* Should we display? */
585 /* Display translated data */
586 *tmp
++ = disp_asc
[GetTerminalPointer(p
)];
591 /* If the physical screen is larger than what we
592 * are using, we need to make sure that each line
593 * starts at the beginning of the line. Otherwise,
594 * we will just string all the lines together.
598 int i
= p
-Host
; /* Be sure the "p++" happened first! */
601 addstr((char *)tmpbuf
);
603 move(ScreenLine(i
), 0);
604 tmpend
= tmpbuf
+ NumberColumns
;
609 addstr((char *)tmpbuf
);
613 Lowest
= HighestScreen()+1;
614 Highest
= LowestScreen()-1;
615 terminalCursorAddress
= CorrectTerminalCursor();
616 if (ERR
== move(ScreenLine(terminalCursorAddress
),
617 ScreenLineOffset(terminalCursorAddress
))) {
618 GoAway("move", terminalCursorAddress
);
622 StringToTerminal(bellSequence
);
625 EmptyTerminal(); /* move data along */
630 /* TryToSend - send data out to user's terminal */
634 #else /* defined(NOT43) */
636 #endif /* defined(NOT43) */
637 (*TryToSend
)(void) = FastScreen
;
649 /* InitTerminal - called to initialize the screen, etc. */
661 InitMapping(); /* Go do mapping file (MAP3270) first */
662 if (!screenInitd
) { /* not initialized */
663 if (initscr() == NULL
) { /* Initialize curses to get line size */
664 ExitString("InitTerminal: Error initializing curses", 1);
667 MaxNumberLines
= LINES
;
668 MaxNumberColumns
= COLS
;
669 ClearArray(Terminal
);
670 terminalCursorAddress
= SetBufferAddress(0,0);
672 signal(SIGHUP
, (void (*)(int))abort
);
675 TryToSend
= FastScreen
;
678 speed
= cfgetospeed(&term
);
679 if ((speed
< 0) || (speed
> 9600)) {
680 max_changes_before_poll
= 1920;
682 max_changes_before_poll
= speed
/10;
683 if (max_changes_before_poll
< 40) {
684 max_changes_before_poll
= 40;
686 TryToSend
= SlowScreen
;
687 HaveInput
= 1; /* get signals going */
689 #endif /* defined(unix) */
696 if (tgetent(termbuf
, getenv("TERM")) == 1) {
698 if ((bsp
= tgetstr("vb", &bsp
)) == NULL
) { /* use visual bell */
700 if ((bsp
= tgetstr("bl", &bsp
)) == NULL
) {
701 strcpy (bellSequence
, "\07");
706 screenStopped
= 0; /* Not stopped */
711 /* StopScreen - called when we are going away... */
714 StopScreen(doNewLine
)
717 if (screenInitd
&& !screenStopped
) {
718 move(NumberLines
-1, 1);
726 StringToTerminal("\r\n");
729 screenStopped
= 1; /* This is stopped */
734 /* RefreshScreen - called to cause the screen to be refreshed */
739 clearok(curscr
, TRUE
);
744 /* ConnectScreen - called to reconnect to the screen */
756 /* LocalClearScreen() - clear the whole ball of wax, cheaply */
761 outputPurge(); /* flush all data to terminal */
762 clear(); /* clear in curses */
763 ClearArray(Terminal
);
765 Lowest
= HighestScreen()+1; /* everything in sync... */
766 Highest
= LowestScreen()+1;
794 if ((bellwin
= newwin(3, len
+2, LINES
/2, 0)) == NULL
) {
795 OurExitString("Error from newwin in RingBell", 1);
799 box(bellwin
, '|', '-');
800 if (wmove(bellwin
, 1, 1) == ERR
) {
801 OurExitString("Error from wmove in RingBell", 1);
804 if (waddch(bellwin
, *s
++) == ERR
) {
805 OurExitString("Error from waddch in RingBell", 1);
809 if (wrefresh(bellwin
) == ERR
) {
810 OurExitString("Error from wrefresh in RingBell", 1);
817 /* returns a 1 if no more output available (so, go ahead and block),
818 or a 0 if there is more output available (so, just poll the other
819 sources/destinations, don't block).
825 /* called just before a select to conserve IO to terminal */
826 if (!(screenInitd
||screenStopped
)) {
827 return 1; /* No output if not initialized */
829 if ((Lowest
<= Highest
) || needToRing
||
830 (terminalCursorAddress
!= CorrectTerminalCursor())) {
833 if (Lowest
> Highest
) {
834 return 1; /* no more output now */
836 return 0; /* more output for future */
841 * The following are defined to handle transparent data.
850 (void) signal(SIGCHLD
, SIG_DFL
);
851 } else if (tcflag
> 0) {
859 (void) signal(SIGCHLD
, SIG_DFL
);
861 #endif /* defined(unix) */
866 TransOut(buffer
, count
, kind
, control
)
867 unsigned char *buffer
;
869 int kind
; /* 0 or 5 */
870 int control
; /* To see if we are done */
873 extern char *transcom
;
874 int inpipefd
[2], outpipefd
[2];
875 #endif /* defined(unix) */
877 while (DoTerminalOutput() == 0) {
880 #endif /* defined(unix) */
883 if (transcom
&& tcflag
== -1) {
884 while (1) { /* go thru once */
885 if (pipe(outpipefd
) < 0) {
888 if (pipe(inpipefd
) < 0) {
891 if ((tcflag
= fork()) == 0) {
892 (void) close(outpipefd
[1]);
894 if (dup(outpipefd
[0]) < 0) {
897 (void) close(outpipefd
[0]);
898 (void) close(inpipefd
[0]);
900 if (dup(inpipefd
[1]) < 0) {
903 (void) close(inpipefd
[1]);
904 if (execl("/bin/csh", "csh", "-c", transcom
, (char *) 0)) {
908 (void) close(inpipefd
[1]);
909 (void) close(outpipefd
[0]);
915 (void) signal(SIGCHLD
, aborttc
);
924 #endif /* defined(unix) */
925 (void) DataToTerminal((char *)buffer
, count
);
926 if (control
&& (kind
== 0)) { /* Send in AID byte */
929 TransInput(1, kind
); /* Go get some data */
947 #endif /* defined(unix) */