From RDB: a few extra escape sequences seen in SCO terminals.
[sgt/putty] / terminal.c
1 #include <windows.h>
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <ctype.h>
6
7 #include <time.h>
8 #include <assert.h>
9 #include "putty.h"
10 #include "tree234.h"
11
12 #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
13 #define CL_VT100 0x0002 /* VT100 */
14 #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
15 #define CL_VT102 0x0008 /* VT102 */
16 #define CL_VT220 0x0010 /* VT220 */
17 #define CL_VT320 0x0020 /* VT320 */
18 #define CL_VT420 0x0040 /* VT420 */
19 #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
20 #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
21 #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
22 #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
23 #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
24
25 #define TM_VT100 (CL_ANSIMIN|CL_VT100)
26 #define TM_VT100AVO (TM_VT100|CL_VT100AVO)
27 #define TM_VT102 (TM_VT100AVO|CL_VT102)
28 #define TM_VT220 (TM_VT102|CL_VT220)
29 #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
30 #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
31
32 #define TM_PUTTY (0xFFFF)
33
34 #define compatibility(x) \
35 if ( ((CL_##x)&compatibility_level) == 0 ) { \
36 termstate=TOPLEVEL; \
37 break; \
38 }
39 #define compatibility2(x,y) \
40 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
41 termstate=TOPLEVEL; \
42 break; \
43 }
44
45 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
46
47 static int compatibility_level = TM_PUTTY;
48
49 static tree234 *scrollback; /* lines scrolled off top of screen */
50 static tree234 *screen; /* lines on primary screen */
51 static tree234 *alt_screen; /* lines on alternate screen */
52 static int disptop; /* distance scrolled back (0 or -ve) */
53
54 static unsigned long *cpos; /* cursor position (convenience) */
55
56 static unsigned long *disptext; /* buffer of text on real screen */
57 static unsigned long *wanttext; /* buffer of text we want on screen */
58
59 #define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
60
61 struct beeptime {
62 struct beeptime *next;
63 long ticks;
64 };
65 static struct beeptime *beephead, *beeptail;
66 int nbeeps;
67 int beep_overloaded;
68 long lastbeep;
69
70 static unsigned char *selspace; /* buffer for building selections in */
71
72 #define TSIZE (sizeof(unsigned long))
73 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
74
75 static unsigned long curr_attr, save_attr;
76 static unsigned long erase_char = ERASE_CHAR;
77
78 typedef struct {
79 int y, x;
80 } pos;
81 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
82 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
83 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
84 #define posdiff(p1,p2) ( ((p2).y - (p1).y) * (cols+1) + (p2).x - (p1).x )
85 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
86 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
87
88 static pos curs; /* cursor */
89 static pos savecurs; /* saved cursor position */
90 static int marg_t, marg_b; /* scroll margins */
91 static int dec_om; /* DEC origin mode flag */
92 static int wrap, wrapnext; /* wrap flags */
93 static int insert; /* insert-mode flag */
94 static int cset; /* 0 or 1: which char set */
95 static int save_cset, save_csattr; /* saved with cursor position */
96 static int rvideo; /* global reverse video flag */
97 static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
98 static int cursor_on; /* cursor enabled flag */
99 static int reset_132; /* Flag ESC c resets to 80 cols */
100 static int use_bce; /* Use Background coloured erase */
101 static int blinker; /* When blinking is the cursor on ? */
102 static int tblinker; /* When the blinking text is on */
103 static int blink_is_real; /* Actually blink blinking text */
104 static int term_echoing; /* Does terminal want local echo? */
105 static int term_editing; /* Does terminal want local edit? */
106
107 static int xterm_mouse; /* send mouse messages to app */
108
109 static unsigned long cset_attr[2];
110
111 /*
112 * Saved settings on the alternate screen.
113 */
114 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
115 static int alt_t, alt_b;
116 static int alt_which;
117
118 #define ARGS_MAX 32 /* max # of esc sequence arguments */
119 #define ARG_DEFAULT 0 /* if an arg isn't specified */
120 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
121 static int esc_args[ARGS_MAX];
122 static int esc_nargs;
123 static int esc_query;
124 #define ANSI(x,y) ((x)+((y)<<8))
125 #define ANSI_QUE(x) ANSI(x,TRUE)
126
127 #define OSC_STR_MAX 2048
128 static int osc_strlen;
129 static char osc_string[OSC_STR_MAX + 1];
130 static int osc_w;
131
132 static char id_string[1024] = "\033[?6c";
133
134 static unsigned char *tabs;
135
136 static enum {
137 TOPLEVEL,
138 SEEN_ESC,
139 SEEN_CSI,
140 SEEN_OSC,
141 SEEN_OSC_W,
142
143 DO_CTRLS,
144
145 IGNORE_NEXT,
146 SET_GL, SET_GR,
147 SEEN_OSC_P,
148 OSC_STRING, OSC_MAYBE_ST,
149 SEEN_ESCHASH,
150 VT52_ESC,
151 VT52_Y1,
152 VT52_Y2
153 } termstate;
154
155 static enum {
156 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
157 } selstate;
158 static enum {
159 SM_CHAR, SM_WORD, SM_LINE
160 } selmode;
161 static pos selstart, selend, selanchor;
162
163 static short wordness[256] = {
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
165 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
166 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
167 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
168 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
169 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
170 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
171 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
172 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
173 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
174 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
175 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
176 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
177 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
178 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
179 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
180 };
181
182 static unsigned char sel_nl[] = SEL_NL;
183 static char *paste_buffer = 0;
184 static int paste_len, paste_pos, paste_hold;
185
186 /*
187 * Internal prototypes.
188 */
189 static void do_paint(Context, int);
190 static void erase_lots(int, int, int);
191 static void swap_screen(int);
192 static void update_sbar(void);
193 static void deselect(void);
194 /* log session to file stuff ... */
195 static FILE *lgfp = NULL;
196 static void logtraffic(unsigned char c, int logmode);
197
198 /*
199 * Retrieve a line of the screen or of the scrollback, according to
200 * whether the y coordinate is non-negative or negative
201 * (respectively).
202 */
203 unsigned long *lineptr(int y, int lineno)
204 {
205 unsigned long *line, lineattrs;
206 tree234 *whichtree;
207 int i, treeindex, oldlen;
208
209 if (y >= 0) {
210 whichtree = screen;
211 treeindex = y;
212 } else {
213 whichtree = scrollback;
214 treeindex = y + count234(scrollback);
215 }
216 line = index234(whichtree, treeindex);
217
218 /* We assume that we don't screw up and retrieve something out of range. */
219 assert(line != NULL);
220
221 if (line[0] != cols) {
222 /*
223 * This line is the wrong length, which probably means it
224 * hasn't been accessed since a resize. Resize it now.
225 */
226 oldlen = line[0];
227 lineattrs = line[oldlen + 1];
228 delpos234(whichtree, treeindex);
229 line = srealloc(line, TSIZE * (2 + cols));
230 line[0] = cols;
231 for (i = oldlen; i < cols; i++)
232 line[i + 1] = ERASE_CHAR;
233 line[cols + 1] = lineattrs & LATTR_MODE;
234 addpos234(whichtree, line, treeindex);
235 }
236
237 return line + 1;
238 }
239
240 #define lineptr(x) lineptr(x,__LINE__)
241 /*
242 * Set up power-on settings for the terminal.
243 */
244 static void power_on(void)
245 {
246 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
247 alt_t = marg_t = 0;
248 if (rows != -1)
249 alt_b = marg_b = rows - 1;
250 else
251 alt_b = marg_b = 0;
252 if (cols != -1) {
253 int i;
254 for (i = 0; i < cols; i++)
255 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
256 }
257 alt_om = dec_om = cfg.dec_om;
258 alt_wnext = wrapnext = alt_ins = insert = FALSE;
259 alt_wrap = wrap = cfg.wrap_mode;
260 alt_cset = cset = 0;
261 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
262 rvideo = 0;
263 in_vbell = FALSE;
264 cursor_on = 1;
265 save_attr = curr_attr = ATTR_DEFAULT;
266 term_editing = term_echoing = FALSE;
267 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
268 app_cursor_keys = cfg.app_cursor;
269 app_keypad_keys = cfg.app_keypad;
270 use_bce = cfg.bce;
271 blink_is_real = cfg.blinktext;
272 erase_char = ERASE_CHAR;
273 alt_which = 0;
274 {
275 int i;
276 for (i = 0; i < 256; i++)
277 wordness[i] = cfg.wordness[i];
278 }
279 if (screen) {
280 swap_screen(1);
281 erase_lots(FALSE, TRUE, TRUE);
282 swap_screen(0);
283 erase_lots(FALSE, TRUE, TRUE);
284 }
285 }
286
287 /*
288 * Force a screen update.
289 */
290 void term_update(void)
291 {
292 Context ctx;
293 ctx = get_ctx();
294 if (ctx) {
295 if ((seen_key_event && (cfg.scroll_on_key)) ||
296 (seen_disp_event && (cfg.scroll_on_disp))) {
297 disptop = 0; /* return to main screen */
298 seen_disp_event = seen_key_event = 0;
299 update_sbar();
300 }
301 do_paint(ctx, TRUE);
302 sys_cursor(curs.x, curs.y - disptop);
303 free_ctx(ctx);
304 }
305 }
306
307 /*
308 * Same as power_on(), but an external function.
309 */
310 void term_pwron(void)
311 {
312 power_on();
313 fix_cpos;
314 disptop = 0;
315 deselect();
316 term_update();
317 }
318
319 /*
320 * Clear the scrollback.
321 */
322 void term_clrsb(void)
323 {
324 unsigned long *line;
325 disptop = 0;
326 while ((line = delpos234(scrollback, 0)) != NULL) {
327 sfree(line);
328 }
329 update_sbar();
330 }
331
332 /*
333 * Initialise the terminal.
334 */
335 void term_init(void)
336 {
337 screen = alt_screen = scrollback = NULL;
338 disptop = 0;
339 disptext = wanttext = NULL;
340 tabs = NULL;
341 selspace = NULL;
342 deselect();
343 rows = cols = -1;
344 power_on();
345 beephead = beeptail = NULL;
346 nbeeps = 0;
347 lastbeep = FALSE;
348 beep_overloaded = FALSE;
349 }
350
351 /*
352 * Set up the terminal for a given size.
353 */
354 void term_size(int newrows, int newcols, int newsavelines)
355 {
356 tree234 *newsb, *newscreen, *newalt;
357 unsigned long *newdisp, *newwant, *oldline, *line;
358 int i, j, ccols;
359 int sblen;
360 int save_alt_which = alt_which;
361
362 if (newrows == rows && newcols == cols && newsavelines == savelines)
363 return; /* nothing to do */
364
365 deselect();
366 swap_screen(0);
367
368 alt_t = marg_t = 0;
369 alt_b = marg_b = newrows - 1;
370
371 if (rows == -1) {
372 scrollback = newtree234(NULL);
373 screen = newtree234(NULL);
374 rows = 0;
375 }
376
377 /*
378 * Resize the screen and scrollback. We only need to shift
379 * lines around within our data structures, because lineptr()
380 * will take care of resizing each individual line if
381 * necessary. So:
382 *
383 * - If the new screen and the old screen differ in length, we
384 * must shunt some lines in from the scrollback or out to
385 * the scrollback.
386 *
387 * - If doing that fails to provide us with enough material to
388 * fill the new screen (i.e. the number of rows needed in
389 * the new screen exceeds the total number in the previous
390 * screen+scrollback), we must invent some blank lines to
391 * cover the gap.
392 *
393 * - Then, if the new scrollback length is less than the
394 * amount of scrollback we actually have, we must throw some
395 * away.
396 */
397 sblen = count234(scrollback);
398 /* Do this loop to expand the screen if newrows > rows */
399 for (i = rows; i < newrows; i++) {
400 if (sblen > 0) {
401 line = delpos234(scrollback, --sblen);
402 } else {
403 line = smalloc(TSIZE * (newcols + 2));
404 line[0] = newcols;
405 for (j = 0; j <= newcols; j++)
406 line[j + 1] = ERASE_CHAR;
407 }
408 addpos234(screen, line, 0);
409 }
410 /* Do this loop to shrink the screen if newrows < rows */
411 for (i = newrows; i < rows; i++) {
412 line = delpos234(screen, 0);
413 addpos234(scrollback, line, sblen++);
414 }
415 assert(count234(screen) == newrows);
416 while (sblen > newsavelines) {
417 line = delpos234(scrollback, 0);
418 sfree(line);
419 sblen--;
420 }
421 assert(count234(scrollback) <= newsavelines);
422 disptop = 0;
423
424 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
425 for (i = 0; i < newrows * (newcols + 1); i++)
426 newdisp[i] = ATTR_INVALID;
427 sfree(disptext);
428 disptext = newdisp;
429
430 newwant = smalloc(newrows * (newcols + 1) * TSIZE);
431 for (i = 0; i < newrows * (newcols + 1); i++)
432 newwant[i] = ATTR_INVALID;
433 sfree(wanttext);
434 wanttext = newwant;
435
436 newalt = newtree234(NULL);
437 for (i = 0; i < newrows; i++) {
438 line = smalloc(TSIZE * (newcols + 2));
439 line[0] = newcols;
440 for (j = 0; j <= newcols; j++)
441 line[j + 1] = erase_char;
442 addpos234(newalt, line, i);
443 }
444 if (alt_screen) {
445 while (NULL != (line = delpos234(alt_screen, 0)))
446 sfree(line);
447 freetree234(alt_screen);
448 }
449 alt_screen = newalt;
450
451 sfree(selspace);
452 selspace =
453 smalloc((newrows + newsavelines) * (newcols + sizeof(sel_nl)));
454
455 tabs = srealloc(tabs, newcols * sizeof(*tabs));
456 {
457 int i;
458 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
459 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
460 }
461
462 if (rows > 0)
463 curs.y += newrows - rows;
464 if (curs.y < 0)
465 curs.y = 0;
466 if (curs.y >= newrows)
467 curs.y = newrows - 1;
468 if (curs.x >= newcols)
469 curs.x = newcols - 1;
470 alt_x = alt_y = 0;
471 wrapnext = alt_wnext = FALSE;
472
473 rows = newrows;
474 cols = newcols;
475 savelines = newsavelines;
476 fix_cpos;
477
478 swap_screen(save_alt_which);
479
480 update_sbar();
481 term_update();
482 }
483
484 /*
485 * Swap screens.
486 */
487 static void swap_screen(int which)
488 {
489 int t;
490 tree234 *ttr;
491
492 if (which == alt_which)
493 return;
494
495 alt_which = which;
496
497 ttr = alt_screen;
498 alt_screen = screen;
499 screen = ttr;
500 t = curs.x;
501 curs.x = alt_x;
502 alt_x = t;
503 t = curs.y;
504 curs.y = alt_y;
505 alt_y = t;
506 t = marg_t;
507 marg_t = alt_t;
508 alt_t = t;
509 t = marg_b;
510 marg_b = alt_b;
511 alt_b = t;
512 t = dec_om;
513 dec_om = alt_om;
514 alt_om = t;
515 t = wrap;
516 wrap = alt_wrap;
517 alt_wrap = t;
518 t = wrapnext;
519 wrapnext = alt_wnext;
520 alt_wnext = t;
521 t = insert;
522 insert = alt_ins;
523 alt_ins = t;
524 t = cset;
525 cset = alt_cset;
526 alt_cset = t;
527
528 fix_cpos;
529 }
530
531 /*
532 * Update the scroll bar.
533 */
534 static void update_sbar(void)
535 {
536 int nscroll;
537
538 nscroll = count234(scrollback);
539
540 set_sbar(nscroll + rows, nscroll + disptop, rows);
541 }
542
543 /*
544 * Check whether the region bounded by the two pointers intersects
545 * the scroll region, and de-select the on-screen selection if so.
546 */
547 static void check_selection(pos from, pos to)
548 {
549 if (poslt(from, selend) && poslt(selstart, to))
550 deselect();
551 }
552
553 /*
554 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
555 * for backward.) `sb' is TRUE if the scrolling is permitted to
556 * affect the scrollback buffer.
557 *
558 * NB this function invalidates all pointers into lines of the
559 * screen data structures. In particular, you MUST call fix_cpos
560 * after calling scroll() and before doing anything else that
561 * uses the cpos shortcut pointer.
562 */
563 static void scroll(int topline, int botline, int lines, int sb)
564 {
565 unsigned long *line, *line2;
566 int i;
567
568 if (topline != 0 || alt_which != 0)
569 sb = FALSE;
570
571 if (lines < 0) {
572 while (lines < 0) {
573 line = delpos234(screen, botline);
574 for (i = 0; i < cols; i++)
575 line[i + 1] = erase_char;
576 line[cols + 1] = 0;
577 addpos234(screen, line, topline);
578
579 if (selstart.y >= topline && selstart.y <= botline) {
580 selstart.y++;
581 if (selstart.y > botline) {
582 selstart.y = botline;
583 selstart.x = 0;
584 }
585 }
586 if (selend.y >= topline && selend.y <= botline) {
587 selend.y++;
588 if (selend.y > botline) {
589 selend.y = botline;
590 selend.x = 0;
591 }
592 }
593
594 lines++;
595 }
596 } else {
597 while (lines > 0) {
598 line = delpos234(screen, topline);
599 if (sb && savelines > 0) {
600 int sblen = count234(scrollback);
601 /*
602 * We must add this line to the scrollback. We'll
603 * remove a line from the top of the scrollback to
604 * replace it, or allocate a new one if the
605 * scrollback isn't full.
606 */
607 if (sblen == savelines) {
608 sblen--, line2 = delpos234(scrollback, 0);
609 } else {
610 line2 = smalloc(TSIZE * (cols + 2));
611 line2[0] = cols;
612 }
613 addpos234(scrollback, line, sblen);
614 line = line2;
615 }
616 for (i = 0; i < cols; i++)
617 line[i + 1] = erase_char;
618 line[cols + 1] = 0;
619 addpos234(screen, line, botline);
620
621 if (selstart.y >= topline && selstart.y <= botline) {
622 selstart.y--;
623 if (selstart.y < topline) {
624 selstart.y = topline;
625 selstart.x = 0;
626 }
627 }
628 if (selend.y >= topline && selend.y <= botline) {
629 selend.y--;
630 if (selend.y < topline) {
631 selend.y = topline;
632 selend.x = 0;
633 }
634 }
635
636 lines--;
637 }
638 }
639 }
640
641 /*
642 * Move the cursor to a given position, clipping at boundaries. We
643 * may or may not want to clip at the scroll margin: marg_clip is 0
644 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
645 * even _being_ outside the margins.
646 */
647 static void move(int x, int y, int marg_clip)
648 {
649 if (x < 0)
650 x = 0;
651 if (x >= cols)
652 x = cols - 1;
653 if (marg_clip) {
654 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
655 y = marg_t;
656 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
657 y = marg_b;
658 }
659 if (y < 0)
660 y = 0;
661 if (y >= rows)
662 y = rows - 1;
663 curs.x = x;
664 curs.y = y;
665 fix_cpos;
666 wrapnext = FALSE;
667 }
668
669 /*
670 * Save or restore the cursor and SGR mode.
671 */
672 static void save_cursor(int save)
673 {
674 if (save) {
675 savecurs = curs;
676 save_attr = curr_attr;
677 save_cset = cset;
678 save_csattr = cset_attr[cset];
679 } else {
680 curs = savecurs;
681 /* Make sure the window hasn't shrunk since the save */
682 if (curs.x >= cols)
683 curs.x = cols - 1;
684 if (curs.y >= rows)
685 curs.y = rows - 1;
686
687 curr_attr = save_attr;
688 cset = save_cset;
689 cset_attr[cset] = save_csattr;
690 fix_cpos;
691 if (use_bce)
692 erase_char = (' ' | (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
693 }
694 }
695
696 /*
697 * Erase a large portion of the screen: the whole screen, or the
698 * whole line, or parts thereof.
699 */
700 static void erase_lots(int line_only, int from_begin, int to_end)
701 {
702 pos start, end;
703 int erase_lattr;
704 unsigned long *ldata;
705
706 if (line_only) {
707 start.y = curs.y;
708 start.x = 0;
709 end.y = curs.y + 1;
710 end.x = 0;
711 erase_lattr = FALSE;
712 } else {
713 start.y = 0;
714 start.x = 0;
715 end.y = rows;
716 end.x = 0;
717 erase_lattr = TRUE;
718 }
719 if (!from_begin) {
720 start = curs;
721 }
722 if (!to_end) {
723 end = curs;
724 }
725 check_selection(start, end);
726
727 /* Clear screen also forces a full window redraw, just in case. */
728 if (start.y == 0 && start.x == 0 && end.y == rows)
729 term_invalidate();
730
731 ldata = lineptr(start.y);
732 while (poslt(start, end)) {
733 if (start.y == cols && !erase_lattr)
734 ldata[start.x] &= ~ATTR_WRAPPED;
735 else
736 ldata[start.x] = erase_char;
737 if (incpos(start) && start.y < rows)
738 ldata = lineptr(start.y);
739 }
740 }
741
742 /*
743 * Insert or delete characters within the current line. n is +ve if
744 * insertion is desired, and -ve for deletion.
745 */
746 static void insch(int n)
747 {
748 int dir = (n < 0 ? -1 : +1);
749 int m;
750 pos cursplus;
751 unsigned long *ldata;
752
753 n = (n < 0 ? -n : n);
754 if (n > cols - curs.x)
755 n = cols - curs.x;
756 m = cols - curs.x - n;
757 cursplus.y = curs.y;
758 cursplus.x = curs.x + n;
759 check_selection(curs, cursplus);
760 ldata = lineptr(curs.y);
761 if (dir < 0) {
762 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
763 while (n--)
764 ldata[curs.x + m++] = erase_char;
765 } else {
766 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
767 while (n--)
768 ldata[curs.x + n] = erase_char;
769 }
770 }
771
772 /*
773 * Toggle terminal mode `mode' to state `state'. (`query' indicates
774 * whether the mode is a DEC private one or a normal one.)
775 */
776 static void toggle_mode(int mode, int query, int state)
777 {
778 long ticks;
779
780 if (query)
781 switch (mode) {
782 case 1: /* application cursor keys */
783 app_cursor_keys = state;
784 break;
785 case 2: /* VT52 mode */
786 vt52_mode = !state;
787 break;
788 case 3: /* 80/132 columns */
789 deselect();
790 request_resize(state ? 132 : 80, rows, 1);
791 reset_132 = state;
792 break;
793 case 5: /* reverse video */
794 /*
795 * Toggle reverse video. If we receive an OFF within the
796 * visual bell timeout period after an ON, we trigger an
797 * effective visual bell, so that ESC[?5hESC[?5l will
798 * always be an actually _visible_ visual bell.
799 */
800 ticks = GetTickCount();
801 if (rvideo && !state && /* we're turning it off */
802 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
803 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
804 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
805 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
806 } else if (!rvideo && state) {
807 /* This is an ON, so we notice the time and save it. */
808 rvbell_timeout = ticks + VBELL_TIMEOUT;
809 }
810 rvideo = state;
811 seen_disp_event = TRUE;
812 if (state)
813 term_update();
814 break;
815 case 6: /* DEC origin mode */
816 dec_om = state;
817 break;
818 case 7: /* auto wrap */
819 wrap = state;
820 break;
821 case 8: /* auto key repeat */
822 repeat_off = !state;
823 break;
824 case 10: /* set local edit mode */
825 term_editing = state;
826 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
827 break;
828 case 25: /* enable/disable cursor */
829 compatibility2(OTHER, VT220);
830 cursor_on = state;
831 seen_disp_event = TRUE;
832 break;
833 case 47: /* alternate screen */
834 compatibility(OTHER);
835 deselect();
836 swap_screen(state);
837 disptop = 0;
838 break;
839 case 1000: /* xterm mouse 1 */
840 xterm_mouse = state ? 1 : 0;
841 set_raw_mouse_mode(state);
842 break;
843 case 1002: /* xterm mouse 2 */
844 xterm_mouse = state ? 2 : 0;
845 set_raw_mouse_mode(state);
846 break;
847 } else
848 switch (mode) {
849 case 4: /* set insert mode */
850 compatibility(VT102);
851 insert = state;
852 break;
853 case 12: /* set echo mode */
854 term_echoing = !state;
855 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
856 break;
857 case 20: /* Return sends ... */
858 cr_lf_return = state;
859 break;
860 }
861 }
862
863 /*
864 * Process an OSC sequence: set window title or icon name.
865 */
866 static void do_osc(void)
867 {
868 if (osc_w) {
869 while (osc_strlen--)
870 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
871 } else {
872 osc_string[osc_strlen] = '\0';
873 switch (esc_args[0]) {
874 case 0:
875 case 1:
876 set_icon(osc_string);
877 if (esc_args[0] == 1)
878 break;
879 /* fall through: parameter 0 means set both */
880 case 2:
881 case 21:
882 set_title(osc_string);
883 break;
884 }
885 }
886 }
887
888 /*
889 * Remove everything currently in `inbuf' and stick it up on the
890 * in-memory display. There's a big state machine in here to
891 * process escape sequences...
892 */
893 void term_out(void)
894 {
895 int c, inbuf_reap;
896
897 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
898 c = inbuf[inbuf_reap];
899
900 /*
901 * Optionally log the session traffic to a file. Useful for
902 * debugging and possibly also useful for actual logging.
903 */
904 logtraffic((unsigned char) c, LGTYP_DEBUG);
905
906 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
907 * be able to display 8-bit characters, but I'll let that go 'cause
908 * of i18n.
909 */
910 if (((c & 0x60) == 0 || c == '\177') &&
911 termstate < DO_CTRLS && ((c & 0x80) == 0 || has_compat(VT220))) {
912 switch (c) {
913 case '\005': /* terminal type query */
914 /* Strictly speaking this is VT100 but a VT100 defaults to
915 * no response. Other terminals respond at their option.
916 *
917 * Don't put a CR in the default string as this tends to
918 * upset some weird software.
919 *
920 * An xterm returns "xterm" (5 characters)
921 */
922 compatibility(ANSIMIN);
923 {
924 char abuf[256], *s, *d;
925 int state = 0;
926 for (s = cfg.answerback, d = abuf; *s; s++) {
927 if (state) {
928 if (*s >= 'a' && *s <= 'z')
929 *d++ = (*s - ('a' - 1));
930 else if ((*s >= '@' && *s <= '_') ||
931 *s == '?' || (*s & 0x80))
932 *d++ = ('@' ^ *s);
933 else if (*s == '~')
934 *d++ = '^';
935 state = 0;
936 } else if (*s == '^') {
937 state = 1;
938 } else
939 *d++ = xlat_kbd2tty((unsigned char) *s);
940 }
941 ldisc_send(abuf, d - abuf);
942 }
943 break;
944 case '\007':
945 {
946 struct beeptime *newbeep;
947 long ticks;
948
949 ticks = GetTickCount();
950
951 if (!beep_overloaded) {
952 newbeep = smalloc(sizeof(struct beeptime));
953 newbeep->ticks = ticks;
954 newbeep->next = NULL;
955 if (!beephead)
956 beephead = newbeep;
957 else
958 beeptail->next = newbeep;
959 beeptail = newbeep;
960 nbeeps++;
961 }
962
963 /*
964 * Throw out any beeps that happened more than
965 * t seconds ago.
966 */
967 while (beephead &&
968 beephead->ticks < ticks - cfg.bellovl_t) {
969 struct beeptime *tmp = beephead;
970 beephead = tmp->next;
971 sfree(tmp);
972 if (!beephead)
973 beeptail = NULL;
974 nbeeps--;
975 }
976
977 if (cfg.bellovl && beep_overloaded &&
978 ticks - lastbeep >= cfg.bellovl_s) {
979 /*
980 * If we're currently overloaded and the
981 * last beep was more than s seconds ago,
982 * leave overload mode.
983 */
984 beep_overloaded = FALSE;
985 } else if (cfg.bellovl && !beep_overloaded &&
986 nbeeps >= cfg.bellovl_n) {
987 /*
988 * Now, if we have n or more beeps
989 * remaining in the queue, go into overload
990 * mode.
991 */
992 beep_overloaded = TRUE;
993 }
994 lastbeep = ticks;
995
996 /*
997 * Perform an actual beep if we're not overloaded.
998 */
999 if ((!cfg.bellovl || !beep_overloaded)
1000 && cfg.beep != 0) {
1001 if (cfg.beep != 2)
1002 beep(cfg.beep);
1003 else if (cfg.beep == 2) {
1004 in_vbell = TRUE;
1005 vbell_timeout = ticks + VBELL_TIMEOUT;
1006 term_update();
1007 }
1008 }
1009 disptop = 0;
1010 }
1011 break;
1012 case '\b':
1013 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1014 else if (curs.x == 0 && curs.y > 0)
1015 curs.x = cols - 1, curs.y--;
1016 else if (wrapnext)
1017 wrapnext = FALSE;
1018 else
1019 curs.x--;
1020 fix_cpos;
1021 seen_disp_event = TRUE;
1022 break;
1023 case '\016':
1024 compatibility(VT100);
1025 cset = 1;
1026 break;
1027 case '\017':
1028 compatibility(VT100);
1029 cset = 0;
1030 break;
1031 case '\033':
1032 if (vt52_mode)
1033 termstate = VT52_ESC;
1034 else {
1035 compatibility(ANSIMIN);
1036 termstate = SEEN_ESC;
1037 }
1038 break;
1039 case 0233:
1040 compatibility(VT220);
1041 termstate = SEEN_CSI;
1042 esc_nargs = 1;
1043 esc_args[0] = ARG_DEFAULT;
1044 esc_query = FALSE;
1045 break;
1046 case 0235:
1047 compatibility(VT220);
1048 termstate = SEEN_OSC;
1049 esc_args[0] = 0;
1050 break;
1051 case '\r':
1052 curs.x = 0;
1053 wrapnext = FALSE;
1054 fix_cpos;
1055 seen_disp_event = TRUE;
1056 paste_hold = 0;
1057 logtraffic((unsigned char) c, LGTYP_ASCII);
1058 break;
1059 case '\014':
1060 if (has_compat(SCOANSI)) {
1061 move(0, 0, 0);
1062 erase_lots(FALSE, FALSE, TRUE);
1063 disptop = 0;
1064 wrapnext = FALSE;
1065 seen_disp_event = 1;
1066 break;
1067 }
1068 case '\013':
1069 compatibility(VT100);
1070 case '\n':
1071 if (curs.y == marg_b)
1072 scroll(marg_t, marg_b, 1, TRUE);
1073 else if (curs.y < rows - 1)
1074 curs.y++;
1075 if (cfg.lfhascr)
1076 curs.x = 0;
1077 fix_cpos;
1078 wrapnext = FALSE;
1079 seen_disp_event = 1;
1080 paste_hold = 0;
1081 logtraffic((unsigned char) c, LGTYP_ASCII);
1082 break;
1083 case '\t':
1084 {
1085 pos old_curs = curs;
1086 unsigned long *ldata = lineptr(curs.y);
1087
1088 do {
1089 curs.x++;
1090 } while (curs.x < cols - 1 && !tabs[curs.x]);
1091
1092 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1093 if (curs.x >= cols / 2)
1094 curs.x = cols / 2 - 1;
1095 } else {
1096 if (curs.x >= cols)
1097 curs.x = cols - 1;
1098 }
1099
1100 fix_cpos;
1101 check_selection(old_curs, curs);
1102 }
1103 seen_disp_event = TRUE;
1104 break;
1105 case '\177': /* Destructive backspace
1106 This does nothing on a real VT100 */
1107 compatibility(OTHER);
1108 if (curs.x && !wrapnext)
1109 curs.x--;
1110 wrapnext = FALSE;
1111 fix_cpos;
1112 *cpos = (' ' | curr_attr | ATTR_ASCII);
1113 break;
1114 }
1115 } else
1116 switch (termstate) {
1117 case TOPLEVEL:
1118 /* Only graphic characters get this far, ctrls are stripped above */
1119 if (wrapnext && wrap) {
1120 cpos[1] |= ATTR_WRAPPED;
1121 if (curs.y == marg_b)
1122 scroll(marg_t, marg_b, 1, TRUE);
1123 else if (curs.y < rows - 1)
1124 curs.y++;
1125 curs.x = 0;
1126 fix_cpos;
1127 wrapnext = FALSE;
1128 }
1129 if (insert)
1130 insch(1);
1131 if (selstate != NO_SELECTION) {
1132 pos cursplus = curs;
1133 incpos(cursplus);
1134 check_selection(curs, cursplus);
1135 }
1136 switch (cset_attr[cset]) {
1137 /*
1138 * Linedraw characters are different from 'ESC ( B'
1139 * only for a small range. For ones outside that
1140 * range, make sure we use the same font as well as
1141 * the same encoding.
1142 */
1143 case ATTR_LINEDRW:
1144 if (c < 0x5f || c > 0x7F)
1145 *cpos++ =
1146 xlat_tty2scr((unsigned char) c) | curr_attr |
1147 ATTR_ASCII;
1148 else if (c == 0x5F)
1149 *cpos++ = ' ' | curr_attr | ATTR_ASCII;
1150 else
1151 *cpos++ =
1152 ((unsigned char) c) | curr_attr | ATTR_LINEDRW;
1153 break;
1154 case ATTR_GBCHR:
1155 /* If UK-ASCII, make the '#' a LineDraw Pound */
1156 if (c == '#') {
1157 *cpos++ = '}' | curr_attr | ATTR_LINEDRW;
1158 break;
1159 }
1160 /*FALLTHROUGH*/ default:
1161 *cpos = xlat_tty2scr((unsigned char) c) | curr_attr |
1162 (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
1163 logtraffic((unsigned char) c, LGTYP_ASCII);
1164 cpos++;
1165 break;
1166 }
1167 curs.x++;
1168 if (curs.x == cols) {
1169 cpos--;
1170 curs.x--;
1171 wrapnext = TRUE;
1172 }
1173 seen_disp_event = 1;
1174 break;
1175
1176 case IGNORE_NEXT:
1177 termstate = TOPLEVEL;
1178 break;
1179 case OSC_MAYBE_ST:
1180 /*
1181 * This state is virtually identical to SEEN_ESC, with the
1182 * exception that we have an OSC sequence in the pipeline,
1183 * and _if_ we see a backslash, we process it.
1184 */
1185 if (c == '\\') {
1186 do_osc();
1187 termstate = TOPLEVEL;
1188 break;
1189 }
1190 /* else fall through */
1191 case SEEN_ESC:
1192 termstate = TOPLEVEL;
1193 switch (c) {
1194 case ' ': /* some weird sequence? */
1195 compatibility(VT220);
1196 termstate = IGNORE_NEXT;
1197 break;
1198 case '[': /* enter CSI mode */
1199 termstate = SEEN_CSI;
1200 esc_nargs = 1;
1201 esc_args[0] = ARG_DEFAULT;
1202 esc_query = FALSE;
1203 break;
1204 case ']': /* xterm escape sequences */
1205 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1206 compatibility(OTHER);
1207 termstate = SEEN_OSC;
1208 esc_args[0] = 0;
1209 break;
1210 case '(': /* should set GL */
1211 compatibility(VT100);
1212 termstate = SET_GL;
1213 break;
1214 case ')': /* should set GR */
1215 compatibility(VT100);
1216 termstate = SET_GR;
1217 break;
1218 case '7': /* save cursor */
1219 compatibility(VT100);
1220 save_cursor(TRUE);
1221 break;
1222 case '8': /* restore cursor */
1223 compatibility(VT100);
1224 save_cursor(FALSE);
1225 seen_disp_event = TRUE;
1226 break;
1227 case '=':
1228 compatibility(VT100);
1229 app_keypad_keys = TRUE;
1230 break;
1231 case '>':
1232 compatibility(VT100);
1233 app_keypad_keys = FALSE;
1234 break;
1235 case 'D': /* exactly equivalent to LF */
1236 compatibility(VT100);
1237 if (curs.y == marg_b)
1238 scroll(marg_t, marg_b, 1, TRUE);
1239 else if (curs.y < rows - 1)
1240 curs.y++;
1241 fix_cpos;
1242 wrapnext = FALSE;
1243 seen_disp_event = TRUE;
1244 break;
1245 case 'E': /* exactly equivalent to CR-LF */
1246 compatibility(VT100);
1247 curs.x = 0;
1248 if (curs.y == marg_b)
1249 scroll(marg_t, marg_b, 1, TRUE);
1250 else if (curs.y < rows - 1)
1251 curs.y++;
1252 fix_cpos;
1253 wrapnext = FALSE;
1254 seen_disp_event = TRUE;
1255 break;
1256 case 'M': /* reverse index - backwards LF */
1257 compatibility(VT100);
1258 if (curs.y == marg_t)
1259 scroll(marg_t, marg_b, -1, TRUE);
1260 else if (curs.y > 0)
1261 curs.y--;
1262 fix_cpos;
1263 wrapnext = FALSE;
1264 seen_disp_event = TRUE;
1265 break;
1266 case 'Z': /* terminal type query */
1267 compatibility(VT100);
1268 ldisc_send(id_string, strlen(id_string));
1269 break;
1270 case 'c': /* restore power-on settings */
1271 compatibility(VT100);
1272 power_on();
1273 if (reset_132) {
1274 request_resize(80, rows, 1);
1275 reset_132 = 0;
1276 }
1277 fix_cpos;
1278 disptop = 0;
1279 seen_disp_event = TRUE;
1280 break;
1281 case '#': /* ESC # 8 fills screen with Es :-) */
1282 compatibility(VT100);
1283 termstate = SEEN_ESCHASH;
1284 break;
1285 case 'H': /* set a tab */
1286 compatibility(VT100);
1287 tabs[curs.x] = TRUE;
1288 break;
1289 }
1290 break;
1291 case SEEN_CSI:
1292 termstate = TOPLEVEL; /* default */
1293 if (isdigit(c)) {
1294 if (esc_nargs <= ARGS_MAX) {
1295 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1296 esc_args[esc_nargs - 1] = 0;
1297 esc_args[esc_nargs - 1] =
1298 10 * esc_args[esc_nargs - 1] + c - '0';
1299 }
1300 termstate = SEEN_CSI;
1301 } else if (c == ';') {
1302 if (++esc_nargs <= ARGS_MAX)
1303 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1304 termstate = SEEN_CSI;
1305 } else if (c < '@') {
1306 if (esc_query)
1307 esc_query = -1;
1308 else if (c == '?')
1309 esc_query = TRUE;
1310 else
1311 esc_query = c;
1312 termstate = SEEN_CSI;
1313 } else
1314 switch (ANSI(c, esc_query)) {
1315 case 'A': /* move up N lines */
1316 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1317 seen_disp_event = TRUE;
1318 break;
1319 case 'e': /* move down N lines */
1320 compatibility(ANSI);
1321 case 'B':
1322 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1323 seen_disp_event = TRUE;
1324 break;
1325 case 'a': /* move right N cols */
1326 compatibility(ANSI);
1327 case ANSI('c', '>'): /* report xterm version */
1328 compatibility(OTHER);
1329 /* this reports xterm version 136 so that VIM can
1330 use the drag messages from the mouse reporting */
1331 ldisc_send("\033[>0;136;0c", 11);
1332 break;
1333 case 'C':
1334 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1335 seen_disp_event = TRUE;
1336 break;
1337 case 'D': /* move left N cols */
1338 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1339 seen_disp_event = TRUE;
1340 break;
1341 case 'E': /* move down N lines and CR */
1342 compatibility(ANSI);
1343 move(0, curs.y + def(esc_args[0], 1), 1);
1344 seen_disp_event = TRUE;
1345 break;
1346 case 'F': /* move up N lines and CR */
1347 compatibility(ANSI);
1348 move(0, curs.y - def(esc_args[0], 1), 1);
1349 seen_disp_event = TRUE;
1350 break;
1351 case 'G':
1352 case '`': /* set horizontal posn */
1353 compatibility(ANSI);
1354 move(def(esc_args[0], 1) - 1, curs.y, 0);
1355 seen_disp_event = TRUE;
1356 break;
1357 case 'd': /* set vertical posn */
1358 compatibility(ANSI);
1359 move(curs.x,
1360 (dec_om ? marg_t : 0) + def(esc_args[0],
1361 1) - 1,
1362 (dec_om ? 2 : 0));
1363 seen_disp_event = TRUE;
1364 break;
1365 case 'H':
1366 case 'f': /* set horz and vert posns at once */
1367 if (esc_nargs < 2)
1368 esc_args[1] = ARG_DEFAULT;
1369 move(def(esc_args[1], 1) - 1,
1370 (dec_om ? marg_t : 0) + def(esc_args[0],
1371 1) - 1,
1372 (dec_om ? 2 : 0));
1373 seen_disp_event = TRUE;
1374 break;
1375 case 'J': /* erase screen or parts of it */
1376 {
1377 unsigned int i = def(esc_args[0], 0) + 1;
1378 if (i > 3)
1379 i = 0;
1380 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1381 }
1382 disptop = 0;
1383 seen_disp_event = TRUE;
1384 break;
1385 case 'K': /* erase line or parts of it */
1386 {
1387 unsigned int i = def(esc_args[0], 0) + 1;
1388 if (i > 3)
1389 i = 0;
1390 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1391 }
1392 seen_disp_event = TRUE;
1393 break;
1394 case 'L': /* insert lines */
1395 compatibility(VT102);
1396 if (curs.y <= marg_b)
1397 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1398 FALSE);
1399 fix_cpos;
1400 seen_disp_event = TRUE;
1401 break;
1402 case 'M': /* delete lines */
1403 compatibility(VT102);
1404 if (curs.y <= marg_b)
1405 scroll(curs.y, marg_b, def(esc_args[0], 1),
1406 TRUE);
1407 fix_cpos;
1408 seen_disp_event = TRUE;
1409 break;
1410 case '@': /* insert chars */
1411 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1412 compatibility(VT102);
1413 insch(def(esc_args[0], 1));
1414 seen_disp_event = TRUE;
1415 break;
1416 case 'P': /* delete chars */
1417 compatibility(VT102);
1418 insch(-def(esc_args[0], 1));
1419 seen_disp_event = TRUE;
1420 break;
1421 case 'c': /* terminal type query */
1422 compatibility(VT100);
1423 /* This is the response for a VT102 */
1424 ldisc_send(id_string, strlen(id_string));
1425 break;
1426 case 'n': /* cursor position query */
1427 if (esc_args[0] == 6) {
1428 char buf[32];
1429 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1430 curs.x + 1);
1431 ldisc_send(buf, strlen(buf));
1432 } else if (esc_args[0] == 5) {
1433 ldisc_send("\033[0n", 4);
1434 }
1435 break;
1436 case 'h': /* toggle modes to high */
1437 case ANSI_QUE('h'):
1438 compatibility(VT100);
1439 {
1440 int i;
1441 for (i = 0; i < esc_nargs; i++)
1442 toggle_mode(esc_args[i], esc_query, TRUE);
1443 }
1444 break;
1445 case 'l': /* toggle modes to low */
1446 case ANSI_QUE('l'):
1447 compatibility(VT100);
1448 {
1449 int i;
1450 for (i = 0; i < esc_nargs; i++)
1451 toggle_mode(esc_args[i], esc_query, FALSE);
1452 }
1453 break;
1454 case 'g': /* clear tabs */
1455 compatibility(VT100);
1456 if (esc_nargs == 1) {
1457 if (esc_args[0] == 0) {
1458 tabs[curs.x] = FALSE;
1459 } else if (esc_args[0] == 3) {
1460 int i;
1461 for (i = 0; i < cols; i++)
1462 tabs[i] = FALSE;
1463 }
1464 }
1465 break;
1466 case 'r': /* set scroll margins */
1467 compatibility(VT100);
1468 if (esc_nargs <= 2) {
1469 int top, bot;
1470 top = def(esc_args[0], 1) - 1;
1471 bot = (esc_nargs <= 1
1472 || esc_args[1] ==
1473 0 ? rows : def(esc_args[1], rows)) - 1;
1474 if (bot >= rows)
1475 bot = rows - 1;
1476 /* VTTEST Bug 9 - if region is less than 2 lines
1477 * don't change region.
1478 */
1479 if (bot - top > 0) {
1480 marg_t = top;
1481 marg_b = bot;
1482 curs.x = 0;
1483 /*
1484 * I used to think the cursor should be
1485 * placed at the top of the newly marginned
1486 * area. Apparently not: VMS TPU falls over
1487 * if so.
1488 *
1489 * Well actually it should for Origin mode - RDB
1490 */
1491 curs.y = (dec_om ? marg_t : 0);
1492 fix_cpos;
1493 seen_disp_event = TRUE;
1494 }
1495 }
1496 break;
1497 case 'm': /* set graphics rendition */
1498 {
1499 /*
1500 * A VT100 without the AVO only had one attribute, either
1501 * underline or reverse video depending on the cursor type,
1502 * this was selected by CSI 7m.
1503 *
1504 * case 2:
1505 * This is DIM on the VT100-AVO and VT102
1506 * case 5:
1507 * This is BLINK on the VT100-AVO and VT102+
1508 * case 8:
1509 * This is INVIS on the VT100-AVO and VT102
1510 * case 21:
1511 * This like 22 disables BOLD, DIM and INVIS
1512 *
1513 * The ANSI colours appear on any terminal that has colour
1514 * (obviously) but the interaction between sgr0 and the
1515 * colours varies but is usually related to the background
1516 * colour erase item.
1517 * The interaction between colour attributes and the mono
1518 * ones is also very implementation dependent.
1519 *
1520 * The 39 and 49 attributes are likely to be unimplemented.
1521 */
1522 int i;
1523 for (i = 0; i < esc_nargs; i++) {
1524 switch (def(esc_args[i], 0)) {
1525 case 0: /* restore defaults */
1526 curr_attr = ATTR_DEFAULT;
1527 break;
1528 case 1: /* enable bold */
1529 compatibility(VT100AVO);
1530 curr_attr |= ATTR_BOLD;
1531 break;
1532 case 21: /* (enable double underline) */
1533 compatibility(OTHER);
1534 case 4: /* enable underline */
1535 compatibility(VT100AVO);
1536 curr_attr |= ATTR_UNDER;
1537 break;
1538 case 5: /* enable blink */
1539 compatibility(VT100AVO);
1540 curr_attr |= ATTR_BLINK;
1541 break;
1542 case 7: /* enable reverse video */
1543 curr_attr |= ATTR_REVERSE;
1544 break;
1545 case 22: /* disable bold */
1546 compatibility2(OTHER, VT220);
1547 curr_attr &= ~ATTR_BOLD;
1548 break;
1549 case 24: /* disable underline */
1550 compatibility2(OTHER, VT220);
1551 curr_attr &= ~ATTR_UNDER;
1552 break;
1553 case 25: /* disable blink */
1554 compatibility2(OTHER, VT220);
1555 curr_attr &= ~ATTR_BLINK;
1556 break;
1557 case 27: /* disable reverse video */
1558 compatibility2(OTHER, VT220);
1559 curr_attr &= ~ATTR_REVERSE;
1560 break;
1561 case 30:
1562 case 31:
1563 case 32:
1564 case 33:
1565 case 34:
1566 case 35:
1567 case 36:
1568 case 37:
1569 /* foreground */
1570 curr_attr &= ~ATTR_FGMASK;
1571 curr_attr |=
1572 (esc_args[i] - 30) << ATTR_FGSHIFT;
1573 break;
1574 case 39: /* default-foreground */
1575 curr_attr &= ~ATTR_FGMASK;
1576 curr_attr |= ATTR_DEFFG;
1577 break;
1578 case 40:
1579 case 41:
1580 case 42:
1581 case 43:
1582 case 44:
1583 case 45:
1584 case 46:
1585 case 47:
1586 /* background */
1587 curr_attr &= ~ATTR_BGMASK;
1588 curr_attr |=
1589 (esc_args[i] - 40) << ATTR_BGSHIFT;
1590 break;
1591 case 49: /* default-background */
1592 curr_attr &= ~ATTR_BGMASK;
1593 curr_attr |= ATTR_DEFBG;
1594 break;
1595 }
1596 }
1597 if (use_bce)
1598 erase_char =
1599 (' ' |
1600 (curr_attr &
1601 (ATTR_FGMASK | ATTR_BGMASK |
1602 ATTR_BLINK)));
1603 }
1604 break;
1605 case 's': /* save cursor */
1606 save_cursor(TRUE);
1607 break;
1608 case 'u': /* restore cursor */
1609 save_cursor(FALSE);
1610 seen_disp_event = TRUE;
1611 break;
1612 case 't': /* set page size - ie window height */
1613 /*
1614 * VT340/VT420 sequence DECSLPP, DEC only allows values
1615 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1616 * illegal values (eg first arg 1..9) for window changing
1617 * and reports.
1618 */
1619 compatibility(VT340TEXT);
1620 if (esc_nargs <= 1
1621 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1622 request_resize(cols, def(esc_args[0], 24), 0);
1623 deselect();
1624 }
1625 break;
1626 case 'S':
1627 compatibility(SCOANSI);
1628 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1629 fix_cpos;
1630 wrapnext = FALSE;
1631 seen_disp_event = TRUE;
1632 break;
1633 case 'T':
1634 compatibility(SCOANSI);
1635 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1636 fix_cpos;
1637 wrapnext = FALSE;
1638 seen_disp_event = TRUE;
1639 break;
1640 case ANSI('|', '*'):
1641 /* VT420 sequence DECSNLS
1642 * Set number of lines on screen
1643 * VT420 uses VGA like hardware and can support any size in
1644 * reasonable range (24..49 AIUI) with no default specified.
1645 */
1646 compatibility(VT420);
1647 if (esc_nargs == 1 && esc_args[0] > 0) {
1648 request_resize(cols,
1649 def(esc_args[0], cfg.height),
1650 0);
1651 deselect();
1652 }
1653 break;
1654 case ANSI('|', '$'):
1655 /* VT340/VT420 sequence DECSCPP
1656 * Set number of columns per page
1657 * Docs imply range is only 80 or 132, but I'll allow any.
1658 */
1659 compatibility(VT340TEXT);
1660 if (esc_nargs <= 1) {
1661 request_resize(def(esc_args[0], cfg.width),
1662 rows, 0);
1663 deselect();
1664 }
1665 break;
1666 case 'X': /* write N spaces w/o moving cursor */
1667 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1668 compatibility(ANSIMIN);
1669 {
1670 int n = def(esc_args[0], 1);
1671 pos cursplus;
1672 unsigned long *p = cpos;
1673 if (n > cols - curs.x)
1674 n = cols - curs.x;
1675 cursplus = curs;
1676 cursplus.x += n;
1677 check_selection(curs, cursplus);
1678 while (n--)
1679 *p++ = erase_char;
1680 seen_disp_event = TRUE;
1681 }
1682 break;
1683 case 'x': /* report terminal characteristics */
1684 compatibility(VT100);
1685 {
1686 char buf[32];
1687 int i = def(esc_args[0], 0);
1688 if (i == 0 || i == 1) {
1689 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1690 buf[2] += i;
1691 ldisc_send(buf, 20);
1692 }
1693 }
1694 break;
1695 case ANSI('L', '='):
1696 compatibility(OTHER);
1697 use_bce = (esc_args[0] <= 0);
1698 erase_char = ERASE_CHAR;
1699 if (use_bce)
1700 erase_char =
1701 (' ' |
1702 (curr_attr &
1703 (ATTR_FGMASK | ATTR_BGMASK)));
1704 break;
1705 case ANSI('E', '='):
1706 compatibility(OTHER);
1707 blink_is_real = (esc_args[0] >= 1);
1708 break;
1709 case ANSI('p', '"'):
1710 /* Allow the host to make this emulator a 'perfect' VT102.
1711 * This first appeared in the VT220, but we do need to get
1712 * back to PuTTY mode so I won't check it.
1713 *
1714 * The arg in 40..42 are a PuTTY extension.
1715 * The 2nd arg, 8bit vs 7bit is not checked.
1716 *
1717 * Setting VT102 mode should also change the Fkeys to
1718 * generate PF* codes as a real VT102 has no Fkeys.
1719 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1720 * send nothing.
1721 *
1722 * Note ESC c will NOT change this!
1723 */
1724
1725 switch (esc_args[0]) {
1726 case 61:
1727 compatibility_level &= ~TM_VTXXX;
1728 compatibility_level |= TM_VT102;
1729 break;
1730 case 62:
1731 compatibility_level &= ~TM_VTXXX;
1732 compatibility_level |= TM_VT220;
1733 break;
1734
1735 default:
1736 if (esc_args[0] > 60 && esc_args[0] < 70)
1737 compatibility_level |= TM_VTXXX;
1738 break;
1739
1740 case 40:
1741 compatibility_level &= TM_VTXXX;
1742 break;
1743 case 41:
1744 compatibility_level = TM_PUTTY;
1745 break;
1746 case 42:
1747 compatibility_level = TM_SCOANSI;
1748 break;
1749
1750 case ARG_DEFAULT:
1751 compatibility_level = TM_PUTTY;
1752 break;
1753 case 50:
1754 break;
1755 }
1756
1757 /* Change the response to CSI c */
1758 if (esc_args[0] == 50) {
1759 int i;
1760 char lbuf[64];
1761 strcpy(id_string, "\033[?");
1762 for (i = 1; i < esc_nargs; i++) {
1763 if (i != 1)
1764 strcat(id_string, ";");
1765 sprintf(lbuf, "%d", esc_args[i]);
1766 strcat(id_string, lbuf);
1767 }
1768 strcat(id_string, "c");
1769 }
1770 #if 0
1771 /* Is this a good idea ?
1772 * Well we should do a soft reset at this point ...
1773 */
1774 if (!has_compat(VT420) && has_compat(VT100)) {
1775 if (reset_132)
1776 request_resize(132, 24, 1);
1777 else
1778 request_resize(80, 24, 1);
1779 }
1780 #endif
1781 break;
1782 }
1783 break;
1784 case SET_GL:
1785 case SET_GR:
1786 /* VT100 only here, checked above */
1787 switch (c) {
1788 case 'A':
1789 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
1790 break;
1791 case '0':
1792 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
1793 break;
1794 case 'B':
1795 default: /* specifically, 'B' */
1796 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
1797 break;
1798 }
1799 if (!has_compat(VT220) || c != '%')
1800 termstate = TOPLEVEL;
1801 break;
1802 case SEEN_OSC:
1803 osc_w = FALSE;
1804 switch (c) {
1805 case 'P': /* Linux palette sequence */
1806 termstate = SEEN_OSC_P;
1807 osc_strlen = 0;
1808 break;
1809 case 'R': /* Linux palette reset */
1810 palette_reset();
1811 term_invalidate();
1812 termstate = TOPLEVEL;
1813 break;
1814 case 'W': /* word-set */
1815 termstate = SEEN_OSC_W;
1816 osc_w = TRUE;
1817 break;
1818 case '0':
1819 case '1':
1820 case '2':
1821 case '3':
1822 case '4':
1823 case '5':
1824 case '6':
1825 case '7':
1826 case '8':
1827 case '9':
1828 esc_args[0] = 10 * esc_args[0] + c - '0';
1829 break;
1830 case 'L':
1831 /*
1832 * Grotty hack to support xterm and DECterm title
1833 * sequences concurrently.
1834 */
1835 if (esc_args[0] == 2) {
1836 esc_args[0] = 1;
1837 break;
1838 }
1839 /* else fall through */
1840 default:
1841 termstate = OSC_STRING;
1842 osc_strlen = 0;
1843 }
1844 break;
1845 case OSC_STRING:
1846 /*
1847 * This OSC stuff is EVIL. It takes just one character to get into
1848 * sysline mode and it's not initially obvious how to get out.
1849 * So I've added CR and LF as string aborts.
1850 * This shouldn't effect compatibility as I believe embedded
1851 * control characters are supposed to be interpreted (maybe?)
1852 * and they don't display anything useful anyway.
1853 *
1854 * -- RDB
1855 */
1856 if (c == '\n' || c == '\r') {
1857 termstate = TOPLEVEL;
1858 } else if (c == 0234 || c == '\007') {
1859 /*
1860 * These characters terminate the string; ST and BEL
1861 * terminate the sequence and trigger instant
1862 * processing of it, whereas ESC goes back to SEEN_ESC
1863 * mode unless it is followed by \, in which case it is
1864 * synonymous with ST in the first place.
1865 */
1866 do_osc();
1867 termstate = TOPLEVEL;
1868 } else if (c == '\033')
1869 termstate = OSC_MAYBE_ST;
1870 else if (osc_strlen < OSC_STR_MAX)
1871 osc_string[osc_strlen++] = c;
1872 break;
1873 case SEEN_OSC_P:
1874 {
1875 int max = (osc_strlen == 0 ? 21 : 16);
1876 int val;
1877 if (c >= '0' && c <= '9')
1878 val = c - '0';
1879 else if (c >= 'A' && c <= 'A' + max - 10)
1880 val = c - 'A' + 10;
1881 else if (c >= 'a' && c <= 'a' + max - 10)
1882 val = c - 'a' + 10;
1883 else
1884 termstate = TOPLEVEL;
1885 osc_string[osc_strlen++] = val;
1886 if (osc_strlen >= 7) {
1887 palette_set(osc_string[0],
1888 osc_string[1] * 16 + osc_string[2],
1889 osc_string[3] * 16 + osc_string[4],
1890 osc_string[5] * 16 + osc_string[6]);
1891 term_invalidate();
1892 termstate = TOPLEVEL;
1893 }
1894 }
1895 break;
1896 case SEEN_OSC_W:
1897 switch (c) {
1898 case '0':
1899 case '1':
1900 case '2':
1901 case '3':
1902 case '4':
1903 case '5':
1904 case '6':
1905 case '7':
1906 case '8':
1907 case '9':
1908 esc_args[0] = 10 * esc_args[0] + c - '0';
1909 break;
1910 default:
1911 termstate = OSC_STRING;
1912 osc_strlen = 0;
1913 }
1914 break;
1915 case SEEN_ESCHASH:
1916 {
1917 unsigned long nlattr;
1918 unsigned long *ldata;
1919 int i, j;
1920 pos scrtop, scrbot;
1921
1922 switch (c) {
1923 case '8':
1924 for (i = 0; i < rows; i++) {
1925 ldata = lineptr(i);
1926 for (j = 0; j < cols; j++)
1927 ldata[j] = ATTR_DEFAULT | 'E';
1928 ldata[cols] = 0;
1929 }
1930 disptop = 0;
1931 seen_disp_event = TRUE;
1932 scrtop.x = scrtop.y = 0;
1933 scrbot.x = 0;
1934 scrbot.y = rows;
1935 check_selection(scrtop, scrbot);
1936 break;
1937
1938 case '3':
1939 case '4':
1940 case '5':
1941 case '6':
1942 switch (c) {
1943 case '3':
1944 nlattr = LATTR_TOP;
1945 break;
1946 case '4':
1947 nlattr = LATTR_BOT;
1948 break;
1949 case '5':
1950 nlattr = LATTR_NORM;
1951 break;
1952 case '6':
1953 nlattr = LATTR_WIDE;
1954 break;
1955 }
1956
1957 ldata = lineptr(curs.y);
1958 ldata[cols] &= ~LATTR_MODE;
1959 ldata[cols] |= nlattr;
1960 }
1961 }
1962 termstate = TOPLEVEL;
1963 break;
1964 case VT52_ESC:
1965 termstate = TOPLEVEL;
1966 seen_disp_event = TRUE;
1967 switch (c) {
1968 case 'A':
1969 move(curs.x, curs.y - 1, 1);
1970 break;
1971 case 'B':
1972 move(curs.x, curs.y + 1, 1);
1973 break;
1974 case 'C':
1975 move(curs.x + 1, curs.y, 1);
1976 break;
1977 case 'D':
1978 move(curs.x - 1, curs.y, 1);
1979 break;
1980 case 'F':
1981 cset_attr[cset = 0] = ATTR_LINEDRW;
1982 break;
1983 case 'G':
1984 cset_attr[cset = 0] = ATTR_ASCII;
1985 break;
1986 case 'H':
1987 move(0, 0, 0);
1988 break;
1989 case 'I':
1990 if (curs.y == 0)
1991 scroll(0, rows - 1, -1, TRUE);
1992 else if (curs.y > 0)
1993 curs.y--;
1994 fix_cpos;
1995 wrapnext = FALSE;
1996 break;
1997 case 'J':
1998 erase_lots(FALSE, FALSE, TRUE);
1999 disptop = 0;
2000 break;
2001 case 'K':
2002 erase_lots(TRUE, FALSE, TRUE);
2003 break;
2004 case 'V':
2005 /* XXX Print cursor line */
2006 break;
2007 case 'W':
2008 /* XXX Start controller mode */
2009 break;
2010 case 'X':
2011 /* XXX Stop controller mode */
2012 break;
2013 case 'Y':
2014 termstate = VT52_Y1;
2015 break;
2016 case 'Z':
2017 ldisc_send("\033/Z", 3);
2018 break;
2019 case '=':
2020 app_keypad_keys = TRUE;
2021 break;
2022 case '>':
2023 app_keypad_keys = FALSE;
2024 break;
2025 case '<':
2026 /* XXX This should switch to VT100 mode not current or default
2027 * VT mode. But this will only have effect in a VT220+
2028 * emulation.
2029 */
2030 vt52_mode = FALSE;
2031 break;
2032 case '^':
2033 /* XXX Enter auto print mode */
2034 break;
2035 case '_':
2036 /* XXX Exit auto print mode */
2037 break;
2038 case ']':
2039 /* XXX Print screen */
2040 break;
2041 }
2042 break;
2043 case VT52_Y1:
2044 termstate = VT52_Y2;
2045 move(curs.x, c - ' ', 0);
2046 break;
2047 case VT52_Y2:
2048 termstate = TOPLEVEL;
2049 move(c - ' ', curs.y, 0);
2050 break;
2051 }
2052 if (selstate != NO_SELECTION) {
2053 pos cursplus = curs;
2054 incpos(cursplus);
2055 check_selection(curs, cursplus);
2056 }
2057 }
2058 inbuf_head = 0;
2059 }
2060
2061 /*
2062 * Compare two lines to determine whether they are sufficiently
2063 * alike to scroll-optimise one to the other. Return the degree of
2064 * similarity.
2065 */
2066 static int linecmp(unsigned long *a, unsigned long *b)
2067 {
2068 int i, n;
2069
2070 for (i = n = 0; i < cols; i++)
2071 n += (*a++ == *b++);
2072 return n;
2073 }
2074
2075 /*
2076 * Given a context, update the window. Out of paranoia, we don't
2077 * allow WM_PAINT responses to do scrolling optimisations.
2078 */
2079 static void do_paint(Context ctx, int may_optimise)
2080 {
2081 int i, j, start, our_curs_y;
2082 unsigned long attr, rv, cursor;
2083 pos scrpos;
2084 char ch[1024];
2085 long ticks;
2086
2087 /*
2088 * Check the visual bell state.
2089 */
2090 if (in_vbell) {
2091 ticks = GetTickCount();
2092 if (ticks - vbell_timeout >= 0)
2093 in_vbell = FALSE;
2094 }
2095
2096 /* Depends on:
2097 * screen array, disptop, scrtop,
2098 * selection, rv,
2099 * cfg.blinkpc, blink_is_real, tblinker,
2100 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus
2101 */
2102 if (cursor_on) {
2103 if (has_focus) {
2104 if (blinker || !cfg.blink_cur)
2105 cursor = ATTR_ACTCURS;
2106 else
2107 cursor = 0;
2108 } else
2109 cursor = ATTR_PASCURS;
2110 if (wrapnext)
2111 cursor |= ATTR_RIGHTCURS;
2112 } else
2113 cursor = 0;
2114 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2115 our_curs_y = curs.y - disptop;
2116
2117 for (i = 0; i < rows; i++) {
2118 unsigned long *ldata;
2119 int lattr;
2120 scrpos.y = i + disptop;
2121 ldata = lineptr(scrpos.y);
2122 lattr = (ldata[cols] & LATTR_MODE);
2123 for (j = 0; j <= cols; j++) {
2124 unsigned long d = ldata[j];
2125 int idx = i * (cols + 1) + j;
2126 scrpos.x = j;
2127
2128 wanttext[idx] = lattr | (((d & ~ATTR_WRAPPED) ^ rv
2129 ^ (posle(selstart, scrpos) &&
2130 poslt(scrpos, selend) ?
2131 ATTR_REVERSE : 0)) |
2132 (i == our_curs_y
2133 && j == curs.x ? cursor : 0));
2134 if (blink_is_real) {
2135 if (has_focus && tblinker && (wanttext[idx] & ATTR_BLINK)) {
2136 wanttext[idx] &= ATTR_MASK;
2137 wanttext[idx] += ' ';
2138 }
2139 wanttext[idx] &= ~ATTR_BLINK;
2140 }
2141 }
2142 }
2143
2144 /*
2145 * We would perform scrolling optimisations in here, if they
2146 * didn't have a nasty tendency to cause the whole sodding
2147 * program to hang for a second at speed-critical moments.
2148 * We'll leave it well alone...
2149 */
2150
2151 for (i = 0; i < rows; i++) {
2152 int idx = i * (cols + 1);
2153 int lattr = (wanttext[idx + cols] & LATTR_MODE);
2154 start = -1;
2155 for (j = 0; j <= cols; j++, idx++) {
2156 unsigned long t = wanttext[idx];
2157 int needs_update = (j < cols && t != disptext[idx]);
2158 int keep_going = (start != -1 && needs_update &&
2159 (t & ATTR_MASK) == attr &&
2160 j - start < sizeof(ch));
2161 if (start != -1 && !keep_going) {
2162 do_text(ctx, start, i, ch, j - start, attr, lattr);
2163 start = -1;
2164 }
2165 if (needs_update) {
2166 if (start == -1) {
2167 start = j;
2168 attr = t & ATTR_MASK;
2169 }
2170 ch[j - start] = (char) (t & CHAR_MASK);
2171 }
2172 disptext[idx] = t;
2173 }
2174 }
2175 }
2176
2177 /*
2178 * Flick the switch that says if blinking things should be shown or hidden.
2179 */
2180
2181 void term_blink(int flg)
2182 {
2183 static long last_blink = 0;
2184 static long last_tblink = 0;
2185 long now, blink_diff;
2186
2187 now = GetTickCount();
2188 blink_diff = now - last_tblink;
2189
2190 /* Make sure the text blinks no more than 2Hz */
2191 if (blink_diff < 0 || blink_diff > 450) {
2192 last_tblink = now;
2193 tblinker = !tblinker;
2194 }
2195
2196 if (flg) {
2197 blinker = 1;
2198 last_blink = now;
2199 return;
2200 }
2201
2202 blink_diff = now - last_blink;
2203
2204 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2205 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2206 return;
2207
2208 last_blink = now;
2209 blinker = !blinker;
2210 }
2211
2212 /*
2213 * Invalidate the whole screen so it will be repainted in full.
2214 */
2215 void term_invalidate(void)
2216 {
2217 int i;
2218
2219 for (i = 0; i < rows * (cols + 1); i++)
2220 disptext[i] = ATTR_INVALID;
2221 }
2222
2223 /*
2224 * Paint the window in response to a WM_PAINT message.
2225 */
2226 void term_paint(Context ctx, int l, int t, int r, int b)
2227 {
2228 int i, j, left, top, right, bottom;
2229
2230 left = l / font_width;
2231 right = (r - 1) / font_width;
2232 top = t / font_height;
2233 bottom = (b - 1) / font_height;
2234 for (i = top; i <= bottom && i < rows; i++) {
2235 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2236 for (j = left; j <= right && j < cols; j++)
2237 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2238 else
2239 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2240 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2241 }
2242
2243 /* This should happen soon enough, also for some reason it sometimes
2244 * fails to actually do anything when re-sizing ... painting the wrong
2245 * window perhaps ?
2246 do_paint (ctx, FALSE);
2247 */
2248 }
2249
2250 /*
2251 * Attempt to scroll the scrollback. The second parameter gives the
2252 * position we want to scroll to; the first is +1 to denote that
2253 * this position is relative to the beginning of the scrollback, -1
2254 * to denote it is relative to the end, and 0 to denote that it is
2255 * relative to the current position.
2256 */
2257 void term_scroll(int rel, int where)
2258 {
2259 int sbtop = -count234(scrollback);
2260
2261 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2262 if (disptop < sbtop)
2263 disptop = sbtop;
2264 if (disptop > 0)
2265 disptop = 0;
2266 update_sbar();
2267 term_update();
2268 }
2269
2270 static void clipme(pos top, pos bottom, char *workbuf)
2271 {
2272 char *wbptr; /* where next char goes within workbuf */
2273 int wblen = 0; /* workbuf len */
2274 int buflen; /* amount of memory allocated to workbuf */
2275
2276 if (workbuf != NULL) { /* user supplied buffer? */
2277 buflen = -1; /* assume buffer passed in is big enough */
2278 wbptr = workbuf; /* start filling here */
2279 } else
2280 buflen = 0; /* No data is available yet */
2281
2282 while (poslt(top, bottom)) {
2283 int nl = FALSE;
2284 unsigned long *ldata = lineptr(top.y);
2285 pos nlpos;
2286
2287 nlpos.y = top.y;
2288 nlpos.x = cols;
2289
2290 if (!(ldata[cols] & ATTR_WRAPPED)) {
2291 while ((ldata[nlpos.x - 1] & CHAR_MASK) == 0x20
2292 && poslt(top, nlpos)) decpos(nlpos);
2293 if (poslt(nlpos, bottom))
2294 nl = TRUE;
2295 }
2296 while (poslt(top, bottom) && poslt(top, nlpos)) {
2297 int ch = (ldata[top.x] & CHAR_MASK);
2298 int set = (ldata[top.x] & CSET_MASK);
2299
2300 /* VT Specials -> ISO8859-1 for Cut&Paste */
2301 static const unsigned char poorman2[] =
2302 "* # HTFFCRLF\xB0 \xB1 NLVT+ + + + + - - - - - + + + + | <=>=PI!=\xA3 \xB7 ";
2303
2304 if (set && !cfg.rawcnp) {
2305 if (set == ATTR_LINEDRW && ch >= 0x60 && ch < 0x7F) {
2306 int x;
2307 if ((x = poorman2[2 * (ch - 0x60) + 1]) == ' ')
2308 x = 0;
2309 ch = (x << 8) + poorman2[2 * (ch - 0x60)];
2310 }
2311 }
2312
2313 while (ch != 0) {
2314 if (cfg.rawcnp || !!(ch & 0xE0)) {
2315 if (wblen == buflen) {
2316 workbuf = srealloc(workbuf, buflen += 100);
2317 wbptr = workbuf + wblen;
2318 }
2319 wblen++;
2320 *wbptr++ = (unsigned char) ch;
2321 }
2322 ch >>= 8;
2323 }
2324 top.x++;
2325 }
2326 if (nl) {
2327 int i;
2328 for (i = 0; i < sizeof(sel_nl); i++) {
2329 if (wblen == buflen) {
2330 workbuf = srealloc(workbuf, buflen += 100);
2331 wbptr = workbuf + wblen;
2332 }
2333 wblen++;
2334 *wbptr++ = sel_nl[i];
2335 }
2336 }
2337 top.y++;
2338 top.x = 0;
2339 }
2340 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2341 if (buflen > 0) /* indicates we allocated this buffer */
2342 sfree(workbuf);
2343
2344 }
2345 void term_copyall(void)
2346 {
2347 pos top;
2348 top.y = -count234(scrollback);
2349 top.x = 0;
2350 clipme(top, curs, NULL /* dynamic allocation */ );
2351 }
2352
2353 /*
2354 * Spread the selection outwards according to the selection mode.
2355 */
2356 static pos sel_spread_half(pos p, int dir)
2357 {
2358 unsigned long *ldata;
2359 short wvalue;
2360
2361 ldata = lineptr(p.y);
2362
2363 switch (selmode) {
2364 case SM_CHAR:
2365 /*
2366 * In this mode, every character is a separate unit, except
2367 * for runs of spaces at the end of a non-wrapping line.
2368 */
2369 if (!(ldata[cols] & ATTR_WRAPPED)) {
2370 unsigned long *q = ldata + cols;
2371 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2372 q--;
2373 if (q == ldata + cols)
2374 q--;
2375 if (p.x >= q - ldata)
2376 p.x = (dir == -1 ? q - ldata : cols - 1);
2377 }
2378 break;
2379 case SM_WORD:
2380 /*
2381 * In this mode, the units are maximal runs of characters
2382 * whose `wordness' has the same value.
2383 */
2384 wvalue = wordness[ldata[p.x] & CHAR_MASK];
2385 if (dir == +1) {
2386 while (p.x < cols
2387 && wordness[ldata[p.x + 1] & CHAR_MASK] ==
2388 wvalue) p.x++;
2389 } else {
2390 while (p.x > 0
2391 && wordness[ldata[p.x - 1] & CHAR_MASK] ==
2392 wvalue) p.x--;
2393 }
2394 break;
2395 case SM_LINE:
2396 /*
2397 * In this mode, every line is a unit.
2398 */
2399 p.x = (dir == -1 ? 0 : cols - 1);
2400 break;
2401 }
2402 return p;
2403 }
2404
2405 static void sel_spread(void)
2406 {
2407 selstart = sel_spread_half(selstart, -1);
2408 decpos(selend);
2409 selend = sel_spread_half(selend, +1);
2410 incpos(selend);
2411 }
2412
2413 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
2414 int shift, int ctrl)
2415 {
2416 pos selpoint;
2417 unsigned long *ldata;
2418
2419 if (y < 0)
2420 y = 0;
2421 if (y >= rows)
2422 y = rows - 1;
2423 if (x < 0) {
2424 if (y > 0) {
2425 x = cols - 1;
2426 y--;
2427 } else
2428 x = 0;
2429 }
2430 if (x >= cols)
2431 x = cols - 1;
2432
2433 selpoint.y = y + disptop;
2434 selpoint.x = x;
2435 ldata = lineptr(selpoint.y);
2436 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
2437 selpoint.x /= 2;
2438
2439 if (xterm_mouse) {
2440 int encstate = 0, r, c;
2441 char abuf[16];
2442 static int is_down = 0;
2443
2444 switch (b) {
2445 case MBT_LEFT:
2446 encstate = 0x20; /* left button down */
2447 break;
2448 case MBT_MIDDLE:
2449 encstate = 0x21;
2450 break;
2451 case MBT_RIGHT:
2452 encstate = 0x22;
2453 break;
2454 case MBT_WHEEL_UP:
2455 encstate = 0x60;
2456 break;
2457 case MBT_WHEEL_DOWN:
2458 encstate = 0x61;
2459 break;
2460 }
2461 switch (a) {
2462 case MA_DRAG:
2463 if (xterm_mouse == 1)
2464 return;
2465 encstate += 0x20;
2466 break;
2467 case MA_RELEASE:
2468 encstate = 0x23;
2469 is_down = 0;
2470 break;
2471 case MA_CLICK:
2472 if (is_down == b)
2473 return;
2474 is_down = b;
2475 break;
2476 }
2477 if (shift)
2478 encstate += 0x04;
2479 if (ctrl)
2480 encstate += 0x10;
2481 r = y + 33;
2482 c = x + 33;
2483
2484 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
2485 ldisc_send(abuf, 6);
2486 return;
2487 }
2488
2489 b = translate_button(b);
2490
2491 if (b == MBT_SELECT && a == MA_CLICK) {
2492 deselect();
2493 selstate = ABOUT_TO;
2494 selanchor = selpoint;
2495 selmode = SM_CHAR;
2496 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
2497 deselect();
2498 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
2499 selstate = DRAGGING;
2500 selstart = selanchor = selpoint;
2501 selend = selstart;
2502 incpos(selend);
2503 sel_spread();
2504 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
2505 (b == MBT_EXTEND && a != MA_RELEASE)) {
2506 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
2507 return;
2508 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
2509 if (posdiff(selpoint, selstart) <
2510 posdiff(selend, selstart) / 2) {
2511 selanchor = selend;
2512 decpos(selanchor);
2513 } else {
2514 selanchor = selstart;
2515 }
2516 selstate = DRAGGING;
2517 }
2518 if (selstate != ABOUT_TO && selstate != DRAGGING)
2519 selanchor = selpoint;
2520 selstate = DRAGGING;
2521 if (poslt(selpoint, selanchor)) {
2522 selstart = selpoint;
2523 selend = selanchor;
2524 incpos(selend);
2525 } else {
2526 selstart = selanchor;
2527 selend = selpoint;
2528 incpos(selend);
2529 }
2530 sel_spread();
2531 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
2532 if (selstate == DRAGGING) {
2533 /*
2534 * We've completed a selection. We now transfer the
2535 * data to the clipboard.
2536 */
2537 clipme(selstart, selend, selspace);
2538 selstate = SELECTED;
2539 } else
2540 selstate = NO_SELECTION;
2541 } else if (b == MBT_PASTE
2542 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
2543 char *data;
2544 int len;
2545
2546 get_clip((void **) &data, &len);
2547 if (data) {
2548 char *p, *q;
2549
2550 if (paste_buffer)
2551 sfree(paste_buffer);
2552 paste_pos = paste_hold = paste_len = 0;
2553 paste_buffer = smalloc(len);
2554
2555 p = q = data;
2556 while (p < data + len) {
2557 while (p < data + len &&
2558 !(p <= data + len - sizeof(sel_nl) &&
2559 !memcmp(p, sel_nl, sizeof(sel_nl))))
2560 p++;
2561
2562 {
2563 int i;
2564 unsigned char c;
2565 for (i = 0; i < p - q; i++) {
2566 c = xlat_kbd2tty(q[i]);
2567 paste_buffer[paste_len++] = c;
2568 }
2569 }
2570
2571 if (p <= data + len - sizeof(sel_nl) &&
2572 !memcmp(p, sel_nl, sizeof(sel_nl))) {
2573 paste_buffer[paste_len++] = '\r';
2574 p += sizeof(sel_nl);
2575 }
2576 q = p;
2577 }
2578
2579 /* Assume a small paste will be OK in one go. */
2580 if (paste_len < 256) {
2581 ldisc_send(paste_buffer, paste_len);
2582 if (paste_buffer)
2583 sfree(paste_buffer);
2584 paste_buffer = 0;
2585 paste_pos = paste_hold = paste_len = 0;
2586 }
2587 }
2588 get_clip(NULL, NULL);
2589 }
2590
2591 term_update();
2592 }
2593
2594 void term_nopaste()
2595 {
2596 if (paste_len == 0)
2597 return;
2598 sfree(paste_buffer);
2599 paste_buffer = 0;
2600 paste_len = 0;
2601 }
2602
2603 void term_paste()
2604 {
2605 static long last_paste = 0;
2606 long now, paste_diff;
2607
2608 if (paste_len == 0)
2609 return;
2610
2611 /* Don't wait forever to paste */
2612 if (paste_hold) {
2613 now = GetTickCount();
2614 paste_diff = now - last_paste;
2615 if (paste_diff >= 0 && paste_diff < 450)
2616 return;
2617 }
2618 paste_hold = 0;
2619
2620 while (paste_pos < paste_len) {
2621 int n = 0;
2622 while (n + paste_pos < paste_len) {
2623 if (paste_buffer[paste_pos + n++] == '\r')
2624 break;
2625 }
2626 ldisc_send(paste_buffer + paste_pos, n);
2627 paste_pos += n;
2628
2629 if (paste_pos < paste_len) {
2630 paste_hold = 1;
2631 return;
2632 }
2633 }
2634 sfree(paste_buffer);
2635 paste_buffer = 0;
2636 paste_len = 0;
2637 }
2638
2639 static void deselect(void)
2640 {
2641 selstate = NO_SELECTION;
2642 selstart.x = selstart.y = selend.x = selend.y = 0;
2643 }
2644
2645 void term_deselect(void)
2646 {
2647 deselect();
2648 term_update();
2649 }
2650
2651 int term_ldisc(int option)
2652 {
2653 if (option == LD_ECHO)
2654 return term_echoing;
2655 if (option == LD_EDIT)
2656 return term_editing;
2657 return FALSE;
2658 }
2659
2660 /*
2661 * from_backend(), to get data from the backend for the terminal.
2662 */
2663 void from_backend(int is_stderr, char *data, int len)
2664 {
2665 while (len--) {
2666 if (inbuf_head >= INBUF_SIZE)
2667 term_out();
2668 inbuf[inbuf_head++] = *data++;
2669 }
2670 }
2671
2672 /*
2673 * Log session traffic.
2674 */
2675 void logtraffic(unsigned char c, int logmode)
2676 {
2677 if (cfg.logtype > 0) {
2678 if (cfg.logtype == logmode) {
2679 /* deferred open file from pgm start? */
2680 if (!lgfp)
2681 logfopen();
2682 if (lgfp)
2683 fputc(c, lgfp);
2684 }
2685 }
2686 }
2687
2688 /* open log file append/overwrite mode */
2689 void logfopen(void)
2690 {
2691 char buf[256];
2692 time_t t;
2693 struct tm *tm;
2694 char writemod[4];
2695
2696 if (!cfg.logtype)
2697 return;
2698 sprintf(writemod, "wb"); /* default to rewrite */
2699 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
2700 if (lgfp) {
2701 int i;
2702 fclose(lgfp);
2703 i = askappend(cfg.logfilename);
2704 if (i == 1)
2705 writemod[0] = 'a'; /* set append mode */
2706 else if (i == 0) { /* cancelled */
2707 lgfp = NULL;
2708 cfg.logtype = 0; /* disable logging */
2709 return;
2710 }
2711 }
2712
2713 lgfp = fopen(cfg.logfilename, writemod);
2714 if (lgfp) { /* enter into event log */
2715 sprintf(buf, "%s session log (%s mode) to file : ",
2716 (writemod[0] == 'a') ? "Appending" : "Writing new",
2717 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
2718 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
2719 /* Make sure we do not exceed the output buffer size */
2720 strncat(buf, cfg.logfilename, 128);
2721 buf[strlen(buf)] = '\0';
2722 logevent(buf);
2723
2724 /* --- write header line iinto log file */
2725 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
2726 time(&t);
2727 tm = localtime(&t);
2728 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
2729 fputs(buf, lgfp);
2730 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
2731 }
2732 }
2733
2734 void logfclose(void)
2735 {
2736 if (lgfp) {
2737 fclose(lgfp);
2738 lgfp = NULL;
2739 }
2740 }