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 */
563 * Process an OSC sequence: set window title or icon name.
565 static void do_osc(void) {
568 wordness[(unsigned char)osc_string[osc_strlen]] = esc_args[0];
570 osc_string[osc_strlen] = '\0';
571 switch (esc_args[0]) {
574 set_icon (osc_string);
575 if (esc_args[0] == 1)
577 /* fall through: parameter 0 means set both */
580 set_title (osc_string);
587 NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL,
588 BS, HT, LF, VT, FF, CR, SO, SI,
589 DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB,
590 CAN, EM, SUB, ESC, IS1, IS2, IS3, IS4
594 BPH = 0x82, NBH, IND, NEL, SSA, ESA,
595 HTS, HTJ, VTS, PLD, PLU, RI, SS1, SS2,
596 DCS, PU1, PU2, STS, CCH, MW, SPA, EPA,
597 SOS, SCI = 0x9a, CSI, ST, OSC, PM, APC
601 * Remove everything currently in `inbuf' and stick it up on the
602 * in-memory display. There's a big state machine in here to
603 * process escape sequences...
605 void term_out(void) {
607 int must_update = FALSE;
608 int reprocess = FALSE;
610 while (reprocess || (c = inbuf_getc()) != -1) {
613 static FILE *fp = NULL;
614 if (!fp) fp = fopen("putty.log", "wb");
615 if (fp) fputc (c, fp);
623 case '\005': /* terminal type query */
624 back->send ("\033[?1;2c", 7);
632 if (curs_x == 0 && curs_y > 0)
633 curs_x = cols-1, curs_y--;
653 if (curs_y == marg_b)
654 scroll (marg_t, marg_b, 1, TRUE);
655 else if (curs_y < rows-1)
667 } while (curs_x < cols-1 && !tabs[curs_x]);
671 unsigned long *old_cpos = cpos;
673 check_selection (old_cpos, cpos);
685 termstate = SEEN_ESC;
687 case NEL: /* exactly equivalent to CR-LF */
690 if (curs_y == marg_b)
691 scroll (marg_t, marg_b, 1, TRUE);
692 else if (curs_y < rows-1)
699 case HTS: /* set a tab */
702 case RI: /* reverse index - backwards LF */
703 if (curs_y == marg_t)
704 scroll (marg_t, marg_b, -1, TRUE);
712 case SCI: /* terminal type query */
713 /* This sequence is standardised as something else entirely. */
714 back->send ("\033[?6c", 5);
717 termstate = SEEN_CSI;
719 esc_args[0] = ARG_DEFAULT;
723 termstate = SEEN_OSC;
727 if (c >= ' ' && c < 0x7f || c >= 0xa0 ) {
729 cpos[1] = ATTR_WRAPPED;
730 if (curs_y == marg_b)
731 scroll (marg_t, marg_b, 1, TRUE);
732 else if (curs_y < rows-1)
741 check_selection (cpos, cpos+1);
742 *cpos++ = c | curr_attr |
743 (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
745 if (curs_x == cols) {
756 termstate = TOPLEVEL;
760 * This state is virtually identical to SEEN_ESC, with the
761 * exception that we have an OSC sequence in the pipeline,
762 * and _if_ we see a backslash, we process it.
766 termstate = TOPLEVEL;
769 /* else fall through */
772 * According to ECMA-35, an escape sequence consists of
773 * ESC, a sequence (possibly empty) of intermediate bytes
774 * from column 02 (SPACE--/), and a final byte from
775 * columns 03-07 (0--~).
777 termstate = TOPLEVEL;
778 if (c >= 0x40 && c < 0x60) {
779 /* Fe sequences -- C1 control as an escape sequence */
783 /* nF sequences -- with intermediate bytes */
784 case '#': /* Single control functions */
785 termstate = SEEN_ESCHASH;
787 case '(': /* GZD4: should set G0 */
788 termstate = SEEN_GZD4;
790 case ')': /* G1D4: should set G1 */
791 termstate = SEEN_G1D4;
793 /* Fp sequences -- private control functions */
794 case '7': /* save cursor */
797 case '8': /* restore cursor */
803 app_keypad_keys = TRUE;
806 app_keypad_keys = FALSE;
808 /* Fs sequences -- standardised control functions */
809 case 'c': /* RIS: restore power-on settings */
816 termstate = SEEN_ESC_CONFUSED;
821 case SEEN_ESC_CONFUSED:
823 * We're in an escape sequence, but we no longer know what
824 * it means and we just want it to go away
826 termstate = TOPLEVEL;
827 if (c < 0x20 || c >= 0x7f)
829 * ECMA-35 says this isn't allowed, so we can do what
834 /* Intermediate byte -- more to come */
835 termstate = SEEN_ESC_CONFUSED;
836 /* Otherwise, that was a final byte and we're free! */
840 * In theory, a control sequence consists of CSI, then a
841 * sequence (possibly empty) of parameter bytes (0--?)
842 * then a sequence (possibly empty) of intermediate bytes
843 * (SPACE--/), then a final byte (@--~). We're rather
844 * more relaxed, and don't differentiate between parameter
845 * and intermediate bytes.
847 termstate = TOPLEVEL; /* default */
849 case '0': case '1': case '2': case '3': case '4':
850 case '5': case '6': case '7': case '8': case '9':
851 if (esc_nargs <= ARGS_MAX) {
852 if (esc_args[esc_nargs-1] == ARG_DEFAULT)
853 esc_args[esc_nargs-1] = 0;
854 esc_args[esc_nargs-1] =
855 10 * esc_args[esc_nargs-1] + c - '0';
857 termstate = SEEN_CSI;
860 if (++esc_nargs <= ARGS_MAX)
861 esc_args[esc_nargs-1] = ARG_DEFAULT;
862 termstate = SEEN_CSI;
866 termstate = SEEN_CSI;
868 case 'A': /* move up N lines */
869 move (curs_x, curs_y - def(esc_args[0], 1), 1);
873 case 'B': case 'e': /* move down N lines */
874 move (curs_x, curs_y + def(esc_args[0], 1), 1);
878 case 'C': case 'a': /* move right N cols */
879 move (curs_x + def(esc_args[0], 1), curs_y, 1);
883 case 'D': /* move left N cols */
884 move (curs_x - def(esc_args[0], 1), curs_y, 1);
888 case 'E': /* move down N lines and CR */
889 move (0, curs_y + def(esc_args[0], 1), 1);
893 case 'F': /* move up N lines and CR */
894 move (0, curs_y - def(esc_args[0], 1), 1);
898 case 'G': case '`': /* set horizontal posn */
899 move (def(esc_args[0], 1) - 1, curs_y, 0);
903 case 'd': /* set vertical posn */
904 move (curs_x, (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
909 case 'H': case 'f': /* set horz and vert posns at once */
911 esc_args[1] = ARG_DEFAULT;
912 move (def(esc_args[1], 1) - 1,
913 (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
918 case 'J': /* erase screen or parts of it */
920 unsigned int i = def(esc_args[0], 0) + 1;
923 erase_lots(FALSE, !!(i & 2), !!(i & 1));
928 case 'K': /* erase line or parts of it */
930 unsigned int i = def(esc_args[0], 0) + 1;
933 erase_lots(TRUE, !!(i & 2), !!(i & 1));
938 case 'L': /* insert lines */
939 if (curs_y <= marg_b)
940 scroll (curs_y, marg_b, -def(esc_args[0], 1), FALSE);
944 case 'M': /* delete lines */
945 if (curs_y <= marg_b)
946 scroll (curs_y, marg_b, def(esc_args[0], 1), FALSE);
950 case '@': /* insert chars */
951 insch (def(esc_args[0], 1));
955 case 'P': /* delete chars */
956 insch (-def(esc_args[0], 1));
960 case 'c': /* terminal type query */
961 back->send ("\033[?6c", 5);
963 case 'n': /* cursor position query */
964 if (esc_args[0] == 6) {
966 sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1);
967 back->send (buf, strlen(buf));
970 case 'h': /* toggle a mode to high */
971 toggle_mode (esc_args[0], esc_query, TRUE);
973 case 'l': /* toggle a mode to low */
974 toggle_mode (esc_args[0], esc_query, FALSE);
976 case 'g': /* clear tabs */
977 if (esc_nargs == 1) {
978 if (esc_args[0] == 0) {
979 tabs[curs_x] = FALSE;
980 } else if (esc_args[0] == 3) {
982 for (i = 0; i < cols; i++)
987 case 'r': /* set scroll margins */
988 if (esc_nargs <= 2) {
990 top = def(esc_args[0], 1) - 1;
993 bot = (esc_nargs == 1 ? rows :
994 def(esc_args[1], rows)) - 1;
1002 * I used to think the cursor should be
1003 * placed at the top of the newly marginned
1004 * area. Apparently not: VMS TPU falls over
1014 case 'm': /* set graphics rendition */
1017 for (i=0; i<esc_nargs; i++) {
1018 switch (def(esc_args[i], 0)) {
1019 case 0: /* restore defaults */
1020 curr_attr = ATTR_DEFAULT; break;
1021 case 1: /* enable bold */
1022 curr_attr |= ATTR_BOLD; break;
1023 case 4: /* enable underline */
1024 case 21: /* (enable double underline) */
1025 curr_attr |= ATTR_UNDER; break;
1026 case 7: /* enable reverse video */
1027 curr_attr |= ATTR_REVERSE; break;
1028 case 22: /* disable bold */
1029 curr_attr &= ~ATTR_BOLD; break;
1030 case 24: /* disable underline */
1031 curr_attr &= ~ATTR_UNDER; break;
1032 case 27: /* disable reverse video */
1033 curr_attr &= ~ATTR_REVERSE; break;
1034 case 30: case 31: case 32: case 33:
1035 case 34: case 35: case 36: case 37:
1037 curr_attr &= ~ATTR_FGMASK;
1038 curr_attr |= (esc_args[i] - 30) << ATTR_FGSHIFT;
1040 case 39: /* default-foreground */
1041 curr_attr &= ~ATTR_FGMASK;
1042 curr_attr |= ATTR_DEFFG;
1044 case 40: case 41: case 42: case 43:
1045 case 44: case 45: case 46: case 47:
1047 curr_attr &= ~ATTR_BGMASK;
1048 curr_attr |= (esc_args[i] - 40) << ATTR_BGSHIFT;
1050 case 49: /* default-background */
1051 curr_attr &= ~ATTR_BGMASK;
1052 curr_attr |= ATTR_DEFBG;
1058 case 's': /* save cursor */
1061 case 'u': /* restore cursor */
1062 save_cursor (FALSE);
1066 case 't': /* set page size - ie window height */
1067 request_resize (cols, def(esc_args[0], 24));
1070 case 'X': /* write N spaces w/o moving cursor */
1072 int n = def(esc_args[0], 1);
1073 unsigned long *p = cpos;
1074 if (n > cols - curs_x)
1076 check_selection (cpos, cpos+n);
1083 case 'x': /* report terminal characteristics */
1086 int i = def(esc_args[0], 0);
1087 if (i == 0 || i == 1) {
1088 strcpy (buf, "\033[2;1;1;112;112;1;0x");
1090 back->send (buf, 20);
1095 termstate = SEEN_CSI_CONFUSED;
1100 case SEEN_CSI_CONFUSED:
1101 termstate = TOPLEVEL;
1102 if (c < 0x20 || c >= 0x7f)
1105 termstate = SEEN_CSI_CONFUSED;
1110 cset_attr[termstate == SEEN_GZD4 ? 0 : 1] = ATTR_GBCHR;
1113 cset_attr[termstate == SEEN_GZD4 ? 0 : 1] = ATTR_LINEDRW;
1115 default: /* specifically, 'B' */
1116 cset_attr[termstate == SEEN_GZD4 ? 0 : 1] = ATTR_ASCII;
1119 termstate = TOPLEVEL;
1124 case '\005': case '\007': case '\b': case '\016': case '\017':
1125 case '\033': case 0233: case 0234: case 0235: case '\015':
1126 case '\013': case '\014': case '\012': case '\t':
1127 termstate = TOPLEVEL;
1128 goto do_toplevel; /* hack... */
1129 case 'P': /* Linux palette sequence */
1130 termstate = SEEN_OSC_P;
1133 case 'R': /* Linux palette reset */
1136 termstate = TOPLEVEL;
1138 case 'W': /* word-set */
1139 termstate = SEEN_OSC_W;
1142 case '0': case '1': case '2': case '3': case '4':
1143 case '5': case '6': case '7': case '8': case '9':
1144 esc_args[0] = 10 * esc_args[0] + c - '0';
1148 * Grotty hack to support xterm and DECterm title
1149 * sequences concurrently.
1151 if (esc_args[0] == 2) {
1155 /* else fall through */
1157 termstate = OSC_STRING;
1162 if (c == 0234 || c == '\007') {
1164 * These characters terminate the string; ST and BEL
1165 * terminate the sequence and trigger instant
1166 * processing of it, whereas ESC goes back to SEEN_ESC
1167 * mode unless it is followed by \, in which case it is
1168 * synonymous with ST in the first place.
1171 termstate = TOPLEVEL;
1172 } else if (c == '\033')
1173 termstate = OSC_MAYBE_ST;
1174 else if (osc_strlen < OSC_STR_MAX)
1175 osc_string[osc_strlen++] = c;
1179 int max = (osc_strlen == 0 ? 21 : 16);
1181 if (c >= '0' && c <= '9')
1183 else if (c >= 'A' && c <= 'A'+max-10)
1185 else if (c >= 'a' && c <= 'a'+max-10)
1188 termstate = TOPLEVEL;
1189 osc_string[osc_strlen++] = val;
1190 if (osc_strlen >= 7) {
1191 palette_set (osc_string[0],
1192 osc_string[1] * 16 + osc_string[2],
1193 osc_string[3] * 16 + osc_string[4],
1194 osc_string[5] * 16 + osc_string[6]);
1196 termstate = TOPLEVEL;
1202 case '\005': case '\007': case '\b': case '\016': case '\017':
1203 case '\033': case 0233: case 0234: case 0235: case '\015':
1204 case '\013': case '\014': case '\012': case '\t':
1205 termstate = TOPLEVEL;
1206 goto do_toplevel; /* hack... */
1207 case '0': case '1': case '2': case '3': case '4':
1208 case '5': case '6': case '7': case '8': case '9':
1209 esc_args[0] = 10 * esc_args[0] + c - '0';
1212 termstate = OSC_STRING;
1218 unsigned long *p = scrtop;
1219 int n = rows * (cols+1);
1221 *p++ = ATTR_DEFAULT | 'E';
1224 check_selection (scrtop, scrtop + rows * (cols+1));
1226 termstate = TOPLEVEL;
1229 check_selection (cpos, cpos+1);
1232 if (must_update || nl_count > MAXNL)
1237 * Compare two lines to determine whether they are sufficiently
1238 * alike to scroll-optimise one to the other. Return the degree of
1241 static int linecmp (unsigned long *a, unsigned long *b) {
1244 for (i=n=0; i < cols; i++)
1245 n += (*a++ == *b++);
1250 * Given a context, update the window. Out of paranoia, we don't
1251 * allow WM_PAINT responses to do scrolling optimisations.
1253 static void do_paint (Context ctx, int may_optimise){
1254 int i, j, start, our_curs_y;
1255 unsigned long attr, rv, cursor;
1258 cursor = (has_focus ? ATTR_ACTCURS : ATTR_PASCURS);
1259 rv = (rvideo ? ATTR_REVERSE : 0);
1260 our_curs_y = curs_y + (scrtop - disptop) / (cols+1);
1262 for (i=0; i<rows; i++) {
1263 int idx = i*(cols+1);
1264 for (j=0; j<=cols; j++,idx++) {
1265 unsigned long *d = disptop+idx;
1266 wanttext[idx] = ((*d ^ rv
1267 ^ (selstart <= d && d < selend ?
1268 ATTR_REVERSE : 0)) |
1269 (i==our_curs_y && j==curs_x ? cursor : 0));
1274 * We would perform scrolling optimisations in here, if they
1275 * didn't have a nasty tendency to cause the whole sodding
1276 * program to hang for a second at speed-critical moments.
1277 * We'll leave it well alone...
1280 for (i=0; i<rows; i++) {
1281 int idx = i*(cols+1);
1283 for (j=0; j<=cols; j++,idx++) {
1284 unsigned long t = wanttext[idx];
1285 int needs_update = (j < cols && t != disptext[idx]);
1286 int keep_going = (start != -1 && needs_update &&
1287 (t & ATTR_MASK) == attr &&
1288 j-start < sizeof(ch));
1289 if (start != -1 && !keep_going) {
1290 do_text (ctx, start, i, ch, j-start, attr);
1296 attr = t & ATTR_MASK;
1298 ch[j-start] = (char) (t & CHAR_MASK);
1306 * Invalidate the whole screen so it will be repainted in full.
1308 void term_invalidate(void) {
1311 for (i=0; i<rows*(cols+1); i++)
1312 disptext[i] = ATTR_INVALID;
1316 * Paint the window in response to a WM_PAINT message.
1318 void term_paint (Context ctx, int l, int t, int r, int b) {
1319 int i, j, left, top, right, bottom;
1321 left = l / font_width;
1322 right = (r - 1) / font_width;
1323 top = t / font_height;
1324 bottom = (b - 1) / font_height;
1325 for (i = top; i <= bottom && i < rows ; i++)
1326 for (j = left; j <= right && j < cols ; j++)
1327 disptext[i*(cols+1)+j] = ATTR_INVALID;
1329 do_paint (ctx, FALSE);
1333 * Attempt to scroll the scrollback. The second parameter gives the
1334 * position we want to scroll to; the first is +1 to denote that
1335 * this position is relative to the beginning of the scrollback, -1
1336 * to denote it is relative to the end, and 0 to denote that it is
1337 * relative to the current position.
1339 void term_scroll (int rel, int where) {
1340 int n = where * (cols+1);
1341 #ifdef OPTIMISE_SCROLL
1342 unsigned long *olddisptop = disptop;
1344 #endif /* OPTIMISE_SCROLL */
1346 disptop = (rel < 0 ? scrtop :
1347 rel > 0 ? sbtop : disptop) + n;
1348 if (disptop < sbtop)
1350 if (disptop > scrtop)
1353 #ifdef OPTIMISE_SCROLL
1354 shift = (disptop - olddisptop) / (cols + 1);
1355 if (shift < rows && shift > -rows)
1356 scroll_display(0, rows - 1, shift);
1357 #endif /* OPTIMISE_SCROLL */
1362 * Spread the selection outwards according to the selection mode.
1364 static unsigned long *sel_spread_half (unsigned long *p, int dir) {
1365 unsigned long *linestart, *lineend;
1369 x = (p - text) % (cols+1);
1371 lineend = linestart + cols;
1376 * In this mode, every character is a separate unit, except
1377 * for runs of spaces at the end of a non-wrapping line.
1379 if (!(linestart[cols] & ATTR_WRAPPED)) {
1380 unsigned long *q = lineend;
1381 while (q > linestart && (q[-1] & CHAR_MASK) == 0x20)
1386 p = (dir == -1 ? q : lineend - 1);
1391 * In this mode, the units are maximal runs of characters
1392 * whose `wordness' has the same value.
1394 wvalue = wordness[*p & CHAR_MASK];
1396 while (p < lineend && wordness[p[1] & CHAR_MASK] == wvalue)
1399 while (p > linestart && wordness[p[-1] & CHAR_MASK] == wvalue)
1405 * In this mode, every line is a unit.
1407 p = (dir == -1 ? linestart : lineend - 1);
1413 static void sel_spread (void) {
1414 selstart = sel_spread_half (selstart, -1);
1415 selend = sel_spread_half (selend - 1, +1) + 1;
1418 void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) {
1419 unsigned long *selpoint;
1422 if (y>=rows) y = rows-1;
1430 if (x>=cols) x = cols-1;
1432 selpoint = disptop + y * (cols+1) + x;
1434 if (b == MB_SELECT && a == MA_CLICK) {
1436 selstate = ABOUT_TO;
1437 selanchor = selpoint;
1439 } else if (b == MB_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
1441 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
1442 selstate = DRAGGING;
1443 selstart = selanchor = selpoint;
1444 selend = selstart + 1;
1446 } else if ((b == MB_SELECT && a == MA_DRAG) ||
1447 (b == MB_EXTEND && a != MA_RELEASE)) {
1448 if (selstate == ABOUT_TO && selanchor == selpoint)
1450 if (b == MB_EXTEND && a != MA_DRAG && selstate == SELECTED) {
1451 if (selpoint-selstart < (selend-selstart)/2)
1452 selanchor = selend - 1;
1454 selanchor = selstart;
1455 selstate = DRAGGING;
1457 if (selstate != ABOUT_TO && selstate != DRAGGING)
1458 selanchor = selpoint;
1459 selstate = DRAGGING;
1460 if (selpoint < selanchor) {
1461 selstart = selpoint;
1462 selend = selanchor + 1;
1464 selstart = selanchor;
1465 selend = selpoint + 1;
1468 } else if ((b == MB_SELECT || b == MB_EXTEND) && a == MA_RELEASE) {
1469 if (selstate == DRAGGING) {
1471 * We've completed a selection. We now transfer the
1472 * data to the clipboard.
1474 unsigned char *p = selspace;
1475 unsigned long *q = selstart;
1477 while (q < selend) {
1479 unsigned long *lineend = q - (q-text) % (cols+1) + cols;
1480 unsigned long *nlpos = lineend;
1482 if (!(*nlpos & ATTR_WRAPPED)) {
1483 while ((nlpos[-1] & CHAR_MASK) == 0x20 && nlpos > q)
1488 while (q < nlpos && q < selend)
1489 *p++ = (unsigned char) (*q++ & CHAR_MASK);
1492 for (i=0; i<sizeof(sel_nl); i++)
1495 q = lineend + 1; /* start of next line */
1497 write_clip (selspace, p - selspace);
1498 selstate = SELECTED;
1500 selstate = NO_SELECTION;
1501 } else if (b == MB_PASTE && (a==MA_CLICK || a==MA_2CLK || a==MA_3CLK)) {
1505 get_clip((void **) &data, &len);
1509 while (p < data+len) {
1510 while (p < data+len &&
1511 !(p <= data+len-sizeof(sel_nl) &&
1512 !memcmp(p, sel_nl, sizeof(sel_nl))))
1514 back->send (q, p-q);
1515 if (p <= data+len-sizeof(sel_nl) &&
1516 !memcmp(p, sel_nl, sizeof(sel_nl))) {
1517 back->send ("\n", 1);
1518 p += sizeof(sel_nl);
1523 get_clip(NULL, NULL);
1529 static void deselect (void) {
1530 selstate = NO_SELECTION;
1531 selstart = selend = scrtop;
1534 void term_deselect (void) {