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