/************************************************************************ * 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: atari_st.c * abstract: Atari-ST specific stuff for JOVE * author: T.R.Hageman, Groningen, The Netherlands. * created: may 1989 * modified: * 30-Jul-89, add Fn keys * 11-Sep-89, get vdi handle the "official" way * 14-Oct-89, revision of getchar(), Fkey assignments * 18-Feb-90, initial delay in DoMouse; add COLOR option; * interchange Fn and kn keys; add window resize * at Alt-mouse drag of mode line. * 26-Feb-90, interchange K2 and K3 to follow terminfo def. * 8-Mar-90, change keypad key bindings considerably. * 17-Mar-90, Oops! GEMDOS input does not work for TOS 1.4 * so switch to BIOS input. * 24-Mar-90, move Mouse stuff to separate module. * 14-Oct-90, fix bug in SIGINT handling * 11-Nov-90, optional GEMINI console-window support. * 11-Jan-91, add PROC_TYPEOUT support, * revised NATIONAL keyboard support. * 07-May-91, revise I/O redirection in ShellToBuf * 04-Jun-91, add `dense' screen mode setting. * 18-Jun-91, add FILESELECTOR option. * 20-Jun-91, fix bug in ShellToBuf that ate up file descriptors. * 26-Jun-91, replace ShellToBuf with UnixToBuf to let proc.c * handle the system-independent part. * 15-Jul-91, get colors right (at last). * 01-Aug-91, redefine F10 as "k;" instead of "k0" * description: * this file complements jove.c, proc.c, misc.c and tty.c * and completely replaces fkeys.c and term.c *---*/ #include "tune.h" #ifdef ATARIST RCS("$Id: atari_st.c,v 14.32.0.9 1994/01/06 00:13:15 tom Exp tom $") #if __TURBOC__ # include # include #pragma warn -par # define GEM_VERSION (_GemParBlk.global[0]) #else # ifdef __GNUC__ # include # define __NO_MFDB__ /* MFDB definition conflicts with */ # include extern unsigned short _global[]; /* This was missing from */ # define GEM_VERSION (_global[0]) # else /* assume Lattice C */ # include # define Int short # endif #endif #ifndef Int /* Int type for vdi arrays */ # define Int int #endif #ifndef GEM_VERSION # define GEM_VERSION 0 #endif #define FSEL_EXINPUT_AVAILABLE (GEM_VERSION >= 0x130) #include #ifndef __GNUC__ # if __TURBOC__ /* adapt to Gnu-C naming conventions for variables we need. */ # define linea0 linea_init # define M_HID_CT (Vdiesc->m_hid_ct) # define V_CEL_HT (Vdiesc->v_cel_ht) # define V_CEL_WR (Vdiesc->v_cel_wr) # define V_CEL_MX (Vdiesc->v_cel_mx) # define V_CEL_MY (Vdiesc->v_cel_my) # define V_X_MAX (Vdiesc->v_rez_hz) # define V_Y_MAX (Vdiesc->v_rez_vt) # define V_FNT_WR (Vdiesc->v_fnt_wd) # define V_FNT_ST (Vdiesc->v_fnt_st) # define V_FNT_ND (Vdiesc->v_fnt_nd) # define V_OFF_AD (Vdiesc->v_off_ad) # define V_FNT_AD (Vdiesc->v_fnt_ad) # define FONT_RING (Vdiesc->font_ring) # define DEF_FONT (Vdiesc->def_font) /* Line-A variables */ # define VPLANES (Linea->v_planes) # define VWRAP (Linea->v_lin_wr) /* FONT fields */ # define __FONT FONT_HDR # define font_id id # define first_ade ade_lo # define last_ade ade_hi # define max_cell_width wcel_wdt # define off_table ch_ofst # define dat_table fnt_dta # define form_width frm_wdt # define form_height frm_hgt # else /* assume Lattice C */ # define M_HID_CT (((short *)la_info.li_a0)[-299]) /* etc... I don't really support Lattice C anymore. */ # endif #endif #if (!__TURBOC__) /* {{[TRH] This really should be in a system header file!}} */ /* system variable _sysbase (0x4F2L) points to next structure */ typedef struct _syshdr { unsigned short os_entry; /* $00 BRA to reset handler */ unsigned short os_version; /* $02 TOS version number */ void *os_start; /* $04 -> reset handler */ struct _syshdr *os_base; /* $08 -> baseof OS */ void *os_membot; /* $0c -> end BIOS/GEMDOS/VDI ram usage */ void *os_rsv1; /* $10 << unused,reserved >> */ long *os_magic; /* $14 -> GEM memoryusage parm. block */ long os_gendat; /* $18 Date of system build($MMDDYYYY) */ short os_palmode; /* $1c OS configuration bits */ short os_gendatg; /* $1e DOS-format date of systembuild */ /* The next three fields are only available in TOS versions 1.2 and greater */ void *_root; /* $20 -> base of OS pool */ long *kbshift; /* $24 -> keyboard shift state variable */ long **_run; /* $28 -> GEMDOS PID of current process */ void *p_rsv2; /* $2c << unused, reserved >> */ } SYSHDR; #endif /* the following definitions conflict with GEM stuff. */ #undef BLACK #undef RED #undef GREEN #undef YELLOW #undef BLUE #undef MAGENTA #undef CYAN #undef WHITE #undef UPARROW #include "jove.h" #include "ctype.h" #include "io.h" #include "process.h" #include "screen.h" #include "termcap.h" #include #undef CLK_TCK #include #ifndef SIG_SYS # define SIG_SYS SIG_DFL #endif #ifdef RESHAPING private int set_font __(( __FONT *_(font) )); private __FONT *org_font; # define FONT_SELECT(dense) ((dense) ? ((__FONT **)FONT_RING)[1] : DEF_FONT) /* Stupid cast necessary due to type mismatch in gnu's */ # define FONT_INIT() org_font = FONT_SELECT(DEF_FONT->form_height != V_CEL_HT) # define FONT_EXIT() { if (set_font(org_font)) ttsize(), curstoLL(), printf("\n"); } #else # define FONT_INIT() # define FONT_EXIT() #endif #ifdef MOUSE private int mouse_initially_hidden; #endif #define CONSOLE 2 /* BIOS console device */ DEF_INT( "alternate-path-separator", AltSlash, V_CHAR) _IF(def ATARIST) ZERO; /*======================================================================*/ /* SCREEN.C */ /*======================================================================*/ private int SCR_HANDLE; /* VDI handle of screen */ private Int scr_width, scr_height, char_width, char_height; #ifndef GEMINI # define scr_xorg 0 # define scr_yorg 0 # define clip_flag OFF # define clip_rect ((Int *)genbuf) /* dummy */ #else private Int scr_xorg, scr_yorg, clip_flag, clip_rect[2][2]; #endif #ifdef FAST_IDLINE /* * in {ins,del}line, don't use a raster op. to clear the lines since I * found that mildly annoying to my eyes... * {{we cannot use v_clear() here since we do not know the state of * the screen image at this point...}} */ private void cl_lines __(( int _(top), int _(num) )); private void cl_lines(top, num) { while (--num >= 0) { Placur(top++, 0); putp(CE); } } private void scr_op __(( int _(opcode), Int *_(pxy) )); private void scr_op(opcode, pxy) int opcode; Int *pxy; { MFDB scr_mfdb; scr_mfdb.fd_addr = 0; /* so we use the screen */ vro_cpyfm(SCR_HANDLE, opcode, pxy, &scr_mfdb, &scr_mfdb); } private void STcpy_lines __(( int _(src), int _(dest), int _(num) )); private void STcpy_lines(src, dest, num) { Int pxy[8]; register Int *p = pxy; register int cheight = char_height; /* convert lines -> pixels */ src *= cheight; dest *= cheight; num *= cheight; num--; /* source rectangle (x,y)upper left, (x,y)lower right, resp. */ *p++ = scr_xorg + 0; *p++ = scr_yorg + src; *p++ = scr_xorg + scr_width; *p++ = scr_yorg + src + num; /* dest rectangle (x,y)upper left, (x,y)lower right, resp. */ *p++ = scr_xorg + 0; *p++ = scr_yorg + dest; *p++ = scr_xorg + scr_width; *p++ = scr_yorg + dest + num; scr_op(S_ONLY, pxy); } private void STins_line __(( int _(top), int _(bottom), int _(num) )); private void STins_line(top, bottom, num) { bottom++; STcpy_lines(top, (top + num), bottom - (top + num)); cl_lines(top, num); } private void STdel_line __(( int _(top), int _(bottom), int _(num) )); private void STdel_line(top, bottom, num) { bottom++; STcpy_lines((top + num), top, bottom - (top + num)); cl_lines(bottom - num, num); } # ifdef GEMINI # define Nputpad(cap, M_cap, num, nlines) { \ register int i = num; \ do putp(cap); while (--i); \ } /* the following is ripped off from GEN{i,d}_lines. We need it in case we're running in the GEMINI console window when it is extending off the bottom of the screen. In that particular case we cannot use our fast bitblt scroll since we're not sure that the information to copy actually exists. */ private void SLOWins_line __(( int _(top), int _(bottom), int _(num) )); private void SLOWins_line(top, bottom, num) { Placur(bottom + 1 - num, 0); Nputpad(DL, M_DL, num, ILI - bottom); Placur(top, 0); Nputpad(AL, M_AL, num, ILI - top); } private void SLOWdel_line __(( int _(top), int _(bottom), int _(num) )); private void SLOWdel_line(top, bottom, num) { Placur(top, 0); Nputpad(DL, M_DL, num, ILI - top); Placur(bottom + 1 - num, 0); Nputpad(AL, M_AL, num, ILI - bottom); } # endif /* GEMINI */ void IDline_setup(tname) const char *tname; { # ifdef GEMINI /* We can't use fast scrolling if window extends off the bottom of the screen. */ if (scr_yorg + scr_height > clip_rect[1][1]) { TTins_line = SLOWins_line; TTdel_line = SLOWdel_line; ScrollLimit = 50; return; } # endif TTins_line = STins_line; TTdel_line = STdel_line; ScrollLimit = 100; } /* show a visual bell */ #ifdef FAST_FLASH void flash() { Int pxy[8]; register Int *p = pxy; /* set src and dest. rectangle to entire screen */ *p++ = scr_xorg + 0; *p++ = scr_yorg + 0; *p++ = scr_xorg + scr_width; *p++ = scr_yorg + scr_height; *p++ = scr_xorg + 0; *p++ = scr_yorg + 0; *p++ = scr_xorg + scr_width; *p++ = scr_yorg + scr_height; cursoff(); scr_op(D_INVERT, pxy); DoSit(1); scr_op(D_INVERT, pxy); } #endif /* FAST_FLASH */ #endif /* FAST_IDLINE */ #ifdef COLOR /* * note: vt52 emulator uses palette indices, NOT the VDI definitions * as I originally thought (so much for ATARIs documentation). * The default (low rez) palette colors are as follows: * 0 white 4 blue 8 dark grey 12 light magenta * 1 red 5 green 9 light cyan 13 light yellow * 2 green 6 dark blue 10 dark cyan 14 dark magenta * 3 brown 7 light gray 11 dark yellow 15 black * (medium rez: 4 palettes, where palette 3 == black) */ # define ST_BLACK 15 # define ST_RED 1 # define ST_GREEN 2 # define ST_YELLOW 13 /* or 11 */ # define ST_BLUE 4 /* or 6 */ # define ST_MAGENTA 14 /* or 12 */ # define ST_CYAN 10 /* or 9 */ # define ST_WHITE 0 private int color_mask; private const char colormap[NCOLORS] = { ST_BLACK, ST_RED, ST_GREEN, ST_YELLOW, ST_BLUE, ST_MAGENTA, ST_CYAN, ST_WHITE }; private char prev_color[2] = { -1, -1 }; void set_color(color) { register int fg_color, bg_color; if (color == CurrColor) return; if (False(UseColor)) return; if (CurrColor < 0) /* fool optimization */ prev_color[FG] = prev_color[BG] = -1; CurrColor = color; if (color_mask <= 1) /* i.e. monochrome */ return; fg_color = FG_COLOR(color); bg_color = BG_COLOR(color); if (color_mask < NCOLORS) { /* medium resolution: only 4 colors */ static const char remap[] = { BLACK, RED, GREEN, WHITE, GREEN, RED, GREEN, WHITE }; fg_color = remap[fg_color]; bg_color = remap[bg_color]; } fg_color = colormap[fg_color]; bg_color = colormap[bg_color]; if (fg_color == bg_color) /* make sure it's different */ bg_color ^= color_mask; /* chooses its complementary color */ if (prev_color[FG] != (char) fg_color) { prev_color[FG] = fg_color; printf("\33b%c", fg_color); } if (prev_color[BG] != (char) bg_color) { prev_color[BG] = bg_color; printf("\33c%c", bg_color); } } #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); } /*======================================================================*/ /* TERM.C */ /*======================================================================*/ /* * Hardwired terminal capabilities, so we don't need termcap database */ #ifdef GEMINI # define XXXX(a,b,c,d) ((long)((a)<<8|(b))<<16|(c)<<8|(d)) private int inGEMINI __(( void )); private int inGEMINI() { long sav_ssp = Super(0L); long *_shell_p = *(long **) 0x4f6; int result = (_shell_p && _shell_p[-3] == XXXX('X','B','R','A') && _shell_p[-2] == XXXX('G','M','N','I')); Super(sav_ssp); return result; } # undef XXXX #endif /* GEMINI */ void getTERM() { Int work[57]; int dum; register char *t = getenv("TERM"); if (t == NULL) t = "atari"; TermName = t; /* * init SCR_HANDLE here because we "know" that getTERM is called (once) * in main before anything else that will need it ... */ appl_init(); SCR_HANDLE = graf_handle(&dum, &dum, &dum, &dum); vq_extnd(SCR_HANDLE, 0, work); scr_width = work[0]; /* well actually {width,height} - 1 */ scr_height = work[1]; #ifdef COLOR color_mask = (work[13] - 1) & 0xF; /* nr. of displayable colors */ /* (assumed a power of 2) */ if (color_mask > 1) UseColor++; #endif #ifdef GEMINI clip_rect[1][0] = scr_width; clip_rect[1][1] = scr_height; if (inGEMINI()) { /* we're running under GEMINI? */ Int top_window; # if __TURBOC__ # define _0 # else Int dummy; # define _0 , &dummy # endif /* * check if we're running in the console window. If we * are, the console window will be TOPped, and we should use * the dimensions of its work area as our "screen" size. */ if (!((t = getenv("JOVE_TTP")) && sindex("W:N", t)) && wind_get(0, WF_TOP, &top_window _0 _0 _0) && top_window && wind_get(top_window, WF_WORKXYWH, &scr_xorg, &scr_yorg, &scr_width, &scr_height)) { clip_rect[0][0] = scr_xorg; clip_rect[0][1] = scr_yorg; scr_width -= 1; scr_height -= 1; /* enable clipping if window is partially off-screen */ if (scr_xorg + scr_width > clip_rect[1][0] || scr_yorg + scr_height > clip_rect[1][1]) clip_flag = YES; } } #endif /* GEMINI */ linea0(); FONT_INIT(); #ifdef MOUSE mouse_initially_hidden = M_HID_CT; #endif MetaKey = ESC; CanScroll = YES; IDline_setup(TermName); } /*======================================================================*/ /* FKEYS.C */ /*======================================================================*/ /* These are already defined in / ... #define K_RSHIFT 0x01 #define K_LSHIFT 0x02 #define K_CTRL 0x04 #define K_ALT 0x08 */ #define K_SHIFT (K_LSHIFT|K_RSHIFT) #define K_CAPS 0x10 #define CON_META 0x08 #define SCAN_1 0x02 /* scan code of [1!] key */ #define SCAN_ALT_1 0x78 /* scan code of ALT-[1!] key */ #define PAD 0100 /* "keypad" flag in Fkey table */ /* * character codes >= 0200 are used to define function keys. */ enum FKEYS { ku=0200,kd, kr, kl, k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, K1, K2, K3, K4, K5, kq, kF, kH, kI, kN, kP, kR, kh, F1, F2, F3, F4, F5, F6, F7, F8, F9, F0, kY, kt, kT, ka, kA, kz, kZ, k_, ML, MR }; /* * 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: * * 3B /3C /3D /3E /3F /40 /41 /42 /43 /44 / * [ 54 /55 /56 /57 /58 /59 /5A /5B /5C /5D / ] shifted * * 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 29 0E 62 61 63 64 65 66 * 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 53 52 48 47 67 68 69 4A * [1D ] 1E 1F 20 21 22 23 24 25 26 27 28 1C 2B 4B 50 4D 6A 6B 6C 4E * [2A](60)2C 2D 2E 2F 30 31 32 33 34 35 [ 36] 6D 6E 6F * [ 38] ----------39---------- [ 3A] 70 71 72 * * (Note that US keyboards don't have key 60). * * These are assigned the following FKEY codes: * * k1 /k2 /k3 /k4 /k5 /k6 /k7 /k8 /k9 /k; / * [ F1 /F2 /F3 /F4 /F5 /F6 /F7 /F8 /F9 /F0 / ] shifted * * Esc 1 2 3 4 5 6 7 8 9 0 - = ` Bsp kq kY k1 k2 k3 k4 * Tab q w e r t y u i o p [ ] Del kI ku kh K1 kP K3 kF * Ctrl a s d f g h j k l ; ' Ret # kl kd kr kt K2 kT kR * Shift \ z x c v b n m , . / Shift K4 kN K5 * Alt --------Space--------- Caps k0 ka kA * * Some special codes: * * Key combination scancode (hex) action * --------------- -------- ------ * [Ctrl] + ClrHome 77 = kH * + LeftArrow 73 = kz * + Rightarrow 74 = kZ * [Alt] + 1-9,-,= 78-83 adjust scancode in getchar() * * pseudo-codes ML and MR are used to hook up the mouse-handler for left * and right buttons. Scan 0x74 and 0x75 are sometimes generated by the mouse * buttons (for reasons unknown to me). * A zero entry means use the "standard" character code (see getchar()). * Keys on the numeric keypad have the PAD flag ORed in. * These are regarded as function keys only if "use-function-keys" is on. */ private const unsigned char FKeyTable[] = { /*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, k1, k2, k3, k4, k5, /*40*/ k6, k7, k8, k9, k_, 0, 0, kh, ku, 0, kF|PAD, kl, 0, kr, kR|PAD, 0, /*50*/ kd, 0, kI, 0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F0, 0, 0, /*60*/ 0, kY, kq, k1|PAD, k2|PAD, k3|PAD, k4|PAD, K1|PAD, kP|PAD, K3|PAD, kt|PAD, K2|PAD, kT|PAD, K4|PAD, kN|PAD, K5|PAD, /*70*/ k0|PAD, ka|PAD, kA|PAD, kz, kZ, 0, 0, kH /* scan codes >= 0x78 are adjusted */ }; /* * 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','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','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('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('k','Y'), XX('k','t'), XX('k','T'), XX('k','a'), XX('k','A'), XX('k','z'), XX('k','Z'), XX('k',';'), XX('M','L'), XX('M','R') }; /* * "Foreign" i.e. non-US/UK keyboards use ALT-key combinations * to get some punctuation characters. This macro tests for these * characters. {{Note that we only need this if the Alt-flag is still * left up after manipulation by the foreign keyboard handler. * Which is something I should check as soon as I get access to * a foreign ST}} */ #define ISFOREIGN(c) (Foreign && index(Foreign, c)) /* (use index() since the number of exceptions is usually rather small.) */ private const char *Foreign ZERO; /* * `Foreign' is initialized from the following table, which is indexed * by the language code from the OS header. Its entries are the ASCII * codes of all characters that are generated with some ALT key combination. * {{I could only get the documentation for a limited number of countries, * but extension of this table is simple -- once you know the codes.}} */ #define UNKNOWN NULL /* placeholder for unknown entries */ private const char * const LanguageSpecials[] = { NULL, /* 0 USA */ "[]{}\\@", /* 1 Germany */ "[]{}\\@~", /* 2 France */ NULL, /* 3 Great Britain */ "[]{}#@\x81\x9A", /* 4 Spain */ UNKNOWN, /* 5 Italy */ "[]{}~`", /*?*/ /* 6 Sweden */ "[]{}\\@~", /*???*/ /* 7 Swiss-french */ "[]{}\\@", /*???*/ /* 8 Swiss-german */ UNKNOWN, /* 9 Turkey */ UNKNOWN, /* 10 Finland */ "[]{}~`", /*??*/ /* 11 Norway */ "[]{}~`", /*??*/ /* 12 Denmark */ /* 13 Saudi Arabia */ /* ... */ }; #define _sysbase (*(SYSHDR **) 0x4f2) #define LANG_INIT() \ { \ long sav_ssp = Super(0L); \ int lang = _sysbase->os_palmode >> 1; \ /* shift off PAL/NSTC mode bit */ \ Super(sav_ssp); \ if (lang < sizeof(LanguageSpecials) / sizeof(char *)) \ Foreign = LanguageSpecials[lang]; \ } /* mouse stuff */ #ifdef MOUSE /* * INIT: make sure mouse is invisible and has arrow shape; * EXIT: turn mouse back on for dumb shells like Menu+ */ # define INIT_MOUSE() (mouse_on++, graf_mouse(ARROW, NULL)) # define EXIT_MOUSE() {if(!mouse_initially_hidden) mouseon();} # define SHOW_MOUSE_CURSOR() v_show_c(SCR_HANDLE, 0) # define HIDE_MOUSE_CURSOR() v_hide_c(SCR_HANDLE) # define ALT_KEY_DEPRESSED() (Kbshift(-1) & K_ALT) # define LEFT_BUTTON 1 # ifndef GEMINI # define GET_MOUSE_STATE(b,x,y) vq_mouse(SCR_HANDLE, b, x, y) # else # define GET_MOUSE_STATE(b,x,y) { \ vq_mouse(SCR_HANDLE, b, x, y); \ if ((*(x) -= scr_xorg) < 0) \ *(x) = 0; \ else if (*(x) > scr_width) \ *(x) = scr_width; \ if ((*(y) -= scr_yorg) < 0) \ *(y) = 0; \ else if (*(y) > scr_height) \ *(y) = scr_height; \ } # endif # define MOUSE_BITES_CURSOR # include "mouse.ci" #endif /* MOUSE */ /*======================================================================*/ /* TTY.C */ /*======================================================================*/ void ttsize() { Int rows, cols; vq_chcells(SCR_HANDLE, &rows, &cols); char_width = (scr_width + 1) / cols; char_height = (scr_height + 1) / rows; vs_clip(SCR_HANDLE, clip_flag, (Int *) clip_rect); CO = cols; LI = rows; ILI = LI - 1; } typedef struct { char *normal, *shift, *caps; } KEYTBL; typedef struct { char sg_flags; KEYTBL sg_keytbl; } SGTTY; private SGTTY sg[2]; #define conterm (*(char *) 0x484) void do_sgtty() { register long sav_sp = Super(0L); sg[OFF].sg_flags = conterm; Super(sav_sp); sg[OFF].sg_keytbl = *(KEYTBL *) Keytbl((char *)-1, (char *)-1, (char *)-1); sg[ON] = sg[OFF]; sg[ON].sg_flags |= CON_META; } extern int done_ttinit; void ttinit() { LANG_INIT(); do_sgtty(); done_ttinit = YES; } /* If n is OFF reset to original modes */ void ttyset(on) register int on; { register long sav_sp; if (!done_ttinit) /* Try to reset before we've set! */ return; if (on) { stdout->f_flags |= F_BINARY; } else { stdout->f_flags &= ~F_BINARY; } sav_sp = Super(0L); conterm = sg[on].sg_flags; Super(sav_sp); } void ttexit() { signal(SIGINT, SIG_SYS); /* to de-install signal handler */ FONT_EXIT(); appl_exit(); } #ifndef CLK_TCK # ifdef CLOCKS_PER_SEC /* The final decision of the ANSI committee... */ # define CLK_TCK CLOCKS_PER_SEC # else /* * provide clock support, if not yet in . */ # define CLK_TCK 200 typedef unsigned long clock_t; # define _hz_200 (*(clock_t *) 0x4ba) /* system clock */ clock_t clock __(( void )); clock_t clock() { register long sav_sp; register clock_t ticks; sav_sp = Super(0L); ticks = _hz_200; Super(sav_sp); return ticks; } # endif #endif /* * Emulate alarm() */ private sig_tp (*alarm_proc)__((int _(sig))) = SIG_DFL; private clock_t next_alarm; ALARM_T alarm(seconds) register unsigned seconds; { register clock_t now = clock(), next = next_alarm; next_alarm = (seconds) ? now + seconds * CLK_TCK : 0; return (unsigned)(now >= next ? 0 : (next - now + CLK_TCK - 1) / CLK_TCK); } int CapsLock; /* * check for alarms in a very regularly called routine. * Also monitor CapsLock state from here (for indicator in mode line). */ private int kbhit __(( void )); private int kbhit() { int prev = CapsLock; if ((CapsLock = Kbshift(-1) & K_CAPS) != prev) { updmodline(); redisplay(); } if (next_alarm && (clock() >= next_alarm)) { next_alarm = 0; if (alarm_proc != SIG_DFL && alarm_proc != SIG_IGN) (*alarm_proc)(SIGALRM); } return Bconstat(CONSOLE); /* was: Cconis() */ } private void (*org_pterm)__((void)) = (void (*)()) -1; private sig_tp (*sigint_proc)__((int _(sig))) = SIG_SYS; #define PTERM_EXC 0x102 private void do_sigint __(( void )); private void do_sigint() { int dummy; Super(&dummy); /* back to user mode */ (*signal(SIGINT, SIG_SYS))(SIGINT); /* to re-install org_pterm */ } /* * Emulate signal() * currently this only handles SIGALRM and SIGINT requests; others are ignored. * SIGINT in fact handles all traps that use the default trap handler, * by replacing GEMDOS termination vector, * so SIGINT signal MUST be reset to SIG_SYS before exiting. * {{The alternative (for Turbo C) would be to use the stdlib's signal() and * use raise() to emulate SIGALRM. However, this is not available for * TC versions before 2.0, and even v2.03 is not satisfactory as it hardwires * the interrupt character to ^D.}} */ sig_tp (*signal(sig, proc))() int sig; sig_tp (*proc)__((int _(sig))); { register sig_tp (*prev)(); switch (sig) { case SIGALRM: prev = alarm_proc; alarm_proc = proc; break; case SIGINT: prev = sigint_proc; sigint_proc = proc; proc = Setexc(PTERM_EXC, (proc == SIG_SYS || proc == SIG_IGN || proc == SIG_DFL) ? org_pterm : do_sigint); if (org_pterm == (void (*)()) -1) org_pterm = (void (*)()) proc; break; default: prev = SIG_DFL; break; } return prev; } #if __TURBOC__ void signal_mode(inhibit) { /* dummy; This is used internally in TC library */ } #endif private int peekc = -1; int getchar() { register long rawc; register int c, scancode, mode; /* slowpoke timeout is now handled here. */ extern void (*timeout_proc)__(( void )); clock_t time_out = clock() + ALARM_1_SEC * CLK_TCK; if ((c = peekc) >= 0) { peekc = -1; return c; } while (!kbhit()) { /* wait for character */ if (timeout_proc && clock() >= time_out) (*timeout_proc)(); #ifdef MOUSE switch (mouse()) { /* or mouse click */ case 0: continue; case LEFT_BUTTON: return ML; default: /* right (or middle, if any) button */ return MR; } #endif } #ifdef MOUSE mouseoff(); #endif rawc = Bconin(CONSOLE); /* was: Cnecin() */ scancode = rawc >> 16; mode = scancode >> 8; scancode &= 0xff; if (mode & K_ALT) { /* ALT-key */ /* * for some obscure reasons (well MS-DOS compatibility...) * the scancode of the upper row (digits, -, =) is way off * in combination with Alternate. So adjust. */ if (scancode >= SCAN_ALT_1) scancode += SCAN_1 - SCAN_ALT_1; c = (short) rawc; /* * "Foreign" i.e. non-US/UK keyboards use ALT-key combinations * to get some punctuation characters. So don't disturb them. */ if (ISFOREIGN(c)) { if (c >= 0200) return peekc = c, QuoteChar; return c; } /* * due to a peculiarity in TOS (MessDOS compatibility * strikes times and again!) the character code is * unreliable (i.e. sometimes 0) in combination with * the ALTERNATE key. So get it ourselves. (also when * MetaKey is enabled, to avoid unexpected results when * an extended keyboard handler eg. KBEXT is installed.) */ if (c == '\0' || MetaKey) { c = ((mode & K_CAPS) ? sg[ON].sg_keytbl.caps : (mode &(K_SHIFT|K_CTRL)) ? sg[ON].sg_keytbl.shift : sg[ON].sg_keytbl.normal)[scancode]; if (mode & K_CTRL) c &= 037; } if (!MetaKey) return peekc = (c | 0200), QuoteChar; rawc = c; /* remember... */ if ((c = FKeyTable[scancode]) == 0 || ((c & PAD) && ((c &= ~PAD), False(UseKeyPad)))) if ((c = (short) rawc) >= 0200) return peekc = c, QuoteChar; return peekc = c, MetaKey; } if ((c = FKeyTable[scancode]) == 0 || ((c & PAD) && ((c &= ~PAD), False(UseKeyPad)))) if ((c = (short) rawc) >= 0200) return peekc = c, QuoteChar; return c; } /* Returns non-zero if a character waiting */ int charp() { if (inIOread) return NO; if (InJoverc || peekc >= 0) return YES; return kbhit(); } /* * This does the actual pause, without redisplay. */ int DoSit(delay) register int delay; { register clock_t goal = clock() + delay * (CLK_TCK / 10); while (clock() < goal) { if (InputPending = charp()) return YES; } return NO; } /*======================================================================*/ /* PROC.C */ /*======================================================================*/ int DEFVARG(UnixToBuf, (const char *bufname, int disp, int wsize, int clobber, const char *infile, ...), (bufname, disp, wsize, clobber, infile, va_alist) const char *bufname; register int disp; int clobber; const char *infile;) { /* emulate a pipe by redirecting output to a temporary file, and then reading that file when the command is finished. The "shell" and "shell-flags" variables are not used explicitly. ("shell" is put into the environment variable SHELL, so that system() is able to find it.) */ char tmpfile[3][FILESIZE]; register char *pipe; register int status; static int org_fd[3] ZERO; int new_fd[3]; register int i; va_list ap; register char **argv; #ifdef TRH /* this stuff is to determine whether the shell can be invoked via _shell_p. It requires my [TRH] system() routine. This way we can catch output from internal commands, and still handle output redirection correctly if no shell is installed. */ extern int _sys_kind; extern void _sys_init __(( void )); _sys_init(); # define SHELL_P (_sys_kind > 1) #else # define SHELL_P FALSE #endif exp = 1; if (clobber) isprocbuf(bufname); if (disp) { message("Starting up..."); #ifdef PROC_TYPEOUT if (disp > 0) /* the next comma-separated statement */ #endif pop_wind(bufname, clobber, clobber ? B_PROCESS : B_FILE), set_wsize(wsize); redisplay(); } va_begin(ap, infile); argv = (char **) ap; /* m68k has the ``right'' stack direction. */ /* build a command line for use in system() */ if (argv[0] == Shell && argv[1] == ShFlags) argv += 2; #define cp pipe /* Expand environment variables, just in case the shell doesn't... */ env_expand(cp = strcpy(genbuf, *argv++)); cp += strlen(cp); while (*argv) *cp++ = ' ', cp = appcpy(cp, *argv++); #undef cp va_end(ap); /* save original file descriptors */ if (!org_fd[0]) { { i = 2; } do { org_fd[i] = dup(i); } while (--i >= 0); } /* create a temporary file as our ``pipe'' */ if ((i = creat(pipe = mktmpe(tmpfile[1], "jpXXXXXX"), S_IRUSR|S_IWUSR)) < 0) complain(IOerr("pipe", genbuf)); if (infile == NULL) /* connect stdin to ``/dev/null'' */ close(creat(infile = mktmpe(tmpfile[0], "jpXXXXXX"), S_IRUSR|S_IWUSR)); /* connect stderr, stdout and stdin to our ``pipe'' */ dup2(i, 2); new_fd[2] = i; /* Hmm... */ if (!SHELL_P) { dup2(i, 1); new_fd[1] = i; if ((i = open(infile, 0)) >= 0) { dup2(i, 0); new_fd[0] = i; /* Hmm... */ } } else { /* This is a workaround for an I/O redirection bug in gulam invoked through _shell_p (gulam apparently uses its original stdio channels, NOT the momentary ones): insert I/O redirection directives in the command line in order to catch output from internal shell commands and multiple `;'-separated commands. This means that stdout and stderr output are NOT intermixed, but I can live with that. The alternative, not being able to redirect builtin commands, is much less attractive. Also the input file is redirected this way (only on the first command!) since Gulam eats up the first 3 or 4 characters from a directly redirected stdin and places it in its own command buffer. (?!?!) */ register char *dp = genbuf, *sp = strcpy(iobuff, dp); register char c, quote = '\0'; int inf = NO, outf = NO; do { if (((c = *sp++) == '\0' || c == ';') && !quote) { if (!inf++) dp = appcpy(appcpy(dp, " <"), infile); if (outf) { outf = NO; continue; } if (pipe != tmpfile[2]) pipe = mktmpe(tmpfile[2], "jpXXXXXX"); dp = appcpy(appcpy(dp, " >>"), pipe); } else if (quote) { if (c == quote) quote = '\0'; } else if (c == '"' || c == '\'') { quote = c; } else if (c == '>') { /* already redirecting */ outf++; } else if (c == '<') { inf++; } } while (*dp++ = c); } putenv(sprint("SHELL=%s", Shell)); /* for system */ putenv("NOHOLD=1"); /* for TRH startup */ if (disp) message("Grinding along..."), redisplay(); ttyset(OFF); if ((status = system(genbuf)) == -1) status = -ENOEXEC; /* presumably exec failure */ ResetTerm(); /* just to be sure... */ scr_inval(); /* ...in case screen is clobbered */ if (disp) f_mess("Chugging along..."); /* restore original affiliation of stdin, stdout, stderr */ { i = 2; } do { close(i); /* necessary? YES! */ /* * for some reason dup2()ed file handles need to be closed * *twice* before they're really gone. If you don't, TOS * keeps eating up filehandles until you run out... * (I thought TOS 1.4 was better :-/) */ close(new_fd[i]); close(new_fd[i]); dup2(org_fd[i], i); if (SHELL_P) /* skip stdout */ break; } while (--i >= 0); if (infile == tmpfile[0]) unlink(infile); do { if (bufname != DevNull && (i = open(pipe, 0)) >= 0) read_pipe(bufname, i, disp); unlink(pipe); } while ((pipe -= sizeof tmpfile[0]) != tmpfile[0]); return status; } /* push a shell (well, not really, actually...) */ void Push() { register char *cmd; static char unset_message[] = "\ [There are modified buffers]\r\n\ [type C-G to return to JOVE]\r\n"; register char *mesg_p = unset_message; if (!ModBufs(0)) mesg_p += (sizeof unset_message - 1) / 2; putenv(sprint("SHELL=%s", Shell)); /* for system */ putenv("NOHOLD=1"); alarm(0); curstoLL(); putp(CE); putstr(mesg_p); CATCH cmd = NullStr; for (;;) { f_mess(NullStr); UpdModLine = NO; putp(VS); /* may be turned off by some command */ cmd = ask(cmd, "[JOVE] $ "); printf("\r\n"); flusho(); if (*cmd) { minib_add(cmd, YES); /* history */ system(cmd); } } ENDCATCH ResetTerm(); /* just to be sure... */ ClAndRedraw(); alarm(UpdFreq); chdir(pwd()); /* might be changed... */ #ifdef RESHAPING /* * Some shells (e.g. gulam) are able to switch between 8x16 and * 8x8 fonts in high rez. (thus effectively switching between 25 * and 50 lines); so this is just to be sure. */ win_reshape(); #endif } /*======================================================================*/ /* UTIL.C */ /*======================================================================*/ /* (aka kludge dept.) */ char * getwd(result) char *result; { register char *p = result; register int drive = Dgetdrv(); *p++ = drive + 'a'; *p++ = ':'; Dgetpath(p, 0); FIX_FILENAME(p); return result; } /* change directory -- this is more hairy than you might think since Dsetpath() does not do the entire job for you */ int chdir(path) register const char *path; { register int cur_drive = Dgetdrv(); char pathbuf[FILESIZE]; if (path[0] == '\0') return 0; if (path[1] == ':') { if (Dsetdrv(tolower(path[0]) - 'a') < 0) return -1; path += 2; } if (path[0] != SLASH) { path = strcpy(&pathbuf[1], path); *--(char *)path = SLASH; } if (Dsetpath(path) < 0) { Dsetdrv(cur_drive); return -1; } return 0; } #ifdef stat /* special version of stat tries to generate an unique inode number. */ int stat(filename, st) const char *filename; register struct stat *st; { #undef stat if (stat(filename, st) < 0) return -1; if (sizeof st->st_ino == sizeof(long)) { st->st_ino = st->st_ctime ^ st->st_size; } else { union { long l; short i[2]; } ino; ino.l = st->st_ctime ^ st->st_size; st->st_ino = ino.i[0] ^ ino.i[1]; } return 0; } #endif /* stat */ /* library abort() and _assert() use stdio */ void abort() { _exit(3); } void _assert(mess, file, line) char *mess, file; { curstoLL(); printf("Assertion Failed: %s (file %s, line %d)%s\r\n", mess, file, line, CE); finish(QUIT); } /* * this is to optimize screen output, and to cope with Ctrl-C. * It is a replacement of the write() library routine. * Bconout is slightly (~20%) faster than (F)write, and it cannot be * interrupted with Ctrl-C. */ ssize_t write(fd, buf, nbytes) int fd; const void *buf; size_t nbytes; { long st; if (fd == 1) { register int n = nbytes; register const char *p = buf; while (--n >= 0) Bconout(CONSOLE, *p++); /* Kludge to force MiNT to flush Bconout buffer. */ Bcostat(CONSOLE); return nbytes; } #ifdef write # ifdef __GNUC__ return _write(fd, buf, nbytes); # else # undef write return write(fd, buf, nbytes); # endif #else if ((st = Fwrite(fd, nbytes, buf)) < 0) { # if __TURBOC__ extern int _XltErr __(( int _(tos_errno) )); errno = _XltErr((int) st); # else errno = -st; /* should be translated, but I am lazy... */ # endif return -1; } return st; #endif /* !write */ } #ifdef read /* Jove expects Posix compliant read(), i.e, type of 3rd arg is `size_t'. Unfortunately gnulib's read is non-compliant if compiled with -mshort. Hence this hack. */ ssize_t read(fd, buf, nbytes) int fd; void *buf; size_t nbytes; { # ifdef __GNUC__ return _read(fd, buf, nbytes); # else # undef read return read(fd, buf, nbytes); # endif } #endif /* read */ #ifdef unlink /* Somehow gnulib's unlink() crashes on me. */ int unlink(filename) const char *filename; { int st; if (filename == NULL || *filename == '\0') { errno = EEXIST; return -1; } if ((st = Fdelete(filename)) < 0) { # if __TURBOC__ extern int _XltErr __(( int _(tos_errno) )); errno = _XltErr((int) st); # else errno = -st; /* should be translated, but I am lazy... */ # endif st = -1; } return st; } #endif int getpid() { extern time_t time0; return (*(short *)&time0 ^ *((short *)&time0 + 1)) & 0x7fff; } #ifdef RESHAPING /* set Line-A parameters to change system font */ private int set_font(font) register __FONT *font; { # if __TURBOC__ /* a little optimization */ register VDIESC *vdi = Vdiesc; # define Vdiesc vdi # endif if (font->font_id != 1 || /* no system font */ font->form_height == V_CEL_HT) /* no change */ return NO; V_CEL_HT = font->form_height; V_CEL_WR = font->form_height * VWRAP; V_CEL_MY = V_Y_MAX / font->form_height - 1; V_CEL_MX = V_X_MAX / font->max_cell_width - 1; V_FNT_WR = font->form_width; V_FNT_ST = font->first_ade; V_FNT_ND = font->last_ade; V_OFF_AD = font->off_table; V_FNT_AD = font->dat_table; # undef Vdiesc return YES; } DEF_CMD( "screen-dense", ScrDense, NO ); _IF(def ATARIST)_IF(def RESHAPING) DEF_CMD( "screen-normal", ScrDense, NEGATE ) _IF(def ATARIST)_IF(def RESHAPING) { # if __TURBOC__ /* a little optimization */ register VDIESC *vdi = Vdiesc; # define Vdiesc vdi # endif if (VPLANES > 1) complain("[\"%f\" only in Monochrome mode]"); if (scr_xorg || scr_yorg) complain("[\"%f\" not in window]"); if (set_font((exp == 0) ? org_font : FONT_SELECT(exp > 0))) win_reshape(); # undef Vdiesc } #endif /* RESHAPING */ #ifdef FILESELECTOR public char * FileSelector(prompt, def, buf) const char *prompt, *def; char *buf; { int valid = NO; char base_name[FILESIZE]; char *base_p = "*.*"; # ifdef CHDIR # ifndef TINY extern int ask_dir_only; if (ask_dir_only >= 0) /* only select directories */ base_p = "."; # endif # endif make_filename(buf, pwd(), base_p); strupr(buf); base_name[0] = '\0'; /*- FSelMode = READ; /* not used */ f_mess((def && def[0]) ? "%s(default %s)" : "%s", prompt, def); mouseon(); cursoff(); if (FSEL_EXINPUT_AVAILABLE) { char header[MESG_SIZE]; /* Build a nice header; assume prompt ends with blank */ sprintf(header, "\360\360 %s\360\360 ", (prompt[0] == ':') ? prompt + 2 : prompt); fsel_exinput(buf, base_name, &valid, strupr(header)); } else { fsel_input(buf, base_name, &valid); } ttsize(); /* to reset clip region */ mouseoff(); scr_inval(); /* File selector messes screen */ if (!valid) complain("[Aborted]"); base_p = strcpy(basename(buf), base_name); /* append filename */ FIX_FILENAME(buf); if (base_p[0] == '\0') { if (def == NULL || numcomp(buf, pwd()) == (base_p - buf - 1)) buf = (char *) def; # ifdef CHDIR # ifndef TINY /* if we are not asking for a directory, and user changed the directory path, append default filename. */ else if (!(ask_dir_only >= 0)) strcpy(base_p, basename(def)); # endif # endif } /* display the result */ s_mess("%s%s", prompt, buf); return buf; } #endif /* FILESELECTOR */ #endif /* ATARIST */ /*====================================================================== * $Log: atari_st.c,v $ * Revision 14.32.0.9 1994/01/06 00:13:15 tom * (LanguageSpecials): add Scandinavian, Swiss support. * * Revision 14.32.0.6 1993/10/28 00:54:48 tom * ported to gcc (2.3.3) + gnulib; * UnixToBuf: expand environment variables in command; * getwd, FileSelector: replace strlwr() with FIX_FILENAME(). * * Revision 14.32 1993/05/02 22:48:29 tom * (write): add Bconout flush kludge for MiNT. * * Revision 14.31 1993/02/15 02:01:45 tom * remove (void) casts; fix done_ttinit. * * Revision 14.30 1993/02/05 00:07:26 tom * cleanup whitespace; some random optimizations. * * Revision 14.27 1992/09/21 13:15:58 tom * replace CTL('Q') with `QuoteChar'; use ssize_t with write(). * * Revision 14.26 1992/08/26 23:57:02 tom * add RCS directives. * */