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