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