1 /*-
2  * Copyright (c) 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)termout.c	8.1 (Berkeley) 06/06/93";
10 #endif /* not lint */
11 
12 #include <stdio.h>
13 #include <dos.h>
14 #include "../general/general.h"
15 
16 #include "../telnet.ext"
17 
18 #include "../api/disp_asc.h"
19 #include "../ascii/map3270.ext"
20 
21 #include "../ctlr/hostctlr.h"
22 #include "../ctlr/externs.h"
23 #include "../ctlr/declare.h"
24 #include "../ctlr/oia.h"
25 #include "../ctlr/screen.h"
26 
27 #include "../general/globals.h"
28 
29 #include "video.h"
30 
31 extern void EmptyTerminal();
32 
33 #define CorrectTerminalCursor() ((TransparentClock == OutputClock)? \
34 		terminalCursorAddress:UnLocked? CursorAddress: HighestScreen())
35 
36 
37 static int terminalCursorAddress;	/* where the cursor is on term */
38 static int screenInitd; 		/* the screen has been initialized */
39 static int screenStopped;		/* the screen has been stopped */
40 
41 static int needToRing;			/* need to ring terinal bell */
42 
43 typedef struct {
44     char
45 	data,		/* The data for this position */
46 	attr;		/* The attributes for this position */
47 } ScreenBuffer;
48 
49 ScreenBuffer Screen[MAXNUMBERLINES*MAXNUMBERCOLUMNS];
50 ScreenBuffer saveScreen[sizeof Screen/sizeof Screen[0]];
51 
52 /* OurExitString - designed to keep us from going through infinite recursion */
53 
54 static void
55 OurExitString(file, string, value)
56 FILE	*file;
57 char	*string;
58 int	value;
59 {
60     static int recursion = 0;
61 
62     if (!recursion) {
63 	recursion = 1;
64 	ExitString(file, string, value);
65     }
66 }
67 
68 
69 static void
70 GoAway(from, where)
71 char *from;		/* routine that gave error */
72 int	where;		/* cursor address */
73 {
74 	char foo[100];
75 
76 	sprintf(foo, "ERR from %s at %d (%d, %d)\n",
77 		from, where, ScreenLine(where), ScreenLineOffset(where));
78 	OurExitString(stderr, foo, 1);
79 	/* NOTREACHED */
80 }
81 
82 /*
83  * Routines to deal with the screen.  These routines are lifted
84  * from mskermit.
85  */
86 
87 #define	CRT_STATUS	0x3da		/* Color card */
88 #define	DISPLAY_ENABLE	0x08		/* Enable */
89 #define	scrseg()	((crt_mode == 7)? 0xb000 : 0xb800)
90 #define	scrwait()	if (crt_mode != 7) { \
91 			    while ((inp(CRT_STATUS)&DISPLAY_ENABLE) == 0) { \
92 				; \
93 			    } \
94 			}
95 static int
96     		crt_mode,
97 		crt_cols,
98 		crt_lins,
99 		curpage;
100 
101 /*
102  * Set the cursor position to where it belongs.
103  */
104 
105 static void
106 setcursor(row, column, page)
107 int
108     row,
109     column,
110     page;
111 {
112     union REGS inregs, outregs;
113 
114     inregs.h.dh = row;
115     inregs.h.dl = column;
116     inregs.h.bh = page;
117     inregs.h.ah = SetCursorPosition;
118 
119     int86(BIOS_VIDEO, &inregs, &outregs);
120 }
121 /*
122  * Read the state of the video system.  Put the cursor somewhere
123  * reasonable.
124  */
125 
126 static void
127 scrini()
128 {
129     union REGS inregs, outregs;
130 
131     inregs.h.ah = CurrentVideoState;
132     int86(BIOS_VIDEO, &inregs, &outregs);
133 
134     crt_mode = outregs.h.al;
135     crt_cols = outregs.h.ah;
136     crt_lins = 25;
137     curpage = outregs.h.bh;
138 
139     inregs.h.ah = ReadCursorPosition;
140     inregs.h.bh = curpage;
141 
142     int86(BIOS_VIDEO, &inregs, &outregs);
143 
144     if (outregs.h.dh > crt_lins) {
145 	outregs.h.dh = crt_lins;
146     }
147     if (outregs.h.dl > crt_cols) {
148 	outregs.h.dl = crt_cols;
149     }
150     inregs.h.dh = outregs.h.dh;
151     inregs.h.dl = outregs.h.dl;
152     inregs.h.bh = curpage;
153 
154     inregs.h.ah = SetCursorPosition;
155     int86(BIOS_VIDEO, &inregs, &outregs);
156 }
157 
158 
159 static void
160 scrwrite(source, length, offset)
161 ScreenBuffer *source;
162 int
163 	length,
164 	offset;
165 {
166     struct SREGS segregs;
167 
168     segread(&segregs);		/* read the current segment register */
169 
170     scrwait();
171     movedata(segregs.ds, source, scrseg(), sizeof *source*offset,
172 						sizeof *source*length);
173 }
174 
175 static void
176 scrsave(buffer)
177 ScreenBuffer *buffer;
178 {
179     struct SREGS segregs;
180 
181     segread(&segregs);		/* read the current segment register */
182 
183     scrwait();
184     movedata(scrseg(), 0, segregs.ds, buffer, crt_cols*crt_lins*2);
185 }
186 
187 static void
188 scrrest(buffer)
189 ScreenBuffer *buffer;
190 {
191     scrwrite(buffer, crt_cols*crt_lins, 0);
192 }
193 
194 static void
195 TryToSend()
196 {
197 #define	STANDOUT	0x0a	/* Highlighted mode */
198 #define	NORMAL		0x02	/* Normal mode */
199 #define	NONDISPLAY	0x00	/* Don't display */
200 
201 #define	DoAttribute(a) 	    \
202 			    if (screenIsFormatted) { \
203 				if (IsNonDisplayAttr(a)) { \
204 				    a = NONDISPLAY; 	/* don't display */ \
205 				} else if (IsHighlightedAttr(a)) { \
206 				    a = STANDOUT; \
207 				} else { \
208 				    a = NORMAL; \
209 				} \
210 			    } else  { \
211 				a = NORMAL;	/* do display on unformatted */\
212 			    }
213     ScreenImage *p, *upper;
214     ScreenBuffer *sp;
215     int fieldattr;		/* spends most of its time == 0 or 1 */
216     int screenIsFormatted = FormattedScreen();
217 
218 /* OK.  We want to do this a quickly as possible.  So, we assume we
219  * only need to go from Lowest to Highest.  However, if we find a
220  * field in the middle, we do the whole screen.
221  *
222  * In particular, we separate out the two cases from the beginning.
223  */
224     if ((Highest != HighestScreen()) || (Lowest != LowestScreen())) {
225 	sp = &Screen[Lowest];
226 	p = &Host[Lowest];
227 	upper = &Host[Highest];
228 	fieldattr = FieldAttributes(Lowest);
229 	DoAttribute(fieldattr);	/* Set standout, non-display status */
230 
231 	while (p <= upper) {
232 	    if (IsStartFieldPointer(p)) {	/* New field? */
233 		Highest = HighestScreen();
234 		Lowest = LowestScreen();
235 		TryToSend();		/* Recurse */
236 		return;
237 	    } else if (fieldattr) {	/* Should we display? */
238 				/* Display translated data */
239 		sp->data = disp_asc[GetHostPointer(p)];
240 	    } else {
241 		sp->data = ' ';
242 	    }
243 	    sp->attr = fieldattr;
244 	    p++;
245 	    sp++;
246 	}
247     } else {		/* Going from Lowest to Highest */
248 	ScreenImage *End = &Host[ScreenSize]-1;
249 
250 	sp = Screen;
251 	p = Host;
252 	fieldattr = FieldAttributes(LowestScreen());
253 	DoAttribute(fieldattr);	/* Set standout, non-display status */
254 
255 	while (p <= End) {
256 	    if (IsStartFieldPointer(p)) {	/* New field? */
257 		fieldattr = FieldAttributesPointer(p);	/* Get attributes */
258 		DoAttribute(fieldattr);	/* Set standout, non-display */
259 	    }
260 	    if (fieldattr) {	/* Should we display? */
261 			    /* Display translated data */
262 		sp->data = disp_asc[GetHostPointer(p)];
263 	    } else {
264 		sp->data = ' ';
265 	    }
266 	    sp->attr = fieldattr;
267 	    p++;
268 	    sp++;
269 	}
270     }
271     terminalCursorAddress = CorrectTerminalCursor();
272     /*
273      * We might be here just to update the cursor address.
274      */
275     if (Highest >= Lowest) {
276 	scrwrite(Screen+Lowest, (1+Highest-Lowest), Lowest);
277     }
278     setcursor(ScreenLine(terminalCursorAddress),
279 		    ScreenLineOffset(terminalCursorAddress), 0);
280     Lowest = HighestScreen()+1;
281     Highest = LowestScreen()-1;
282     if (needToRing) {
283 	DataToTerminal("\7", 1);
284 	needToRing = 0;
285     }
286     return;
287 }
288 
289 /* InitTerminal - called to initialize the screen, etc. */
290 
291 void
292 InitTerminal()
293 {
294     InitMapping();		/* Go do mapping file (MAP3270) first */
295     if (!screenInitd) { 	/* not initialized */
296 	MaxNumberLines = 24;	/* XXX */
297 	MaxNumberColumns = 80;	/* XXX */
298 	scrini();
299 	scrsave(saveScreen);	/* Save the screen buffer away */
300 	ClearArray(Screen);
301 	terminalCursorAddress = SetBufferAddress(0,0);
302 	screenInitd = 1;
303 	screenStopped = 0;		/* Not stopped */
304     }
305 }
306 
307 
308 /* StopScreen - called when we are going away... */
309 
310 void
311 StopScreen(doNewLine)
312 int doNewLine;
313 {
314     if (screenInitd && !screenStopped) {
315 	scrrest(saveScreen);
316 	setcursor(NumberLines-1, 1, 0);
317 	if (doNewLine) {
318 	    StringToTerminal("\r\n");
319 	}
320 	EmptyTerminal();
321 	screenStopped = 1;
322     }
323 }
324 
325 
326 /* RefreshScreen - called to cause the screen to be refreshed */
327 
328 void
329 RefreshScreen()
330 {
331     Highest = HighestScreen();
332     Lowest = LowestScreen();
333     TryToSend();
334 }
335 
336 
337 /* ConnectScreen - called to reconnect to the screen */
338 
339 void
340 ConnectScreen()
341 {
342     if (screenInitd) {
343 	RefreshScreen();
344 	screenStopped = 0;
345     }
346 }
347 
348 /* LocalClearScreen() - clear the whole ball of wax, cheaply */
349 
350 void
351 LocalClearScreen()
352 {
353     Clear3270();
354     Lowest = LowestScreen(); /* everything in sync... */
355     Highest = HighestScreen();
356     TryToSend();
357 }
358 
359 /*
360  * Implement the bell/error message function.
361  */
362 
363 int
364 	bellwinup = 0;		/* If != 0, length of bell message */
365 static int
366 	bell_len = 0;		/* Length of error message */
367 
368 
369 void
370 BellOff()
371 {
372     ScreenBuffer a[100];
373     int i;
374 
375     if (bellwinup) {
376 	unsigned char blank = ' ';
377 
378 	for (i = 0; i < bell_len; i++) {
379 	    a[i].attr = NORMAL;
380 	    a[i].data = ' ';
381 	}
382     }
383     scrwrite(a, bell_len, 24*80);		/* XXX */
384 }
385 
386 
387 void
388 RingBell(s)
389 char *s;
390 {
391     needToRing = 1;
392     if (s) {
393 	int i;
394 	ScreenBuffer bellstring[100];
395 
396 	bell_len = strlen(s);
397 	bellwinup = 1;
398 	if (bell_len > sizeof bellstring-1) {
399 	    OurExitString(stderr, "Bell string too long.", 1);
400 	}
401 	for (i = 0; i < bell_len; i++) {
402 	    bellstring[i].attr = STANDOUT;
403 	    bellstring[i].data = s[i];
404 	}
405 	scrwrite(bellstring, bell_len, 24*80);		/* XXX */
406     }
407 }
408 
409 /*
410  * Update the OIA area.
411  */
412 
413 void
414 ScreenOIA(oia)
415 OIA *oia;
416 {
417 }
418 
419 
420 /* returns a 1 if no more output available (so, go ahead and block),
421     or a 0 if there is more output available (so, just poll the other
422     sources/destinations, don't block).
423  */
424 
425 int
426 DoTerminalOutput()
427 {
428 	/* called just before a select to conserve IO to terminal */
429     if (!(screenInitd||screenStopped)) {
430 	return 1;		/* No output if not initialized */
431     }
432     if ((Lowest <= Highest) || needToRing ||
433 			(terminalCursorAddress != CorrectTerminalCursor())) {
434 	TryToSend();
435     }
436     if (Lowest > Highest) {
437 	return 1;		/* no more output now */
438     } else {
439 	return 0;		/* more output for future */
440     }
441 }
442 
443 /*
444  * The following are defined to handle transparent data.
445  */
446 
447 void
448 TransStop()
449 {
450     RefreshScreen();
451 }
452 
453 void
454 TransOut(buffer, count, kind, control)
455 unsigned char	*buffer;
456 int		count;
457 int		kind;		/* 0 or 5 */
458 int		control;	/* To see if we are done */
459 {
460     char *ptr;
461 
462     while (DoTerminalOutput() == 0) {
463 	;
464     }
465     for (ptr = buffer; ptr < buffer+count; ptr++) {
466 	*ptr &= 0x7f;		/* Turn off parity bit */
467     }
468     (void) DataToTerminal(buffer, count);
469     if (control && (kind == 0)) {		/* Send in AID byte */
470 	SendToIBM();
471     } else {
472 	TransInput(1, kind);			/* Go get some data */
473     }
474 }
475 
476 /*
477  * init_screen()
478  *
479  * Initialize variables used by screen.
480  */
481 
482 void
483 init_screen()
484 {
485     bellwinup = 0;
486 }
487 
488 
489