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