/************************************************************************ * This program is Copyright (C) 1986 by Jonathan Payne. JOVE is * * provided to you without charge, and with no warranty. You may give * * away copies of JOVE, including sources, provided that this notice is * * included in all the files. * ************************************************************************/ /*+++* * title: mac.c * abstract: Macintosh-specific stuff for JOVE * author: T.R.Hageman, Groningen, The Netherlands. * (loosely based on Ken Mitchum's original 4.14 port * using the LightSpeed C compiler.) * created: june 1991 * modified: * description: * this file complements jove.c, proc.c, misc.c and tty.c * and completely replaces fkeys.c and term.c * * TODO: * probably need to supply / / * Get my hands on a Macintosh C Compiler and TEST IT! *---*/ #include "tune.h" #ifdef MAC RCS("$Id: mac.c,v 0.13 1993/10/29 02:43:03 tom Exp tom $") #include "jove.h" #include "ctype.h" #include "io.h" #include "process.h" #include "readdir.h" #include "screen.h" #include "termcap.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for clock() */ /* * clock support (if not in ) */ #ifndef CLK_TCK typedef unsigned long clock_t; # define CLK_TCK 60 # define clock() TickCount() #endif /*======================================================================*/ /* SCREEN.C */ /*======================================================================*/ #ifdef COLOR void set_color(color) { register int bg_color, fg_color; if (color == CurrColor) return; if (False(UseColor)) return; CurrColor = color; bg_color = BG_COLOR(color); fg_color = FG_COLOR(color); if (fg_color == bg_color) fg_color ^= WHITE; /* choose complementary color */ printf("\033[%d;%dm", fg_color + 30, bg_color + 40); } #endif /* COLOR */ private void scr_inval __(( void )); private void scr_inval() { register int i = ILI; do { i_set(i, 0); v_inval(); } while (--i >= 0); } /*=================== ANSI video terminal emulation ====================*/ /* Initial font (should be mono-spaced) */ #define FONT_ID monaco #define FONT_WIDTH 6 #define FONT_SIZE 9 #define FONT_DESCENT 2 #define FONT_HEIGHT (FONT_SIZE + FONT_DESCENT) /* These are system constants, really. */ #define MENUHEIGHT 20 /* height (in pixels) of menu bar */ #define WTITLEHEIGHT 20 /* height (in pixels) of title bar */ private WindowPtr theScreen; /* Mac window used as screen for JOVE */ private WindowRecord theScreenRecord;/* window record for JOVEs screen */ private Rect theLimitRect, /* limiting rect for window moves */ theBoundsRect; /* current bounds rect for window */ #define MAX_WIDTH (theLimitRect.right - theLimitRect.left) #define MAX_HEIGTH (theLimitRect.bottom - theLimitRect.top - WTITLEHEIGHT) #define MIN_ROWS 10 /* JOVEs window at least this big */ #define MIN_COLS 40 private struct wind_config { int w_width, /* size of the screen */ w_height; int w_rows; /* rows of characters which fit the window */ int w_cols; } wc_std, wc_user, *wc; private Rect workarea; /* the text portion of theScreen, i.e. ((0, 0), (CO*char_width-1, LI*char_height-1)) */ /* [TRH] Does quickdraw use Rects inclusive or exclusive lower/right boundary? */ #define scr_width (workarea.right) #define scr_height (workarea.bottom) #define SetWork(wc) (scr_width = (wc)->w_width - SCROLL_WIDTH - 1, \ scr_height = (wc)->w_height - 1) #define SetBounds(wc) (SetRect(&theBoundsRect, theLimitRect.left, \ theLimitRect.top + WTITLEHEIGHT, \ theLimitRect.left + (wc)->w_width, \ theLimitRect.top + WTITLEHEIGHT + \ (wc)->w_height)) private short char_width = FONT_WIDTH, char_height = FONT_HEIGHT, char_descent = FONT_DESCENT; private void wc_adjust __(( struct wind_config *_(wc), int _(width), int _(height) )); private void wc_adjust(wc, width, height) { int rows = (height - 1) / char_height, max_rows = (MAX_HEIGHT - 1) / char_height, cols = (width - SCROLL_WIDTH - 1) / char_width, max_cols = (MAX_WIDTH - SCROLL_WIDTH - 1) / char_width; if (rows < MIN_ROWS) rows = MIN_ROWS; if (rows > max_rows) rows = max_rows; if (cols < MIN_COLS) cols = min_cols; if (cols > max_cols) cols = max_cols; if (cols > MAXCOLS) /* this is JOVEs limit */ cols = MAXCOLS; wc->w_rows = rows; wc->w_cols = cols; wc->w_height = rows * char_height + 1; wc->w_width = cols * char_width + SCROLL_WIDTH + 1; SetWork(wc); } private void wc_init() { wc = &wc_std; wc_adjust(wc, MAX_WIDTH, MAX_HEIGHT); SetBounds(wc); wc_user = wc_std; } private void scroll_lines __(( int _(top), int _(bottom), int _(num) )); private void scroll_lines(top, bottom, num) { register int cheight = char_height; Rect r; RgnHandle updateRgn = NewRgn(); SetRect(&r, 0, top * cheight, scr_width, (bottom + 1) * cheight - 1); ScrollRect(&r, 0, num * cheight, updateRgn); DisposeRgn(updateRgn); } #ifdef FAST_IDLINE private void MACins_line __(( int _(top), int _(bottom), int _(num) )); private void MACins_line(top, bottom, num) { SetPort(theScreen); scroll_lines(top, bottom, num); } private void MACdel_line __(( int _(top), int _(bottom), int _(num) )); private void MACdel_line(top, bottom, num) { SetPort(theScreen); scroll_lines(top, bottom, -num); } void IDline_setup(tname) const char *tname; { TTins_line = MACins_line; TTdel_line = MACdel_line; ScrollLimit = 100; } #endif #ifdef FAST_FLASH void flash() { cursoff(); SetPort(theScreen); InvertRect(&workarea); DoSit(1); SetPort(theScreen); InvertRect(&workarea); } #endif /* FAST_FLASH */ /*#define FULL /* uncomment this if you want full implementation */ #ifdef FULL # define MAX_ESC_ARGS 6 /* should be enough... */ #else # define MAX_ESC_ARGS 2 /* minimal version */ #endif private int esc_arg[MAX_ESC_ARGS+1], /* esc_args[0] used as accumulator */ esc_state; /* * escape state: * = 0 outside escape sequence, * > 0 getting argument (index into esc_args), * < 0 intermediate states. */ #define NO_ESC_SEEN 0 #define ESC_SEEN -1 #define ESC_BRACKET_SEEN -2 private char qe_seen; /* set if "ESC [ =" or "ESC [ ?" seen */ private int Vcol, Vrow; /* Virtual cursor position */ private int Vraw ZERO; /* raw/cooked newline mode */ #define Vfgcolor (thePort->fgColor) #define Vbgcolor (thePort->bgColor) #ifdef COLOR private int colormap[] = { blackColor, redColor, greenColor, yellowColor, blueColor, magentaColor, cyanColor, whiteColor }; #endif #define Ring() (SysBeep(2)) #define ClrEol() { \ Rect r; \ int y = Vrow * char_height; \ SetRect(&r, Vcol * char_width, y, scr_width - 1, y + char_height - 1); \ EraseRect(&r); \ } #define ClrEos() { \ EraseRect(&workarea); \ Vcol = Vrow = 0; /* home cursor */ \ } #define CurAbs(row, col) { \ if ((Vrow = row) > ILI) Vrow = ILI; \ if ((Vcol = col) >= CO) Vcol = CO-1; \ } #define CurRight(i) { \ if ((Vcol += i) >= CO) Vcol = CO-1; \ } #define CurLeft(i) { \ if ((Vcol -= i) < 0) Vcol = 0; \ } #define CurUp(i) { \ if ((Vrow -= i) < 0) Vrow = 0; \ } #define CurDown(i) { \ if ((Vrow += i) > ILI) Vrow = ILI; \ } #ifdef FULL private int saved_Vpos; #define CurSave() (saved_Vpos = Vpos) #define CurRestore() (Vpos = saved_Vpos) #endif /* FULL */ /* cursor display: */ private int Vcurs ZERO; /* >0 ON, =0 OFF, <0 INHIBITED */ private Rect Vcursrect; private clock_t Vcursflip; private void HideTxtCursor __((void)); private void HideTxtCursor() /* Remove cursor */ { if (Vcurs > OFF) { /* cursor is on */ Vcurs = OFF; InvertRect(&Vcursrect); /* so turn it off */ } } private void ShowTxtCursor __((void)); private void ShowTxtCursor() /* Display cursor (if not inhibited) */ { if (Vcurs == OFF) { register int x = Vcol * char_width, y = Vrow * char_height; SetRect(&Vcursrect, x, y, x + char_width - 1, y + char_height - 1); InvertRect(&Vcursrect); Vcurs = ON; Vcursflip = clock() + GetCaretTime(); } } #define BlinkTxtCursor() do { /* toggle cursor if it's time */ \ if (Vcurs >= OFF && clock() >= Vcursflip) { \ SetPort(theScreen); \ InvertRect(&Vcursrect); \ Vcurs ^= ON; \ Vcursflip = clock() + GetCaretTime(); \ } \ } while (0) private void SetModes __(( int _(on) )); private void SetModes(on) { if (esc_arg[1] == 25) /* inhibit cursor */ Vcurs = on - ON; } /* ANSI attribute definitions */ #define ANSI_CLEARALL 0 #define ANSI_BOLD 1 #define ANSI_UNDERLINE 4 #define ANSI_BLINK 5 #define ANSI_REVERSE 7 #define ANSI_INVIS 8 #define ANSI_FG(i) (30+i) #define ANSI_BG(i) (40+i) private void SetAttrib __(( void )); private void SetAttrib() { #ifdef COLOR static char reversed; #endif register int i = 0; while (++i < esc_state) switch (esc_arg[i]) { case ANSI_CLEARALL: #ifdef COLOR if (reversed) { long temp; reversed = 0; temp = Vfgcolor; Vfgcolor = Vbgcolor; Vbgcolor = temp; } #else Vfgcolor = blackColor; Vbgcolor = whiteColor; #endif #ifdef FULL /* clear Text Attributes */ #endif continue; case ANSI_REVERSE: #ifdef COLOR if (reversed) continue; reversed = 1; { /* swap */ long temp = Vfgcolor; Vfgcolor = Vbgcolor; Vbgcolor = temp; } #else Vfgcolor = whiteColor; Vbgcolor = blackColor; #endif continue; case ANSI_BOLD: /* add Text attribute */ continue; #ifdef FULL case ANSI_UNDERLINE: /* add Text attribute */ continue; case ANSI_BLINK: continue; case ANSI_INVIS: /* set FG color to BG color */ Vfgcolor = Vbgcolor; continue; #endif #ifdef COLOR case ANSI_FG(0): case ANSI_FG(1): case ANSI_FG(2): case ANSI_FG(3): case ANSI_FG(4): case ANSI_FG(5): case ANSI_FG(6): case ANSI_FG(7): if (reversed) Vbgcolor = colormap[esc_arg[i] - ANSI_FG(0)]; else Vfgcolor = colormap[esc_arg[i] - ANSI_FG(0)]; continue; case ANSI_BG(0): case ANSI_BG(1): case ANSI_BG(2): case ANSI_BG(3): case ANSI_BG(4): case ANSI_BG(5): case ANSI_BG(6): case ANSI_BG(7): if (reversed) Vfgcolor = colormap[esc_arg[i] - ANSI_BG(0)]; else Vbgcolor = colormap[esc_arg[i] - ANSI_BG(0)]; continue; #endif } } ssize_t write(fd, buf, nbytes) #undef write /* use the real thing */ int fd; const void *buf; size_t nbytes; { /* "normal" files are just written out */ if (fd != 1) { return write(fd, buf, nbytes); } { /* screen output is handled specially */ register char *p = (char *) buf, *base; register char *end = p + nbytes; register int arg1; SetPort(theScreen); HideTxtCursor(); /* * We can be left off in the middle of an escape sequence. * The switch brings us back to the "right" place in the loop. */ switch (esc_state) { case NO_ESC_SEEN: for (; p < end; p++) { switch (*p) { default: base = p; while (++p < end && !isctrl(*p)) ; MoveTo((Vcol) * char_width, (Vrow + 1) * char_height - char_descent); DrawText(base, 0, (int)(p - base)); CurRight((int)(p - base)); --p; continue; case '\b': CurLeft(1); continue; case '\t': CurRight(8 - (Vcol & 7)); continue; case '\n': if (Vrow < ILI) ++Vrow; else scroll_lines(0, LI, -1); if (Vraw) continue; case '\r': Vcol = 0; continue; case '\7': Ring(); continue; case ESC: break; } /* here we saw the escape... */ p++; esc_state = ESC_SEEN; case ESC_SEEN: if (p >= end) break; if (*p != '[') { Ring(); esc_state = NO_ESC_SEEN; continue; } p++; esc_state = ESC_BRACKET_SEEN; case ESC_BRACKET_SEEN: if (p >= end) break; qe_seen = 0; if (*p == '?' || *p == '=') p++, qe_seen++; esc_state = 1; do { esc_arg[0] = 0; default: while (p < end && isdigit(*p)) esc_arg[0] = esc_arg[0] * 10 + (*p++ - '0'); if (p >= end) break; if (esc_state <= MAX_ESC_ARGS) esc_arg[esc_state++] = esc_arg[0]; } while (*p == ';' && (p++, TRUE)); if (p >= end) break; /* now, handle the escape codes */ if ((arg1 = esc_arg[1]) == 0) /* usual arg. */ arg1 = 1; if (qe_seen) { if (*p == 'h') SetModes(1); else if (*p == 'l') SetModes(0); else Ring(); } else switch (*p) { #ifdef FULL case 'f': #endif case 'H': --arg1; if (esc_state <= 2 || --esc_arg[2] < 0) esc_arg[2] = 0; CurAbs(arg1, esc_arg[2]); break; case 'A': CurUp(arg1); break; #ifdef FULL case 'B': CurDown(arg1); break; case 'C': CurRight(arg1); break; case 'D': CurLeft(arg1); break; case 'M': /* delete lines */ arg1 = -arg1; case 'L': /* add lines */ scroll_lines(Vrow, LI, arg1); break; case 's': CurSave(); break; case 'u': CurRestore(); break; #endif /* FULL */ case 'm': SetAttrib(); break; case 'K': ClrEol(); break; case 'J': ClrEos(); break; default: Ring(); break; } /* switch (*p) */ esc_state = NO_ESC_SEEN; /* reset!! */ } /* for ( ; p < end; p++) */ } /* switch (esc_state) */ } ShowTxtCursor(); return nbytes; } /*======================================================================*/ /* TERM.C */ /*======================================================================*/ /* * Hardwired terminal capabilities, so we don't need termcap database */ void getTERM() { register char *t = getenv("TERM"); if (t == NULL) t = "mac"; TermName = t; MetaKey = ESC; CanScroll = YES; IDline_setup(t); #ifdef COLOR /* {{should check whether we are a color system...}} */ UseColor++; #endif } /*======================================================================*/ /* FKEYS.C */ /*======================================================================*/ /* shift key mask definitions */ #define K_ALT cmdKey #define K_SHIFT shiftKey #define K_CAPS alphalockKey #define K_OPT optionKey #define K_CTRL controlKey /* key codes for the above, to be used with KeyDown */ #define ALT_KEY 0x37 #define SHIFT_KEY 0x38 #define CAPS_KEY 0x39 #define OPT_KEY 0x3A #define CTRL_KEY 0x3B /* really? */ #define PAD 0100 /* "keypad" flag in Fkey table */ /* * character codes >= 0200 are used to define function keys. */ enum FKEYS { ku=0200,kd, kr, kl, K1, K2, K3, K4, K5, kq, kF, kH, kI, kN, kP, kR, kh, kY, kt, kT, ka, kA, kz, kZ, k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k_, F1, F2, F3, F4, F5, F6, F7, F8, F9, F0, ML, MR }; /* * This is the function key name table. * All (macro) references to function keys are by these names, * to make it independent of the actual code of the function key * (which vary from machine to machine) * {{warning: the byte order in the XX macro is processor-dependent}} */ #define XX(a,b) (a<<8|b) short FKeyMap[NFKEYS] = { XX('k','u'), XX('k','d'), XX('k','r'), XX('k','l'), XX('K','1'), XX('K','2'), XX('K','3'), XX('K','4'), XX('K','5'), XX('k','q'), XX('k','F'), XX('k','H'), XX('k','I'), XX('k','N'), XX('k','P'), XX('k','R'), XX('k','h'), XX('k','Y'), XX('k','t'), XX('k','T'), XX('k','a'), XX('k','A'), XX('k','z'), XX('k','Z'), XX('k','0'), XX('k','1'), XX('k','2'), XX('k','3'), XX('k','4'), XX('k','5'), XX('k','6'), XX('k','7'), XX('k','8'), XX('k','9'), XX('k',';'), XX('F','1'), XX('F','2'), XX('F','3'), XX('F','4'), XX('F','5'), XX('F','6'), XX('F','7'), XX('F','8'), XX('F','9'), XX('F','0'), XX('M','L'), XX('M','R') }; /* * The next table is indexed with the keyboard scan code to determine if * it was a function key. The keyboard (scancode) layout is as follows: * (shown is Mac II; in |..| for extended keyboards only; in {..} incompatible * with Mac Plus (..) / Classic :..:) * * As you can see the Mac Plus has the {= / *} :- < >: (= / *) (48 4D 42) * scancodes originally assigned to the {+} :^: (-) (4E) * cursor keys all mixed up. This should {-} :v: (+) (46) * somehow be compensated for. * :4E 46 42: * |35 7A 7B 63 76 60 61 62 64 65 6D 67 6F 69 6B 71| :4D: * :48: * 32 12 13 14 15 17 16 1A 1C 19 1D 1B 18 33 |72 73 74| 47{51 4B 43} * 30 0C 0D 0E 0F 11 10 20 22 1F 23 21 1E |75 77 79| 59 5B 5C{45} * Ctrl 00 01 02 03 05 04 26 28 25 29 27 24 2A 56 57 58{4E} * [38] 06 07 08 09 0B 2D 2E 2B 2F 2C Shift(4D)| 7E | 53 54 55 * [39] [37] --------Space--------- [3A](46 42 48)|7B 7D 7C| 52 41 4C * * These are assigned the following FKEY codes: * :kR kl kr: * |Esc k1 k2 k3 k4 k5 k6 k7 k8 k9 k; kq kY ?? ?? ??| :ku: * :kd: * ` 1 2 3 4 5 6 7 8 9 0 - = Bsp |kI kh kP|Esc{k2 k3 k4} * Tab q w e r t y u i o p [ ] |Del kH kN| K1 kP K3{kF} * Ctrl a s d f g h j k l ; ' Ret \ kt K2 kT{kR} * Shift z x c v b n m , . / Shift(ku)| ku | K4 kN K5 * Caps Cmd --------Space--------- Opt (kl kr kd)|kl kd kr| k0 ka kA * * In combination with