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