1 /* $Id: ttyio.c,v 1.6 2001/03/02 08:48:30 amura Exp $ */
2 /*
3  * Name:	MG 2a
4  *		Amiga terminal window I/O, with all kinds o' trimmings.
5  *		This module is 'way too big.
6  * Last Edit:	01-Dec-87 mic@emx.cc.utexas.edu
7  * Created:	21-Apr-86 mic@emx.cc.utexas.edu
8  */
9 
10 /*
11  * $Log: ttyio.c,v $
12  * Revision 1.6  2001/03/02 08:48:30  amura
13  * now AUTOSAVE feature implemented almost all (except for WIN32
14  *
15  * Revision 1.5  2001/01/20 15:48:46  amura
16  * very big terminal supported
17  *
18  * Revision 1.4  2000/12/21 16:54:20  amura
19  * fix usage of strncat()
20  *
21  * Revision 1.3  2000/10/02 16:13:06  amura
22  * ignore mouse event in minibuffer editing
23  *
24  * Revision 1.2  2000/09/29 17:26:17  amura
25  * small patch for new ttymenu.c
26  *
27  * Revision 1.1.1.1  2000/06/27 01:48:01  amura
28  * import to CVS
29  *
30  */
31 
32 /*
33  * Lots of includes.
34  */
35 
36 #include <exec/types.h>
37 #include <exec/nodes.h>
38 #include <exec/lists.h>
39 #include <exec/tasks.h>
40 #include <exec/ports.h>
41 #include <exec/io.h>
42 #include <devices/console.h>
43 #include <devices/inputevent.h>
44 #include <libraries/dos.h>
45 #include <graphics/clip.h>
46 #include <graphics/view.h>
47 #include <graphics/rastport.h>
48 #include <graphics/layers.h>
49 #include <graphics/text.h>
50 #include <graphics/gfxbase.h>
51 #include <intuition/intuition.h>
52 #include <intuition/intuitionbase.h>
53 #include <libraries/diskfont.h>
54 #ifdef KANJI
55 # ifdef V2
56 #include <clib/input_protos.h>
57 # endif
58 #endif
59 
60 #undef	TRUE			/* avoid redefinition messages 		*/
61 #undef	FALSE
62 #include "config.h"	/* Dec. 15, 1992 by H.Ohkubo */
63 #include "def.h"		/* includes sysdef.h and ttydef.h	*/
64 
65 #ifdef	DO_METAKEY
66 #define	IEQUALIFIER_ALT		(IEQUALIFIER_RALT | IEQUALIFIER_LALT)
67 #endif	/* DO_METAKEY */
68 
69 /* ARexx support. this is mg3b's feature! */
70 #ifdef	REXX	/* Dec.20,1992 Add by H.Ohkubo */
71 #include	"key.h"
72 #endif
73 
74 /*
75  * External Amiga functions.
76  */
77 extern	LONG			 AbortIO();
78 extern	LONG			 CloseDevice();
79 extern	LONG			 CloseLibrary();
80 extern	LONG			 CloseWindow();
81 extern	struct	MsgPort		*CreatePort();
82 extern	struct	IOStdReq	*CreateStdIO();
83 extern	LONG			 DeletePort();
84 extern	LONG			 DeleteStdIO();
85 extern	struct	IntuiMessage	*GetMsg();
86 #ifndef	V11
87 extern	LONG			GetScreenData();
88 #endif
89 extern	int			 OpenConsole();
90 extern	char			*OpenLibrary();
91 extern	struct	Window		*OpenWindow();
92 extern	struct TextFont		*OpenDiskFont();
93 extern	LONG			 RectFill();
94 extern	LONG			 ReplyMsg();
95 extern	LONG			 RawKeyConvert();
96 extern	LONG			 SetAPen();
97 extern	LONG			 SetDrMd();
98 extern	LONG			 Wait();
99 
100 #ifdef	DO_MENU
101 extern	LONG			 ClearMenuStrip();	/* menu functions */
102 extern	struct	Menu		*InitEmacsMenu();
103 extern	VOID			 DisposeMenus();
104 extern	struct	MenuItem	*ItemAddress();
105 extern	LONG			 SetMenuStrip();
106 #endif
107 
108 #ifdef	MANX
109 extern	int	Enable_Abort;		/* Do NOT allow abort!		*/
110 #endif
111 
112 /*
113  * External MG functions and variables
114  */
115 extern	int	quit();			/* Defined by "main.c"		*/
116 extern	char	version[];		/* Version information		*/
117 extern	int	ttrow;			/* Current cursor row		*/
118 extern	int	use_metakey;		/* Do meta characters?		*/
119 
120 /*
121  * Non-int internal functions.  P?() is used to conditionally indicate
122  * ANSI-style prototype arguments for compilers (i.e. Lattice) that
123  * support them.
124  */
125 #ifdef	SUPPORT_ANSI
126 #define	P1(a)	a
127 #define	P2(a,b)	a,b
128 #define	P3(a,b,c) a,b,c
129 #else
130 #define	P1(a)
131 #define	P2(a,b)
132 #define P3(a,b,c)
133 #endif
134 
135 /* VOID		panic(P1(char *)); */
136 VOID		setttysize();
137 /* VOID		ttclose(); */
138 /* VOID		ttflush(); */
139 VOID		ttnflush(P1(int));
140 /* VOID		ttputc(P1(unsigned char)); */
141 
142 static VOID	cleanup();
143 static VOID	firstwin();
144 #ifndef	KANJI	/* Dec.19,1992 by H.Ohkubo */
145 static VOID	qkey(P1(KCHAR));
146 #endif
147 #ifdef	DO_MENU
148 static VOID	qmenu(P1(USHORT));
149 #endif
150 #ifdef	MOUSE
151 static VOID	qmouse(P3(SHORT, SHORT, USHORT));
152 extern int	allow_mouse_event;
153 #endif
154 static VOID	ttreopen(P1(int)) ;
155 static VOID	setmaxima() ;
156 static struct Screen	*wbscreen();
157 
158 
159 /*
160  * Intuition window and menu variables.  MG gets used a lot, because it
161  * gets reconfigured on the fly for the amiga-set-font and toggle-border
162  * operations.
163  */
164 
165 #define WINDOWGADGETS (WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE)
166 #define WINDOWFLAGS	(WINDOWGADGETS | ACTIVATE)
167 
168 struct NewWindow MG = {
169 	0,	0,			/* start position       	*/
170 	0,	0,			/* width, height (set by ttopen)*/
171 	0,	1,	     		/* detail pen, block pen	*/
172 #ifdef	DO_MENU
173 	MENUPICK |			/* If menu is used		*/
174 #endif
175 #ifdef	MOUSE
176 	MOUSEBUTTONS | 			/* If mouse is used		*/
177 #endif
178 	INTUITICKS |
179 #ifndef	KANJI	/* Dec.19,1992 by H.Ohkubo */
180 	RAWKEY |
181 #endif
182 	CLOSEWINDOW | NEWSIZE,		/* IDCMP flags			*/
183 	0,				/* window flags	(set by ttopen)	*/
184 	NULL,				/* pointer to first user gadget */
185 	NULL,				/* pointer to user checkmark	*/
186 	NULL,				/* title (filled in later)	*/
187 	NULL,				/* pointer to screen (none)	*/
188 	NULL,				/* pointer to superbitmap	*/
189 	220,40,				/* minimum size	(small!)	*/
190 	0, 0,				/* maximum size (set by ttopen)	*/
191 	WBENCHSCREEN			/* screen in which to open	*/
192 };
193 
194 static short	borderless = FALSE;	/* Flag for borderless window	*/
195 static short	toggle_zooms = TRUE;	/* Does toggling border zoom?	*/
196 static int	last_top, last_left, last_height, last_width;
197 
198 struct Window	*EmW = NULL;		/* Our window			*/
199 struct Screen	*EmS = NULL;		/* Our screen (usually WB)	*/
200 short		toggling = FALSE;	/* Prevent menu wiping		*/
201 #ifndef	V11
202 struct Screen	WBInfo;			/* Info about the WB screen	*/
203 #endif
204 struct TextFont *EmFont = NULL;		/* Our font (usually TOPAZ_xx)	*/
205 
206 #ifdef	DO_MENU
207 static struct Menu	*EmacsMenu = NULL;	/* Our menu		*/
208 #endif
209 
210 static ULONG		class;			/* Intuition event	*/
211 static USHORT		code,			/*   information	*/
212 			qualifier;
213 static APTR		address;
214 static SHORT		x, y;
215 static LONG		intuitionMsgBit;	/* Signal bit		*/
216 #define INTUITION_MESSAGE ((LONG) (1L << intuitionMsgBit))
217 
218 /* * * * * * * * * * * * * console I/O * * * * * * * * * * * * * * * * */
219 
220 #define	CSI	0x9b			/* Command Sequence Introducer	*/
221 #define	NOBUF	512			/* About 1/4 screen		*/
222 #define	NIBUF	256			/* Input buffer			*/
223 
224 static KCHAR		ibuf[NIBUF];	/* keyboard input buffer	*/
225 static int		ibufo, nibuf;	/* head, # of bytes in ibuf	*/
226 
227 #ifndef	PROMPTWAIT
228 #define	PROMPTWAIT 20			/* ticks to wait before timeout	*/
229 #endif
230 static	LONG		tickcount;	/* # intuiticks	since last char	*/
231 
232 #ifdef	REXX	/* Dec.20,1992 Add by H.Ohkubo */
233 extern	struct	MsgPort	*rexxport;
234 #define	REXXPORT_MESSAGE	(1L<<rexxport->mp_SigBit)
235 #endif
236 #ifdef	KANJI	/* Dec.19,1992 by H.Ohkubo */
237 static struct MsgPort	*conReadPort = NULL;	/* I/O ports		*/
238 static struct IOStdReq	*conReadMsg = NULL;	/* I/O messages		*/
239 #define	CONREAD_MESSAGE	(1L<<conReadPort->mp_SigBit)
240 #endif
241 static struct MsgPort	*conWritePort = NULL;	/* I/O ports 		*/
242 static struct IOStdReq	*conWriteMsg = NULL;	/* I/O messages		*/
243 #ifndef KANJI	/* Dec.19,1992 by H.Ohkubo / fixed by amura */
244 struct Device		*ConsoleDevice;	        /* used by RawKeyConvert*/
245 #endif
246 static unsigned char	outbuf[NOBUF+7];	/* output buffer	*/
247 static unsigned char	*obuf;			/* first output char	*/
248 int			nobuf;			/* # of bytes in above	*/
249 int			nrow;			/* Terminal size, rows.	*/
250 int			ncol;			/* Terminal size, cols.	*/
251 
252 #ifdef	KANJI	/* Dec.19,1992 by H.Ohkubo */
253 #define	qkey(k)	{if (nibuf < NIBUF) ibuf[(ibufo + nibuf++)%NIBUF] = (KCHAR)(k);}
254 #endif
255 /* * * * * * * * * functions to open/reopen the window * * * * * * * * * */
256 
257 /*
258  * Open up the virtual terminal MG communicates with. Set up the window,
259  * console, and menu strip.
260  */
261 
ttopen()262 ttopen()
263 {
264 
265 #ifdef	MANX
266 	Enable_Abort = 0;				/* Disable ^C	*/
267 #endif
268 
269 	/* firstwin() is only called the very first time we open the window */
270 	if (toggling == FALSE)
271 		firstwin();
272 
273 	/* Set the window size, set the flags and title, and open it */
274 
275 	setmaxima();
276 	MG.Flags = WINDOWFLAGS;
277 	MG.Flags |= borderless ? BORDERLESS : WINDOWSIZING;
278 	MG.Title = (UBYTE *) &version[0];
279 
280 	if ((EmW = OpenWindow(&MG)) == NULL)
281 		cleanup();
282 	SetFont(EmW->RPort, EmFont);
283 
284 	/* Once the window is created, get the Intuition signal bit, set up
285 	 * the menu, and tell the virtual terminal how big it is.
286  	 */
287 	setttysize();
288 	intuitionMsgBit = EmW->UserPort->mp_SigBit;
289 #ifdef	DO_MENU
290 	if (toggling == FALSE)
291 		EmacsMenu = InitEmacsMenu(EmW);
292 	if (EmacsMenu == NULL)
293 		cleanup();
294 	SetMenuStrip(EmW, EmacsMenu);
295 #endif
296 
297 	/* Attach a console device (purely for output now) to our window
298 	 */
299 
300 	if ((conWritePort = CreatePort("Emacs.con.write", 0L)) == NULL)
301 		cleanup();
302 	if ((conWriteMsg = CreateStdIO(conWritePort)) == NULL)
303 		cleanup();
304 #ifdef	KANJI	/* Dec.19,1992 by H.Ohkubo */
305 	if ((conReadPort = CreatePort("Emacs.con.read", 0L)) == NULL)
306 		cleanup();
307 	if ((conReadMsg = CreateStdIO(conReadPort)) == NULL)
308 		cleanup();
309 #endif
310 
311 #ifdef	KANJI	/* Dec.19,1992 by H.Ohkubo */
312 	if (OpenConsole(conWriteMsg,conReadMsg,EmW) != 0)
313 #else	/* Original */
314 	if (OpenConsole(conWriteMsg,NULL,EmW) != 0)
315 #endif
316 		cleanup();
317 #ifndef	KANJI	/* Dec.19,1992 by H.Ohkubo */
318 	ConsoleDevice = conWriteMsg->io_Device;
319 #endif
320 	nibuf = ibufo = 0;
321 
322 	return (0);
323 }
324 
325 /*
326  * Set up the initial state of the window.  Opens up libraries, decides how
327  * big the initial window should be, and whether it should be borderless.
328  */
329 
firstwin()330 static VOID firstwin()
331 {
332 	/* Get our screen and font, then figure out if we can go borderless
333 	*/
334 	if ((EmS = wbscreen()) == NULL)
335 		cleanup();
336 	EmFont = OpenDiskFont(EmS->Font);
337 	if ((EmS->Width >= ((INIT_COLS * EmFont->tf_XSize) + LR_BORDER)) &&
338 		(EmS->Height >= ((INIT_ROWS * EmFont->tf_YSize) + TB_BORDER)))
339 		borderless = FALSE;
340 
341 	/* Set the size of the initial window and fake the last one
342 	 */
343 	last_width = MG.Width = EmS->Width;
344 	last_height = MG.Height = EmS->Height;
345 	last_left = MG.LeftEdge = 0;
346 	last_top = MG.TopEdge = 0;
347 
348 	bcopy(outbuf,"\2330 p", 4);	/* preload cursor off sequence */
349 	obuf = outbuf + 4;
350 }
351 
352 /*
353  * Make sure the window isn't bigger than NROW * NCOL, while accounting
354  * for borders & such.  Since the window might not be at its largest right
355  * now, deadstop both the current width and the maxwidth.
356  */
357 
setmaxima()358 static VOID setmaxima()
359 {
360 	register int maxw, maxh;
361 
362 	MG.MaxWidth = EmS->Width;
363 	MG.MaxHeight = EmS->Height;
364 	maxw = NCOL * EmFont->tf_XSize + (borderless ? 0 : LR_BORDER);
365 	maxh = NROW * EmFont->tf_YSize + (borderless ? TOP_OFFSET : TB_BORDER);
366 
367 	if (MG.MaxWidth > maxw)		MG.MaxWidth = maxw;
368 	if (MG.Width > maxw)		MG.Width = maxw;
369 
370 	if (MG.MaxHeight > maxh)	MG.MaxHeight = maxh;
371 	if (MG.Height > maxh)		MG.Height = maxh;
372 }
373 
374 
375 /* Return a pointer the workbench screen, using GetScreenData() to do
376  * things like a good citizen.  Left the V11 code in as a reminder
377  * that what works is not always the _best_ way to do things.
378  * Thanks to Tom Rokicki for reminding me (mpk) this had to be done.
379  */
380 
381 static struct Screen
wbscreen()382 *wbscreen()
383 {
384 #ifndef	V11
385 	return GetScreenData(&WBInfo, (ULONG) sizeof(WBInfo),
386 		WBENCHSCREEN, NULL) ? &WBInfo : ((struct Screen *)NULL);
387 #else
388 	register struct Screen	*s;
389 	extern	struct IntuitionBase *IntuitionBase;/* Dec.20,1992 by H.Ohkubo */
390 
391 	Forbid();
392 	for (s = IntuitionBase->FirstScreen; s ; s = s->NextScreen)
393 		if ((s->Flags & SCREENTYPE) == WBENCHSCREEN)
394 			break;
395 	Permit();
396 	return (s);
397 #endif
398 }
399 
400 /*
401  * Hide the window and open it up again.  If resize is TRUE, they're
402  * being called as part of a resize operation, so assume that the
403  * NewWindow structure is set correctly.  Otherwise, store the current
404  * window size and position in the NewWindow structure.
405  *
406  * These two functions are split so we can do things like ttreopen() and
407  * tticon() cleanly.
408  */
409 
410 VOID
tthide(resize)411 tthide(resize)
412 int resize;
413 {
414 	toggling = TRUE;
415 	if (resize == FALSE) {		     /* if we're resizing,	*/
416 		MG.LeftEdge = EmW->LeftEdge; /* use current window size	*/
417 		MG.TopEdge = EmW->TopEdge;
418 		MG.Width = EmW->Width;
419 		MG.Height = EmW->Height;
420 	}
421 	ttclose();				/* reset to zero	*/
422 }
423 
424 VOID
ttshow(resize)425 ttshow(resize)
426 int resize;
427 {
428 	ttopen();				/* re-open tty window	*/
429 	ttinit();				/* re-initalize tty	*/
430 	sgarbf = TRUE;				/* screen was trashed	*/
431 	if (resize == TRUE)
432 		nrow = ncol = -1;		/* trash screen size	*/
433 	refresh();				/* and redraw it	*/
434 	toggling = FALSE;			/* Ok, done		*/
435 }
436 
437 /*
438  * ttreopen() was split into the two functions above when tticon()
439  * was introduced.
440  */
441 
442 static VOID
ttreopen(resize)443 ttreopen(resize)
444 int resize;
445 {
446 	tthide(resize);
447 	ttshow(resize);
448 }
449 
450 /* * * * * * * * * * * * functions to close the window * * * * * * * * */
451 
452 /*
453  * Close the virtual terminal.  If toggling, don't release all
454  * the other resources we've allocated.
455  */
456 /* VOID */
ttclose()457 ttclose()
458 {
459 	ttflush();
460 	CloseDevice(conWriteMsg);
461 	DeleteStdIO(conWriteMsg);	conWriteMsg = NULL;
462 	DeletePort(conWritePort);	conWritePort = NULL;
463 #ifdef	KANJI	/* Dec.19,1992 by H.Ohkubo */
464 	if (CheckIO(conReadMsg)) {
465 		AbortIO(conReadMsg);
466 		WaitIO(conReadMsg);
467 	}
468 	DeleteStdIO(conReadMsg);	conReadMsg = NULL;
469 	DeletePort(conReadPort);	conReadPort = NULL;
470 #endif
471 #ifdef	DO_MENU
472 	ClearMenuStrip(EmW);
473 #endif
474 	CloseWindow(EmW);
475 	if (toggling == FALSE)
476 		cleanup();		/* clean up everything	*/
477 #ifdef	MANX
478 	Enable_Abort = 1;
479 #endif
480 }
481 
482 
483 /*
484  * Clean up.  Done only when we're really closing up shop
485  */
486 
487 static VOID
cleanup()488 cleanup()
489 {
490 	if (conWriteMsg)	DeleteStdIO(conWriteMsg);
491 	if (conWritePort)	DeletePort(conWritePort);
492 #ifdef	KANJI	/* Dec.19,1992 by H.Ohkubo */
493 	if (conReadMsg)	{
494 		if (CheckIO(conReadMsg)) {
495 			AbortIO(conReadMsg);
496 			WaitIO(conReadMsg);
497 		}
498 		DeleteStdIO(conReadMsg);
499 	}
500 	if (conReadPort)	DeletePort(conReadPort);
501 #endif
502 #ifdef	DO_MENU
503 	if (EmacsMenu)		DisposeMenus(EmacsMenu);
504 #endif
505 	if (EmFont)		CloseFont(EmFont);
506 }
507 
508 /* * * * * * * * functions that diddle the window and reopen it * * * * * */
509 
510 /*
511  * Toggle between a borderless window and a sizeable window. This lets you
512  * use the whole screen if you want. Bound to "amiga-toggle-border".
513  */
514 
togglewindow(f,n)515 togglewindow(f, n)
516 {
517 
518 	if ((borderless = !borderless) == TRUE) {/* *always* save last	 */
519 		last_top = EmW->TopEdge;	/* bordered window size	 */
520 		last_left = EmW->LeftEdge;
521 		last_width = EmW->Width;
522 		last_height = EmW->Height;
523 	}
524 
525 	if (toggle_zooms == FALSE) {		/* just use current size */
526 		ttreopen(FALSE);
527 		return (TRUE);
528 	}
529 
530 	/* zooming -- if borderless, go as big as possible.  If
531 	 * bordered, set to last saved value of bordered window
532 	 */
533 	if (borderless) {
534 		MG.LeftEdge = 0;
535 		MG.TopEdge = 0;
536 		MG.Width = MG.MaxWidth;
537 		MG.Height = MG.MaxHeight;
538 	} else {
539 		MG.LeftEdge = last_left;
540 		MG.TopEdge = last_top;
541 		MG.Width = last_width;
542 		MG.Height = last_height;
543 	}
544 	ttreopen(TRUE);			/* open with new size	*/
545 	return (TRUE);
546 }
547 
548 /*
549  * Modify the action of "amiga-toggle-border", reporting outcome to user.
550  * Bound to "amiga-zoom-mode".
551  */
togglezooms(f,n)552 togglezooms(f, n)
553 {
554 	toggle_zooms = !toggle_zooms;
555 	ewprintf("Toggling border %s",
556 		toggle_zooms ?  "expands window to screen size" :
557 				"retains current window size");
558 	return (TRUE);
559 }
560 
561 #ifdef	CHANGE_FONT
562 /*
563  * Select a different font for the MG window. This does not work very well with
564  * proportional fonts, so we ask the user to confirm before he uses one. It's
565  * available if you want to be able to use your own disk font (or Topaz 11
566  * under 1.2) to edit with.
567  */
568 
setfont(f,n)569 setfont(f, n)
570 {
571 	register int	s, size;
572 	register struct TextFont *newfont;
573 	char		fontname[NFILEN], fontpath[NFILEN], fontsize[3];
574 	struct TextAttr	ta;
575 
576 	/* If negative size, reset to default font
577 	 */
578 	if ((f & FFARG) && (n <= 0)) {
579 		CloseFont(EmFont);			/* return old font  */
580 		EmFont = OpenDiskFont(EmS->Font);	/* screen's default */
581 		ttreopen(FALSE);			/* no resize	    */
582 		ewprintf("Now using default font");
583 		return (TRUE);
584 	}
585 
586 	if ((s = ereply("Font name: ",fontname, sizeof(fontname))) != TRUE)
587 		return (s);
588 	/* make name */
589 	strncpy(fontpath,fontname,sizeof(fontpath));
590 	fontpath[sizeof(fontpath)-1] = '\0';
591 	strncat(fontpath,".font",sizeof(fontpath)-strlen(fontpath)-1);
592 
593 	/* Get font size */
594 	if (f & FFARG)
595 		size = n;
596 	else {
597 		if ((s = ereply("Font size: ",
598 				fontsize, sizeof(fontsize))) != TRUE)
599 			return (s);
600 		size = atoi(fontsize);
601 	}
602 
603 	/* Set up text attributes */
604 	ta.ta_Name = (UBYTE *)fontpath;
605 	ta.ta_YSize = size;
606 	ta.ta_Style = FS_NORMAL;
607 	ta.ta_Flags = 0;
608 
609 	/* Look for the font */
610 	ewprintf("Looking for %s %d...",fontname,size);
611 	if ((newfont = OpenDiskFont(&ta)) == NULL) {
612 		ewprintf("Can't find %s %d!",fontname,size);
613 		return (FALSE);
614 	}
615 
616 	/* Found it! Check before using it */
617 	if ((newfont->tf_YSize != size) &&
618 		((s = eyesno("Size unavailable - use closest")) != TRUE)) {
619 		CloseFont(newfont);
620 		return (FALSE);
621 	}
622 	if ((newfont->tf_Flags & FPF_PROPORTIONAL) &&
623 		(((s = eyesno("Use proportional font")))!= TRUE)) {
624 			CloseFont(newfont);
625 			return (FALSE);
626 	}
627 
628 	/* Get rid of old font and reopen with the new one */
629 	CloseFont(EmFont);
630 	EmFont = newfont;
631 	ttreopen(FALSE);
632 	ewprintf("Now using font %s %d",fontname,EmFont->tf_YSize);
633 	return (TRUE);
634 }
635 #endif
636 
637 /* * * * * * * * * * * * * console output functions  * * * * * * * * * * * * */
638 
639 /*
640  * Write a single character to the screen. Buffered for speed, so ttflush()
641  * does all the work.
642  */
643 /* VOID */
ttputc(c)644 ttputc(c)
645 int c;
646 {
647 	obuf[nobuf++] = (unsigned char)c;
648 	if (nobuf >= NOBUF)
649 		ttflush();
650 }
651 
652 /*
653  * Flush characters from the output buffer.  If the # of characters is
654  * greater than a certain ad-hoc value, turn the cursor off while doing
655  * the write. To avoid extra writes, the output buffer has been preloaded
656  * with the cursor-off sequence.  Outbuf is large enough to hold the extra
657  * 7 characters.
658  */
659 #define	MIN_OFF	8
660 /* VOID */
ttflush()661 ttflush()
662 {
663 	if (nobuf > 0) {
664 		if (nobuf <= MIN_OFF)	/* don't turn off for short writes */
665 			ConWrite(conWriteMsg, obuf, nobuf);
666 		else {
667 			obuf[nobuf++] = '\x9b';
668 			obuf[nobuf++] = ' ';
669 			obuf[nobuf++] = 'p';
670 			ConWrite(conWriteMsg, outbuf, nobuf + 4);
671 		}
672 		nobuf = 0;
673 	}
674 }
675 
676 /*
677  * The caller intends to output an escape sequence, but only flush
678  * the buffer if there's not enough room to hold the complete sequence.
679  * This avoids breaking up escape sequences when we turn the cursor
680  * off in ttflush(), at the expense of some extra function calls.
681  */
ttnflush(n)682 VOID ttnflush(n)
683 int n;
684 {
685 	if ((nobuf + n) > NOBUF)
686 		ttflush();
687 }
688 
689 /* * * * * * * * * * * * * console input functions  * * * * * * * * * * * * */
690 
691 /* Dec.17,1992 Add by H.Ohkubo */
692 #ifdef	KANJI	/* 90.02.05  by S.Yoshida */
693 static	int	nkey = 0;		/* The number of ungetc charactor. */
694 static	int	keybuf[4];		/* Ungetc charactors.		*/
695 #endif	/* KANJI */
696 
697 /*
698  * Read a character (really a KCHAR, > 8 bits), blocking till a character
699  * is put in the input buffer and can be returned.
700  */
ttgetc()701 ttgetc()
702 {
703 	static handle_kbd();
704 /* Dec.17,1992 Add by H.Ohkubo */
705 #ifdef	KANJI	/* 90.02.05  by S.Yoshida */
706 	if (nkey > 0) {
707 		return(keybuf[--nkey]);
708 	}	/* 91.01.14  by K.Maeda ---remove else */
709 #endif	/* KANJI */
710 #ifdef	AUTOSAVE
711 	while (handle_kbd(TRUE))
712 		autosave_handler();
713 #endif	/* AUTOSAVE */
714 	return handle_kbd(FALSE);
715 }
716 
717 /* Dec.17,1992 Add by H.Ohkubo */
718 #ifdef	KANJI	/* 90.02.05  by S.Yoshida */
719 /*
720  * Save pre-readed char to read again.
721  */
ttungetc(c)722 ttungetc(c)
723 int	c;
724 {
725 	keybuf[nkey++] = c;
726 }
727 #endif	/* KANJI */
728 
729 /*
730  * Return TRUE if we've waited for 2 seconds and nothing has happened,
731  * else return false.
732  */
733 
ttwait()734 ttwait()
735 {
736 	static handle_kbd();
737 /* Dec.17,1992 Add by H.Ohkubo */
738 #ifdef	KANJI	/* 90.02.05  by S.Yoshida */
739 	if (nkey > 0) {
740 		return(FALSE);
741 	}
742 #endif	/* KANJI */
743 	return handle_kbd(TRUE);	/* time out after 2 sec */
744 }
745 
746 /*
747  * Common routine for handling character input, with and without timeout.
748  * Handle events until:
749  *
750  *	1) a character is put in the input buffer
751  *	2) if timeout == TRUE, PROMPTWAIT IntuiTicks have gone by
752  *
753  * If timeout == FALSE, the input character is returned and removed from
754  *	the input buffer.
755  *
756  * If timeout == TRUE, returns TRUE if the read timed out, else FALSE.
757  *	Leaves any character typed in the input buffer.
758  */
759 
handle_kbd(timeout)760 static handle_kbd(timeout)
761 register int timeout;
762 {
763 	register struct	IntuiMessage *message;	/* IDCMP message 	*/
764 	register LONG	wakeupmask;		/* which signals?	*/
765 	register int	charfound;		/* got a character yet?	*/
766 	static dispatch(),nextkey();
767 
768 	tickcount = 0;				/* *always* zero the count */
769 	if (nibuf)				/* any chars? return if so */
770 		return timeout ? FALSE : nextkey();
771 
772 	charfound = FALSE;			/* nope -- have to wait	*/
773 	while (!charfound) {
774 #ifdef	KANJI	/* Dec.19,1992 by H.Ohkubo */
775 #ifdef	REXX	/* Dec.20,1992 by H.Ohkubo */
776 		wakeupmask = Wait(INTUITION_MESSAGE | CONREAD_MESSAGE | REXXPORT_MESSAGE);
777 #else	/* NO REXX */
778 		wakeupmask = Wait(INTUITION_MESSAGE | CONREAD_MESSAGE);
779 #endif
780 		if (wakeupmask & CONREAD_MESSAGE) {
781 			register UBYTE	*ch;
782 			int	n;
783 			UBYTE	*ConRead();
784 
785 			ch = ConRead(conReadPort, &n);
786 			if (n > 0) {
787 #ifdef V2
788 			    unsigned short q = PeekQualifier();
789 #endif
790 			    charfound = TRUE;
791 			    while (n-- > 0) {
792 #ifdef V2
793 				if (*ch==' ' && (q & IEQUALIFIER_CONTROL))
794 				    {qkey((KCHAR)0x00);}
795 #ifdef 	LAMIGA_META
796 				else if (use_metakey&&(q&IEQUALIFIER_LCOMMAND))
797 				{ qkey((KCHAR)((*ch&0x7f)|METABIT));}
798 #endif
799 				else
800 				    {qkey((KCHAR)(*ch & 0xff));}
801 				ch++;
802 #else	/* !V2 */
803 				qkey((KCHAR)(*ch & 0xff)); ch++;
804 #endif	/* V2 */
805 			    }
806 			}
807 		}
808 		if (wakeupmask & INTUITION_MESSAGE)
809 #else	/* Original */
810 #ifdef	REXX	/* Dec.20,1992 by H.Ohkubo */
811 		wakeupmask = Wait(INTUITION_MESSAGE|REXXPORT_MESSAGE);
812 #else
813 		wakeupmask = Wait(INTUITION_MESSAGE);
814 #endif
815 #endif	/* KANJI */
816 		/*  Handle Intuiticks specially for speed */
817 		while(message =	GetMsg(EmW->UserPort))
818 			if (message->Class == INTUITICKS) {
819 				tickcount++;
820 				ReplyMsg(message);
821 			} else if (dispatch(message) == TRUE)
822 				charfound = TRUE;
823 #ifdef	REXX	/* Dec.20,1992 by H.Ohkubo */
824 		/* Now handle any rexx messages if we need them */
825 		if (wakeupmask & REXXPORT_MESSAGE) {
826 			struct key	savekey;/* save current keystrokes */
827 
828 			savekey = key;
829 			disprexx(rexxport);
830 			update();
831 			key = savekey;
832 		}
833 #endif
834 
835 		/* time out if enough ticks have gone by without
836 		 * any keyboard input.  We do this *after* all the
837 		 * events in the current list have been dispatched.
838 		 */
839 		if (timeout && (tickcount > PROMPTWAIT))
840 			break;
841 	}
842 
843 
844 	/* If called by ttwait(), return FALSE if a character was found.
845 	 * Else return the next character in the input buffer
846 	*/
847 	return timeout ? (!charfound) : nextkey();
848 }
849 
850 /*
851  * Handle the events we handle...  The result returned indicates if we've put
852  * a character in the input buffer.
853  */
dispatch(msg)854 static dispatch(msg)
855 register struct IntuiMessage *msg;
856 {
857 #ifdef	DO_MENU
858 	register struct	MenuItem	*item;
859 #endif
860 
861 	register int			txheight, txwidth;
862 	register struct RastPort	*rp;
863 	int				dx, dy, fgpen, drmode;
864 #ifndef	KANJI	/* Jan.7,1992 by H.Ohkubo */
865 	static struct InputEvent FakedEvent = { NULL, IECLASS_RAWKEY, 0, 0, 0 };
866 	unsigned char			keybuf[64], altbuf[64];
867 	int				keylen, altlen, i;
868 #ifndef	V11
869 	APTR				deadcodes;
870 #endif
871 #endif	/* KANJI */
872 	class =	msg->Class;		/* grab the info before we 	*/
873 	code = msg->Code;		/* reply to the message		*/
874 	qualifier = msg->Qualifier;
875 	address = msg->IAddress;
876 	x = msg->MouseX;
877 	y = msg->MouseY;
878 #ifndef	KANJI	/* Jan.7,1993 by H.Ohkubo */
879 #ifndef	V11
880 	if (class == RAWKEY)		/* get dead key info		*/
881 		deadcodes = (APTR)address;
882 #endif
883 #endif	/* KANJI */
884 	ReplyMsg(msg);			/* return it to Intuition	*/
885 
886 	switch(class) {			/* see what the fuss is about	*/
887 #ifndef	KANJI	/* Dec.19,1992 by H.Ohkubo */
888 	case RAWKEY:
889 		FakedEvent.ie_Code = code;
890 		FakedEvent.ie_Qualifier = qualifier;
891 #ifndef	V11
892 		FakedEvent.ie_EventAddress = deadcodes;
893 #endif
894 		keylen = (int) RawKeyConvert(&FakedEvent,
895 			keybuf,	(LONG)sizeof(keybuf), NULL);
896 #ifdef	DO_METAKEY
897 		/* Special mapping for ALT-ed keys.  The intent is to get
898 		 * around keymaps where the ALT'ed characters map to
899 		 * things other than (0x80 | (c)).  This may not work
900 		 * for all possible keymaps, but it seems to be ok
901 		 * for the keymaps distributed with 1.2.
902 		 */
903 #ifdef	LAMIGA_META
904 		if (use_metakey &&
905 		    (qualifier & (IEQUALIFIER_ALT|IEQUALIFIER_LCOMMAND))) {
906 			FakedEvent.ie_Qualifier &=
907 			    ~(IEQUALIFIER_ALT | IEQUALIFIER_LCOMMAND);
908 			if (qualifier & IEQUALIFIER_ALT)
909 			    altlen =
910 				(int) RawKeyConvert(&FakedEvent, altbuf,
911 				(LONG)sizeof(altbuf), NULL);
912 			else
913 			{
914 			    altlen = 1;
915 			    altbuf[0] = keybuf[0];
916 			}
917 			if (altlen >= 1)
918 			    qkey((KCHAR)(altbuf[0]|METABIT));
919 			for (i = 1; i < altlen ; i++)
920 			    qkey((KCHAR) altbuf[i]);
921 			return (altlen > 0) ? TRUE : FALSE;
922 		}
923 #else
924 		if (use_metakey &&
925 		    (qualifier & IEQUALIFIER_ALT) {
926 			FakedEvent.ie_Qualifier &= ~IEQUALIFIER_ALT;
927 			altlen = (int) RawKeyConvert(&FakedEvent, altbuf,
928 				(LONG)sizeof(altbuf), NULL);
929 			if (altlen == 1)
930 				altbuf[0] |= METABIT;
931 			for (i = 0; i < altlen ; i++)
932 				qkey((KCHAR) altbuf[i]);
933 			return (altlen > 0) ? TRUE : FALSE;
934 		}
935 #endif	/* LAMIGA_META */
936 #endif	/* DO_METAKEY */
937 		if (keybuf[0]==' ' && (qualifier&IEQUALIFIER_CONTROL))
938 		    keybuf[0] = '\0';
939 		for (i = 0; i < keylen ; i++)
940 			qkey((KCHAR) keybuf[i]);
941 		return (keylen > 0) ? TRUE : FALSE;
942 		break;
943 #endif	/* KANJI */
944 
945 #ifdef	DO_MENU
946 	case MENUPICK:
947 		if (code == MENUNULL)
948 			return (FALSE);
949 		while (code != MENUNULL) {/* handle multiple selection	*/
950 			qmenu(code);
951 			item = ItemAddress(EmacsMenu,(LONG) code);
952 			code = item->NextSelect;
953 		}
954 		return (TRUE);		/* puts KMENU in event queue	*/
955 		break;
956 #endif
957 
958 #ifdef	MOUSE
959 	case MOUSEBUTTONS:			/* fake the mouse key	*/
960 		if (code != SELECTDOWN)		/* ignore SELECTUP	*/
961 			return (FALSE);
962 		qmouse(x, y, qualifier);
963 		return (TRUE);
964 		break;
965 #endif
966 
967 	case NEWSIZE:
968 		/* Sometimes when you resize the window to make it smaller,
969 		 * garbage is left at the right and bottom sides of the
970 		 * window. This code is devoted to (somehow) getting rid
971 		 * of this garbage.  Any suggestions?
972 		 */
973 
974 		rp = EmW->RPort;
975 		fgpen = rp->FgPen;		/* save params		*/
976 		drmode = rp->DrawMode;
977 		SetDrMd(rp, (LONG) JAM1);
978 		SetAPen(rp, (LONG) EmW->RPort->BgPen);
979 
980 		/* Check the bottom of the window
981 		 */
982 		txheight = EmW->Height - EmW->BorderTop - EmW->BorderBottom;
983 		if (dy = (txheight % FontHeight(EmW)))
984 			RectFill(rp,
985 				(LONG) EmW->BorderLeft,
986 				(LONG) EmW->BorderTop + txheight - dy - 1,
987 				(LONG) (EmW->Width - 1) - EmW->BorderRight,
988 				(LONG) (EmW->Height - 1) - EmW->BorderBottom);
989 
990 		/* Check the right side
991 		 */
992 		txwidth = EmW->Width - EmW->BorderLeft - EmW->BorderRight;
993 		if (dx = txwidth % FontWidth(EmW))
994 			RectFill(rp,
995 				(LONG) EmW->BorderLeft + txwidth - dx - 1,
996 				(LONG) EmW->BorderTop,
997 				(LONG) (EmW->Width - 1) - EmW->BorderRight,
998 				(LONG) (EmW->Height - 1) - EmW->BorderBottom);
999 
1000 		SetDrMd(rp, (LONG) drmode);
1001 		SetAPen(rp, (LONG) fgpen);	/* restore colors */
1002 
1003 		/* Tell the console device to resize itself */
1004 		ttputc(CSI);
1005 		ttputc('t');
1006 		ttputc(CSI);
1007 		ttputc('u');
1008 		ttflush();
1009 
1010 		/* Signal the editor that a new size has occurred.
1011 		 * I may break down and do this asynchronously...
1012 		 */
1013 		qkey(KRESIZE);
1014 		return (TRUE);			/* we done (finally)	*/
1015 		break;
1016 
1017         case CLOSEWINDOW:
1018 		/* Calling quit() directly is not a guaranteed win. */
1019 		quit(FFRAND, 1);
1020 		return (FALSE);
1021                 break;
1022 
1023 	default:
1024 		panic("HandleMsg: unknown event!!!");
1025 		break;
1026 	}
1027 	return(FALSE);
1028 }
1029 
1030 /*
1031  * Return the current size of the virtual terminal in nrow and ncol,
1032  * making sure we don't go beyond the size of the internal video array.
1033  * Assumes the current font is monospaced.
1034  */
1035 VOID
1036 setttysize()
1037 {
1038 	nrow = (EmW->Height - TOP_OFFSET
1039 			- EmW->BorderBottom) / FontHeight(EmW);
1040 	ncol = (EmW->Width - EmW->BorderLeft
1041 			- EmW->BorderRight) / FontWidth(EmW);
1042 	if (nrow < 1)		nrow = 1;
1043 	if (ncol < 1)		ncol = 1;
1044 }
1045 
1046 /*
1047  * Exit as soon as possible, after displaying the message.
1048  */
1049 /* VOID */
1050 panic(s)
1051 char *s;
1052 {
1053 	ewprintf(s);		/* put message at bottom	*/
1054 	Delay((ULONG) 90);	/* wait 1.5 seconds		*/
1055 	ttclose();		/* get rid of window &resources	*/
1056 	exit(10000);		/* go 'way			*/
1057 }
1058 
1059 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1060  *			 Event buffer management		 *
1061  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1062 
1063 /*
1064  * Return next key in the input buffer, if any available.  Returns -1 if not.
1065  */
1066 static int
1067 nextkey()
1068 {
1069 	register KCHAR k;
1070 
1071 	if (nibuf <= 0) {		/* shouldn't happen, but could... */
1072 		nibuf = 0;
1073 		return -1;
1074 	} else {
1075 		k = ibuf[ibufo++];
1076 		nibuf--;
1077 		ibufo %= NIBUF;
1078 		return (int) k;
1079 	}
1080 }
1081 
1082 /*
1083  * Return true if there are some characters available in the input buffer.
1084  */
1085 typeahead()
1086 {
1087 /* Dec.17,1992 by H.Ohkubo */
1088 #ifdef	KANJI	/* 90.02.05  by S.Yoshida */
1089 	if (nkey > 0) {
1090 		return(TRUE);
1091 	}
1092 #endif	/* KANJI */
1093 	return (nibuf > 0);
1094 }
1095 #ifndef	KANJI	/* Dec.19,1992 by H.Ohkubo */
1096 /*
1097  * Add a key to the input queue
1098  */
1099 static VOID
1100 qkey(k)
1101 KCHAR k;
1102 {
1103 	if (nibuf < NIBUF)
1104 		ibuf[(ibufo + nibuf++) % NIBUF] = k;
1105 }
1106 #endif
1107 
1108 #ifdef	MOUSE
1109 /*
1110  * Add a mouse event to the input queue, calculating the row and column
1111  * value from the current height and width of the window's font.
1112  */
1113 
1114 static VOID
1115 qmouse(x, y, qual)
1116 SHORT x, y;
1117 USHORT qual;
1118 {
1119 	register int	myqual = MQ_NOQUAL;
1120 	register int	row, col;
1121 	register WINDOW	*wp;
1122 
1123 	if (!allow_mouse_event)
1124 		return;
1125 
1126 	/* get row, column	*/
1127 	col = (x - EmW->BorderLeft) / FontWidth(EmW);
1128 	row = (y - TOP_OFFSET) / FontHeight(EmW);
1129 
1130 	/* find out which kind of window was clicked in */
1131 	for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
1132 		if ((row >= wp->w_toprow) &&
1133 			(row <= (wp->w_toprow + wp->w_ntrows)))
1134 			break;
1135 	if (wp == NULL)
1136 		myqual |= MQ_ECHO;
1137 	else if (row == (wp->w_toprow + wp->w_ntrows))
1138 		myqual |= MQ_MODE;
1139 	else
1140 		myqual |= MQ_WINDOW;
1141 
1142 	/* figure out qualifiers	*/
1143 	if (qual & IEQUALIFIER_CONTROL)
1144 		myqual |= MQ_CTRL;
1145 	if (qual & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
1146 		myqual |= MQ_SHIFT;
1147 	if (qual & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
1148 		myqual |= MQ_ALT;
1149 #ifdef META_LAMIGA
1150 	if (qual & IEQUALIFIER_LCOMMAND)
1151 		myqual |= MQ_ALT;
1152 #endif
1153 	/*
1154  	 * Queue up the whole mess.  If user didn't click in the echo
1155 	 * line, transmit the x, y values to the mouse function
1156 	 */
1157 	qkey(KW___MOUSE + myqual);
1158 	if (MQ_WHERE(myqual) != MQ_ECHO) {
1159 		qkey(M_X_ZERO + col);
1160 		qkey(M_Y_ZERO + row);
1161 	}
1162 }
1163 #endif
1164 
1165 #ifdef	DO_MENU
1166 /*
1167  * Add a menu event to the queue.
1168  */
1169 static VOID
1170 qmenu(code)
1171 USHORT code;
1172 {
1173 	qkey(KMENU);		/* menu key sequence	*/
1174 	qkey(((KCHAR) MENUNUM(code)) + MN_OFFSET);
1175 	qkey(((KCHAR) ITEMNUM(code)) + MN_OFFSET);
1176 	qkey(((KCHAR) SUBNUM(code)) + MN_OFFSET);
1177 }
1178 #endif
1179