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