3 #endif /* not macintosh */
11 static unsigned long *text; /* buffer of text on terminal screen */
12 static unsigned long *scrtop; /* top of working screen */
13 static unsigned long *disptop; /* top of displayed screen */
14 static unsigned long *sbtop; /* top of scrollback */
15 static unsigned long *cpos; /* cursor position (convenience) */
16 static unsigned long *disptext; /* buffer of text on real screen */
17 static unsigned long *wanttext; /* buffer of text we want on screen */
18 static unsigned long *alttext; /* buffer of text on alt. screen */
20 static unsigned char *selspace; /* buffer for building selections in */
22 #define TSIZE (sizeof(*text))
23 #define fix_cpos do { cpos = scrtop + curs_y * (cols+1) + curs_x; } while(0)
25 static unsigned long curr_attr, save_attr;
27 static int curs_x, curs_y; /* cursor */
28 static int save_x, save_y; /* saved cursor position */
29 static int marg_t, marg_b; /* scroll margins */
30 static int dec_om; /* DEC origin mode flag */
31 static int lfhascr; /* Auto-cr mode flag */
32 static int wrap, wrapnext; /* wrap flags */
33 static int insert; /* insert-mode flag */
34 static int cset; /* 0 or 1: which char set */
35 static int save_cset, save_csattr; /* saved with cursor position */
36 static int rvideo; /* global reverse video flag */
38 static unsigned long cset_attr[2];
41 * Saved settings on the alternate screen.
43 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
44 static int alt_t, alt_b;
47 #define ARGS_MAX 32 /* max # of esc sequence arguments */
48 #define ARG_DEFAULT -1 /* if an arg isn't specified */
49 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
50 static int esc_args[ARGS_MAX];
54 #define OSC_STR_MAX 2048
55 static int osc_strlen;
56 static char osc_string[OSC_STR_MAX+1];
59 static unsigned char *tabs;
65 TOPLEVEL, IGNORE_NEXT,
66 SEEN_ESC, SEEN_CSI, SEEN_GZD4, SEEN_G1D4,
67 SEEN_OSC, SEEN_OSC_P, SEEN_OSC_W, OSC_STRING, OSC_MAYBE_ST,
69 SEEN_ESC_CONFUSED, SEEN_CSI_CONFUSED,
73 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
76 SM_CHAR, SM_WORD, SM_LINE
78 static unsigned long *selstart, *selend, *selanchor;
80 static short wordness[256] = {
81 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 01 */
82 0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2, 2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1, /* 23 */
83 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2, /* 45 */
84 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1, /* 67 */
85 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 89 */
86 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* AB */
87 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2, /* CD */
88 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2, /* EF */
91 static unsigned char sel_nl[] = SEL_NL;
94 * Internal prototypes.
96 static void do_paint (Context, int);
97 static void erase_lots (int, int, int);
98 static void swap_screen (int);
99 static void update_sbar (void);
100 static void deselect (void);
101 static void scroll_display(int, int, int);
104 * Set up power-on settings for the terminal.
106 static void power_on(void) {
107 curs_x = curs_y = alt_x = alt_y = save_x = save_y = 0;
110 alt_b = marg_b = rows - 1;
115 for (i = 0; i < cols; i++)
116 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
118 alt_om = dec_om = cfg.dec_om;
119 alt_wnext = wrapnext = alt_ins = insert = FALSE;
120 alt_wrap = wrap = cfg.wrap_mode;
122 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
124 lfhascr = cfg.lfhascr;
125 save_attr = curr_attr = ATTR_DEFAULT;
126 app_cursor_keys = cfg.app_cursor;
127 app_keypad_keys = cfg.app_keypad;
131 for (i = 0; i < 256; i++)
132 wordness[i] = cfg.wordness[i];
136 erase_lots (FALSE, TRUE, TRUE);
138 erase_lots (FALSE, TRUE, TRUE);
143 * Force a screen update.
145 void term_update(void) {
149 do_paint (ctx, TRUE);
156 * Same as power_on(), but an external function.
158 void term_pwron(void) {
167 * Clear the scrollback.
169 void term_clrsb(void) {
170 disptop = sbtop = scrtop;
175 * Initialise the terminal.
177 void term_init(void) {
178 text = sbtop = scrtop = disptop = cpos = NULL;
179 disptext = wanttext = NULL;
189 * Set up the terminal for a given size.
191 void term_size(int newrows, int newcols, int newsavelines) {
192 unsigned long *newtext, *newdisp, *newwant, *newalt;
193 int i, j, crows, ccols;
195 if (newrows == rows && newcols == cols && newsavelines == savelines)
196 return; /* nothing to do */
199 alt_b = marg_b = newrows - 1;
201 newtext = smalloc ((newrows+newsavelines)*(newcols+1)*TSIZE);
202 disptop = newtext + newsavelines*(newcols+1);
203 for (i=0; i<(newrows+newsavelines)*(newcols+1); i++)
204 newtext[i] = ERASE_CHAR;
206 crows = rows + (scrtop - sbtop) / (cols+1);
207 if (crows > newrows+newsavelines)
208 crows = newrows+newsavelines;
209 ccols = (cols < newcols ? cols : newcols);
210 for (i=0; i<crows; i++) {
211 int oldidx = (rows + savelines - crows + i) * (cols+1);
212 int newidx = (newrows + newsavelines - crows + i) * (newcols+1);
213 for (j=0; j<ccols; j++)
214 newtext[newidx+j] = text[oldidx+j];
215 newtext[newidx+newcols] =
216 (cols == newcols ? text[oldidx+cols] : 0);
218 sbtop = disptop - (crows - newrows) * (newcols+1);
227 newdisp = smalloc (newrows*(newcols+1)*TSIZE);
228 for (i=0; i<newrows*(newcols+1); i++)
229 newdisp[i] = ATTR_INVALID;
233 newwant = smalloc (newrows*(newcols+1)*TSIZE);
234 for (i=0; i<newrows*(newcols+1); i++)
235 newwant[i] = ATTR_INVALID;
239 newalt = smalloc (newrows*(newcols+1)*TSIZE);
240 for (i=0; i<newrows*(newcols+1); i++)
241 newalt[i] = ERASE_CHAR;
246 selspace = smalloc ( (newrows+newsavelines) * (newcols+sizeof(sel_nl)) );
248 tabs = srealloc (tabs, newcols*sizeof(*tabs));
251 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
252 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
256 curs_y += newrows - rows;
259 if (curs_y >= newrows)
261 if (curs_x >= newcols)
264 wrapnext = alt_wnext = FALSE;
268 savelines = newsavelines;
279 static void swap_screen (int which) {
283 if (which == alt_which)
288 for (t=0; t<rows*(cols+1); t++) {
289 tt = scrtop[t]; scrtop[t] = alttext[t]; alttext[t] = tt;
292 t = curs_x; curs_x = alt_x; alt_x = t;
293 t = curs_y; curs_y = alt_y; alt_y = t;
294 t = marg_t; marg_t = alt_t; alt_t = t;
295 t = marg_b; marg_b = alt_b; alt_b = t;
296 t = dec_om; dec_om = alt_om; alt_om = t;
297 t = wrap; wrap = alt_wrap; alt_wrap = t;
298 t = wrapnext; wrapnext = alt_wnext; alt_wnext = t;
299 t = insert; insert = alt_ins; alt_ins = t;
300 t = cset; cset = alt_cset; alt_cset = t;
306 * Retrieve a character from `inbuf'.
308 static int inbuf_getc(void) {
309 if (inbuf_head == inbuf_reap)
313 inbuf_reap = (inbuf_reap+1) & INBUF_MASK;
319 * Update the scroll bar.
321 static void update_sbar(void) {
324 min = (sbtop - text) / (cols+1);
325 set_sbar ((scrtop - text) / (cols+1) + rows - min,
326 (disptop - text) / (cols+1) - min,
331 * Check whether the region bounded by the two pointers intersects
332 * the scroll region, and de-select the on-screen selection if so.
334 static void check_selection (unsigned long *from, unsigned long *to) {
335 if (from < selend && selstart < to)
340 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
341 * for backward.) `sb' is TRUE if the scrolling is permitted to
342 * affect the scrollback buffer.
344 static void scroll (int topline, int botline, int lines, int sb) {
345 unsigned long *scroll_top;
346 int scroll_size, size, i;
348 scroll_top = scrtop + topline*(cols+1);
349 size = (lines < 0 ? -lines : lines) * (cols+1);
350 scroll_size = (botline - topline + 1) * (cols+1) - size;
352 if (lines > 0 && topline == 0 && botline == (rows-1) && sb) {
354 * Since we're going to scroll the whole screen upwards,
355 * let's also affect the scrollback buffer.
357 sbtop -= lines * (cols+1);
360 scroll_size += scroll_top - sbtop;
365 if (scroll_size < 0) {
372 memmove (scroll_top, scroll_top + size, scroll_size*TSIZE);
373 for (i = 0; i < size; i++)
374 scroll_top[i+scroll_size] = ERASE_CHAR;
375 if (selstart > scroll_top &&
376 selstart < scroll_top + size + scroll_size) {
378 if (selstart < scroll_top)
379 selstart = scroll_top;
381 if (selend > scroll_top &&
382 selend < scroll_top + size + scroll_size) {
384 if (selend < scroll_top)
389 memmove (scroll_top + size, scroll_top, scroll_size*TSIZE);
390 for (i = 0; i < size; i++)
391 scroll_top[i] = ERASE_CHAR;
392 if (selstart > scroll_top &&
393 selstart < scroll_top + size + scroll_size) {
395 if (selstart > scroll_top + size + scroll_size)
396 selstart = scroll_top + size + scroll_size;
398 if (selend > scroll_top &&
399 selend < scroll_top + size + scroll_size) {
401 if (selend > scroll_top + size + scroll_size)
402 selend = scroll_top + size + scroll_size;
405 #ifdef OPTIMISE_SCROLL
406 scroll_display(topline, botline, lines);
410 #ifdef OPTIMISE_SCROLL
411 static void scroll_display(int topline, int botline, int lines) {
412 unsigned long *start, *end;
413 int distance, size, i;
415 start = disptext + topline * (cols + 1);
416 end = disptext + (botline + 1) * (cols + 1);
417 distance = (lines > 0 ? lines : -lines) * (cols + 1);
418 size = end - start - distance;
420 memmove(start, start + distance, size * TSIZE);
421 for (i = 0; i < distance; i++)
422 (start + size)[i] |= ATTR_INVALID;
424 memmove(start + distance, start, size * TSIZE);
425 for (i = 0; i < distance; i++)
426 start[i] |= ATTR_INVALID;
428 do_scroll(topline, botline, lines);
430 #endif /* OPTIMISE_SCROLL */
434 * Move the cursor to a given position, clipping at boundaries. We
435 * may or may not want to clip at the scroll margin: marg_clip is 0
436 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
437 * even _being_ outside the margins.
439 static void move (int x, int y, int marg_clip) {
445 if ((curs_y >= marg_t || marg_clip == 2) && y < marg_t)
447 if ((curs_y <= marg_b || marg_clip == 2) && y > marg_b)
461 * Save or restore the cursor and SGR mode.
463 static void save_cursor(int save) {
467 save_attr = curr_attr;
469 save_csattr = cset_attr[cset];
473 curr_attr = save_attr;
475 cset_attr[cset] = save_csattr;
481 * Erase a large portion of the screen: the whole screen, or the
482 * whole line, or parts thereof.
484 static void erase_lots (int line_only, int from_begin, int to_end) {
485 unsigned long *startpos, *endpos;
488 startpos = cpos - curs_x;
489 endpos = startpos + cols+1;
492 endpos = startpos + rows * (cols+1);
498 check_selection (startpos, endpos);
499 while (startpos < endpos)
500 *startpos++ = ERASE_CHAR;
504 * Insert or delete characters within the current line. n is +ve if
505 * insertion is desired, and -ve for deletion.
507 static void insch (int n) {
508 int dir = (n < 0 ? -1 : +1);
511 n = (n < 0 ? -n : n);
512 if (n > cols - curs_x)
514 m = cols - curs_x - n;
515 check_selection (cpos, cpos+n);
517 memmove (cpos, cpos+n, m*TSIZE);
519 cpos[m++] = ERASE_CHAR;
521 memmove (cpos+n, cpos, m*TSIZE);
523 cpos[n] = ERASE_CHAR;
528 * Toggle terminal mode `mode' to state `state'. (`query' indicates
529 * whether the mode is a DEC private one or a normal one.)
531 static void toggle_mode (int mode, int query, int state) {
532 if (query) switch (mode) {
533 case 1: /* application cursor keys */
534 app_cursor_keys = state;
536 case 3: /* 80/132 columns */
538 request_resize (state ? 132 : 80, rows);
540 case 5: /* reverse video */
544 case 6: /* DEC origin mode */
547 case 7: /* auto wrap */
550 case 47: /* alternate screen */
555 } else switch (mode) {
556 case 4: /* set insert mode */
559 case 20: /* line feed/new line mode */
566 * Process an OSC sequence: set window title or icon name.
568 static void do_osc(void) {
571 wordness[(unsigned char)osc_string[osc_strlen]] = esc_args[0];
573 osc_string[osc_strlen] = '\0';
574 switch (esc_args[0]) {
577 set_icon (osc_string);
578 if (esc_args[0] == 1)
580 /* fall through: parameter 0 means set both */
583 set_title (osc_string);
590 NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL,
591 BS, HT, LF, VT, FF, CR, SO, SI,
592 DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB,
593 CAN, EM, SUB, ESC, IS1, IS2, IS3, IS4
597 BPH = 0x82, NBH, IND, NEL, SSA, ESA,
598 HTS, HTJ, VTS, PLD, PLU, RI, SS1, SS2,
599 DCS, PU1, PU2, STS, CCH, MW, SPA, EPA,
600 SOS, SCI = 0x9a, CSI, ST, OSC, PM, APC
604 * Remove everything currently in `inbuf' and stick it up on the
605 * in-memory display. There's a big state machine in here to
606 * process escape sequences...
608 void term_out(void) {
610 int must_update = FALSE;
611 int reprocess = FALSE;
613 while (reprocess || (c = inbuf_getc()) != -1) {
616 static FILE *fp = NULL;
617 if (!fp) fp = fopen("putty.log", "wb");
618 if (fp) fputc (c, fp);
626 case '\005': /* terminal type query */
627 back->send ("\033[?1;2c", 7);
635 if (curs_x == 0 && curs_y > 0)
636 curs_x = cols-1, curs_y--;
656 if (curs_y == marg_b)
657 scroll (marg_t, marg_b, 1, TRUE);
658 else if (curs_y < rows-1)
670 } while (curs_x < cols-1 && !tabs[curs_x]);
674 unsigned long *old_cpos = cpos;
676 check_selection (old_cpos, cpos);
688 termstate = SEEN_ESC;
690 case NEL: /* exactly equivalent to CR-LF */
693 if (curs_y == marg_b)
694 scroll (marg_t, marg_b, 1, TRUE);
695 else if (curs_y < rows-1)
702 case HTS: /* set a tab */
705 case RI: /* reverse index - backwards LF */
706 if (curs_y == marg_t)
707 scroll (marg_t, marg_b, -1, TRUE);
715 case SCI: /* terminal type query */
716 /* This sequence is standardised as something else entirely. */
717 back->send ("\033[?6c", 5);
720 termstate = SEEN_CSI;
722 esc_args[0] = ARG_DEFAULT;
726 termstate = SEEN_OSC;
730 if (c >= ' ' && c < 0x7f || c >= 0xa0 ) {
732 cpos[1] = ATTR_WRAPPED;
733 if (curs_y == marg_b)
734 scroll (marg_t, marg_b, 1, TRUE);
735 else if (curs_y < rows-1)
744 check_selection (cpos, cpos+1);
745 *cpos++ = c | curr_attr |
746 (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
748 if (curs_x == cols) {
759 termstate = TOPLEVEL;
763 * This state is virtually identical to SEEN_ESC, with the
764 * exception that we have an OSC sequence in the pipeline,
765 * and _if_ we see a backslash, we process it.
769 termstate = TOPLEVEL;
772 /* else fall through */
775 * According to ECMA-35, an escape sequence consists of
776 * ESC, a sequence (possibly empty) of intermediate bytes
777 * from column 02 (SPACE--/), and a final byte from
778 * columns 03-07 (0--~).
780 termstate = TOPLEVEL;
781 if (c >= 0x40 && c < 0x60) {
782 /* Fe sequences -- C1 control as an escape sequence */
786 /* nF sequences -- with intermediate bytes */
787 case '#': /* Single control functions */
788 termstate = SEEN_ESCHASH;
790 case '(': /* GZD4: should set G0 */
791 termstate = SEEN_GZD4;
793 case ')': /* G1D4: should set G1 */
794 termstate = SEEN_G1D4;
796 /* Fp sequences -- private control functions */
797 case '7': /* save cursor */
800 case '8': /* restore cursor */
806 app_keypad_keys = TRUE;
809 app_keypad_keys = FALSE;
811 /* Fs sequences -- standardised control functions */
812 case 'c': /* RIS: restore power-on settings */
819 termstate = SEEN_ESC_CONFUSED;
824 case SEEN_ESC_CONFUSED:
826 * We're in an escape sequence, but we no longer know what
827 * it means and we just want it to go away
829 termstate = TOPLEVEL;
830 if (c < 0x20 || c >= 0x7f)
832 * ECMA-35 says this isn't allowed, so we can do what
837 /* Intermediate byte -- more to come */
838 termstate = SEEN_ESC_CONFUSED;
839 /* Otherwise, that was a final byte and we're free! */
843 * In theory, a control sequence consists of CSI, then a
844 * sequence (possibly empty) of parameter bytes (0--?)
845 * then a sequence (possibly empty) of intermediate bytes
846 * (SPACE--/), then a final byte (@--~). We're rather
847 * more relaxed, and don't differentiate between parameter
848 * and intermediate bytes.
850 termstate = TOPLEVEL; /* default */
852 case '0': case '1': case '2': case '3': case '4':
853 case '5': case '6': case '7': case '8': case '9':
854 if (esc_nargs <= ARGS_MAX) {
855 if (esc_args[esc_nargs-1] == ARG_DEFAULT)
856 esc_args[esc_nargs-1] = 0;
857 esc_args[esc_nargs-1] =
858 10 * esc_args[esc_nargs-1] + c - '0';
860 termstate = SEEN_CSI;
863 if (++esc_nargs <= ARGS_MAX)
864 esc_args[esc_nargs-1] = ARG_DEFAULT;
865 termstate = SEEN_CSI;
869 termstate = SEEN_CSI;
871 case 'A': /* move up N lines */
872 move (curs_x, curs_y - def(esc_args[0], 1), 1);
876 case 'B': case 'e': /* move down N lines */
877 move (curs_x, curs_y + def(esc_args[0], 1), 1);
881 case 'C': case 'a': /* move right N cols */
882 move (curs_x + def(esc_args[0], 1), curs_y, 1);
886 case 'D': /* move left N cols */
887 move (curs_x - def(esc_args[0], 1), curs_y, 1);
891 case 'E': /* move down N lines and CR */
892 move (0, curs_y + def(esc_args[0], 1), 1);
896 case 'F': /* move up N lines and CR */
897 move (0, curs_y - def(esc_args[0], 1), 1);
901 case 'G': case '`': /* set horizontal posn */
902 move (def(esc_args[0], 1) - 1, curs_y, 0);
906 case 'd': /* set vertical posn */
907 move (curs_x, (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
912 case 'H': case 'f': /* set horz and vert posns at once */
914 esc_args[1] = ARG_DEFAULT;
915 move (def(esc_args[1], 1) - 1,
916 (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
921 case 'J': /* erase screen or parts of it */
923 unsigned int i = def(esc_args[0], 0) + 1;
926 erase_lots(FALSE, !!(i & 2), !!(i & 1));
931 case 'K': /* erase line or parts of it */
933 unsigned int i = def(esc_args[0], 0) + 1;
936 erase_lots(TRUE, !!(i & 2), !!(i & 1));
941 case 'L': /* insert lines */
942 if (curs_y <= marg_b)
943 scroll (curs_y, marg_b, -def(esc_args[0], 1), FALSE);
947 case 'M': /* delete lines */
948 if (curs_y <= marg_b)
949 scroll (curs_y, marg_b, def(esc_args[0], 1), FALSE);
953 case '@': /* insert chars */
954 insch (def(esc_args[0], 1));
958 case 'P': /* delete chars */
959 insch (-def(esc_args[0], 1));
963 case 'c': /* terminal type query */
964 back->send ("\033[?6c", 5);
966 case 'n': /* cursor position query */
967 if (esc_args[0] == 6) {
969 sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1);
970 back->send (buf, strlen(buf));
973 case 'h': /* toggle a mode to high */
974 toggle_mode (esc_args[0], esc_query, TRUE);
976 case 'l': /* toggle a mode to low */
977 toggle_mode (esc_args[0], esc_query, FALSE);
979 case 'g': /* clear tabs */
980 if (esc_nargs == 1) {
981 if (esc_args[0] == 0) {
982 tabs[curs_x] = FALSE;
983 } else if (esc_args[0] == 3) {
985 for (i = 0; i < cols; i++)
990 case 'r': /* set scroll margins */
991 if (esc_nargs <= 2) {
993 top = def(esc_args[0], 1) - 1;
996 bot = (esc_nargs == 1 ? rows :
997 def(esc_args[1], rows)) - 1;
1005 * I used to think the cursor should be
1006 * placed at the top of the newly marginned
1007 * area. Apparently not: VMS TPU falls over
1017 case 'm': /* set graphics rendition */
1020 for (i=0; i<esc_nargs; i++) {
1021 switch (def(esc_args[i], 0)) {
1022 case 0: /* restore defaults */
1023 curr_attr = ATTR_DEFAULT; break;
1024 case 1: /* enable bold */
1025 curr_attr |= ATTR_BOLD; break;
1026 case 4: /* enable underline */
1027 case 21: /* (enable double underline) */
1028 curr_attr |= ATTR_UNDER; break;
1029 case 7: /* enable reverse video */
1030 curr_attr |= ATTR_REVERSE; break;
1031 case 22: /* disable bold */
1032 curr_attr &= ~ATTR_BOLD; break;
1033 case 24: /* disable underline */
1034 curr_attr &= ~ATTR_UNDER; break;
1035 case 27: /* disable reverse video */
1036 curr_attr &= ~ATTR_REVERSE; break;
1037 case 30: case 31: case 32: case 33:
1038 case 34: case 35: case 36: case 37:
1040 curr_attr &= ~ATTR_FGMASK;
1041 curr_attr |= (esc_args[i] - 30) << ATTR_FGSHIFT;
1043 case 39: /* default-foreground */
1044 curr_attr &= ~ATTR_FGMASK;
1045 curr_attr |= ATTR_DEFFG;
1047 case 40: case 41: case 42: case 43:
1048 case 44: case 45: case 46: case 47:
1050 curr_attr &= ~ATTR_BGMASK;
1051 curr_attr |= (esc_args[i] - 40) << ATTR_BGSHIFT;
1053 case 49: /* default-background */
1054 curr_attr &= ~ATTR_BGMASK;
1055 curr_attr |= ATTR_DEFBG;
1061 case 's': /* save cursor */
1064 case 'u': /* restore cursor */
1065 save_cursor (FALSE);
1069 case 't': /* set page size - ie window height */
1070 request_resize (cols, def(esc_args[0], 24));
1073 case 'X': /* write N spaces w/o moving cursor */
1075 int n = def(esc_args[0], 1);
1076 unsigned long *p = cpos;
1077 if (n > cols - curs_x)
1079 check_selection (cpos, cpos+n);
1086 case 'x': /* report terminal characteristics */
1089 int i = def(esc_args[0], 0);
1090 if (i == 0 || i == 1) {
1091 strcpy (buf, "\033[2;1;1;112;112;1;0x");
1093 back->send (buf, 20);
1098 termstate = SEEN_CSI_CONFUSED;
1103 case SEEN_CSI_CONFUSED:
1104 termstate = TOPLEVEL;
1105 if (c < 0x20 || c >= 0x7f)
1108 termstate = SEEN_CSI_CONFUSED;
1113 cset_attr[termstate == SEEN_GZD4 ? 0 : 1] = ATTR_GBCHR;
1116 cset_attr[termstate == SEEN_GZD4 ? 0 : 1] = ATTR_LINEDRW;
1118 default: /* specifically, 'B' */
1119 cset_attr[termstate == SEEN_GZD4 ? 0 : 1] = ATTR_ASCII;
1122 termstate = TOPLEVEL;
1127 case '\005': case '\007': case '\b': case '\016': case '\017':
1128 case '\033': case 0233: case 0234: case 0235: case '\015':
1129 case '\013': case '\014': case '\012': case '\t':
1130 termstate = TOPLEVEL;
1131 goto do_toplevel; /* hack... */
1132 case 'P': /* Linux palette sequence */
1133 termstate = SEEN_OSC_P;
1136 case 'R': /* Linux palette reset */
1139 termstate = TOPLEVEL;
1141 case 'W': /* word-set */
1142 termstate = SEEN_OSC_W;
1145 case '0': case '1': case '2': case '3': case '4':
1146 case '5': case '6': case '7': case '8': case '9':
1147 esc_args[0] = 10 * esc_args[0] + c - '0';
1151 * Grotty hack to support xterm and DECterm title
1152 * sequences concurrently.
1154 if (esc_args[0] == 2) {
1158 /* else fall through */
1160 termstate = OSC_STRING;
1165 if (c == 0234 || c == '\007') {
1167 * These characters terminate the string; ST and BEL
1168 * terminate the sequence and trigger instant
1169 * processing of it, whereas ESC goes back to SEEN_ESC
1170 * mode unless it is followed by \, in which case it is
1171 * synonymous with ST in the first place.
1174 termstate = TOPLEVEL;
1175 } else if (c == '\033')
1176 termstate = OSC_MAYBE_ST;
1177 else if (osc_strlen < OSC_STR_MAX)
1178 osc_string[osc_strlen++] = c;
1182 int max = (osc_strlen == 0 ? 21 : 16);
1184 if (c >= '0' && c <= '9')
1186 else if (c >= 'A' && c <= 'A'+max-10)
1188 else if (c >= 'a' && c <= 'a'+max-10)
1191 termstate = TOPLEVEL;
1192 osc_string[osc_strlen++] = val;
1193 if (osc_strlen >= 7) {
1194 palette_set (osc_string[0],
1195 osc_string[1] * 16 + osc_string[2],
1196 osc_string[3] * 16 + osc_string[4],
1197 osc_string[5] * 16 + osc_string[6]);
1199 termstate = TOPLEVEL;
1205 case '\005': case '\007': case '\b': case '\016': case '\017':
1206 case '\033': case 0233: case 0234: case 0235: case '\015':
1207 case '\013': case '\014': case '\012': case '\t':
1208 termstate = TOPLEVEL;
1209 goto do_toplevel; /* hack... */
1210 case '0': case '1': case '2': case '3': case '4':
1211 case '5': case '6': case '7': case '8': case '9':
1212 esc_args[0] = 10 * esc_args[0] + c - '0';
1215 termstate = OSC_STRING;
1221 unsigned long *p = scrtop;
1222 int n = rows * (cols+1);
1224 *p++ = ATTR_DEFAULT | 'E';
1227 check_selection (scrtop, scrtop + rows * (cols+1));
1229 termstate = TOPLEVEL;
1232 check_selection (cpos, cpos+1);
1235 if (must_update || nl_count > MAXNL)
1240 * Compare two lines to determine whether they are sufficiently
1241 * alike to scroll-optimise one to the other. Return the degree of
1244 static int linecmp (unsigned long *a, unsigned long *b) {
1247 for (i=n=0; i < cols; i++)
1248 n += (*a++ == *b++);
1253 * Given a context, update the window. Out of paranoia, we don't
1254 * allow WM_PAINT responses to do scrolling optimisations.
1256 static void do_paint (Context ctx, int may_optimise){
1257 int i, j, start, our_curs_y;
1258 unsigned long attr, rv, cursor;
1261 cursor = (has_focus ? ATTR_ACTCURS : ATTR_PASCURS);
1262 rv = (rvideo ? ATTR_REVERSE : 0);
1263 our_curs_y = curs_y + (scrtop - disptop) / (cols+1);
1265 for (i=0; i<rows; i++) {
1266 int idx = i*(cols+1);
1267 for (j=0; j<=cols; j++,idx++) {
1268 unsigned long *d = disptop+idx;
1269 wanttext[idx] = ((*d ^ rv
1270 ^ (selstart <= d && d < selend ?
1271 ATTR_REVERSE : 0)) |
1272 (i==our_curs_y && j==curs_x ? cursor : 0));
1277 * We would perform scrolling optimisations in here, if they
1278 * didn't have a nasty tendency to cause the whole sodding
1279 * program to hang for a second at speed-critical moments.
1280 * We'll leave it well alone...
1283 for (i=0; i<rows; i++) {
1284 int idx = i*(cols+1);
1286 for (j=0; j<=cols; j++,idx++) {
1287 unsigned long t = wanttext[idx];
1288 int needs_update = (j < cols && t != disptext[idx]);
1289 int keep_going = (start != -1 && needs_update &&
1290 (t & ATTR_MASK) == attr &&
1291 j-start < sizeof(ch));
1292 if (start != -1 && !keep_going) {
1293 do_text (ctx, start, i, ch, j-start, attr);
1299 attr = t & ATTR_MASK;
1301 ch[j-start] = (char) (t & CHAR_MASK);
1309 * Invalidate the whole screen so it will be repainted in full.
1311 void term_invalidate(void) {
1314 for (i=0; i<rows*(cols+1); i++)
1315 disptext[i] = ATTR_INVALID;
1319 * Paint the window in response to a WM_PAINT message.
1321 void term_paint (Context ctx, int l, int t, int r, int b) {
1322 int i, j, left, top, right, bottom;
1324 left = l / font_width;
1325 right = (r - 1) / font_width;
1326 top = t / font_height;
1327 bottom = (b - 1) / font_height;
1328 for (i = top; i <= bottom && i < rows ; i++)
1329 for (j = left; j <= right && j < cols ; j++)
1330 disptext[i*(cols+1)+j] = ATTR_INVALID;
1332 do_paint (ctx, FALSE);
1336 * Attempt to scroll the scrollback. The second parameter gives the
1337 * position we want to scroll to; the first is +1 to denote that
1338 * this position is relative to the beginning of the scrollback, -1
1339 * to denote it is relative to the end, and 0 to denote that it is
1340 * relative to the current position.
1342 void term_scroll (int rel, int where) {
1343 int n = where * (cols+1);
1344 #ifdef OPTIMISE_SCROLL
1345 unsigned long *olddisptop = disptop;
1347 #endif /* OPTIMISE_SCROLL */
1349 disptop = (rel < 0 ? scrtop :
1350 rel > 0 ? sbtop : disptop) + n;
1351 if (disptop < sbtop)
1353 if (disptop > scrtop)
1356 #ifdef OPTIMISE_SCROLL
1357 shift = (disptop - olddisptop) / (cols + 1);
1358 if (shift < rows && shift > -rows)
1359 scroll_display(0, rows - 1, shift);
1360 #endif /* OPTIMISE_SCROLL */
1365 * Spread the selection outwards according to the selection mode.
1367 static unsigned long *sel_spread_half (unsigned long *p, int dir) {
1368 unsigned long *linestart, *lineend;
1372 x = (p - text) % (cols+1);
1374 lineend = linestart + cols;
1379 * In this mode, every character is a separate unit, except
1380 * for runs of spaces at the end of a non-wrapping line.
1382 if (!(linestart[cols] & ATTR_WRAPPED)) {
1383 unsigned long *q = lineend;
1384 while (q > linestart && (q[-1] & CHAR_MASK) == 0x20)
1389 p = (dir == -1 ? q : lineend - 1);
1394 * In this mode, the units are maximal runs of characters
1395 * whose `wordness' has the same value.
1397 wvalue = wordness[*p & CHAR_MASK];
1399 while (p < lineend && wordness[p[1] & CHAR_MASK] == wvalue)
1402 while (p > linestart && wordness[p[-1] & CHAR_MASK] == wvalue)
1408 * In this mode, every line is a unit.
1410 p = (dir == -1 ? linestart : lineend - 1);
1416 static void sel_spread (void) {
1417 selstart = sel_spread_half (selstart, -1);
1418 selend = sel_spread_half (selend - 1, +1) + 1;
1421 void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) {
1422 unsigned long *selpoint;
1425 if (y>=rows) y = rows-1;
1433 if (x>=cols) x = cols-1;
1435 selpoint = disptop + y * (cols+1) + x;
1437 if (b == MB_SELECT && a == MA_CLICK) {
1439 selstate = ABOUT_TO;
1440 selanchor = selpoint;
1442 } else if (b == MB_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
1444 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
1445 selstate = DRAGGING;
1446 selstart = selanchor = selpoint;
1447 selend = selstart + 1;
1449 } else if ((b == MB_SELECT && a == MA_DRAG) ||
1450 (b == MB_EXTEND && a != MA_RELEASE)) {
1451 if (selstate == ABOUT_TO && selanchor == selpoint)
1453 if (b == MB_EXTEND && a != MA_DRAG && selstate == SELECTED) {
1454 if (selpoint-selstart < (selend-selstart)/2)
1455 selanchor = selend - 1;
1457 selanchor = selstart;
1458 selstate = DRAGGING;
1460 if (selstate != ABOUT_TO && selstate != DRAGGING)
1461 selanchor = selpoint;
1462 selstate = DRAGGING;
1463 if (selpoint < selanchor) {
1464 selstart = selpoint;
1465 selend = selanchor + 1;
1467 selstart = selanchor;
1468 selend = selpoint + 1;
1471 } else if ((b == MB_SELECT || b == MB_EXTEND) && a == MA_RELEASE) {
1472 if (selstate == DRAGGING) {
1474 * We've completed a selection. We now transfer the
1475 * data to the clipboard.
1477 unsigned char *p = selspace;
1478 unsigned long *q = selstart;
1480 while (q < selend) {
1482 unsigned long *lineend = q - (q-text) % (cols+1) + cols;
1483 unsigned long *nlpos = lineend;
1485 if (!(*nlpos & ATTR_WRAPPED)) {
1486 while ((nlpos[-1] & CHAR_MASK) == 0x20 && nlpos > q)
1491 while (q < nlpos && q < selend)
1492 *p++ = (unsigned char) (*q++ & CHAR_MASK);
1495 for (i=0; i<sizeof(sel_nl); i++)
1498 q = lineend + 1; /* start of next line */
1500 write_clip (selspace, p - selspace);
1501 selstate = SELECTED;
1503 selstate = NO_SELECTION;
1504 } else if (b == MB_PASTE && (a==MA_CLICK || a==MA_2CLK || a==MA_3CLK)) {
1508 get_clip((void **) &data, &len);
1512 while (p < data+len) {
1513 while (p < data+len &&
1514 !(p <= data+len-sizeof(sel_nl) &&
1515 !memcmp(p, sel_nl, sizeof(sel_nl))))
1517 back->send (q, p-q);
1518 if (p <= data+len-sizeof(sel_nl) &&
1519 !memcmp(p, sel_nl, sizeof(sel_nl))) {
1520 back->send ("\n", 1);
1521 p += sizeof(sel_nl);
1526 get_clip(NULL, NULL);
1532 static void deselect (void) {
1533 selstate = NO_SELECTION;
1534 selstart = selend = scrtop;
1537 void term_deselect (void) {