/**************************************************** * vt100 emulator - window/keyboard support * :ts=8 * * v2.9 ACS - See change summary. * v2.7 870825 ACS - Provide an info/status window rather than using * req(). Better error handling. * v2.6 870227 DBW - bug fixes for all the stuff in v2.5 * v2.5 870214 DBW - more additions (see readme file) * v2.4 861214 DBW - lots of fixes/additions (see readme file) * v2.3 861101 DBW - minor bug fixes * v2.2 861012 DBW - more of the same * v2.1 860915 DBW - new features (see README) * 860823 DBW - Integrated and rewrote lots of code * v2.0 860809 DBW - Major rewrite * v1.1 860720 DBW - Switches, 80 cols, colors, bug fixes * v1.0 860712 DBW - First version released * ****************************************************/ #include "vt100.h" static char *infkey[] = { /* F-keys resulting from RawKeyConvert() */ "0~", "1~", "2~", "3~", "4~", "5~", "6~", "7~", "8~", "9~", "10~", "11~", "12~", "13~", "14~", "15~", "16~", "17~", "18~", "19~", NULL}; /* Cursor keys resulting from RawKeyConvert() and their output values */ static struct { char *in; /* in sequence */ char *out_curapp; /* out sequence in p_curapp */ char *out; /* out sequence !in p_curapp */ } ckeys[] = { "A", "OA", "[A", "T", "OA", "[A", "B", "OB", "[B", "S", "OB", "[B", "C", "OC", "[C", " A~", "OC", "[C", "D", "OD", "[D", " @~", "OD", "[D", NULL, NULL, NULL }; /* Numeric keypad for A2000 and A500 have keys that the A1000 doesn't have. ** Use them for the VT100 F-keys. */ static char npfkey[] = { '(', 'P', ')', 'Q', '*', 'R', '/', 'S', 0, 0}; /* Numeric keypad return values excluding HELP, '-' and ENTER */ static char keypad[] = { 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', '\0'}; /* Numeric keypad return values for HELP, - and ENTER */ static char speckeypad[] = { '-', 'l', '-', '.', 'n', '.', '\015', 'M', '\015', '\0', '\0', '\0' }; /* For InfoMsg...may be changed by a NEWSIZE msg in vt100.c */ int reqminx, /* Min value for x in reqwindow (pixels) */ reqmaxx, /* Max value for x in reqwindow (pixels) */ reqmaxlen, /* Max # chars in reqwindow */ reqminy, /* Min value for y in reqwindow (scan lines) */ reqmaxy, /* Max value for y in reqwindow (scan lines) */ reqfudge; /* Clear space between border and start of 1st char */ int reqy; /* Current pixel location in reqwindow */ void ReqNewSize(), OpenReqWindow(); /*************************************************** * function to swap the use of backspace and delete ***************************************************/ void swap_bs_del() { if (p_bs_del) p_bs_del = 0; else p_bs_del = 1; } /************************************************* * function to get file name (via a requestor) *************************************************/ void req(prmpt,name,getinp) char *prmpt,*name; int getinp; { ULONG class; #if MANX USHORT RemoveGadget(); #endif /* MANX */ int lprmpt, lname; struct IntuiMessage *Msg; if(reqwinup == 0) OpenReqWindow(); if(!getinp) { InfoMsg2Line(prmpt, name); return; } lprmpt = strlen(prmpt); lname = strlen(name); /* Don't use strings longer than what we've provided space for. */ if(lprmpt > (MAXGADSTR-1)) { emits("Prompt too long - truncated.\n"); lprmpt = MAXGADSTR-1; } if(lname > (MAXGADSTR-1)) { emits("Name too long - truncated.\n"); lname = MAXGADSTR-1; } if (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) { class = Msg->Class; ReplyMsg((struct Message *)Msg); if(class == REQCLEAR) numreqs = 0; if(class == NEWSIZE) ReqNewSize(reqwindow->Height, reqwindow->Width); } /* Make sure the prompt gets updated */ if (numreqs == 1 && strcmp(Prompt,prmpt) != 0) { EndRequest(&myrequest,reqwindow); do { Wait(1L << reqwindow->UserPort->mp_SigBit); while (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) { class = Msg->Class; ReplyMsg((struct Message *)Msg); if(class == NEWSIZE) ReqNewSize(reqwindow->Height, reqwindow->Width); } } while (class != REQCLEAR); numreqs = 0; } /* copy in a prompt and a default */ strncpy(Prompt,prmpt,lprmpt); Prompt[lprmpt] = '\0'; strncpy(InpBuf,name,lname); InpBuf[lname] = '\0'; mystrinfo.BufferPos = lname; if (numreqs == 1) { /* If there is a requester... reuse it */ RefreshGadgets(&mystrgad, reqwindow, &myrequest); Delay(2L); } else { /* otherwise create it */ while(numreqs != 1) { if (Request(&myrequest, reqwindow) == 0) { emits("ERROR - CAN'T CREATE REQUESTOR FOR:\n"); emits(Prompt); emit('\n'); emits(InpBuf); emit('\n'); return; } else numreqs = 1; do { Wait(1L << reqwindow->UserPort->mp_SigBit); while (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) { class = Msg->Class; ReplyMsg((struct Message *)Msg); if(class == REQCLEAR) numreqs = 0; if(class == NEWSIZE) ReqNewSize(reqwindow->Height, reqwindow->Width); } } while (class != REQSET); } /* end while numreqs != 0 */ } /* end else */ /* if we don't want input, we're done */ if (getinp == 0 || numreqs == 0) return; if((reqwindow->Flags & WINDOWACTIVE) != WINDOWACTIVE) { WindowToFront(reqwindow); ActivateWindow(reqwindow); do { Wait(1L << reqwindow->UserPort->mp_SigBit); while(Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) { class = Msg->Class; ReplyMsg((struct Message *)Msg); if(class == NEWSIZE) ReqNewSize(reqwindow->Height, reqwindow->Width); } } while (class != ACTIVEWINDOW); } /* here is where we pre-select the gadget */ if (!ActivateGadget(&mystrgad,reqwindow,&myrequest)) { /* wait for his/her hands to get off the keyboard (Amiga-key) */ Delay(20L); while (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) { ReplyMsg((struct Message *)Msg); if(class == NEWSIZE) ReqNewSize(reqwindow->Height, reqwindow->Width); } /* try once more before giving up... */ ActivateGadget(&mystrgad,reqwindow,&myrequest); } /* wait for input to show up */ while (1) { if ((NewMessage = (struct IntuiMessage *) GetMsg(reqwindow->UserPort)) == FALSE) { Wait(1L<UserPort->mp_SigBit); continue; } class = NewMessage->Class; ReplyMsg((struct Message *)NewMessage); /* the requestor got terminated... yea!! */ if (class == REQCLEAR) break; if(class == NEWSIZE) ReqNewSize(reqwindow->Height, reqwindow->Width); /* maybe this is a menu item to handle */ /* if (class == MENUPICK) handle_menupick(class,code); */ } /* all done, so return the result */ numreqs = 0; strcpy(name,InpBuf); if (reqwinup && ((reqwindow->Flags) & WINDOWACTIVE)) ActivateWindow(mywindow); } /************************************************* * function to print a string *************************************************/ void emits(string) char string[]; { int i; char c; i=0; while (string[i] != 0) { c=string[i]; if (c == 10) emit(13); emit(c); i += 1; } } /************************************************* * function to output ascii chars to window *************************************************/ void emit(c) char c; { static char wrap_flag = 0; /* are we at column 80? */ c &= 0x7F; switch( c ) { case '\t': x += (Ysize * 8) - ((x-MINX) % (Ysize * 8)); break; case 10: /* lf */ doindex('D'); break; case 13: /* cr */ x = MINX; break; case 8: /* backspace */ x -= Xsize; if (x < MINX) x = MINX; break; case 12: /* page */ x = MINX; y = MINY; SetAPen(mywindow->RPort,0L); RectFill(mywindow->RPort,(long)MINX, (long)(MINY-BaseLine),(long)(MAXX+(Xsize-1)),(long)(MAXY+1)); SetAPen(mywindow->RPort,1L); break; case 7: /* bell */ cmd_beep(0L); break; default: if (c < ' ' || c > '~') break; if (p_wrap && wrap_flag && x >= MAXX) { x = MINX; doindex('D'); if (y > MAXY) { y = MAXY; ScrollRaster(mywindow->RPort,0L,(long)Ysize,(long)MINX, (long)(MINY-(BaseLine+1)),(long)(MAXX+(Xsize - 1)), (long)(MAXY+1)); } } Move(mywindow->RPort,(long)x,(long)y); if (curmode&FSF_BOLD) { if (p_depth > 1) { SetAPen(mywindow->RPort,(long)(2+(1^p_screen))); SetSoftStyle(mywindow->RPort,(long)curmode,253L); } else SetSoftStyle(mywindow->RPort,(long)curmode,255L); } else SetSoftStyle(mywindow->RPort,(long)curmode,255L); if (curmode&FSF_REVERSE) { SetDrMd(mywindow->RPort,(long)(JAM2+INVERSVID)); Text(mywindow->RPort,&c,1L); SetDrMd(mywindow->RPort,(long)JAM2); } else Text(mywindow->RPort,&c,1L); if (curmode&FSF_BOLD) SetAPen(mywindow->RPort,1L); x += Xsize; } /* end of switch */ if (y > MAXY) { y = MAXY; x = MINX; ScrollRaster(mywindow->RPort,0L,(long)Ysize,(long)MINX, (long)(MINY-(BaseLine+1)),(long)(MAXX+(Xsize - 1)), (long)(MAXY+1)); } if (x > MAXX) { wrap_flag = 1; x = MAXX; } else wrap_flag = 0; } /************************************************* * function to output ascii chars to window (batched) *************************************************/ void emitbatch(la,lookahead) int la; char *lookahead; { int i; Move(mywindow->RPort,(long)x,(long)y); i = x / Xsize; if (i+la >= maxcol) { if (p_wrap == 0) la = maxcol - i; else { lookahead[la] = 0; emits(lookahead); return; } } if (curmode&FSF_BOLD) { if (p_depth > 1) { SetAPen(mywindow->RPort,(long)(2+(1^p_screen))); SetSoftStyle(mywindow->RPort,(long)curmode,253L); } else SetSoftStyle(mywindow->RPort,(long)curmode,255L); } else SetSoftStyle(mywindow->RPort,(long)curmode,255L); if (curmode&FSF_REVERSE) { SetDrMd(mywindow->RPort,(long)(JAM2+INVERSVID)); Text(mywindow->RPort,lookahead,(long)la); SetDrMd(mywindow->RPort,(long)JAM2); } else Text(mywindow->RPort,lookahead,(long)la); if (curmode&FSF_BOLD) SetAPen(mywindow->RPort,1L); x += (Xsize * la); } /****************************** * Manipulate cursor ******************************/ void cursorflip() { SetDrMd(mywindow->RPort,(long)COMPLEMENT); SetAPen(mywindow->RPort,3L); RectFill(mywindow->RPort, (long)(x), (long)(y-BaseLine), (long)(x+Xsize-1), (long)(y+(Ysize-BaseLine-1))); SetAPen(mywindow->RPort,1L); SetDrMd(mywindow->RPort,(long)JAM2); } /************************************************ * function to take raw key data and convert it * into ascii chars **************************************************/ int toasc(retstr, code, qual, maxlen, ia, local) unsigned char *retstr; unsigned int code,qual; int local, maxlen; APTR ia; { unsigned int ctrl, alt, npad; int i, cmatch, length = 0; /* length of returned string */ unsigned char *p = retstr; static struct InputEvent ievent = {NULL, IECLASS_RAWKEY,0,0,0}; *p = '\0'; if(code >= 0x5a && code <= 0x5d) { /* Appears to be one of the keys (, ), /, * on the newer keypads. ** Convert them to VT100 F1-F4 */ char t, *convert = "()*/"; t = convert[code - 0x5a]; for(i = 0; npfkey[i]; i++) if(t == npfkey[i]) { strcpy(p, "\033O"); *(p+2) = npfkey[i+1]; *(p+3) = '\0'; length = 3; break; } if(*p) { sendstring(p); return length; } } ctrl = qual & IEQUALIFIER_CONTROL; alt = qual & (IEQUALIFIER_LALT | IEQUALIFIER_RALT); npad = qual & IEQUALIFIER_NUMERICPAD; ievent.ie_Qualifier = qual; ievent.ie_Code = code; /* get previous codes from location pointed to by IAddress * this "magic" pointer is valid intil the IntiiMessage is * replied */ ievent.ie_position.ie_addr = ia; length = RawKeyConvert(&ievent, retstr, (LONG)maxlen, NULL); if(length == 0) return length; *(p+length) = '\0'; /* Null terminate the value */ if(npad && length == 1) { /* keypad (excluding HELP key)? */ register char t = *p; if(t == '(' || t == ')' || t == '*' || t == '/') { for(i = 0; npfkey[i]; i++) if(t == npfkey[i]) { strcpy(p, "\033O"); *(p+2) = npfkey[i+1]; *(p+3) = '\0'; length = 3; break; } } else if((t >= '0') && (t <= '9')) { if(p_keyapp) { strcpy(p, "\033O"); *(p+2) = keypad[t-'0']; *(p+3) = '\0'; length = 3; } /* else *p is correct */ } else for(i = 0; speckeypad[i]; i += 3) if(speckeypad[i] == t) { if(p_keyapp) { strcpy(p, "\033O"); *(p+2) = speckeypad[i+1]; length = 3; } else *p = speckeypad[i+2]; break; } } else if((length == 3) && (strcmp(p, "\233?~") == 0)) { /* HELP key -- only gen something if in app keypad mode */ if(p_keyapp) { strcpy(p, "\033Om"); length = 3; } else { *p = '\0'; length = 0; } } else if(length > 1 && retstr[0] == 0x9b) { /* cursor or F-keys? */ cmatch = 0; for(i = 0; ckeys[i].in && !cmatch; i++) { if(p_curapp && strcmp((p+1), ckeys[i].in) == 0) { strcpy((p+1), ckeys[i].out_curapp); *p = 0x1b; length = strlen(ckeys[i].out_curapp)+1; cmatch = 1; } else if(strcmp((p+1), ckeys[i].in) == 0) { strcpy((p+1), ckeys[i].out); *p = 0x1b; length = strlen(ckeys[i].out)+1; cmatch = 1; } } if(!cmatch) { /* Not cursor, try F-keys */ for(i = 0; infkey[i]; i++) { if(strcmp((p+1), infkey[i]) == 0) { if(i > 9) strcpy(p, p_F[i-10]); else strcpy(p, p_f[i]); if(!script_on && *p == p_keyscript) { script_start(p+1); *p = '\0'; length = 0; } length = strlen(p); break; } } } } else if(ctrl && (length == 1)) { /* Control key shortcuts? */ switch(*p) { case '6': *p = 30; break; case '2': case ' ': /* @ done by RawKeyConvert? */ if(!local) *p = (alt ? 128 : 0); break; case '-': case '?': *p = 31; break; } } else if(alt && !local && length == 1) *p |= 0x80; /* Add hi bit if ALT is the only modifier */ else if(p_bs_del && *p == 8 && length == 1) *p = 0x7f; else if(p_bs_del && *p == 0x7f && length == 1) *p = 8; /* if (ctrl) { Are all of these taken care of? if (c > '`' && c <= 127) c -= 96; else if (c > '@' && c <= '_') c -= 64; else if (c == '6') c = 30; else if (c == '-' || c == '?') c = 31; } */ for(i = 0; i < length; i++) sendchar(*(p++)); return(length); } void KillReq() { struct IntuiMessage *Msg; ULONG class; if(numreqs != 0) { EndRequest(&myrequest,reqwindow); do { Wait(1L << reqwindow->UserPort->mp_SigBit); while (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) { class = Msg->Class; ReplyMsg((struct Message *)Msg); } } while (class != REQCLEAR); numreqs = 0; } if(reqwinup) { /* First, clear out all pending messages */ while (Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) { class = Msg->Class; ReplyMsg((struct Message *)Msg); } NewReqWindow.LeftEdge = reqwindow->LeftEdge; /* Remember ... */ NewReqWindow.TopEdge = reqwindow->TopEdge; /* ...where... */ NewReqWindow.Width = reqwindow->Width; /* ...the user... */ NewReqWindow.Height = reqwindow->Height; /* ...put it. */ CloseWindow(reqwindow); /* Now we can close the window */ reqwinup = 0; } } void InfoMsg2Line(header, msg) char *header, *msg; { ScrollInfoMsg(1); InfoMsgNoScroll(header); ScrollInfoMsg(1); InfoMsgNoScroll(msg); ScrollInfoMsg(1); } void InfoMsg1Line(msg) char *msg; { ScrollInfoMsg(1); InfoMsgNoScroll(msg); ScrollInfoMsg(1); } /* Output the specified data to the "info" window */ void ScrollInfoMsg(lines) int lines; { /* ULONG class; struct IntuiMessage *Msg; */ int pixels = lines * Ysize; if(!reqwinup) OpenReqWindow(); /* if(Msg=(struct IntuiMessage *)GetMsg(reqwindow->UserPort)) { class = Msg->Class; ReplyMsg(Msg); if(class == NEWSIZE) ReqNewSize(reqwindow->Height, reqwindow->Width); } */ if ( (reqy += pixels) > reqmaxy) { reqy = reqmaxy; if(pixels > 0) ScrollRaster(reqwindow->RPort, 0L, (LONG)pixels, (LONG)reqminx, (LONG)reqminy, (LONG)(reqmaxx+(Xsize - 1)), (LONG)(reqmaxy+(Ysize - 1))); /* Was: (LONG)(wp->Width - wp->BorderRight), (LONG)(wp->Height - wp->BorderBottom)); */ } } void InfoMsgNoScroll(msg) char *msg; { LONG msglen = strlen(msg); ScrollInfoMsg(0); /* Ensure that the msg will be visible */ if(msglen > reqmaxlen) msglen = reqmaxlen; /* Position the pen at the baseline of the character (BaseLine scan ** lines into it). */ Move(reqwindow->RPort, (LONG)reqminx, (LONG)(reqy+BaseLine)); Text(reqwindow->RPort, msg, msglen); } void ReqNewSize(height, width) SHORT height, width; { register struct Window *wp = reqwindow; int oldmaxy; /* Compute min and max for x and y coordinates. Note that for y the ** value is for the *top* of the character, not the baseline. Text() ** uses a baseline value and so it must be adjusted prior to the call. ** When computing the max values, calculate them so that we will have ** sufficient room for an entire character. */ oldmaxy = reqmaxy; reqminy = wp->BorderTop + reqfudge; reqmaxy = (((height - reqminy - wp->BorderBottom) / Ysize) * Ysize) + (reqminy-Ysize); reqminx = wp->BorderLeft + reqfudge; reqmaxx = (((width - reqminx - wp->BorderRight) / Xsize) * Xsize) + (reqminx-Xsize); reqmaxlen = (reqmaxx+(Xsize-1)) / Xsize; if(oldmaxy > reqmaxy) { /* Clean up the bottom of the window */ int temp = height - wp->BorderBottom - reqmaxy; ScrollRaster(wp->RPort, 0L, (LONG)temp, (LONG)reqminx, (LONG)reqmaxy, (LONG)(width - wp->BorderRight), (LONG)(height - wp->BorderBottom)); } } void OpenReqWindow() { struct IntuiMessage *Msg; ULONG class; void ReqNewSize(); static init = 1; if(init) { myrequest.LeftEdge = (myrequest.LeftEdge * Xsize) + 5; myrequest.TopEdge = (myrequest.TopEdge * Ysize) + 2; myrequest.Width = (myrequest.Width * Ysize) + 4; myrequest.Height = (myrequest.Height * Xsize) + 6; mydonegad.LeftEdge = (mydonegad.LeftEdge * Xsize) + 2; mydonegad.TopEdge = (mydonegad.TopEdge * Ysize) + 2; mydonegad.Width = ((strlen(donetxt.IText) + 1) * Xsize) + 0; mydonegad.Height = (mydonegad.Height * Ysize) + 2; mystrgad.LeftEdge = (mystrgad.LeftEdge * Xsize) + 2; mystrgad.TopEdge = (mystrgad.TopEdge * Ysize) + 4; mystrgad.Width = (mystrgad.Width * Ysize) + 0; mystrgad.Height = (mystrgad.Height * Xsize) + 2; donetxt.LeftEdge = (donetxt.LeftEdge * Xsize) + 0; donetxt.TopEdge = (donetxt.TopEdge * Ysize) + 0; mystrtxt.LeftEdge = (mystrtxt.LeftEdge * Xsize) + 2; mystrtxt.TopEdge = (mystrtxt.TopEdge * Ysize) + 2; NewReqWindow.Width = (NewReqWindow.Width * Xsize) + 4 + 18; NewReqWindow.LeftEdge = (NewReqWindow.LeftEdge * Xsize) + 2; NewReqWindow.LeftEdge = NewWindow.LeftEdge + NewWindow.Width - NewReqWindow.Width; NewReqWindow.TopEdge = (NewReqWindow.TopEdge * Ysize) + 7; NewReqWindow.Height = (NewReqWindow.Height * Ysize) + 11 + 2; init = 0; } reqwindow = OpenWindow(&NewReqWindow); do { Wait(1L << reqwindow->UserPort->mp_SigBit); while(Msg = (struct IntuiMessage *)GetMsg(reqwindow->UserPort)) { class = Msg->Class; ReplyMsg((struct Message *)Msg); } } while (class != ACTIVEWINDOW); reqfudge = 0; /* Leave 0 pixels/scan lines between border and char */ ReqNewSize(reqwindow->Height, reqwindow->Width); reqy = reqminy; /* Top of character set by ReqNewSize() */ reqwinup = 1; if (reqwinup && ((reqwindow->Flags) & WINDOWACTIVE)) ActivateWindow(mywindow); }