1 /*
2  *	Copyright (c) 1984, 1985, 1986 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[] = "@(#)outbound.c	3.1  10/29/86";
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 "../ctlr/hostctlr.h"
34 #include "../ctlr/inbound.ext"
35 #include "../ctlr/oia.h"
36 #include "../ctlr/options.ext"
37 #include "../ctlr/outbound.ext"
38 #include "../ctlr/screen.h"
39 
40 #include "../ascii/map3270.ext"
41 
42 #include "../general/globals.h"
43 
44 #include "video.h"
45 
46 extern void EmptyTerminal();
47 
48 #define CorrectTerminalCursor() ((TransparentClock == OutputClock)? \
49 		terminalCursorAddress:UnLocked? CursorAddress: HighestScreen())
50 
51 
52 static int terminalCursorAddress;	/* where the cursor is on term */
53 static int screenInitd; 		/* the screen has been initialized */
54 static int screenStopped;		/* the screen has been stopped */
55 
56 static int needToRing;			/* need to ring terinal bell */
57 
58 typedef struct {
59     char
60 	data,		/* The data for this position */
61 	attr;		/* The attributes for this position */
62 } ScreenBuffer;
63 
64 ScreenBuffer Screen[MAXNUMBERLINES*MAXNUMBERCOLUMNS];
65 ScreenBuffer saveScreen[sizeof Screen/sizeof Screen[0]];
66 
67 /* Variables for transparent mode */
68 
69 #include "disp_asc.out"
70 
71 
72 /* OurExitString - designed to keep us from going through infinite recursion */
73 
74 static void
75 OurExitString(file, string, value)
76 FILE	*file;
77 char	*string;
78 int	value;
79 {
80     static int recursion = 0;
81 
82     if (!recursion) {
83 	recursion = 1;
84 	ExitString(file, string, value);
85     }
86 }
87 
88 
89 static void
90 GoAway(from, where)
91 char *from;		/* routine that gave error */
92 int	where;		/* cursor address */
93 {
94 	char foo[100];
95 
96 	sprintf(foo, "ERR from %s at %d (%d, %d)\n",
97 		from, where, ScreenLine(where), ScreenLineOffset(where));
98 	OurExitString(stderr, foo, 1);
99 	/* NOTREACHED */
100 }
101 
102 /*
103  * Routines to deal with the screen.  These routines are lifted
104  * from mskermit.
105  */
106 
107 #define	CRT_STATUS	0x3da		/* Color card */
108 #define	DISPLAY_ENABLE	0x08		/* Enable */
109 #define	scrseg()	((crt_mode == 7)? 0xb000 : 0xb800)
110 #define	scrwait()	if (crt_mode != 7) { \
111 			    while ((inp(CRT_STATUS)&DISPLAY_ENABLE) == 0) { \
112 				; \
113 			    } \
114 			}
115 static int
116     		crt_mode,
117 		crt_cols,
118 		crt_lins,
119 		curpage;
120 
121 /*
122  * Set the cursor position to where it belongs.
123  */
124 
125 static void
126 setcursor(row, column, page)
127 int
128     row,
129     column,
130     page;
131 {
132     union REGS inregs, outregs;
133 
134     inregs.h.dh = row;
135     inregs.h.dl = column;
136     inregs.h.bh = page;
137     inregs.h.ah = SetCursorPosition;
138 
139     int86(BIOS_VIDEO, &inregs, &outregs);
140 }
141 /*
142  * Read the state of the video system.  Put the cursor somewhere
143  * reasonable.
144  */
145 
146 static void
147 scrini()
148 {
149     union REGS inregs, outregs;
150 
151     inregs.h.ah = CurrentVideoState;
152     int86(BIOS_VIDEO, &inregs, &outregs);
153 
154     crt_mode = outregs.h.al;
155     crt_cols = outregs.h.ah;
156     crt_lins = 25;
157     curpage = outregs.h.bh;
158 
159     inregs.h.ah = ReadCursorPosition;
160     inregs.h.bh = curpage;
161 
162     int86(BIOS_VIDEO, &inregs, &outregs);
163 
164     if (outregs.h.dh > crt_lins) {
165 	outregs.h.dh = crt_lins;
166     }
167     if (outregs.h.dl > crt_cols) {
168 	outregs.h.dl = crt_cols;
169     }
170     inregs.h.dh = outregs.h.dh;
171     inregs.h.dl = outregs.h.dl;
172     inregs.h.bh = curpage;
173 
174     inregs.h.ah = SetCursorPosition;
175     int86(BIOS_VIDEO, &inregs, &outregs);
176 }
177 
178 
179 static void
180 scrwrite(source, length, offset)
181 ScreenBuffer *source;
182 int
183 	length,
184 	offset;
185 {
186     struct SREGS segregs;
187 
188     segread(&segregs);		/* read the current segment register */
189 
190     scrwait();
191     movedata(segregs.ds, source, scrseg(), sizeof *source*offset,
192 						sizeof *source*length);
193 }
194 
195 static void
196 scrsave(buffer)
197 ScreenBuffer *buffer;
198 {
199     struct SREGS segregs;
200 
201     segread(&segregs);		/* read the current segment register */
202 
203     scrwait();
204     movedata(scrseg(), 0, segregs.ds, buffer, crt_cols*crt_lins*2);
205 }
206 
207 static void
208 scrrest(buffer)
209 ScreenBuffer *buffer;
210 {
211     scrwrite(buffer, crt_cols*crt_lins, 0);
212 }
213 
214 static void
215 TryToSend()
216 {
217 #define	STANDOUT	0x0a	/* Highlighted mode */
218 #define	NORMAL		0x02	/* Normal mode */
219 #define	NONDISPLAY	0x00	/* Don't display */
220 
221 #define	DoAttribute(a) 	    \
222 			    if (screenIsFormatted) { \
223 				if (IsNonDisplayAttr(a)) { \
224 				    a = NONDISPLAY; 	/* don't display */ \
225 				} else if (IsHighlightedAttr(a)) { \
226 				    a = STANDOUT; \
227 				} else { \
228 				    a = NORMAL; \
229 				} \
230 			    } else  { \
231 				a = NORMAL;	/* do display on unformatted */\
232 			    }
233     ScreenImage *p, *upper;
234     ScreenBuffer *sp;
235     int fieldattr;		/* spends most of its time == 0 or 1 */
236     int screenIsFormatted = FormattedScreen();
237 
238 /* OK.  We want to do this a quickly as possible.  So, we assume we
239  * only need to go from Lowest to Highest.  However, if we find a
240  * field in the middle, we do the whole screen.
241  *
242  * In particular, we separate out the two cases from the beginning.
243  */
244     if ((Highest != HighestScreen()) || (Lowest != LowestScreen())) {
245 	sp = &Screen[Lowest];
246 	p = &Host[Lowest];
247 	upper = &Host[Highest];
248 	fieldattr = FieldAttributes(Lowest);
249 	DoAttribute(fieldattr);	/* Set standout, non-display status */
250 
251 	while (p <= upper) {
252 	    if (IsStartFieldPointer(p)) {	/* New field? */
253 		Highest = HighestScreen();
254 		Lowest = LowestScreen();
255 		TryToSend();		/* Recurse */
256 		return;
257 	    } else if (fieldattr) {	/* Should we display? */
258 				/* Display translated data */
259 		sp->data = disp_asc[GetHostPointer(p)];
260 	    } else {
261 		sp->data = ' ';
262 	    }
263 	    sp->attr = fieldattr;
264 	    p++;
265 	    sp++;
266 	}
267     } else {		/* Going from Lowest to Highest */
268 	ScreenImage *End = &Host[ScreenSize]-1;
269 
270 	sp = Screen;
271 	p = Host;
272 	fieldattr = FieldAttributes(LowestScreen());
273 	DoAttribute(fieldattr);	/* Set standout, non-display status */
274 
275 	while (p <= End) {
276 	    if (IsStartFieldPointer(p)) {	/* New field? */
277 		fieldattr = FieldAttributesPointer(p);	/* Get attributes */
278 		DoAttribute(fieldattr);	/* Set standout, non-display */
279 	    }
280 	    if (fieldattr) {	/* Should we display? */
281 			    /* Display translated data */
282 		sp->data = disp_asc[GetHostPointer(p)];
283 	    } else {
284 		sp->data = ' ';
285 	    }
286 	    sp->attr = fieldattr;
287 	    p++;
288 	    sp++;
289 	}
290     }
291     terminalCursorAddress = CorrectTerminalCursor();
292     /*
293      * We might be here just to update the cursor address.
294      */
295     if (Highest >= Lowest) {
296 	scrwrite(Screen+Lowest, (1+Highest-Lowest), Lowest);
297     }
298     setcursor(ScreenLine(terminalCursorAddress),
299 		    ScreenLineOffset(terminalCursorAddress), 0);
300     Lowest = HighestScreen()+1;
301     Highest = LowestScreen()-1;
302     if (needToRing) {
303 	DataToTerminal("\7", 1);
304 	needToRing = 0;
305     }
306     return;
307 }
308 
309 /* InitTerminal - called to initialize the screen, etc. */
310 
311 void
312 InitTerminal()
313 {
314     InitMapping();		/* Go do mapping file (MAP3270) first */
315     if (!screenInitd) { 	/* not initialized */
316 	MaxNumberLines = 24;	/* XXX */
317 	MaxNumberColumns = 80;	/* XXX */
318 	scrini();
319 	scrsave(saveScreen);	/* Save the screen buffer away */
320 	ClearArray(Screen);
321 	terminalCursorAddress = SetBufferAddress(0,0);
322 	screenInitd = 1;
323 	screenStopped = 0;		/* Not stopped */
324     }
325     Initialized = 1;
326 }
327 
328 
329 /* StopScreen - called when we are going away... */
330 
331 void
332 StopScreen(doNewLine)
333 int doNewLine;
334 {
335     if (screenInitd && !screenStopped) {
336 	scrrest(saveScreen);
337 	setcursor(NumberLines-1, NumberColumns-1, 0);
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 	bellpos0 = 0;		/* Where error message goes */
383 
384 static char	bellstring[100];/* Where message goes */
385 
386 #define	BELL_SPACES	2	/* 2 spaces between border and bell */
387 
388 #define	BELL_HIGH_LOW(h,l) { \
389 	    h = bellpos0+2*NumberColumns+bellwinup+BELL_SPACES*2; \
390 	    l = bellpos0; \
391 	}
392 
393 void
394 BellOff()
395 {
396     if (bellwinup) {
397 	BELL_HIGH_LOW(Highest,Lowest);
398 	TryToSend();
399     }
400 }
401 
402 
403 void
404 RingBell(s)
405 char *s;
406 {
407     needToRing = 1;
408     if (s) {
409 	int len = strlen(s);
410 
411 	if (len > sizeof bellstring-1) {
412 	    OurExitString(stderr, "Bell string too long.", 1);
413 	}
414 	memcpy(bellstring, s, len+1);
415 	BELL_HIGH_LOW(Highest,Lowest);
416 	TryToSend();
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 (!Initialized) {
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)
466 unsigned char	*buffer;
467 int		count;
468 {
469 
470     while (DoTerminalOutput() == 0) {
471 	;
472     }
473     (void) DataToTerminal(buffer, count);
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