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