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