]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - terminal.c
64b5685bea2c2042a17ac1aec3d094cc4c189ff5
[PuTTY.git] / 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);
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 '\013':
1060               case '\014':
1061                 compatibility(VT100);
1062               case '\n':
1063                 if (curs.y == marg_b)
1064                     scroll(marg_t, marg_b, 1, TRUE);
1065                 else if (curs.y < rows - 1)
1066                     curs.y++;
1067                 if (cfg.lfhascr)
1068                     curs.x = 0;
1069                 fix_cpos;
1070                 wrapnext = FALSE;
1071                 seen_disp_event = 1;
1072                 paste_hold = 0;
1073                 logtraffic((unsigned char) c, LGTYP_ASCII);
1074                 break;
1075               case '\t':
1076                 {
1077                     pos old_curs = curs;
1078                     unsigned long *ldata = lineptr(curs.y);
1079
1080                     do {
1081                         curs.x++;
1082                     } while (curs.x < cols - 1 && !tabs[curs.x]);
1083
1084                     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1085                         if (curs.x >= cols / 2)
1086                             curs.x = cols / 2 - 1;
1087                     } else {
1088                         if (curs.x >= cols)
1089                             curs.x = cols - 1;
1090                     }
1091
1092                     fix_cpos;
1093                     check_selection(old_curs, curs);
1094                 }
1095                 seen_disp_event = TRUE;
1096                 break;
1097               case '\177':             /* Destructive backspace
1098                                           This does nothing on a real VT100 */
1099                 compatibility(OTHER);
1100                 if (curs.x && !wrapnext)
1101                     curs.x--;
1102                 wrapnext = FALSE;
1103                 fix_cpos;
1104                 *cpos = (' ' | curr_attr | ATTR_ASCII);
1105                 break;
1106             }
1107         } else
1108             switch (termstate) {
1109               case TOPLEVEL:
1110                 /* Only graphic characters get this far, ctrls are stripped above */
1111                 if (wrapnext && wrap) {
1112                     cpos[1] |= ATTR_WRAPPED;
1113                     if (curs.y == marg_b)
1114                         scroll(marg_t, marg_b, 1, TRUE);
1115                     else if (curs.y < rows - 1)
1116                         curs.y++;
1117                     curs.x = 0;
1118                     fix_cpos;
1119                     wrapnext = FALSE;
1120                 }
1121                 if (insert)
1122                     insch(1);
1123                 if (selstate != NO_SELECTION) {
1124                     pos cursplus = curs;
1125                     incpos(cursplus);
1126                     check_selection(curs, cursplus);
1127                 }
1128                 switch (cset_attr[cset]) {
1129                     /*
1130                      * Linedraw characters are different from 'ESC ( B'
1131                      * only for a small range. For ones outside that
1132                      * range, make sure we use the same font as well as
1133                      * the same encoding.
1134                      */
1135                   case ATTR_LINEDRW:
1136                     if (c < 0x5f || c > 0x7F)
1137                         *cpos++ =
1138                             xlat_tty2scr((unsigned char) c) | curr_attr |
1139                             ATTR_ASCII;
1140                     else if (c == 0x5F)
1141                         *cpos++ = ' ' | curr_attr | ATTR_ASCII;
1142                     else
1143                         *cpos++ =
1144                             ((unsigned char) c) | curr_attr | ATTR_LINEDRW;
1145                     break;
1146                   case ATTR_GBCHR:
1147                     /* If UK-ASCII, make the '#' a LineDraw Pound */
1148                     if (c == '#') {
1149                         *cpos++ = '}' | curr_attr | ATTR_LINEDRW;
1150                         break;
1151                     }
1152                   /*FALLTHROUGH*/ default:
1153                     *cpos = xlat_tty2scr((unsigned char) c) | curr_attr |
1154                         (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
1155                     logtraffic((unsigned char) c, LGTYP_ASCII);
1156                     cpos++;
1157                     break;
1158                 }
1159                 curs.x++;
1160                 if (curs.x == cols) {
1161                     cpos--;
1162                     curs.x--;
1163                     wrapnext = TRUE;
1164                 }
1165                 seen_disp_event = 1;
1166                 break;
1167
1168               case IGNORE_NEXT:
1169                 termstate = TOPLEVEL;
1170                 break;
1171               case OSC_MAYBE_ST:
1172                 /*
1173                  * This state is virtually identical to SEEN_ESC, with the
1174                  * exception that we have an OSC sequence in the pipeline,
1175                  * and _if_ we see a backslash, we process it.
1176                  */
1177                 if (c == '\\') {
1178                     do_osc();
1179                     termstate = TOPLEVEL;
1180                     break;
1181                 }
1182                 /* else fall through */
1183               case SEEN_ESC:
1184                 termstate = TOPLEVEL;
1185                 switch (c) {
1186                   case ' ':            /* some weird sequence? */
1187                     compatibility(VT220);
1188                     termstate = IGNORE_NEXT;
1189                     break;
1190                   case '[':            /* enter CSI mode */
1191                     termstate = SEEN_CSI;
1192                     esc_nargs = 1;
1193                     esc_args[0] = ARG_DEFAULT;
1194                     esc_query = FALSE;
1195                     break;
1196                   case ']':            /* xterm escape sequences */
1197                     /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1198                     compatibility(OTHER);
1199                     termstate = SEEN_OSC;
1200                     esc_args[0] = 0;
1201                     break;
1202                   case '(':            /* should set GL */
1203                     compatibility(VT100);
1204                     termstate = SET_GL;
1205                     break;
1206                   case ')':            /* should set GR */
1207                     compatibility(VT100);
1208                     termstate = SET_GR;
1209                     break;
1210                   case '7':            /* save cursor */
1211                     compatibility(VT100);
1212                     save_cursor(TRUE);
1213                     break;
1214                   case '8':            /* restore cursor */
1215                     compatibility(VT100);
1216                     save_cursor(FALSE);
1217                     seen_disp_event = TRUE;
1218                     break;
1219                   case '=':
1220                     compatibility(VT100);
1221                     app_keypad_keys = TRUE;
1222                     break;
1223                   case '>':
1224                     compatibility(VT100);
1225                     app_keypad_keys = FALSE;
1226                     break;
1227                   case 'D':            /* exactly equivalent to LF */
1228                     compatibility(VT100);
1229                     if (curs.y == marg_b)
1230                         scroll(marg_t, marg_b, 1, TRUE);
1231                     else if (curs.y < rows - 1)
1232                         curs.y++;
1233                     fix_cpos;
1234                     wrapnext = FALSE;
1235                     seen_disp_event = TRUE;
1236                     break;
1237                   case 'E':            /* exactly equivalent to CR-LF */
1238                     compatibility(VT100);
1239                     curs.x = 0;
1240                     if (curs.y == marg_b)
1241                         scroll(marg_t, marg_b, 1, TRUE);
1242                     else if (curs.y < rows - 1)
1243                         curs.y++;
1244                     fix_cpos;
1245                     wrapnext = FALSE;
1246                     seen_disp_event = TRUE;
1247                     break;
1248                   case 'M':            /* reverse index - backwards LF */
1249                     compatibility(VT100);
1250                     if (curs.y == marg_t)
1251                         scroll(marg_t, marg_b, -1, TRUE);
1252                     else if (curs.y > 0)
1253                         curs.y--;
1254                     fix_cpos;
1255                     wrapnext = FALSE;
1256                     seen_disp_event = TRUE;
1257                     break;
1258                   case 'Z':            /* terminal type query */
1259                     compatibility(VT100);
1260                     ldisc_send(id_string, strlen(id_string));
1261                     break;
1262                   case 'c':            /* restore power-on settings */
1263                     compatibility(VT100);
1264                     power_on();
1265                     if (reset_132) {
1266                         request_resize(80, rows, 1);
1267                         reset_132 = 0;
1268                     }
1269                     fix_cpos;
1270                     disptop = 0;
1271                     seen_disp_event = TRUE;
1272                     break;
1273                   case '#':            /* ESC # 8 fills screen with Es :-) */
1274                     compatibility(VT100);
1275                     termstate = SEEN_ESCHASH;
1276                     break;
1277                   case 'H':            /* set a tab */
1278                     compatibility(VT100);
1279                     tabs[curs.x] = TRUE;
1280                     break;
1281                 }
1282                 break;
1283               case SEEN_CSI:
1284                 termstate = TOPLEVEL;  /* default */
1285                 if (isdigit(c)) {
1286                     if (esc_nargs <= ARGS_MAX) {
1287                         if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1288                             esc_args[esc_nargs - 1] = 0;
1289                         esc_args[esc_nargs - 1] =
1290                             10 * esc_args[esc_nargs - 1] + c - '0';
1291                     }
1292                     termstate = SEEN_CSI;
1293                 } else if (c == ';') {
1294                     if (++esc_nargs <= ARGS_MAX)
1295                         esc_args[esc_nargs - 1] = ARG_DEFAULT;
1296                     termstate = SEEN_CSI;
1297                 } else if (c < '@') {
1298                     if (esc_query)
1299                         esc_query = -1;
1300                     else if (c == '?')
1301                         esc_query = TRUE;
1302                     else
1303                         esc_query = c;
1304                     termstate = SEEN_CSI;
1305                 } else
1306                     switch (ANSI(c, esc_query)) {
1307                       case 'A':       /* move up N lines */
1308                         move(curs.x, curs.y - def(esc_args[0], 1), 1);
1309                         seen_disp_event = TRUE;
1310                         break;
1311                       case 'e':       /* move down N lines */
1312                         compatibility(ANSI);
1313                       case 'B':
1314                         move(curs.x, curs.y + def(esc_args[0], 1), 1);
1315                         seen_disp_event = TRUE;
1316                         break;
1317                       case 'a':       /* move right N cols */
1318                         compatibility(ANSI);
1319                       case ANSI('c', '>'):      /* report xterm version */
1320                         compatibility(OTHER);
1321                         /* this reports xterm version 136 so that VIM can
1322                            use the drag messages from the mouse reporting */
1323                         ldisc_send("\033[>0;136;0c", 11);
1324                         break;
1325                       case 'C':
1326                         move(curs.x + def(esc_args[0], 1), curs.y, 1);
1327                         seen_disp_event = TRUE;
1328                         break;
1329                       case 'D':       /* move left N cols */
1330                         move(curs.x - def(esc_args[0], 1), curs.y, 1);
1331                         seen_disp_event = TRUE;
1332                         break;
1333                       case 'E':       /* move down N lines and CR */
1334                         compatibility(ANSI);
1335                         move(0, curs.y + def(esc_args[0], 1), 1);
1336                         seen_disp_event = TRUE;
1337                         break;
1338                       case 'F':       /* move up N lines and CR */
1339                         compatibility(ANSI);
1340                         move(0, curs.y - def(esc_args[0], 1), 1);
1341                         seen_disp_event = TRUE;
1342                         break;
1343                       case 'G':
1344                       case '`':       /* set horizontal posn */
1345                         compatibility(ANSI);
1346                         move(def(esc_args[0], 1) - 1, curs.y, 0);
1347                         seen_disp_event = TRUE;
1348                         break;
1349                       case 'd':       /* set vertical posn */
1350                         compatibility(ANSI);
1351                         move(curs.x,
1352                              (dec_om ? marg_t : 0) + def(esc_args[0],
1353                                                          1) - 1,
1354                              (dec_om ? 2 : 0));
1355                         seen_disp_event = TRUE;
1356                         break;
1357                       case 'H':
1358                       case 'f':       /* set horz and vert posns at once */
1359                         if (esc_nargs < 2)
1360                             esc_args[1] = ARG_DEFAULT;
1361                         move(def(esc_args[1], 1) - 1,
1362                              (dec_om ? marg_t : 0) + def(esc_args[0],
1363                                                          1) - 1,
1364                              (dec_om ? 2 : 0));
1365                         seen_disp_event = TRUE;
1366                         break;
1367                       case 'J':       /* erase screen or parts of it */
1368                         {
1369                             unsigned int i = def(esc_args[0], 0) + 1;
1370                             if (i > 3)
1371                                 i = 0;
1372                             erase_lots(FALSE, !!(i & 2), !!(i & 1));
1373                         }
1374                         disptop = 0;
1375                         seen_disp_event = TRUE;
1376                         break;
1377                       case 'K':       /* erase line or parts of it */
1378                         {
1379                             unsigned int i = def(esc_args[0], 0) + 1;
1380                             if (i > 3)
1381                                 i = 0;
1382                             erase_lots(TRUE, !!(i & 2), !!(i & 1));
1383                         }
1384                         seen_disp_event = TRUE;
1385                         break;
1386                       case 'L':       /* insert lines */
1387                         compatibility(VT102);
1388                         if (curs.y <= marg_b)
1389                             scroll(curs.y, marg_b, -def(esc_args[0], 1),
1390                                    FALSE);
1391                         fix_cpos;
1392                         seen_disp_event = TRUE;
1393                         break;
1394                       case 'M':       /* delete lines */
1395                         compatibility(VT102);
1396                         if (curs.y <= marg_b)
1397                             scroll(curs.y, marg_b, def(esc_args[0], 1),
1398                                    TRUE);
1399                         fix_cpos;
1400                         seen_disp_event = TRUE;
1401                         break;
1402                       case '@':       /* insert chars */
1403                         /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1404                         compatibility(VT102);
1405                         insch(def(esc_args[0], 1));
1406                         seen_disp_event = TRUE;
1407                         break;
1408                       case 'P':       /* delete chars */
1409                         compatibility(VT102);
1410                         insch(-def(esc_args[0], 1));
1411                         seen_disp_event = TRUE;
1412                         break;
1413                       case 'c':       /* terminal type query */
1414                         compatibility(VT100);
1415                         /* This is the response for a VT102 */
1416                         ldisc_send(id_string, strlen(id_string));
1417                         break;
1418                       case 'n':       /* cursor position query */
1419                         if (esc_args[0] == 6) {
1420                             char buf[32];
1421                             sprintf(buf, "\033[%d;%dR", curs.y + 1,
1422                                     curs.x + 1);
1423                             ldisc_send(buf, strlen(buf));
1424                         } else if (esc_args[0] == 5) {
1425                             ldisc_send("\033[0n", 4);
1426                         }
1427                         break;
1428                       case 'h':       /* toggle modes to high */
1429                       case ANSI_QUE('h'):
1430                         compatibility(VT100);
1431                         {
1432                             int i;
1433                             for (i = 0; i < esc_nargs; i++)
1434                                 toggle_mode(esc_args[i], esc_query, TRUE);
1435                         }
1436                         break;
1437                       case 'l':       /* toggle modes to low */
1438                       case ANSI_QUE('l'):
1439                         compatibility(VT100);
1440                         {
1441                             int i;
1442                             for (i = 0; i < esc_nargs; i++)
1443                                 toggle_mode(esc_args[i], esc_query, FALSE);
1444                         }
1445                         break;
1446                       case 'g':       /* clear tabs */
1447                         compatibility(VT100);
1448                         if (esc_nargs == 1) {
1449                             if (esc_args[0] == 0) {
1450                                 tabs[curs.x] = FALSE;
1451                             } else if (esc_args[0] == 3) {
1452                                 int i;
1453                                 for (i = 0; i < cols; i++)
1454                                     tabs[i] = FALSE;
1455                             }
1456                         }
1457                         break;
1458                       case 'r':       /* set scroll margins */
1459                         compatibility(VT100);
1460                         if (esc_nargs <= 2) {
1461                             int top, bot;
1462                             top = def(esc_args[0], 1) - 1;
1463                             bot = (esc_nargs <= 1
1464                                    || esc_args[1] ==
1465                                    0 ? rows : def(esc_args[1], rows)) - 1;
1466                             if (bot >= rows)
1467                                 bot = rows - 1;
1468                             /* VTTEST Bug 9 - if region is less than 2 lines
1469                              * don't change region.
1470                              */
1471                             if (bot - top > 0) {
1472                                 marg_t = top;
1473                                 marg_b = bot;
1474                                 curs.x = 0;
1475                                 /*
1476                                  * I used to think the cursor should be
1477                                  * placed at the top of the newly marginned
1478                                  * area. Apparently not: VMS TPU falls over
1479                                  * if so.
1480                                  *
1481                                  * Well actually it should for Origin mode - RDB
1482                                  */
1483                                 curs.y = (dec_om ? marg_t : 0);
1484                                 fix_cpos;
1485                                 seen_disp_event = TRUE;
1486                             }
1487                         }
1488                         break;
1489                       case 'm':       /* set graphics rendition */
1490                         {
1491                             /* 
1492                              * A VT100 without the AVO only had one attribute, either
1493                              * underline or reverse video depending on the cursor type,
1494                              * this was selected by CSI 7m.
1495                              *
1496                              * case 2:
1497                              *  This is DIM on the VT100-AVO and VT102
1498                              * case 5:
1499                              *  This is BLINK on the VT100-AVO and VT102+
1500                              * case 8:
1501                              *  This is INVIS on the VT100-AVO and VT102
1502                              * case 21:
1503                              *  This like 22 disables BOLD, DIM and INVIS
1504                              *
1505                              * The ANSI colours appear on any terminal that has colour
1506                              * (obviously) but the interaction between sgr0 and the
1507                              * colours varies but is usually related to the background
1508                              * colour erase item.
1509                              * The interaction between colour attributes and the mono
1510                              * ones is also very implementation dependent.
1511                              *
1512                              * The 39 and 49 attributes are likely to be unimplemented.
1513                              */
1514                             int i;
1515                             for (i = 0; i < esc_nargs; i++) {
1516                                 switch (def(esc_args[i], 0)) {
1517                                   case 0:       /* restore defaults */
1518                                     curr_attr = ATTR_DEFAULT;
1519                                     break;
1520                                   case 1:       /* enable bold */
1521                                     compatibility(VT100AVO);
1522                                     curr_attr |= ATTR_BOLD;
1523                                     break;
1524                                   case 21:      /* (enable double underline) */
1525                                     compatibility(OTHER);
1526                                   case 4:       /* enable underline */
1527                                     compatibility(VT100AVO);
1528                                     curr_attr |= ATTR_UNDER;
1529                                     break;
1530                                   case 5:       /* enable blink */
1531                                     compatibility(VT100AVO);
1532                                     curr_attr |= ATTR_BLINK;
1533                                     break;
1534                                   case 7:       /* enable reverse video */
1535                                     curr_attr |= ATTR_REVERSE;
1536                                     break;
1537                                   case 22:      /* disable bold */
1538                                     compatibility2(OTHER, VT220);
1539                                     curr_attr &= ~ATTR_BOLD;
1540                                     break;
1541                                   case 24:      /* disable underline */
1542                                     compatibility2(OTHER, VT220);
1543                                     curr_attr &= ~ATTR_UNDER;
1544                                     break;
1545                                   case 25:      /* disable blink */
1546                                     compatibility2(OTHER, VT220);
1547                                     curr_attr &= ~ATTR_BLINK;
1548                                     break;
1549                                   case 27:      /* disable reverse video */
1550                                     compatibility2(OTHER, VT220);
1551                                     curr_attr &= ~ATTR_REVERSE;
1552                                     break;
1553                                   case 30:
1554                                   case 31:
1555                                   case 32:
1556                                   case 33:
1557                                   case 34:
1558                                   case 35:
1559                                   case 36:
1560                                   case 37:
1561                                     /* foreground */
1562                                     curr_attr &= ~ATTR_FGMASK;
1563                                     curr_attr |=
1564                                         (esc_args[i] - 30) << ATTR_FGSHIFT;
1565                                     break;
1566                                   case 39:      /* default-foreground */
1567                                     curr_attr &= ~ATTR_FGMASK;
1568                                     curr_attr |= ATTR_DEFFG;
1569                                     break;
1570                                   case 40:
1571                                   case 41:
1572                                   case 42:
1573                                   case 43:
1574                                   case 44:
1575                                   case 45:
1576                                   case 46:
1577                                   case 47:
1578                                     /* background */
1579                                     curr_attr &= ~ATTR_BGMASK;
1580                                     curr_attr |=
1581                                         (esc_args[i] - 40) << ATTR_BGSHIFT;
1582                                     break;
1583                                   case 49:      /* default-background */
1584                                     curr_attr &= ~ATTR_BGMASK;
1585                                     curr_attr |= ATTR_DEFBG;
1586                                     break;
1587                                 }
1588                             }
1589                             if (use_bce)
1590                                 erase_char =
1591                                     (' ' |
1592                                      (curr_attr &
1593                                       (ATTR_FGMASK | ATTR_BGMASK |
1594                                        ATTR_BLINK)));
1595                         }
1596                         break;
1597                       case 's':       /* save cursor */
1598                         save_cursor(TRUE);
1599                         break;
1600                       case 'u':       /* restore cursor */
1601                         save_cursor(FALSE);
1602                         seen_disp_event = TRUE;
1603                         break;
1604                       case 't':       /* set page size - ie window height */
1605                         /*
1606                          * VT340/VT420 sequence DECSLPP, DEC only allows values
1607                          *  24/25/36/48/72/144 other emulators (eg dtterm) use
1608                          * illegal values (eg first arg 1..9) for window changing 
1609                          * and reports.
1610                          */
1611                         compatibility(VT340TEXT);
1612                         if (esc_nargs <= 1
1613                             && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1614                             request_resize(cols, def(esc_args[0], 24), 0);
1615                             deselect();
1616                         }
1617                         break;
1618                       case ANSI('|', '*'):
1619                         /* VT420 sequence DECSNLS
1620                          * Set number of lines on screen
1621                          * VT420 uses VGA like hardware and can support any size in
1622                          * reasonable range (24..49 AIUI) with no default specified.
1623                          */
1624                         compatibility(VT420);
1625                         if (esc_nargs == 1 && esc_args[0] > 0) {
1626                             request_resize(cols,
1627                                            def(esc_args[0], cfg.height),
1628                                            0);
1629                             deselect();
1630                         }
1631                         break;
1632                       case ANSI('|', '$'):
1633                         /* VT340/VT420 sequence DECSCPP
1634                          * Set number of columns per page
1635                          * Docs imply range is only 80 or 132, but I'll allow any.
1636                          */
1637                         compatibility(VT340TEXT);
1638                         if (esc_nargs <= 1) {
1639                             request_resize(def(esc_args[0], cfg.width),
1640                                            rows, 0);
1641                             deselect();
1642                         }
1643                         break;
1644                       case 'X':       /* write N spaces w/o moving cursor */
1645                         /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1646                         compatibility(ANSIMIN);
1647                         {
1648                             int n = def(esc_args[0], 1);
1649                             pos cursplus;
1650                             unsigned long *p = cpos;
1651                             if (n > cols - curs.x)
1652                                 n = cols - curs.x;
1653                             cursplus = curs;
1654                             cursplus.x += n;
1655                             check_selection(curs, cursplus);
1656                             while (n--)
1657                                 *p++ = erase_char;
1658                             seen_disp_event = TRUE;
1659                         }
1660                         break;
1661                       case 'x':       /* report terminal characteristics */
1662                         compatibility(VT100);
1663                         {
1664                             char buf[32];
1665                             int i = def(esc_args[0], 0);
1666                             if (i == 0 || i == 1) {
1667                                 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1668                                 buf[2] += i;
1669                                 ldisc_send(buf, 20);
1670                             }
1671                         }
1672                         break;
1673                       case ANSI('L', '='):
1674                         compatibility(OTHER);
1675                         use_bce = (esc_args[0] <= 0);
1676                         erase_char = ERASE_CHAR;
1677                         if (use_bce)
1678                             erase_char =
1679                                 (' ' |
1680                                  (curr_attr &
1681                                   (ATTR_FGMASK | ATTR_BGMASK)));
1682                         break;
1683                       case ANSI('E', '='):
1684                         compatibility(OTHER);
1685                         blink_is_real = (esc_args[0] >= 1);
1686                         break;
1687                       case ANSI('p', '"'):
1688                         /* Allow the host to make this emulator a 'perfect' VT102.
1689                          * This first appeared in the VT220, but we do need to get 
1690                          * back to PuTTY mode so I won't check it.
1691                          *
1692                          * The arg in 40..42 are a PuTTY extension.
1693                          * The 2nd arg, 8bit vs 7bit is not checked.
1694                          *
1695                          * Setting VT102 mode should also change the Fkeys to
1696                          * generate PF* codes as a real VT102 has no Fkeys.
1697                          * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1698                          * send nothing.
1699                          *
1700                          * Note ESC c will NOT change this!
1701                          */
1702
1703                         switch (esc_args[0]) {
1704                           case 61:
1705                             compatibility_level &= ~TM_VTXXX;
1706                             compatibility_level |= TM_VT102;
1707                             break;
1708                           case 62:
1709                             compatibility_level &= ~TM_VTXXX;
1710                             compatibility_level |= TM_VT220;
1711                             break;
1712
1713                           default:
1714                             if (esc_args[0] > 60 && esc_args[0] < 70)
1715                                 compatibility_level |= TM_VTXXX;
1716                             break;
1717
1718                           case 40:
1719                             compatibility_level &= TM_VTXXX;
1720                             break;
1721                           case 41:
1722                             compatibility_level = TM_PUTTY;
1723                             break;
1724                           case 42:
1725                             compatibility_level = TM_SCOANSI;
1726                             break;
1727
1728                           case ARG_DEFAULT:
1729                             compatibility_level = TM_PUTTY;
1730                             break;
1731                           case 50:
1732                             break;
1733                         }
1734
1735                         /* Change the response to CSI c */
1736                         if (esc_args[0] == 50) {
1737                             int i;
1738                             char lbuf[64];
1739                             strcpy(id_string, "\033[?");
1740                             for (i = 1; i < esc_nargs; i++) {
1741                                 if (i != 1)
1742                                     strcat(id_string, ";");
1743                                 sprintf(lbuf, "%d", esc_args[i]);
1744                                 strcat(id_string, lbuf);
1745                             }
1746                             strcat(id_string, "c");
1747                         }
1748 #if 0
1749                         /* Is this a good idea ? 
1750                          * Well we should do a soft reset at this point ...
1751                          */
1752                         if (!has_compat(VT420) && has_compat(VT100)) {
1753                             if (reset_132)
1754                                 request_resize(132, 24, 1);
1755                             else
1756                                 request_resize(80, 24, 1);
1757                         }
1758 #endif
1759                         break;
1760                     }
1761                 break;
1762               case SET_GL:
1763               case SET_GR:
1764                 /* VT100 only here, checked above */
1765                 switch (c) {
1766                   case 'A':
1767                     cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
1768                     break;
1769                   case '0':
1770                     cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
1771                     break;
1772                   case 'B':
1773                   default:             /* specifically, 'B' */
1774                     cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
1775                     break;
1776                 }
1777                 if (!has_compat(VT220) || c != '%')
1778                     termstate = TOPLEVEL;
1779                 break;
1780               case SEEN_OSC:
1781                 osc_w = FALSE;
1782                 switch (c) {
1783                   case 'P':            /* Linux palette sequence */
1784                     termstate = SEEN_OSC_P;
1785                     osc_strlen = 0;
1786                     break;
1787                   case 'R':            /* Linux palette reset */
1788                     palette_reset();
1789                     term_invalidate();
1790                     termstate = TOPLEVEL;
1791                     break;
1792                   case 'W':            /* word-set */
1793                     termstate = SEEN_OSC_W;
1794                     osc_w = TRUE;
1795                     break;
1796                   case '0':
1797                   case '1':
1798                   case '2':
1799                   case '3':
1800                   case '4':
1801                   case '5':
1802                   case '6':
1803                   case '7':
1804                   case '8':
1805                   case '9':
1806                     esc_args[0] = 10 * esc_args[0] + c - '0';
1807                     break;
1808                   case 'L':
1809                     /*
1810                      * Grotty hack to support xterm and DECterm title
1811                      * sequences concurrently.
1812                      */
1813                     if (esc_args[0] == 2) {
1814                         esc_args[0] = 1;
1815                         break;
1816                     }
1817                     /* else fall through */
1818                   default:
1819                     termstate = OSC_STRING;
1820                     osc_strlen = 0;
1821                 }
1822                 break;
1823               case OSC_STRING:
1824                 /*
1825                  * This OSC stuff is EVIL. It takes just one character to get into
1826                  * sysline mode and it's not initially obvious how to get out.
1827                  * So I've added CR and LF as string aborts.
1828                  * This shouldn't effect compatibility as I believe embedded 
1829                  * control characters are supposed to be interpreted (maybe?) 
1830                  * and they don't display anything useful anyway.
1831                  *
1832                  * -- RDB
1833                  */
1834                 if (c == '\n' || c == '\r') {
1835                     termstate = TOPLEVEL;
1836                 } else if (c == 0234 || c == '\007') {
1837                     /*
1838                      * These characters terminate the string; ST and BEL
1839                      * terminate the sequence and trigger instant
1840                      * processing of it, whereas ESC goes back to SEEN_ESC
1841                      * mode unless it is followed by \, in which case it is
1842                      * synonymous with ST in the first place.
1843                      */
1844                     do_osc();
1845                     termstate = TOPLEVEL;
1846                 } else if (c == '\033')
1847                     termstate = OSC_MAYBE_ST;
1848                 else if (osc_strlen < OSC_STR_MAX)
1849                     osc_string[osc_strlen++] = c;
1850                 break;
1851               case SEEN_OSC_P:
1852                 {
1853                     int max = (osc_strlen == 0 ? 21 : 16);
1854                     int val;
1855                     if (c >= '0' && c <= '9')
1856                         val = c - '0';
1857                     else if (c >= 'A' && c <= 'A' + max - 10)
1858                         val = c - 'A' + 10;
1859                     else if (c >= 'a' && c <= 'a' + max - 10)
1860                         val = c - 'a' + 10;
1861                     else
1862                         termstate = TOPLEVEL;
1863                     osc_string[osc_strlen++] = val;
1864                     if (osc_strlen >= 7) {
1865                         palette_set(osc_string[0],
1866                                     osc_string[1] * 16 + osc_string[2],
1867                                     osc_string[3] * 16 + osc_string[4],
1868                                     osc_string[5] * 16 + osc_string[6]);
1869                         term_invalidate();
1870                         termstate = TOPLEVEL;
1871                     }
1872                 }
1873                 break;
1874               case SEEN_OSC_W:
1875                 switch (c) {
1876                   case '0':
1877                   case '1':
1878                   case '2':
1879                   case '3':
1880                   case '4':
1881                   case '5':
1882                   case '6':
1883                   case '7':
1884                   case '8':
1885                   case '9':
1886                     esc_args[0] = 10 * esc_args[0] + c - '0';
1887                     break;
1888                   default:
1889                     termstate = OSC_STRING;
1890                     osc_strlen = 0;
1891                 }
1892                 break;
1893               case SEEN_ESCHASH:
1894                 {
1895                     unsigned long nlattr;
1896                     unsigned long *ldata;
1897                     int i, j;
1898                     pos scrtop, scrbot;
1899
1900                     switch (c) {
1901                       case '8':
1902                         for (i = 0; i < rows; i++) {
1903                             ldata = lineptr(i);
1904                             for (j = 0; j < cols; j++)
1905                                 ldata[j] = ATTR_DEFAULT | 'E';
1906                             ldata[cols] = 0;
1907                         }
1908                         disptop = 0;
1909                         seen_disp_event = TRUE;
1910                         scrtop.x = scrtop.y = 0;
1911                         scrbot.x = 0;
1912                         scrbot.y = rows;
1913                         check_selection(scrtop, scrbot);
1914                         break;
1915
1916                       case '3':
1917                       case '4':
1918                       case '5':
1919                       case '6':
1920                         switch (c) {
1921                           case '3':
1922                             nlattr = LATTR_TOP;
1923                             break;
1924                           case '4':
1925                             nlattr = LATTR_BOT;
1926                             break;
1927                           case '5':
1928                             nlattr = LATTR_NORM;
1929                             break;
1930                           case '6':
1931                             nlattr = LATTR_WIDE;
1932                             break;
1933                         }
1934
1935                         ldata = lineptr(curs.y);
1936                         ldata[cols] &= ~LATTR_MODE;
1937                         ldata[cols] |= nlattr;
1938                     }
1939                 }
1940                 termstate = TOPLEVEL;
1941                 break;
1942               case VT52_ESC:
1943                 termstate = TOPLEVEL;
1944                 seen_disp_event = TRUE;
1945                 switch (c) {
1946                   case 'A':
1947                     move(curs.x, curs.y - 1, 1);
1948                     break;
1949                   case 'B':
1950                     move(curs.x, curs.y + 1, 1);
1951                     break;
1952                   case 'C':
1953                     move(curs.x + 1, curs.y, 1);
1954                     break;
1955                   case 'D':
1956                     move(curs.x - 1, curs.y, 1);
1957                     break;
1958                   case 'F':
1959                     cset_attr[cset = 0] = ATTR_LINEDRW;
1960                     break;
1961                   case 'G':
1962                     cset_attr[cset = 0] = ATTR_ASCII;
1963                     break;
1964                   case 'H':
1965                     move(0, 0, 0);
1966                     break;
1967                   case 'I':
1968                     if (curs.y == 0)
1969                         scroll(0, rows - 1, -1, TRUE);
1970                     else if (curs.y > 0)
1971                         curs.y--;
1972                     fix_cpos;
1973                     wrapnext = FALSE;
1974                     break;
1975                   case 'J':
1976                     erase_lots(FALSE, FALSE, TRUE);
1977                     disptop = 0;
1978                     break;
1979                   case 'K':
1980                     erase_lots(TRUE, FALSE, TRUE);
1981                     break;
1982                   case 'V':
1983                     /* XXX Print cursor line */
1984                     break;
1985                   case 'W':
1986                     /* XXX Start controller mode */
1987                     break;
1988                   case 'X':
1989                     /* XXX Stop controller mode */
1990                     break;
1991                   case 'Y':
1992                     termstate = VT52_Y1;
1993                     break;
1994                   case 'Z':
1995                     ldisc_send("\033/Z", 3);
1996                     break;
1997                   case '=':
1998                     app_keypad_keys = TRUE;
1999                     break;
2000                   case '>':
2001                     app_keypad_keys = FALSE;
2002                     break;
2003                   case '<':
2004                     /* XXX This should switch to VT100 mode not current or default
2005                      *     VT mode. But this will only have effect in a VT220+
2006                      *     emulation.
2007                      */
2008                     vt52_mode = FALSE;
2009                     break;
2010                   case '^':
2011                     /* XXX Enter auto print mode */
2012                     break;
2013                   case '_':
2014                     /* XXX Exit auto print mode */
2015                     break;
2016                   case ']':
2017                     /* XXX Print screen */
2018                     break;
2019                 }
2020                 break;
2021               case VT52_Y1:
2022                 termstate = VT52_Y2;
2023                 move(curs.x, c - ' ', 0);
2024                 break;
2025               case VT52_Y2:
2026                 termstate = TOPLEVEL;
2027                 move(c - ' ', curs.y, 0);
2028                 break;
2029             }
2030         if (selstate != NO_SELECTION) {
2031             pos cursplus = curs;
2032             incpos(cursplus);
2033             check_selection(curs, cursplus);
2034         }
2035     }
2036     inbuf_head = 0;
2037 }
2038
2039 /*
2040  * Compare two lines to determine whether they are sufficiently
2041  * alike to scroll-optimise one to the other. Return the degree of
2042  * similarity.
2043  */
2044 static int linecmp(unsigned long *a, unsigned long *b)
2045 {
2046     int i, n;
2047
2048     for (i = n = 0; i < cols; i++)
2049         n += (*a++ == *b++);
2050     return n;
2051 }
2052
2053 /*
2054  * Given a context, update the window. Out of paranoia, we don't
2055  * allow WM_PAINT responses to do scrolling optimisations.
2056  */
2057 static void do_paint(Context ctx, int may_optimise)
2058 {
2059     int i, j, start, our_curs_y;
2060     unsigned long attr, rv, cursor;
2061     pos scrpos;
2062     char ch[1024];
2063     long ticks;
2064
2065     /*
2066      * Check the visual bell state.
2067      */
2068     if (in_vbell) {
2069         ticks = GetTickCount();
2070         if (ticks - vbell_timeout >= 0)
2071             in_vbell = FALSE;
2072     }
2073
2074     /* Depends on:
2075      * screen array, disptop, scrtop,
2076      * selection, rv, 
2077      * cfg.blinkpc, blink_is_real, tblinker, 
2078      * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus
2079      */
2080     if (cursor_on) {
2081         if (has_focus) {
2082             if (blinker || !cfg.blink_cur)
2083                 cursor = ATTR_ACTCURS;
2084             else
2085                 cursor = 0;
2086         } else
2087             cursor = ATTR_PASCURS;
2088         if (wrapnext)
2089             cursor |= ATTR_RIGHTCURS;
2090     } else
2091         cursor = 0;
2092     rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2093     our_curs_y = curs.y - disptop;
2094
2095     for (i = 0; i < rows; i++) {
2096         unsigned long *ldata;
2097         int lattr;
2098         scrpos.y = i + disptop;
2099         ldata = lineptr(scrpos.y);
2100         lattr = (ldata[cols] & LATTR_MODE);
2101         for (j = 0; j <= cols; j++) {
2102             unsigned long d = ldata[j];
2103             int idx = i * (cols + 1) + j;
2104             scrpos.x = j;
2105
2106             wanttext[idx] = lattr | (((d & ~ATTR_WRAPPED) ^ rv
2107                                       ^ (posle(selstart, scrpos) &&
2108                                          poslt(scrpos, selend) ?
2109                                          ATTR_REVERSE : 0)) |
2110                                      (i == our_curs_y
2111                                       && j == curs.x ? cursor : 0));
2112             if (blink_is_real) {
2113                 if (has_focus && tblinker && (wanttext[idx] & ATTR_BLINK)) {
2114                     wanttext[idx] &= ATTR_MASK;
2115                     wanttext[idx] += ' ';
2116                 }
2117                 wanttext[idx] &= ~ATTR_BLINK;
2118             }
2119         }
2120     }
2121
2122     /*
2123      * We would perform scrolling optimisations in here, if they
2124      * didn't have a nasty tendency to cause the whole sodding
2125      * program to hang for a second at speed-critical moments.
2126      * We'll leave it well alone...
2127      */
2128
2129     for (i = 0; i < rows; i++) {
2130         int idx = i * (cols + 1);
2131         int lattr = (wanttext[idx + cols] & LATTR_MODE);
2132         start = -1;
2133         for (j = 0; j <= cols; j++, idx++) {
2134             unsigned long t = wanttext[idx];
2135             int needs_update = (j < cols && t != disptext[idx]);
2136             int keep_going = (start != -1 && needs_update &&
2137                               (t & ATTR_MASK) == attr &&
2138                               j - start < sizeof(ch));
2139             if (start != -1 && !keep_going) {
2140                 do_text(ctx, start, i, ch, j - start, attr, lattr);
2141                 start = -1;
2142             }
2143             if (needs_update) {
2144                 if (start == -1) {
2145                     start = j;
2146                     attr = t & ATTR_MASK;
2147                 }
2148                 ch[j - start] = (char) (t & CHAR_MASK);
2149             }
2150             disptext[idx] = t;
2151         }
2152     }
2153 }
2154
2155 /*
2156  * Flick the switch that says if blinking things should be shown or hidden.
2157  */
2158
2159 void term_blink(int flg)
2160 {
2161     static long last_blink = 0;
2162     static long last_tblink = 0;
2163     long now, blink_diff;
2164
2165     now = GetTickCount();
2166     blink_diff = now - last_tblink;
2167
2168     /* Make sure the text blinks no more than 2Hz */
2169     if (blink_diff < 0 || blink_diff > 450) {
2170         last_tblink = now;
2171         tblinker = !tblinker;
2172     }
2173
2174     if (flg) {
2175         blinker = 1;
2176         last_blink = now;
2177         return;
2178     }
2179
2180     blink_diff = now - last_blink;
2181
2182     /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2183     if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2184         return;
2185
2186     last_blink = now;
2187     blinker = !blinker;
2188 }
2189
2190 /*
2191  * Invalidate the whole screen so it will be repainted in full.
2192  */
2193 void term_invalidate(void)
2194 {
2195     int i;
2196
2197     for (i = 0; i < rows * (cols + 1); i++)
2198         disptext[i] = ATTR_INVALID;
2199 }
2200
2201 /*
2202  * Paint the window in response to a WM_PAINT message.
2203  */
2204 void term_paint(Context ctx, int l, int t, int r, int b)
2205 {
2206     int i, j, left, top, right, bottom;
2207
2208     left = l / font_width;
2209     right = (r - 1) / font_width;
2210     top = t / font_height;
2211     bottom = (b - 1) / font_height;
2212     for (i = top; i <= bottom && i < rows; i++) {
2213         if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2214             for (j = left; j <= right && j < cols; j++)
2215                 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2216         else
2217             for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2218                 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2219     }
2220
2221     /* This should happen soon enough, also for some reason it sometimes 
2222      * fails to actually do anything when re-sizing ... painting the wrong
2223      * window perhaps ?
2224      do_paint (ctx, FALSE);
2225      */
2226 }
2227
2228 /*
2229  * Attempt to scroll the scrollback. The second parameter gives the
2230  * position we want to scroll to; the first is +1 to denote that
2231  * this position is relative to the beginning of the scrollback, -1
2232  * to denote it is relative to the end, and 0 to denote that it is
2233  * relative to the current position.
2234  */
2235 void term_scroll(int rel, int where)
2236 {
2237     int sbtop = -count234(scrollback);
2238
2239     disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2240     if (disptop < sbtop)
2241         disptop = sbtop;
2242     if (disptop > 0)
2243         disptop = 0;
2244     update_sbar();
2245     term_update();
2246 }
2247
2248 static void clipme(pos top, pos bottom, char *workbuf)
2249 {
2250     char *wbptr;                       /* where next char goes within workbuf */
2251     int wblen = 0;                     /* workbuf len */
2252     int buflen;                        /* amount of memory allocated to workbuf */
2253
2254     if (workbuf != NULL) {             /* user supplied buffer? */
2255         buflen = -1;                   /* assume buffer passed in is big enough */
2256         wbptr = workbuf;               /* start filling here */
2257     } else
2258         buflen = 0;                    /* No data is available yet */
2259
2260     while (poslt(top, bottom)) {
2261         int nl = FALSE;
2262         unsigned long *ldata = lineptr(top.y);
2263         pos nlpos;
2264
2265         nlpos.y = top.y;
2266         nlpos.x = cols;
2267
2268         if (!(ldata[cols] & ATTR_WRAPPED)) {
2269             while ((ldata[nlpos.x - 1] & CHAR_MASK) == 0x20
2270                    && poslt(top, nlpos)) decpos(nlpos);
2271             if (poslt(nlpos, bottom))
2272                 nl = TRUE;
2273         }
2274         while (poslt(top, bottom) && poslt(top, nlpos)) {
2275             int ch = (ldata[top.x] & CHAR_MASK);
2276             int set = (ldata[top.x] & CSET_MASK);
2277
2278             /* VT Specials -> ISO8859-1 for Cut&Paste */
2279             static const unsigned char poorman2[] =
2280                 "* # HTFFCRLF\xB0 \xB1 NLVT+ + + + + - - - - - + + + + | <=>=PI!=\xA3 \xB7 ";
2281
2282             if (set && !cfg.rawcnp) {
2283                 if (set == ATTR_LINEDRW && ch >= 0x60 && ch < 0x7F) {
2284                     int x;
2285                     if ((x = poorman2[2 * (ch - 0x60) + 1]) == ' ')
2286                         x = 0;
2287                     ch = (x << 8) + poorman2[2 * (ch - 0x60)];
2288                 }
2289             }
2290
2291             while (ch != 0) {
2292                 if (cfg.rawcnp || !!(ch & 0xE0)) {
2293                     if (wblen == buflen) {
2294                         workbuf = srealloc(workbuf, buflen += 100);
2295                         wbptr = workbuf + wblen;
2296                     }
2297                     wblen++;
2298                     *wbptr++ = (unsigned char) ch;
2299                 }
2300                 ch >>= 8;
2301             }
2302             top.x++;
2303         }
2304         if (nl) {
2305             int i;
2306             for (i = 0; i < sizeof(sel_nl); i++) {
2307                 if (wblen == buflen) {
2308                     workbuf = srealloc(workbuf, buflen += 100);
2309                     wbptr = workbuf + wblen;
2310                 }
2311                 wblen++;
2312                 *wbptr++ = sel_nl[i];
2313             }
2314         }
2315         top.y++;
2316         top.x = 0;
2317     }
2318     write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2319     if (buflen > 0)                    /* indicates we allocated this buffer */
2320         sfree(workbuf);
2321
2322 }
2323 void term_copyall(void)
2324 {
2325     pos top;
2326     top.y = -count234(scrollback);
2327     top.x = 0;
2328     clipme(top, curs, NULL /* dynamic allocation */ );
2329 }
2330
2331 /*
2332  * Spread the selection outwards according to the selection mode.
2333  */
2334 static pos sel_spread_half(pos p, int dir)
2335 {
2336     unsigned long *ldata;
2337     short wvalue;
2338
2339     ldata = lineptr(p.y);
2340
2341     switch (selmode) {
2342       case SM_CHAR:
2343         /*
2344          * In this mode, every character is a separate unit, except
2345          * for runs of spaces at the end of a non-wrapping line.
2346          */
2347         if (!(ldata[cols] & ATTR_WRAPPED)) {
2348             unsigned long *q = ldata + cols;
2349             while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2350                 q--;
2351             if (q == ldata + cols)
2352                 q--;
2353             if (p.x >= q - ldata)
2354                 p.x = (dir == -1 ? q - ldata : cols - 1);
2355         }
2356         break;
2357       case SM_WORD:
2358         /*
2359          * In this mode, the units are maximal runs of characters
2360          * whose `wordness' has the same value.
2361          */
2362         wvalue = wordness[ldata[p.x] & CHAR_MASK];
2363         if (dir == +1) {
2364             while (p.x < cols
2365                    && wordness[ldata[p.x + 1] & CHAR_MASK] ==
2366                    wvalue) p.x++;
2367         } else {
2368             while (p.x > 0
2369                    && wordness[ldata[p.x - 1] & CHAR_MASK] ==
2370                    wvalue) p.x--;
2371         }
2372         break;
2373       case SM_LINE:
2374         /*
2375          * In this mode, every line is a unit.
2376          */
2377         p.x = (dir == -1 ? 0 : cols - 1);
2378         break;
2379     }
2380     return p;
2381 }
2382
2383 static void sel_spread(void)
2384 {
2385     selstart = sel_spread_half(selstart, -1);
2386     decpos(selend);
2387     selend = sel_spread_half(selend, +1);
2388     incpos(selend);
2389 }
2390
2391 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
2392                 int shift, int ctrl)
2393 {
2394     pos selpoint;
2395     unsigned long *ldata;
2396
2397     if (y < 0)
2398         y = 0;
2399     if (y >= rows)
2400         y = rows - 1;
2401     if (x < 0) {
2402         if (y > 0) {
2403             x = cols - 1;
2404             y--;
2405         } else
2406             x = 0;
2407     }
2408     if (x >= cols)
2409         x = cols - 1;
2410
2411     selpoint.y = y + disptop;
2412     selpoint.x = x;
2413     ldata = lineptr(selpoint.y);
2414     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
2415         selpoint.x /= 2;
2416
2417     if (xterm_mouse) {
2418         int encstate = 0, r, c;
2419         char abuf[16];
2420         static int is_down = 0;
2421
2422         switch (b) {
2423           case MBT_LEFT:
2424             encstate = 0x20;           /* left button down */
2425             break;
2426           case MBT_MIDDLE:
2427             encstate = 0x21;
2428             break;
2429           case MBT_RIGHT:
2430             encstate = 0x22;
2431             break;
2432           case MBT_WHEEL_UP:
2433             encstate = 0x60;
2434             break;
2435           case MBT_WHEEL_DOWN:
2436             encstate = 0x61;
2437             break;
2438         }
2439         switch (a) {
2440           case MA_DRAG:
2441             if (xterm_mouse == 1)
2442                 return;
2443             encstate += 0x20;
2444             break;
2445           case MA_RELEASE:
2446             encstate = 0x23;
2447             is_down = 0;
2448             break;
2449           case MA_CLICK:
2450             if (is_down == b)
2451                 return;
2452             is_down = b;
2453             break;
2454         }
2455         if (shift)
2456             encstate += 0x04;
2457         if (ctrl)
2458             encstate += 0x10;
2459         r = y + 33;
2460         c = x + 33;
2461
2462         sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
2463         ldisc_send(abuf, 6);
2464         return;
2465     }
2466
2467     b = translate_button(b);
2468
2469     if (b == MBT_SELECT && a == MA_CLICK) {
2470         deselect();
2471         selstate = ABOUT_TO;
2472         selanchor = selpoint;
2473         selmode = SM_CHAR;
2474     } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
2475         deselect();
2476         selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
2477         selstate = DRAGGING;
2478         selstart = selanchor = selpoint;
2479         selend = selstart;
2480         incpos(selend);
2481         sel_spread();
2482     } else if ((b == MBT_SELECT && a == MA_DRAG) ||
2483                (b == MBT_EXTEND && a != MA_RELEASE)) {
2484         if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
2485             return;
2486         if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
2487             if (posdiff(selpoint, selstart) <
2488                 posdiff(selend, selstart) / 2) {
2489                 selanchor = selend;
2490                 decpos(selanchor);
2491             } else {
2492                 selanchor = selstart;
2493             }
2494             selstate = DRAGGING;
2495         }
2496         if (selstate != ABOUT_TO && selstate != DRAGGING)
2497             selanchor = selpoint;
2498         selstate = DRAGGING;
2499         if (poslt(selpoint, selanchor)) {
2500             selstart = selpoint;
2501             selend = selanchor;
2502             incpos(selend);
2503         } else {
2504             selstart = selanchor;
2505             selend = selpoint;
2506             incpos(selend);
2507         }
2508         sel_spread();
2509     } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
2510         if (selstate == DRAGGING) {
2511             /*
2512              * We've completed a selection. We now transfer the
2513              * data to the clipboard.
2514              */
2515             clipme(selstart, selend, selspace);
2516             selstate = SELECTED;
2517         } else
2518             selstate = NO_SELECTION;
2519     } else if (b == MBT_PASTE
2520                && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
2521         char *data;
2522         int len;
2523
2524         get_clip((void **) &data, &len);
2525         if (data) {
2526             char *p, *q;
2527
2528             if (paste_buffer)
2529                 sfree(paste_buffer);
2530             paste_pos = paste_hold = paste_len = 0;
2531             paste_buffer = smalloc(len);
2532
2533             p = q = data;
2534             while (p < data + len) {
2535                 while (p < data + len &&
2536                        !(p <= data + len - sizeof(sel_nl) &&
2537                          !memcmp(p, sel_nl, sizeof(sel_nl))))
2538                     p++;
2539
2540                 {
2541                     int i;
2542                     unsigned char c;
2543                     for (i = 0; i < p - q; i++) {
2544                         c = xlat_kbd2tty(q[i]);
2545                         paste_buffer[paste_len++] = c;
2546                     }
2547                 }
2548
2549                 if (p <= data + len - sizeof(sel_nl) &&
2550                     !memcmp(p, sel_nl, sizeof(sel_nl))) {
2551                     paste_buffer[paste_len++] = '\r';
2552                     p += sizeof(sel_nl);
2553                 }
2554                 q = p;
2555             }
2556
2557             /* Assume a small paste will be OK in one go. */
2558             if (paste_len < 256) {
2559                 ldisc_send(paste_buffer, paste_len);
2560                 if (paste_buffer)
2561                     sfree(paste_buffer);
2562                 paste_buffer = 0;
2563                 paste_pos = paste_hold = paste_len = 0;
2564             }
2565         }
2566         get_clip(NULL, NULL);
2567     }
2568
2569     term_update();
2570 }
2571
2572 void term_nopaste()
2573 {
2574     if (paste_len == 0)
2575         return;
2576     sfree(paste_buffer);
2577     paste_buffer = 0;
2578     paste_len = 0;
2579 }
2580
2581 void term_paste()
2582 {
2583     static long last_paste = 0;
2584     long now, paste_diff;
2585
2586     if (paste_len == 0)
2587         return;
2588
2589     /* Don't wait forever to paste */
2590     if (paste_hold) {
2591         now = GetTickCount();
2592         paste_diff = now - last_paste;
2593         if (paste_diff >= 0 && paste_diff < 450)
2594             return;
2595     }
2596     paste_hold = 0;
2597
2598     while (paste_pos < paste_len) {
2599         int n = 0;
2600         while (n + paste_pos < paste_len) {
2601             if (paste_buffer[paste_pos + n++] == '\r')
2602                 break;
2603         }
2604         ldisc_send(paste_buffer + paste_pos, n);
2605         paste_pos += n;
2606
2607         if (paste_pos < paste_len) {
2608             paste_hold = 1;
2609             return;
2610         }
2611     }
2612     sfree(paste_buffer);
2613     paste_buffer = 0;
2614     paste_len = 0;
2615 }
2616
2617 static void deselect(void)
2618 {
2619     selstate = NO_SELECTION;
2620     selstart.x = selstart.y = selend.x = selend.y = 0;
2621 }
2622
2623 void term_deselect(void)
2624 {
2625     deselect();
2626     term_update();
2627 }
2628
2629 int term_ldisc(int option)
2630 {
2631     if (option == LD_ECHO)
2632         return term_echoing;
2633     if (option == LD_EDIT)
2634         return term_editing;
2635     return FALSE;
2636 }
2637
2638 /*
2639  * from_backend(), to get data from the backend for the terminal.
2640  */
2641 void from_backend(int is_stderr, char *data, int len)
2642 {
2643     while (len--) {
2644         if (inbuf_head >= INBUF_SIZE)
2645             term_out();
2646         inbuf[inbuf_head++] = *data++;
2647     }
2648 }
2649
2650 /*
2651  * Log session traffic.
2652  */
2653 void logtraffic(unsigned char c, int logmode)
2654 {
2655     if (cfg.logtype > 0) {
2656         if (cfg.logtype == logmode) {
2657             /* deferred open file from pgm start? */
2658             if (!lgfp)
2659                 logfopen();
2660             if (lgfp)
2661                 fputc(c, lgfp);
2662         }
2663     }
2664 }
2665
2666 /* open log file append/overwrite mode */
2667 void logfopen(void)
2668 {
2669     char buf[256];
2670     time_t t;
2671     struct tm *tm;
2672     char writemod[4];
2673
2674     if (!cfg.logtype)
2675         return;
2676     sprintf(writemod, "wb");           /* default to rewrite */
2677     lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
2678     if (lgfp) {
2679         int i;
2680         fclose(lgfp);
2681         i = askappend(cfg.logfilename);
2682         if (i == 1)
2683             writemod[0] = 'a';         /* set append mode */
2684         else if (i == 0) {             /* cancelled */
2685             lgfp = NULL;
2686             cfg.logtype = 0;           /* disable logging */
2687             return;
2688         }
2689     }
2690
2691     lgfp = fopen(cfg.logfilename, writemod);
2692     if (lgfp) {                        /* enter into event log */
2693         sprintf(buf, "%s session log (%s mode) to file : ",
2694                 (writemod[0] == 'a') ? "Appending" : "Writing new",
2695                 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
2696                  cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
2697         /* Make sure we do not exceed the output buffer size */
2698         strncat(buf, cfg.logfilename, 128);
2699         buf[strlen(buf)] = '\0';
2700         logevent(buf);
2701
2702         /* --- write header line iinto log file */
2703         fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
2704         time(&t);
2705         tm = localtime(&t);
2706         strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
2707         fputs(buf, lgfp);
2708         fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
2709     }
2710 }
2711
2712 void logfclose(void)
2713 {
2714     if (lgfp) {
2715         fclose(lgfp);
2716         lgfp = NULL;
2717     }
2718 }