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