context@uw-june.UUCP (Ronald Blanford) (09/25/87)
This is a summary of responses received to my proposal of a week ago to change the format of the Minix cursor positioning sequence. The essence of that proposal was: > I would like to propose that the Minix cursor positioning escape > sequence be changed from ESC x y to ESC y x. This change would make > it possible to add support for ANSI escape sequences which begin with > ESC [. There were 9 responses. I've summarized their comments and added my own. > Timothy L. Kay <tim@csvax.caltech.edu> expressed support for the > proposed change. > Bill Gray <bgray@mu.edu>, Martin Minow <decvax!minow>, Gordon W. Ross > <gwr%linus@mitre-bedford.ARPA>, and Thos Sumner <thos@cca.ucsf.edu> > suggested eliminating the current scheme entirely in favor of ANSI > sequences. My feeling is that Minix was intended to be an experimental/teaching system, so that the exploration of alternatives to accepted practice should be encouraged. While the introduction of yet another system of nonstandard terminal controls in the face of existing standards seems to me to be counterproductive, I did not wish to usurp the prerogatives of the original implementors by eliminating their method altogether. Therefore I proposed what seemed to be the minimal modification to the existing method which would allow implementation of the increased functionality and standardization of ANSI sequences. > Vincent P. Broman <broman@cod.nosc.mil> suggested changing the Minix > row numbering in addition to the argument order. If support for ANSI sequences were to be added, programs wishing a top-down row numbering scheme could choose the ANSI alternative. To modify the Minix interpretation of row numbers exceeds the demands of minimal change. > Eric Gisin <egisin@orchid.waterloo.edu> points out that ESC ~ is also > used (at least on DEC terminals) to map G1 into GR. > Andrew S. Tanenbaum <mcvax!cs.vu.nl!ast@uunet.UU.NET> supports a move > to ANSI standards, but suggests modifying the current method to > ESC # x y. The ESC # prefix proposed by Tanenbaum is also currently in use by DEC to generate double-width/height lines and alignment patterns. My proposal did not address these potential sources of conflict. > Kok Chen <sun!imagen!kchen> suggests the use of a special sequence to > switch between Minix and ANSI modes. This would certainly take care of any possible conflicts between native and ANSI escape sequences. Unfortunately, such a switch sequence would either require the programmer to be aware of the environment in which the program is executing, or else require the user to bracket the program with echo commands to switch the modes. Now, my conclusions and revised proposal: None of the responses offered any support of the existing scheme. My own conclusion from these responses, especially Tanenbaum's, is that the existing escape sequences were hacked together with minimal forethought just to allow Mined to be ported. I feel that the experiment they represent has been adequately demonstrated, and that there is no further reason not to adopt ANSI sequences as the native mode. In fact there are great imperatives why the existing Minix sequences should be eliminated. First of all, the ESC z attribute sequence represents a continued dependence on the IBM PC hardware configuration. How can this be condoned in an operating system that supports remote terminals and runs on non-IBM 32000 and 68000 systems as well? Secondly, the existing set is simply not powerful enough for many applications. Kok Chen indicated that he was writing an application requiring insertion/deletion of characters and lines, and that it had become necessary to bypass the tty driver and manipulate screen memory directly, presumably through /dev/mem. Rather than comment on his solution, let's just say that it well illustrates the problem. Any effort spent to increase the power of expression would be best spent in conjunction with standardization. Consequently, I have changed all programs currently using escape sequences to conform to ANSI standards. These are: mm/main.c, mined1.c, clr.c, and more.c. I have changed tty.c to recognize the ANSI equivalents in lieu of the sequences which were previously implemented (position cursor, reverse index, clear to end of screen, and set graphic rendition). While I spent some effort to make these changes easily extensible, I believe that the current code for ANSI parsing and command execution will need to be replaced when a full parser is implemented. The current changes will minimize and localize to the kernel the necessary modifications when that time comes. Eric Gisin has already supplied me with skeleton code for a full parser, and Gordon Ross has offered to help with the implementation. The following shell archive contains the diffs engendered by these changes. The diffs are relative to the 1.2 release. ------------------------- CUT HERE ---------------------------------- echo x - clr.dif gres '^X' '' > clr.dif << '/' X7,9c7,9 X< prints("\033 8\033~0"); X< exit(0); X< } X--- X> prints("\033[H\033[J"); X> exit(0); X> } / echo x - mined1.dif gres '^X' '' > mined1.dif << '/' X840,842c840,843 X< string_print(pos_string); X< putchar(X_PLUS + nx); X< putchar(Y_PLUS + YMAX - ny);/* Driver has (0,0) at lower left corner */ X--- X> char text_buffer[10]; X> X> build_string(text_buffer, pos_string, ny+1, nx+1); X> string_print(text_buffer); X1330,1334c1331,1335 X< char *enter_string = "\033 8\033~0"; /* String printed on entering mined */ X< char *pos_string = "\033"; /* Absolute cursor position */ X< char *rev_scroll = "\033~1"; /* String for reverse scrolling */ X< char *rev_video = "\033z\160"; /* String for starting reverse video */ X< char *normal_video = "\033z\007"; /* String for leaving reverse video */ X--- X> char *enter_string = "\033[H\033[J"; /* String printed on entering mined */ X> char *pos_string = "\033[%d;%dH"; /* Absolute cursor position */ X> char *rev_scroll = "\033M"; /* String for reverse scrolling */ X> char *rev_video = "\033[7m"; /* String for starting reverse video */ X> char *normal_video = "\033[m"; /* String for leaving reverse video */ / echo x - mm_main.dif gres '^X' '' > mm_main.dif << '/' X165c165 X< printf("%c 8%c~0",033, 033); /* go to top of screen and clear screen */ X--- X> printf("%c[H%c[J",033, 033); /* go to top of screen and clear screen */ / echo x - more.dif gres '^X' '' > more.dif << '/' X9,11c9,11 X< #define reverse() write(1, "\033z\160", 3) /* reverse video */ X< #define normal() write(1, "\033z\7", 3) /* undo reverse() */ X< #define clearln() write(1, "\r\033~0", 4) /* clear line */ X--- X> #define reverse() write(1, "\033[7m", 4) /* reverse video */ X> #define normal() write(1, "\033[m", 3) /* undo reverse() */ X> #define clearln() write(1, "\r\033[J", 4) /* clear line */ / echo x - tty.dif gres '^X' '' > tty.dif << '/' X64a65 X> #define MAX_ESC_PARMS 2 /* number of escape sequence parameters allowed */ X96,97c97,100 X< char tty_esc_state; /* 0=normal, 1 = ESC seen, 2 = ESC + x seen */ X< char tty_echar; /* first character following an ESC */ X--- X> char tty_esc_state; /* 0=normal, 1=ESC, 2=ESC[ */ X> char tty_esc_intro; /* Distinguishing character following ESC */ X> int tty_esc_parmv[MAX_ESC_PARMS]; /* list of escape parameters */ X> int *tty_esc_parmp; /* pointer to current escape parameter */ X104c107 X< int tty_row; /* current row (0 at bottom of screen) */ X--- X> int tty_row; /* current row (0 at top of screen) */ X957,967c960,963 X< /* Output a character on the console. Check for escape sequences, including X< * ESC 32+x 32+y to move cursor to (x, y) X< * ESC ~ 0 to clear from cursor to end of screen X< * ESC ~ 1 to reverse scroll the screen 1 line X< * ESC z x to set the attribute byte to x (z is a literal here) X< */ X< X< /* Check to see if we are part way through an escape sequence. */ X< if (tp->tty_esc_state == 1) { X< tp->tty_echar = c; X< tp->tty_esc_state = 2; X--- X> /* Output a character on the console. Check for escape sequences first. */ X> X> if (tp->tty_esc_state > 0) { X> parse_escape(tp, c); X971,976d966 X< if (tp->tty_esc_state == 2) { X< escape(tp, tp->tty_echar, c); X< tp->tty_esc_state = 0; X< return; X< } X< X984c974 X< move_to(tp, tp->tty_column, tp->tty_row + 1); X--- X> move_to(tp, tp->tty_column, tp->tty_row - 1); X1001,1004c991,994 X< if (tp->tty_row == 0) X< scroll_screen(tp, GO_FORWARD); X< else X< tp->tty_row--; X--- X> if (tp->tty_row == SCR_LINES-1) X> scroll_screen(tp, GO_FORWARD); X> else X> tp->tty_row++; X1030c1020 X< tp->tty_ramqueue[tp->tty_rwords++] = tp->tty_attribute | c; X--- X> tp->tty_ramqueue[tp->tty_rwords++] = tp->tty_attribute | (c & BYTE); X1083c1073 X< int y; /* row (0 <= y <= 24, 0 at bottom) */ X--- X> int y; /* row (0 <= y <= 24, 0 at top) */ X1091c1081 X< tp->tty_vid = (tp->tty_org + 2*(SCR_LINES-1-y)*LINE_WIDTH + 2*x); X--- X> tp->tty_vid = (tp->tty_org + 2*y*LINE_WIDTH + 2*x); X1099,1135c1089,1199 X< PRIVATE escape(tp, x, y) X< register struct tty_struct *tp; /* pointer to tty struct */ X< char x; /* escape sequence is ESC x y; this is x */ X< char y; /* escape sequence is ESC x y; this is y */ X< { X< /* Handle an escape sequence. */ X< X< int n, ct, vx; X< X< X< /* Check for ESC z attribute - used to change attribute byte. */ X< if (x == 'z') { X< /* Set attribute byte */ X< tp->tty_attribute = y << 8; X< return; X< } X< /* Check for ESC ~ n - used for clear screen, reverse scroll. */ X< if (x == '~') { X< if (y == '0') { X< /* Clear from cursor to end of screen */ X< n = 2 * LINE_WIDTH * (tp->tty_row + 1) - 2 * tp->tty_column; X< vx = tp->tty_vid; X< while (n > 0) { X< ct = MIN(n, vid_retrace); X< vid_copy(NIL_PTR, vid_base, vx, ct/2); X< vx += ct; X< n -= ct; X< } X< } else if (y == '1') { X< /* Reverse scroll. */ X< scroll_screen(tp, GO_BACKWARD); X< } X< return; X< } X< X< /* Must be cursor movement (or invalid). */ X< move_to(tp, x - 32, y - 32); X--- X> X> PRIVATE parse_escape(tp, c) X> register struct tty_struct *tp; /* pointer to tty struct */ X> char c; /* next character in escape sequence */ X> { X> /* The following ANSI escape sequences are currently supported: X> * ESC M to reverse index the screen X> * ESC [ y ; x H to move cursor to (x, y) [default (1,1)] X> * ESC [ 0 J to clear from cursor to end of screen X> * ESC [ n m to set the screen rendition X> * n: 0 = normal [default] X> * 7 = reverse X> */ X> X> switch (tp->tty_esc_state) { X> case 1: /* ESC seen */ X> tp->tty_esc_intro = '\0'; X> tp->tty_esc_parmp = tp->tty_esc_parmv; X> tp->tty_esc_parmv[0] = tp->tty_esc_parmv[1] = 0; X> switch (c) { X> case '[': /* Control Sequence Introducer */ X> tp->tty_esc_intro = c; X> tp->tty_esc_state = 2; X> break; X> case 'M': /* Reverse Index */ X> do_escape(tp, c); X> break; X> default: X> tp->tty_esc_state = 0; X> break; X> } X> break; X> X> case 2: /* ESC [ seen */ X> if (c >= '0' && c <= '9') { X> if (tp->tty_esc_parmp X> < tp->tty_esc_parmv + MAX_ESC_PARMS) X> *tp->tty_esc_parmp = X> *tp->tty_esc_parmp * 10 + (c - '0'); X> break; X> } X> else if (c == ';') { X> if (++tp->tty_esc_parmp X> < tp->tty_esc_parmv + MAX_ESC_PARMS) X> *tp->tty_esc_parmp = 0; X> break; X> } X> else { X> do_escape(tp, c); X> } X> break; X> default: /* illegal state */ X> tp->tty_esc_state = 0; X> break; X> } X> } X> X> PRIVATE do_escape(tp, c) X> register struct tty_struct *tp; /* pointer to tty struct */ X> char c; /* next character in escape sequence */ X> { X> int n, ct, vx; X> X> /* Handle a sequence beginning with just ESC */ X> if (tp->tty_esc_intro == '\0') { X> switch (c) { X> case 'M': /* Reverse Index */ X> if (tp->tty_row == 0) X> scroll_screen(tp, GO_BACKWARD); X> else X> tp->tty_row--; X> move_to(tp, tp->tty_column, tp->tty_row); X> break; X> default: break; X> } X> } X> else X> /* Handle a sequence beginning with ESC [ and parameters */ X> if (tp->tty_esc_intro == '[') { X> switch (c) { X> case 'H': /* Position cursor */ X> move_to(tp, X> MAX(1, MIN(LINE_WIDTH, tp->tty_esc_parmv[1])) - 1, X> MAX(1, MIN(SCR_LINES, tp->tty_esc_parmv[0])) - 1 ); X> break; X> case 'J': /* Clear from cursor to end of screen */ X> if (tp->tty_esc_parmv[0] == 0) { X> n = 2 * ((SCR_LINES - (tp->tty_row + 1)) * LINE_WIDTH X> + LINE_WIDTH - (tp->tty_column + 1)); X> vx = tp->tty_vid; X> while (n > 0) { X> ct = MIN(n, vid_retrace); X> vid_copy(NIL_PTR, vid_base, vx, ct/2); X> vx += ct; X> n -= ct; X> } X> } X> break; X> case 'm': /* Set graphic rendition */ X> switch (tp->tty_esc_parmv[0]) { X> case 7: tp->tty_attribute = 0160 << 8; X> break; X> default: tp->tty_attribute = 0007 << 8; X> break; X> } X> break; X> default: X> break; X> } X> } X> tp->tty_esc_state = 0; X1234c1298 X< move_to(&tty_struct[0], 0, 0); /* move cursor to lower left corner */ X--- X> move_to(&tty_struct[0], 0, SCR_LINES-1); /* move cursor to lower left corner */ /