]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - terminal.c
Extensive changes that _should_ fix the socket buffering problems,
[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     for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
938         c = inbuf[inbuf_reap];
939
940         /*
941          * Optionally log the session traffic to a file. Useful for
942          * debugging and possibly also useful for actual logging.
943          */
944         logtraffic((unsigned char) c, LGTYP_DEBUG);
945
946         /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
947          * be able to display 8-bit characters, but I'll let that go 'cause
948          * of i18n.
949          */
950
951         /* First see about all those translations. */
952         if (termstate == TOPLEVEL) {
953             if (in_utf)
954                 switch (utf_state) {
955                   case 0:
956                     if (c < 0x80) {
957                         /* UTF-8 must be stateless so we ignore iso2022. */
958                         if (unitab_ctrl[c] != 0xFF) 
959                              c = unitab_ctrl[c];
960                         else c = ((unsigned char)c) | ATTR_ASCII;
961                         break;
962                     } else if ((c & 0xe0) == 0xc0) {
963                         utf_size = utf_state = 1;
964                         utf_char = (c & 0x1f);
965                     } else if ((c & 0xf0) == 0xe0) {
966                         utf_size = utf_state = 2;
967                         utf_char = (c & 0x0f);
968                     } else if ((c & 0xf8) == 0xf0) {
969                         utf_size = utf_state = 3;
970                         utf_char = (c & 0x07);
971                     } else if ((c & 0xfc) == 0xf8) {
972                         utf_size = utf_state = 4;
973                         utf_char = (c & 0x03);
974                     } else if ((c & 0xfe) == 0xfc) {
975                         utf_size = utf_state = 5;
976                         utf_char = (c & 0x01);
977                     } else {
978                         c = UCSERR;
979                         break;
980                     }
981                     continue;
982                   case 1:
983                   case 2:
984                   case 3:
985                   case 4:
986                   case 5:
987                     if ((c & 0xC0) != 0x80) {
988                         inbuf_reap--;  /* This causes the faulting character */
989                         c = UCSERR;    /* to be logged twice - not really a */
990                         utf_state = 0; /* serious problem. */
991                         break;
992                     }
993                     utf_char = (utf_char << 6) | (c & 0x3f);
994                     if (--utf_state)
995                         continue;
996
997                     c = utf_char;
998
999                     /* Is somebody trying to be evil! */
1000                     if (c < 0x80 ||
1001                         (c < 0x800 && utf_size >= 2) ||
1002                         (c < 0x10000 && utf_size >= 3) ||
1003                         (c < 0x200000 && utf_size >= 4) ||
1004                         (c < 0x4000000 && utf_size >= 5))
1005                         c = UCSERR;
1006
1007                     /* Unicode line separator and paragraph separator are CR-LF */
1008                     if (c == 0x2028 || c == 0x2029)
1009                         c = 0x85;
1010
1011                     /* High controls are probably a Baaad idea too. */
1012                     if (c < 0xA0)
1013                         c = 0xFFFD;
1014
1015                     /* The UTF-16 surrogates are not nice either. */
1016                     /*       The standard give the option of decoding these: 
1017                      *       I don't want to! */
1018                     if (c >= 0xD800 && c < 0xE000)
1019                         c = UCSERR;
1020
1021                     /* ISO 10646 characters now limited to UTF-16 range. */
1022                     if (c > 0x10FFFF)
1023                         c = UCSERR;
1024
1025                     /* This is currently a TagPhobic application.. */
1026                     if (c >= 0xE0000 && c <= 0xE007F)
1027                         continue;
1028
1029                     /* U+FEFF is best seen as a null. */
1030                     if (c == 0xFEFF)
1031                         continue;
1032                     /* But U+FFFE is an error. */
1033                     if (c == 0xFFFE || c == 0xFFFF)
1034                         c = UCSERR;
1035
1036                     /* Oops this is a 16bit implementation */
1037                     if (c >= 0x10000)
1038                         c = 0xFFFD;
1039                     break;
1040             }
1041             /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1042             else if(sco_acs && 
1043                     (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1044             {
1045                if (sco_acs == 2) c ^= 0x80;
1046                c |= ATTR_SCOACS;
1047             } else {
1048                 switch (cset_attr[cset]) {
1049                     /* 
1050                      * Linedraw characters are different from 'ESC ( B'
1051                      * only for a small range. For ones outside that
1052                      * range, make sure we use the same font as well as
1053                      * the same encoding.
1054                      */
1055                   case ATTR_LINEDRW:
1056                     if (unitab_ctrl[c] != 0xFF)
1057                         c = unitab_ctrl[c];
1058                     else
1059                         c = ((unsigned char) c) | ATTR_LINEDRW;
1060                     break;
1061
1062                   case ATTR_GBCHR:
1063                     /* If UK-ASCII, make the '#' a LineDraw Pound */
1064                     if (c == '#') {
1065                         c = '}' | ATTR_LINEDRW;
1066                         break;
1067                     }
1068                   /*FALLTHROUGH*/ case ATTR_ASCII:
1069                     if (unitab_ctrl[c] != 0xFF)
1070                         c = unitab_ctrl[c];
1071                     else
1072                         c = ((unsigned char) c) | ATTR_ASCII;
1073                     break;
1074                 case ATTR_SCOACS:
1075                     if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1076                     break;
1077                 }
1078             }
1079         }
1080
1081         /* How about C1 controls ? */
1082         if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1083             has_compat(VT220)) {
1084             termstate = SEEN_ESC;
1085             esc_query = FALSE;
1086             c = '@' + (c & 0x1F);
1087         }
1088
1089         /* Or the GL control. */
1090         if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1091             if (curs.x && !wrapnext)
1092                 curs.x--;
1093             wrapnext = FALSE;
1094             fix_cpos;
1095             *cpos = (' ' | curr_attr | ATTR_ASCII);
1096         } else
1097             /* Or normal C0 controls. */
1098         if ((c & -32) == 0 && termstate < DO_CTRLS) {
1099             switch (c) {
1100               case '\005':             /* terminal type query */
1101                 /* Strictly speaking this is VT100 but a VT100 defaults to
1102                  * no response. Other terminals respond at their option.
1103                  *
1104                  * Don't put a CR in the default string as this tends to
1105                  * upset some weird software.
1106                  *
1107                  * An xterm returns "xterm" (5 characters)
1108                  */
1109                 compatibility(ANSIMIN);
1110                 {
1111                     char abuf[256], *s, *d;
1112                     int state = 0;
1113                     for (s = cfg.answerback, d = abuf; *s; s++) {
1114                         if (state) {
1115                             if (*s >= 'a' && *s <= 'z')
1116                                 *d++ = (*s - ('a' - 1));
1117                             else if ((*s >= '@' && *s <= '_') ||
1118                                      *s == '?' || (*s & 0x80))
1119                                 *d++ = ('@' ^ *s);
1120                             else if (*s == '~')
1121                                 *d++ = '^';
1122                             state = 0;
1123                         } else if (*s == '^') {
1124                             state = 1;
1125                         } else
1126                             *d++ = *s;
1127                     }
1128                     lpage_send(CP_ACP, abuf, d - abuf);
1129                 }
1130                 break;
1131               case '\007':
1132                 {
1133                     struct beeptime *newbeep;
1134                     long ticks;
1135
1136                     ticks = GetTickCount();
1137
1138                     if (!beep_overloaded) {
1139                         newbeep = smalloc(sizeof(struct beeptime));
1140                         newbeep->ticks = ticks;
1141                         newbeep->next = NULL;
1142                         if (!beephead)
1143                             beephead = newbeep;
1144                         else
1145                             beeptail->next = newbeep;
1146                         beeptail = newbeep;
1147                         nbeeps++;
1148                     }
1149
1150                     /*
1151                      * Throw out any beeps that happened more than
1152                      * t seconds ago.
1153                      */
1154                     while (beephead &&
1155                            beephead->ticks < ticks - cfg.bellovl_t) {
1156                         struct beeptime *tmp = beephead;
1157                         beephead = tmp->next;
1158                         sfree(tmp);
1159                         if (!beephead)
1160                             beeptail = NULL;
1161                         nbeeps--;
1162                     }
1163
1164                     if (cfg.bellovl && beep_overloaded &&
1165                         ticks - lastbeep >= cfg.bellovl_s) {
1166                         /*
1167                          * If we're currently overloaded and the
1168                          * last beep was more than s seconds ago,
1169                          * leave overload mode.
1170                          */
1171                         beep_overloaded = FALSE;
1172                     } else if (cfg.bellovl && !beep_overloaded &&
1173                                nbeeps >= cfg.bellovl_n) {
1174                         /*
1175                          * Now, if we have n or more beeps
1176                          * remaining in the queue, go into overload
1177                          * mode.
1178                          */
1179                         beep_overloaded = TRUE;
1180                     }
1181                     lastbeep = ticks;
1182
1183                     /*
1184                      * Perform an actual beep if we're not overloaded.
1185                      */
1186                     if (!cfg.bellovl || !beep_overloaded) {
1187                         beep(cfg.beep);
1188                         if (cfg.beep == BELL_VISUAL) {
1189                             in_vbell = TRUE;
1190                             vbell_timeout = ticks + VBELL_TIMEOUT;
1191                             term_update();
1192                         }
1193                     }
1194                     disptop = 0;
1195                 }
1196                 break;
1197               case '\b':
1198                 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1199                 else if (curs.x == 0 && curs.y > 0)
1200                     curs.x = cols - 1, curs.y--;
1201                 else if (wrapnext)
1202                     wrapnext = FALSE;
1203                 else
1204                     curs.x--;
1205                 fix_cpos;
1206                 seen_disp_event = TRUE;
1207                 break;
1208               case '\016':
1209                 compatibility(VT100);
1210                 cset = 1;
1211                 break;
1212               case '\017':
1213                 compatibility(VT100);
1214                 cset = 0;
1215                 break;
1216               case '\033':
1217                 if (vt52_mode)
1218                     termstate = VT52_ESC;
1219                 else {
1220                     compatibility(ANSIMIN);
1221                     termstate = SEEN_ESC;
1222                     esc_query = FALSE;
1223                 }
1224                 break;
1225               case '\r':
1226                 curs.x = 0;
1227                 wrapnext = FALSE;
1228                 fix_cpos;
1229                 seen_disp_event = TRUE;
1230                 paste_hold = 0;
1231                 logtraffic((unsigned char) c, LGTYP_ASCII);
1232                 break;
1233               case '\014':
1234                 if (has_compat(SCOANSI)) {
1235                     move(0, 0, 0);
1236                     erase_lots(FALSE, FALSE, TRUE);
1237                     disptop = 0;
1238                     wrapnext = FALSE;
1239                     seen_disp_event = 1;
1240                     break;
1241                 }
1242               case '\013':
1243                 compatibility(VT100);
1244               case '\n':
1245                 if (curs.y == marg_b)
1246                     scroll(marg_t, marg_b, 1, TRUE);
1247                 else if (curs.y < rows - 1)
1248                     curs.y++;
1249                 if (cfg.lfhascr)
1250                     curs.x = 0;
1251                 fix_cpos;
1252                 wrapnext = FALSE;
1253                 seen_disp_event = 1;
1254                 paste_hold = 0;
1255                 logtraffic((unsigned char) c, LGTYP_ASCII);
1256                 break;
1257               case '\t':
1258                 {
1259                     pos old_curs = curs;
1260                     unsigned long *ldata = lineptr(curs.y);
1261
1262                     do {
1263                         curs.x++;
1264                     } while (curs.x < cols - 1 && !tabs[curs.x]);
1265
1266                     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1267                         if (curs.x >= cols / 2)
1268                             curs.x = cols / 2 - 1;
1269                     } else {
1270                         if (curs.x >= cols)
1271                             curs.x = cols - 1;
1272                     }
1273
1274                     fix_cpos;
1275                     check_selection(old_curs, curs);
1276                 }
1277                 seen_disp_event = TRUE;
1278                 break;
1279             }
1280         } else
1281             switch (termstate) {
1282               case TOPLEVEL:
1283                 /* Only graphic characters get this far, ctrls are stripped above */
1284                 if (wrapnext && wrap) {
1285                     cpos[1] |= LATTR_WRAPPED;
1286                     if (curs.y == marg_b)
1287                         scroll(marg_t, marg_b, 1, TRUE);
1288                     else if (curs.y < rows - 1)
1289                         curs.y++;
1290                     curs.x = 0;
1291                     fix_cpos;
1292                     wrapnext = FALSE;
1293                 }
1294                 if (insert)
1295                     insch(1);
1296                 if (selstate != NO_SELECTION) {
1297                     pos cursplus = curs;
1298                     incpos(cursplus);
1299                     check_selection(curs, cursplus);
1300                 }
1301                 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1302                     logtraffic((unsigned char) c, LGTYP_ASCII);
1303                 {
1304                     extern int wcwidth(wchar_t ucs);
1305                     int width = 0;
1306                     if (DIRECT_CHAR(c))
1307                         width = 1;
1308                     if (!width)
1309                         width = wcwidth((wchar_t) c);
1310                     switch (width) {
1311                       case 2:
1312                         if (curs.x + 1 != cols) {
1313                             *cpos++ = c | ATTR_WIDE | curr_attr;
1314                             *cpos++ = UCSWIDE | curr_attr;
1315                             curs.x++;
1316                             break;
1317                         }
1318                       case 1:
1319                         *cpos++ = c | curr_attr;
1320                         break;
1321                       default:
1322                         continue;
1323                     }
1324                 }
1325                 curs.x++;
1326                 if (curs.x == cols) {
1327                     cpos--;
1328                     curs.x--;
1329                     wrapnext = TRUE;
1330                     if (wrap && vt52_mode) {
1331                         cpos[1] |= LATTR_WRAPPED;
1332                         if (curs.y == marg_b)
1333                             scroll(marg_t, marg_b, 1, TRUE);
1334                         else if (curs.y < rows - 1)
1335                             curs.y++;
1336                         curs.x = 0;
1337                         fix_cpos;
1338                         wrapnext = FALSE;
1339                     }
1340                 }
1341                 seen_disp_event = 1;
1342                 break;
1343
1344               case OSC_MAYBE_ST:
1345                 /*
1346                  * This state is virtually identical to SEEN_ESC, with the
1347                  * exception that we have an OSC sequence in the pipeline,
1348                  * and _if_ we see a backslash, we process it.
1349                  */
1350                 if (c == '\\') {
1351                     do_osc();
1352                     termstate = TOPLEVEL;
1353                     break;
1354                 }
1355                 /* else fall through */
1356               case SEEN_ESC:
1357                 if (c >= ' ' && c <= '/') {
1358                     if (esc_query)
1359                         esc_query = -1;
1360                     else
1361                         esc_query = c;
1362                     break;
1363                 }
1364                 termstate = TOPLEVEL;
1365                 switch (ANSI(c, esc_query)) {
1366                   case '[':            /* enter CSI mode */
1367                     termstate = SEEN_CSI;
1368                     esc_nargs = 1;
1369                     esc_args[0] = ARG_DEFAULT;
1370                     esc_query = FALSE;
1371                     break;
1372                   case ']':            /* xterm escape sequences */
1373                     /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1374                     compatibility(OTHER);
1375                     termstate = SEEN_OSC;
1376                     esc_args[0] = 0;
1377                     break;
1378                   case '7':            /* save cursor */
1379                     compatibility(VT100);
1380                     save_cursor(TRUE);
1381                     break;
1382                   case '8':            /* restore cursor */
1383                     compatibility(VT100);
1384                     save_cursor(FALSE);
1385                     seen_disp_event = TRUE;
1386                     break;
1387                   case '=':
1388                     compatibility(VT100);
1389                     app_keypad_keys = TRUE;
1390                     break;
1391                   case '>':
1392                     compatibility(VT100);
1393                     app_keypad_keys = FALSE;
1394                     break;
1395                   case 'D':            /* exactly equivalent to LF */
1396                     compatibility(VT100);
1397                     if (curs.y == marg_b)
1398                         scroll(marg_t, marg_b, 1, TRUE);
1399                     else if (curs.y < rows - 1)
1400                         curs.y++;
1401                     fix_cpos;
1402                     wrapnext = FALSE;
1403                     seen_disp_event = TRUE;
1404                     break;
1405                   case 'E':            /* exactly equivalent to CR-LF */
1406                     compatibility(VT100);
1407                     curs.x = 0;
1408                     if (curs.y == marg_b)
1409                         scroll(marg_t, marg_b, 1, TRUE);
1410                     else if (curs.y < rows - 1)
1411                         curs.y++;
1412                     fix_cpos;
1413                     wrapnext = FALSE;
1414                     seen_disp_event = TRUE;
1415                     break;
1416                   case 'M':            /* reverse index - backwards LF */
1417                     compatibility(VT100);
1418                     if (curs.y == marg_t)
1419                         scroll(marg_t, marg_b, -1, TRUE);
1420                     else if (curs.y > 0)
1421                         curs.y--;
1422                     fix_cpos;
1423                     wrapnext = FALSE;
1424                     seen_disp_event = TRUE;
1425                     break;
1426                   case 'Z':            /* terminal type query */
1427                     compatibility(VT100);
1428                     ldisc_send(id_string, strlen(id_string));
1429                     break;
1430                   case 'c':            /* restore power-on settings */
1431                     compatibility(VT100);
1432                     power_on();
1433                     if (reset_132) {
1434                         request_resize(80, rows, 1);
1435                         reset_132 = 0;
1436                     }
1437                     fix_cpos;
1438                     disptop = 0;
1439                     seen_disp_event = TRUE;
1440                     break;
1441                   case 'H':            /* set a tab */
1442                     compatibility(VT100);
1443                     tabs[curs.x] = TRUE;
1444                     break;
1445
1446                   case ANSI('8', '#'):  /* ESC # 8 fills screen with Es :-) */
1447                     compatibility(VT100);
1448                     {
1449                         unsigned long *ldata;
1450                         int i, j;
1451                         pos scrtop, scrbot;
1452
1453                         for (i = 0; i < rows; i++) {
1454                             ldata = lineptr(i);
1455                             for (j = 0; j < cols; j++)
1456                                 ldata[j] = ATTR_DEFAULT | 'E';
1457                             ldata[cols] = 0;
1458                         }
1459                         disptop = 0;
1460                         seen_disp_event = TRUE;
1461                         scrtop.x = scrtop.y = 0;
1462                         scrbot.x = 0;
1463                         scrbot.y = rows;
1464                         check_selection(scrtop, scrbot);
1465                     }
1466                     break;
1467
1468                   case ANSI('3', '#'):
1469                   case ANSI('4', '#'):
1470                   case ANSI('5', '#'):
1471                   case ANSI('6', '#'):
1472                     compatibility(VT100);
1473                     {
1474                         unsigned long nlattr;
1475                         unsigned long *ldata;
1476                         switch (ANSI(c, esc_query)) {
1477                           case ANSI('3', '#'):
1478                             nlattr = LATTR_TOP;
1479                             break;
1480                           case ANSI('4', '#'):
1481                             nlattr = LATTR_BOT;
1482                             break;
1483                           case ANSI('5', '#'):
1484                             nlattr = LATTR_NORM;
1485                             break;
1486                           default: /* spiritually case ANSI('6', '#'): */
1487                             nlattr = LATTR_WIDE;
1488                             break;
1489                         }
1490                         ldata = lineptr(curs.y);
1491                         ldata[cols] &= ~LATTR_MODE;
1492                         ldata[cols] |= nlattr;
1493                     }
1494                     break;
1495
1496                   case ANSI('A', '('):
1497                     compatibility(VT100);
1498                     cset_attr[0] = ATTR_GBCHR;
1499                     break;
1500                   case ANSI('B', '('):
1501                     compatibility(VT100);
1502                     cset_attr[0] = ATTR_ASCII;
1503                     break;
1504                   case ANSI('0', '('):
1505                     compatibility(VT100);
1506                     cset_attr[0] = ATTR_LINEDRW;
1507                     break;
1508                   case ANSI('U', '('): 
1509                     compatibility(OTHER);
1510                     cset_attr[0] = ATTR_SCOACS; 
1511                     break;
1512
1513                   case ANSI('A', ')'):
1514                     compatibility(VT100);
1515                     cset_attr[1] = ATTR_GBCHR;
1516                     break;
1517                   case ANSI('B', ')'):
1518                     compatibility(VT100);
1519                     cset_attr[1] = ATTR_ASCII;
1520                     break;
1521                   case ANSI('0', ')'):
1522                     compatibility(VT100);
1523                     cset_attr[1] = ATTR_LINEDRW;
1524                     break;
1525                   case ANSI('U', ')'): 
1526                     compatibility(OTHER);
1527                     cset_attr[1] = ATTR_SCOACS; 
1528                     break;
1529
1530                   case ANSI('8', '%'):  /* Old Linux code */
1531                   case ANSI('G', '%'):
1532                     compatibility(OTHER);
1533                     utf = 1;
1534                     break;
1535                   case ANSI('@', '%'):
1536                     compatibility(OTHER);
1537                     utf = 0;
1538                     break;
1539                 }
1540                 break;
1541               case SEEN_CSI:
1542                 termstate = TOPLEVEL;  /* default */
1543                 if (isdigit(c)) {
1544                     if (esc_nargs <= ARGS_MAX) {
1545                         if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1546                             esc_args[esc_nargs - 1] = 0;
1547                         esc_args[esc_nargs - 1] =
1548                             10 * esc_args[esc_nargs - 1] + c - '0';
1549                     }
1550                     termstate = SEEN_CSI;
1551                 } else if (c == ';') {
1552                     if (++esc_nargs <= ARGS_MAX)
1553                         esc_args[esc_nargs - 1] = ARG_DEFAULT;
1554                     termstate = SEEN_CSI;
1555                 } else if (c < '@') {
1556                     if (esc_query)
1557                         esc_query = -1;
1558                     else if (c == '?')
1559                         esc_query = TRUE;
1560                     else
1561                         esc_query = c;
1562                     termstate = SEEN_CSI;
1563                 } else
1564                     switch (ANSI(c, esc_query)) {
1565                       case 'A':       /* move up N lines */
1566                         move(curs.x, curs.y - def(esc_args[0], 1), 1);
1567                         seen_disp_event = TRUE;
1568                         break;
1569                       case 'e':       /* move down N lines */
1570                         compatibility(ANSI);
1571                         /* FALLTHROUGH */
1572                       case 'B':
1573                         move(curs.x, curs.y + def(esc_args[0], 1), 1);
1574                         seen_disp_event = TRUE;
1575                         break;
1576                       case ANSI('c', '>'):      /* report xterm version */
1577                         compatibility(OTHER);
1578                         /* this reports xterm version 136 so that VIM can
1579                            use the drag messages from the mouse reporting */
1580                         ldisc_send("\033[>0;136;0c", 11);
1581                         break;
1582                       case 'a':       /* move right N cols */
1583                         compatibility(ANSI);
1584                         /* FALLTHROUGH */
1585                       case 'C':
1586                         move(curs.x + def(esc_args[0], 1), curs.y, 1);
1587                         seen_disp_event = TRUE;
1588                         break;
1589                       case 'D':       /* move left N cols */
1590                         move(curs.x - def(esc_args[0], 1), curs.y, 1);
1591                         seen_disp_event = TRUE;
1592                         break;
1593                       case 'E':       /* move down N lines and CR */
1594                         compatibility(ANSI);
1595                         move(0, curs.y + def(esc_args[0], 1), 1);
1596                         seen_disp_event = TRUE;
1597                         break;
1598                       case 'F':       /* move up N lines and CR */
1599                         compatibility(ANSI);
1600                         move(0, curs.y - def(esc_args[0], 1), 1);
1601                         seen_disp_event = TRUE;
1602                         break;
1603                       case 'G':
1604                       case '`':       /* set horizontal posn */
1605                         compatibility(ANSI);
1606                         move(def(esc_args[0], 1) - 1, curs.y, 0);
1607                         seen_disp_event = TRUE;
1608                         break;
1609                       case 'd':       /* set vertical posn */
1610                         compatibility(ANSI);
1611                         move(curs.x,
1612                              (dec_om ? marg_t : 0) + def(esc_args[0],
1613                                                          1) - 1,
1614                              (dec_om ? 2 : 0));
1615                         seen_disp_event = TRUE;
1616                         break;
1617                       case 'H':
1618                       case 'f':       /* set horz and vert posns at once */
1619                         if (esc_nargs < 2)
1620                             esc_args[1] = ARG_DEFAULT;
1621                         move(def(esc_args[1], 1) - 1,
1622                              (dec_om ? marg_t : 0) + def(esc_args[0],
1623                                                          1) - 1,
1624                              (dec_om ? 2 : 0));
1625                         seen_disp_event = TRUE;
1626                         break;
1627                       case 'J':       /* erase screen or parts of it */
1628                         {
1629                             unsigned int i = def(esc_args[0], 0) + 1;
1630                             if (i > 3)
1631                                 i = 0;
1632                             erase_lots(FALSE, !!(i & 2), !!(i & 1));
1633                         }
1634                         disptop = 0;
1635                         seen_disp_event = TRUE;
1636                         break;
1637                       case 'K':       /* erase line or parts of it */
1638                         {
1639                             unsigned int i = def(esc_args[0], 0) + 1;
1640                             if (i > 3)
1641                                 i = 0;
1642                             erase_lots(TRUE, !!(i & 2), !!(i & 1));
1643                         }
1644                         seen_disp_event = TRUE;
1645                         break;
1646                       case 'L':       /* insert lines */
1647                         compatibility(VT102);
1648                         if (curs.y <= marg_b)
1649                             scroll(curs.y, marg_b, -def(esc_args[0], 1),
1650                                    FALSE);
1651                         fix_cpos;
1652                         seen_disp_event = TRUE;
1653                         break;
1654                       case 'M':       /* delete lines */
1655                         compatibility(VT102);
1656                         if (curs.y <= marg_b)
1657                             scroll(curs.y, marg_b, def(esc_args[0], 1),
1658                                    TRUE);
1659                         fix_cpos;
1660                         seen_disp_event = TRUE;
1661                         break;
1662                       case '@':       /* insert chars */
1663                         /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1664                         compatibility(VT102);
1665                         insch(def(esc_args[0], 1));
1666                         seen_disp_event = TRUE;
1667                         break;
1668                       case 'P':       /* delete chars */
1669                         compatibility(VT102);
1670                         insch(-def(esc_args[0], 1));
1671                         seen_disp_event = TRUE;
1672                         break;
1673                       case 'c':       /* terminal type query */
1674                         compatibility(VT100);
1675                         /* This is the response for a VT102 */
1676                         ldisc_send(id_string, strlen(id_string));
1677                         break;
1678                       case 'n':       /* cursor position query */
1679                         if (esc_args[0] == 6) {
1680                             char buf[32];
1681                             sprintf(buf, "\033[%d;%dR", curs.y + 1,
1682                                     curs.x + 1);
1683                             ldisc_send(buf, strlen(buf));
1684                         } else if (esc_args[0] == 5) {
1685                             ldisc_send("\033[0n", 4);
1686                         }
1687                         break;
1688                       case 'h':       /* toggle modes to high */
1689                       case ANSI_QUE('h'):
1690                         compatibility(VT100);
1691                         {
1692                             int i;
1693                             for (i = 0; i < esc_nargs; i++)
1694                                 toggle_mode(esc_args[i], esc_query, TRUE);
1695                         }
1696                         break;
1697                       case 'l':       /* toggle modes to low */
1698                       case ANSI_QUE('l'):
1699                         compatibility(VT100);
1700                         {
1701                             int i;
1702                             for (i = 0; i < esc_nargs; i++)
1703                                 toggle_mode(esc_args[i], esc_query, FALSE);
1704                         }
1705                         break;
1706                       case 'g':       /* clear tabs */
1707                         compatibility(VT100);
1708                         if (esc_nargs == 1) {
1709                             if (esc_args[0] == 0) {
1710                                 tabs[curs.x] = FALSE;
1711                             } else if (esc_args[0] == 3) {
1712                                 int i;
1713                                 for (i = 0; i < cols; i++)
1714                                     tabs[i] = FALSE;
1715                             }
1716                         }
1717                         break;
1718                       case 'r':       /* set scroll margins */
1719                         compatibility(VT100);
1720                         if (esc_nargs <= 2) {
1721                             int top, bot;
1722                             top = def(esc_args[0], 1) - 1;
1723                             bot = (esc_nargs <= 1
1724                                    || esc_args[1] ==
1725                                    0 ? rows : def(esc_args[1], rows)) - 1;
1726                             if (bot >= rows)
1727                                 bot = rows - 1;
1728                             /* VTTEST Bug 9 - if region is less than 2 lines
1729                              * don't change region.
1730                              */
1731                             if (bot - top > 0) {
1732                                 marg_t = top;
1733                                 marg_b = bot;
1734                                 curs.x = 0;
1735                                 /*
1736                                  * I used to think the cursor should be
1737                                  * placed at the top of the newly marginned
1738                                  * area. Apparently not: VMS TPU falls over
1739                                  * if so.
1740                                  *
1741                                  * Well actually it should for Origin mode - RDB
1742                                  */
1743                                 curs.y = (dec_om ? marg_t : 0);
1744                                 fix_cpos;
1745                                 seen_disp_event = TRUE;
1746                             }
1747                         }
1748                         break;
1749                       case 'm':       /* set graphics rendition */
1750                         {
1751                             /* 
1752                              * A VT100 without the AVO only had one attribute, either
1753                              * underline or reverse video depending on the cursor type,
1754                              * this was selected by CSI 7m.
1755                              *
1756                              * case 2:
1757                              *  This is sometimes DIM, eg on the GIGI and Linux
1758                              * case 8:
1759                              *  This is sometimes INVIS various ANSI.
1760                              * case 21:
1761                              *  This like 22 disables BOLD, DIM and INVIS
1762                              *
1763                              * The ANSI colours appear on any terminal that has colour
1764                              * (obviously) but the interaction between sgr0 and the
1765                              * colours varies but is usually related to the background
1766                              * colour erase item.
1767                              * The interaction between colour attributes and the mono
1768                              * ones is also very implementation dependent.
1769                              *
1770                              * The 39 and 49 attributes are likely to be unimplemented.
1771                              */
1772                             int i;
1773                             for (i = 0; i < esc_nargs; i++) {
1774                                 switch (def(esc_args[i], 0)) {
1775                                   case 0:       /* restore defaults */
1776                                     curr_attr = ATTR_DEFAULT;
1777                                     break;
1778                                   case 1:       /* enable bold */
1779                                     compatibility(VT100AVO);
1780                                     curr_attr |= ATTR_BOLD;
1781                                     break;
1782                                   case 21:      /* (enable double underline) */
1783                                     compatibility(OTHER);
1784                                   case 4:       /* enable underline */
1785                                     compatibility(VT100AVO);
1786                                     curr_attr |= ATTR_UNDER;
1787                                     break;
1788                                   case 5:       /* enable blink */
1789                                     compatibility(VT100AVO);
1790                                     curr_attr |= ATTR_BLINK;
1791                                     break;
1792                                   case 7:       /* enable reverse video */
1793                                     curr_attr |= ATTR_REVERSE;
1794                                     break;
1795                                   case 10:      /* SCO acs off */
1796                                     compatibility(SCOANSI);
1797                                     sco_acs = 0; break;
1798                                   case 11:      /* SCO acs on */
1799                                     compatibility(SCOANSI);
1800                                     sco_acs = 1; break;
1801                                   case 12:      /* SCO acs on flipped */
1802                                     compatibility(SCOANSI);
1803                                     sco_acs = 2; break;
1804                                   case 22:      /* disable bold */
1805                                     compatibility2(OTHER, VT220);
1806                                     curr_attr &= ~ATTR_BOLD;
1807                                     break;
1808                                   case 24:      /* disable underline */
1809                                     compatibility2(OTHER, VT220);
1810                                     curr_attr &= ~ATTR_UNDER;
1811                                     break;
1812                                   case 25:      /* disable blink */
1813                                     compatibility2(OTHER, VT220);
1814                                     curr_attr &= ~ATTR_BLINK;
1815                                     break;
1816                                   case 27:      /* disable reverse video */
1817                                     compatibility2(OTHER, VT220);
1818                                     curr_attr &= ~ATTR_REVERSE;
1819                                     break;
1820                                   case 30:
1821                                   case 31:
1822                                   case 32:
1823                                   case 33:
1824                                   case 34:
1825                                   case 35:
1826                                   case 36:
1827                                   case 37:
1828                                     /* foreground */
1829                                     curr_attr &= ~ATTR_FGMASK;
1830                                     curr_attr |=
1831                                         (esc_args[i] - 30) << ATTR_FGSHIFT;
1832                                     break;
1833                                   case 39:      /* default-foreground */
1834                                     curr_attr &= ~ATTR_FGMASK;
1835                                     curr_attr |= ATTR_DEFFG;
1836                                     break;
1837                                   case 40:
1838                                   case 41:
1839                                   case 42:
1840                                   case 43:
1841                                   case 44:
1842                                   case 45:
1843                                   case 46:
1844                                   case 47:
1845                                     /* background */
1846                                     curr_attr &= ~ATTR_BGMASK;
1847                                     curr_attr |=
1848                                         (esc_args[i] - 40) << ATTR_BGSHIFT;
1849                                     break;
1850                                   case 49:      /* default-background */
1851                                     curr_attr &= ~ATTR_BGMASK;
1852                                     curr_attr |= ATTR_DEFBG;
1853                                     break;
1854                                 }
1855                             }
1856                             if (use_bce)
1857                                 erase_char = (' ' | ATTR_ASCII |
1858                                              (curr_attr & 
1859                                               (ATTR_FGMASK | ATTR_BGMASK)));
1860                         }
1861                         break;
1862                       case 's':       /* save cursor */
1863                         save_cursor(TRUE);
1864                         break;
1865                       case 'u':       /* restore cursor */
1866                         save_cursor(FALSE);
1867                         seen_disp_event = TRUE;
1868                         break;
1869                       case 't':       /* set page size - ie window height */
1870                         /*
1871                          * VT340/VT420 sequence DECSLPP, DEC only allows values
1872                          *  24/25/36/48/72/144 other emulators (eg dtterm) use
1873                          * illegal values (eg first arg 1..9) for window changing 
1874                          * and reports.
1875                          */
1876                         compatibility(VT340TEXT);
1877                         if (esc_nargs <= 1
1878                             && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1879                             request_resize(cols, def(esc_args[0], 24), 0);
1880                             deselect();
1881                         }
1882                         break;
1883                       case 'S':
1884                         compatibility(SCOANSI);
1885                         scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1886                         fix_cpos;
1887                         wrapnext = FALSE;
1888                         seen_disp_event = TRUE;
1889                         break;
1890                       case 'T':
1891                         compatibility(SCOANSI);
1892                         scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1893                         fix_cpos;
1894                         wrapnext = FALSE;
1895                         seen_disp_event = TRUE;
1896                         break;
1897                       case ANSI('|', '*'):
1898                         /* VT420 sequence DECSNLS
1899                          * Set number of lines on screen
1900                          * VT420 uses VGA like hardware and can support any size in
1901                          * reasonable range (24..49 AIUI) with no default specified.
1902                          */
1903                         compatibility(VT420);
1904                         if (esc_nargs == 1 && esc_args[0] > 0) {
1905                             request_resize(cols,
1906                                            def(esc_args[0], cfg.height),
1907                                            0);
1908                             deselect();
1909                         }
1910                         break;
1911                       case ANSI('|', '$'):
1912                         /* VT340/VT420 sequence DECSCPP
1913                          * Set number of columns per page
1914                          * Docs imply range is only 80 or 132, but I'll allow any.
1915                          */
1916                         compatibility(VT340TEXT);
1917                         if (esc_nargs <= 1) {
1918                             request_resize(def(esc_args[0], cfg.width),
1919                                            rows, 0);
1920                             deselect();
1921                         }
1922                         break;
1923                       case 'X':       /* write N spaces w/o moving cursor */
1924                         /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1925                         compatibility(ANSIMIN);
1926                         {
1927                             int n = def(esc_args[0], 1);
1928                             pos cursplus;
1929                             unsigned long *p = cpos;
1930                             if (n > cols - curs.x)
1931                                 n = cols - curs.x;
1932                             cursplus = curs;
1933                             cursplus.x += n;
1934                             check_selection(curs, cursplus);
1935                             while (n--)
1936                                 *p++ = erase_char;
1937                             seen_disp_event = TRUE;
1938                         }
1939                         break;
1940                       case 'x':       /* report terminal characteristics */
1941                         compatibility(VT100);
1942                         {
1943                             char buf[32];
1944                             int i = def(esc_args[0], 0);
1945                             if (i == 0 || i == 1) {
1946                                 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1947                                 buf[2] += i;
1948                                 ldisc_send(buf, 20);
1949                             }
1950                         }
1951                         break;
1952                       case 'Z':         /* BackTab for xterm */
1953                         compatibility(OTHER);
1954                         {
1955                             int i = def(esc_args[0], 1);
1956                             pos old_curs = curs;
1957
1958                             for(;i>0 && curs.x>0; i--) {
1959                                 do {
1960                                     curs.x--;
1961                                 } while (curs.x >0 && !tabs[curs.x]);
1962                             }
1963                             fix_cpos;
1964                             check_selection(old_curs, curs);
1965                         }
1966                         break;
1967                       case ANSI('L', '='):
1968                         compatibility(OTHER);
1969                         use_bce = (esc_args[0] <= 0);
1970                         erase_char = ERASE_CHAR;
1971                         if (use_bce)
1972                             erase_char = (' ' | ATTR_ASCII |
1973                                          (curr_attr & 
1974                                           (ATTR_FGMASK | ATTR_BGMASK)));
1975                         break;
1976                       case ANSI('E', '='):
1977                         compatibility(OTHER);
1978                         blink_is_real = (esc_args[0] >= 1);
1979                         break;
1980                       case ANSI('p', '"'):
1981                         /* Allow the host to make this emulator a 'perfect' VT102.
1982                          * This first appeared in the VT220, but we do need to get 
1983                          * back to PuTTY mode so I won't check it.
1984                          *
1985                          * The arg in 40..42,50 are a PuTTY extension.
1986                          * The 2nd arg, 8bit vs 7bit is not checked.
1987                          *
1988                          * Setting VT102 mode should also change the Fkeys to
1989                          * generate PF* codes as a real VT102 has no Fkeys.
1990                          * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1991                          * send nothing.
1992                          *
1993                          * Note ESC c will NOT change this!
1994                          */
1995
1996                         switch (esc_args[0]) {
1997                           case 61:
1998                             compatibility_level &= ~TM_VTXXX;
1999                             compatibility_level |= TM_VT102;
2000                             break;
2001                           case 62:
2002                             compatibility_level &= ~TM_VTXXX;
2003                             compatibility_level |= TM_VT220;
2004                             break;
2005
2006                           default:
2007                             if (esc_args[0] > 60 && esc_args[0] < 70)
2008                                 compatibility_level |= TM_VTXXX;
2009                             break;
2010
2011                           case 40:
2012                             compatibility_level &= TM_VTXXX;
2013                             break;
2014                           case 41:
2015                             compatibility_level = TM_PUTTY;
2016                             break;
2017                           case 42:
2018                             compatibility_level = TM_SCOANSI;
2019                             break;
2020
2021                           case ARG_DEFAULT:
2022                             compatibility_level = TM_PUTTY;
2023                             break;
2024                           case 50:
2025                             break;
2026                         }
2027
2028                         /* Change the response to CSI c */
2029                         if (esc_args[0] == 50) {
2030                             int i;
2031                             char lbuf[64];
2032                             strcpy(id_string, "\033[?");
2033                             for (i = 1; i < esc_nargs; i++) {
2034                                 if (i != 1)
2035                                     strcat(id_string, ";");
2036                                 sprintf(lbuf, "%d", esc_args[i]);
2037                                 strcat(id_string, lbuf);
2038                             }
2039                             strcat(id_string, "c");
2040                         }
2041 #if 0
2042                         /* Is this a good idea ? 
2043                          * Well we should do a soft reset at this point ...
2044                          */
2045                         if (!has_compat(VT420) && has_compat(VT100)) {
2046                             if (reset_132)
2047                                 request_resize(132, 24, 1);
2048                             else
2049                                 request_resize(80, 24, 1);
2050                         }
2051 #endif
2052                         break;
2053                     }
2054                 break;
2055               case SEEN_OSC:
2056                 osc_w = FALSE;
2057                 switch (c) {
2058                   case 'P':            /* Linux palette sequence */
2059                     termstate = SEEN_OSC_P;
2060                     osc_strlen = 0;
2061                     break;
2062                   case 'R':            /* Linux palette reset */
2063                     palette_reset();
2064                     term_invalidate();
2065                     termstate = TOPLEVEL;
2066                     break;
2067                   case 'W':            /* word-set */
2068                     termstate = SEEN_OSC_W;
2069                     osc_w = TRUE;
2070                     break;
2071                   case '0':
2072                   case '1':
2073                   case '2':
2074                   case '3':
2075                   case '4':
2076                   case '5':
2077                   case '6':
2078                   case '7':
2079                   case '8':
2080                   case '9':
2081                     esc_args[0] = 10 * esc_args[0] + c - '0';
2082                     break;
2083                   case 'L':
2084                     /*
2085                      * Grotty hack to support xterm and DECterm title
2086                      * sequences concurrently.
2087                      */
2088                     if (esc_args[0] == 2) {
2089                         esc_args[0] = 1;
2090                         break;
2091                     }
2092                     /* else fall through */
2093                   default:
2094                     termstate = OSC_STRING;
2095                     osc_strlen = 0;
2096                 }
2097                 break;
2098               case OSC_STRING:
2099                 /*
2100                  * This OSC stuff is EVIL. It takes just one character to get into
2101                  * sysline mode and it's not initially obvious how to get out.
2102                  * So I've added CR and LF as string aborts.
2103                  * This shouldn't effect compatibility as I believe embedded 
2104                  * control characters are supposed to be interpreted (maybe?) 
2105                  * and they don't display anything useful anyway.
2106                  *
2107                  * -- RDB
2108                  */
2109                 if (c == '\n' || c == '\r') {
2110                     termstate = TOPLEVEL;
2111                 } else if (c == 0234 || c == '\007') {
2112                     /*
2113                      * These characters terminate the string; ST and BEL
2114                      * terminate the sequence and trigger instant
2115                      * processing of it, whereas ESC goes back to SEEN_ESC
2116                      * mode unless it is followed by \, in which case it is
2117                      * synonymous with ST in the first place.
2118                      */
2119                     do_osc();
2120                     termstate = TOPLEVEL;
2121                 } else if (c == '\033')
2122                     termstate = OSC_MAYBE_ST;
2123                 else if (osc_strlen < OSC_STR_MAX)
2124                     osc_string[osc_strlen++] = c;
2125                 break;
2126               case SEEN_OSC_P:
2127                 {
2128                     int max = (osc_strlen == 0 ? 21 : 16);
2129                     int val;
2130                     if (c >= '0' && c <= '9')
2131                         val = c - '0';
2132                     else if (c >= 'A' && c <= 'A' + max - 10)
2133                         val = c - 'A' + 10;
2134                     else if (c >= 'a' && c <= 'a' + max - 10)
2135                         val = c - 'a' + 10;
2136                     else {
2137                         termstate = TOPLEVEL;
2138                         break;
2139                     }
2140                     osc_string[osc_strlen++] = val;
2141                     if (osc_strlen >= 7) {
2142                         palette_set(osc_string[0],
2143                                     osc_string[1] * 16 + osc_string[2],
2144                                     osc_string[3] * 16 + osc_string[4],
2145                                     osc_string[5] * 16 + osc_string[6]);
2146                         term_invalidate();
2147                         termstate = TOPLEVEL;
2148                     }
2149                 }
2150                 break;
2151               case SEEN_OSC_W:
2152                 switch (c) {
2153                   case '0':
2154                   case '1':
2155                   case '2':
2156                   case '3':
2157                   case '4':
2158                   case '5':
2159                   case '6':
2160                   case '7':
2161                   case '8':
2162                   case '9':
2163                     esc_args[0] = 10 * esc_args[0] + c - '0';
2164                     break;
2165                   default:
2166                     termstate = OSC_STRING;
2167                     osc_strlen = 0;
2168                 }
2169                 break;
2170               case VT52_ESC:
2171                 termstate = TOPLEVEL;
2172                 seen_disp_event = TRUE;
2173                 switch (c) {
2174                   case 'A':
2175                     move(curs.x, curs.y - 1, 1);
2176                     break;
2177                   case 'B':
2178                     move(curs.x, curs.y + 1, 1);
2179                     break;
2180                   case 'C':
2181                     move(curs.x + 1, curs.y, 1);
2182                     break;
2183                   case 'D':
2184                     move(curs.x - 1, curs.y, 1);
2185                     break;
2186                     /*
2187                      * From the VT100 Manual
2188                      * NOTE: The special graphics characters in the VT100
2189                      *       are different from those in the VT52
2190                      *
2191                      * From VT102 manual:
2192                      *       137 _  Blank             - Same
2193                      *       140 `  Reserved          - Humm.
2194                      *       141 a  Solid rectangle   - Similar
2195                      *       142 b  1/                - Top half of fraction for the
2196                      *       143 c  3/                - subscript numbers below.
2197                      *       144 d  5/
2198                      *       145 e  7/
2199                      *       146 f  Degrees           - Same
2200                      *       147 g  Plus or minus     - Same
2201                      *       150 h  Right arrow
2202                      *       151 i  Ellipsis (dots)
2203                      *       152 j  Divide by
2204                      *       153 k  Down arrow
2205                      *       154 l  Bar at scan 0
2206                      *       155 m  Bar at scan 1
2207                      *       156 n  Bar at scan 2
2208                      *       157 o  Bar at scan 3     - Similar
2209                      *       160 p  Bar at scan 4     - Similar
2210                      *       161 q  Bar at scan 5     - Similar
2211                      *       162 r  Bar at scan 6     - Same
2212                      *       163 s  Bar at scan 7     - Similar
2213                      *       164 t  Subscript 0
2214                      *       165 u  Subscript 1
2215                      *       166 v  Subscript 2
2216                      *       167 w  Subscript 3
2217                      *       170 x  Subscript 4
2218                      *       171 y  Subscript 5
2219                      *       172 z  Subscript 6
2220                      *       173 {  Subscript 7
2221                      *       174 |  Subscript 8
2222                      *       175 }  Subscript 9
2223                      *       176 ~  Paragraph
2224                      *
2225                      */
2226                   case 'F':
2227                     cset_attr[cset = 0] = ATTR_LINEDRW;
2228                     break;
2229                   case 'G':
2230                     cset_attr[cset = 0] = ATTR_ASCII;
2231                     break;
2232                   case 'H':
2233                     move(0, 0, 0);
2234                     break;
2235                   case 'I':
2236                     if (curs.y == 0)
2237                         scroll(0, rows - 1, -1, TRUE);
2238                     else if (curs.y > 0)
2239                         curs.y--;
2240                     fix_cpos;
2241                     wrapnext = FALSE;
2242                     break;
2243                   case 'J':
2244                     erase_lots(FALSE, FALSE, TRUE);
2245                     disptop = 0;
2246                     break;
2247                   case 'K':
2248                     erase_lots(TRUE, FALSE, TRUE);
2249                     break;
2250 #if 0
2251                   case 'V':
2252                     /* XXX Print cursor line */
2253                     break;
2254                   case 'W':
2255                     /* XXX Start controller mode */
2256                     break;
2257                   case 'X':
2258                     /* XXX Stop controller mode */
2259                     break;
2260 #endif
2261                   case 'Y':
2262                     termstate = VT52_Y1;
2263                     break;
2264                   case 'Z':
2265                     ldisc_send("\033/Z", 3);
2266                     break;
2267                   case '=':
2268                     app_keypad_keys = TRUE;
2269                     break;
2270                   case '>':
2271                     app_keypad_keys = FALSE;
2272                     break;
2273                   case '<':
2274                     /* XXX This should switch to VT100 mode not current or default
2275                      *     VT mode. But this will only have effect in a VT220+
2276                      *     emulation.
2277                      */
2278                     vt52_mode = FALSE;
2279                     blink_is_real = cfg.blinktext;
2280                     break;
2281 #if 0
2282                   case '^':
2283                     /* XXX Enter auto print mode */
2284                     break;
2285                   case '_':
2286                     /* XXX Exit auto print mode */
2287                     break;
2288                   case ']':
2289                     /* XXX Print screen */
2290                     break;
2291 #endif
2292
2293 #ifdef VT52_PLUS
2294                   case 'E':
2295                     /* compatibility(ATARI) */
2296                     move(0, 0, 0);
2297                     erase_lots(FALSE, FALSE, TRUE);
2298                     disptop = 0;
2299                     break;
2300                   case 'L':
2301                     /* compatibility(ATARI) */
2302                     if (curs.y <= marg_b)
2303                         scroll(curs.y, marg_b, -1, FALSE);
2304                     break;
2305                   case 'M':
2306                     /* compatibility(ATARI) */
2307                     if (curs.y <= marg_b)
2308                         scroll(curs.y, marg_b, 1, TRUE);
2309                     break;
2310                   case 'b':
2311                     /* compatibility(ATARI) */
2312                     termstate = VT52_FG;
2313                     break;
2314                   case 'c':
2315                     /* compatibility(ATARI) */
2316                     termstate = VT52_BG;
2317                     break;
2318                   case 'd':
2319                     /* compatibility(ATARI) */
2320                     erase_lots(FALSE, TRUE, FALSE);
2321                     disptop = 0;
2322                     break;
2323                   case 'e':
2324                     /* compatibility(ATARI) */
2325                     cursor_on = TRUE;
2326                     break;
2327                   case 'f':
2328                     /* compatibility(ATARI) */
2329                     cursor_on = FALSE;
2330                     break;
2331                     /* case 'j': Save cursor position - broken on ST */
2332                     /* case 'k': Restore cursor position */
2333                   case 'l':
2334                     /* compatibility(ATARI) */
2335                     erase_lots(TRUE, TRUE, TRUE);
2336                     curs.x = 0;
2337                     wrapnext = FALSE;
2338                     fix_cpos;
2339                     break;
2340                   case 'o':
2341                     /* compatibility(ATARI) */
2342                     erase_lots(TRUE, TRUE, FALSE);
2343                     break;
2344                   case 'p':
2345                     /* compatibility(ATARI) */
2346                     curr_attr |= ATTR_REVERSE;
2347                     break;
2348                   case 'q':
2349                     /* compatibility(ATARI) */
2350                     curr_attr &= ~ATTR_REVERSE;
2351                     break;
2352                   case 'v':            /* wrap Autowrap on - Wyse style */
2353                     /* compatibility(ATARI) */
2354                     wrap = 1;
2355                     break;
2356                   case 'w':            /* Autowrap off */
2357                     /* compatibility(ATARI) */
2358                     wrap = 0;
2359                     break;
2360
2361                   case 'R':
2362                     /* compatibility(OTHER) */
2363                     vt52_bold = FALSE;
2364                     curr_attr = ATTR_DEFAULT;
2365                     if (use_bce)
2366                         erase_char = (' ' | ATTR_ASCII |
2367                                      (curr_attr & 
2368                                       (ATTR_FGMASK | ATTR_BGMASK)));
2369                     break;
2370                   case 'S':
2371                     /* compatibility(VI50) */
2372                     curr_attr |= ATTR_UNDER;
2373                     break;
2374                   case 'W':
2375                     /* compatibility(VI50) */
2376                     curr_attr &= ~ATTR_UNDER;
2377                     break;
2378                   case 'U':
2379                     /* compatibility(VI50) */
2380                     vt52_bold = TRUE;
2381                     curr_attr |= ATTR_BOLD;
2382                     break;
2383                   case 'T':
2384                     /* compatibility(VI50) */
2385                     vt52_bold = FALSE;
2386                     curr_attr &= ~ATTR_BOLD;
2387                     break;
2388 #endif
2389                 }
2390                 break;
2391               case VT52_Y1:
2392                 termstate = VT52_Y2;
2393                 move(curs.x, c - ' ', 0);
2394                 break;
2395               case VT52_Y2:
2396                 termstate = TOPLEVEL;
2397                 move(c - ' ', curs.y, 0);
2398                 break;
2399
2400 #ifdef VT52_PLUS
2401               case VT52_FG:
2402                 termstate = TOPLEVEL;
2403                 curr_attr &= ~ATTR_FGMASK;
2404                 curr_attr &= ~ATTR_BOLD;
2405                 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2406                 if ((c & 0x8) || vt52_bold)
2407                     curr_attr |= ATTR_BOLD;
2408
2409                 if (use_bce)
2410                     erase_char = (' ' | ATTR_ASCII |
2411                                  (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2412                 break;
2413               case VT52_BG:
2414                 termstate = TOPLEVEL;
2415                 curr_attr &= ~ATTR_BGMASK;
2416                 curr_attr &= ~ATTR_BLINK;
2417                 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2418
2419                 /* Note: bold background */
2420                 if (c & 0x8)
2421                     curr_attr |= ATTR_BLINK;
2422
2423                 if (use_bce)
2424                     erase_char = (' ' | ATTR_ASCII |
2425                                  (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2426                 break;
2427 #endif
2428               default: break;          /* placate gcc warning about enum use */
2429             }
2430         if (selstate != NO_SELECTION) {
2431             pos cursplus = curs;
2432             incpos(cursplus);
2433             check_selection(curs, cursplus);
2434         }
2435     }
2436     inbuf_head = 0;
2437 }
2438
2439 #if 0
2440 /*
2441  * Compare two lines to determine whether they are sufficiently
2442  * alike to scroll-optimise one to the other. Return the degree of
2443  * similarity.
2444  */
2445 static int linecmp(unsigned long *a, unsigned long *b)
2446 {
2447     int i, n;
2448
2449     for (i = n = 0; i < cols; i++)
2450         n += (*a++ == *b++);
2451     return n;
2452 }
2453 #endif
2454
2455 /*
2456  * Given a context, update the window. Out of paranoia, we don't
2457  * allow WM_PAINT responses to do scrolling optimisations.
2458  */
2459 static void do_paint(Context ctx, int may_optimise)
2460 {
2461     int i, j, our_curs_y;
2462     unsigned long rv, cursor;
2463     pos scrpos;
2464     char ch[1024];
2465     long cursor_background = ERASE_CHAR;
2466     long ticks;
2467
2468     /*
2469      * Check the visual bell state.
2470      */
2471     if (in_vbell) {
2472         ticks = GetTickCount();
2473         if (ticks - vbell_timeout >= 0)
2474             in_vbell = FALSE;
2475     }
2476
2477     rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2478
2479     /* Depends on:
2480      * screen array, disptop, scrtop,
2481      * selection, rv, 
2482      * cfg.blinkpc, blink_is_real, tblinker, 
2483      * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2484      */
2485
2486     /* Has the cursor position or type changed ? */
2487     if (cursor_on) {
2488         if (has_focus) {
2489             if (blinker || !cfg.blink_cur)
2490                 cursor = TATTR_ACTCURS;
2491             else
2492                 cursor = 0;
2493         } else
2494             cursor = TATTR_PASCURS;
2495         if (wrapnext)
2496             cursor |= TATTR_RIGHTCURS;
2497     } else
2498         cursor = 0;
2499     our_curs_y = curs.y - disptop;
2500
2501     if (dispcurs && (curstype != cursor ||
2502                      dispcurs !=
2503                      disptext + our_curs_y * (cols + 1) + curs.x)) {
2504         if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2505             dispcurs[-1] |= ATTR_INVALID;
2506         if ((*dispcurs & ATTR_WIDE))
2507             dispcurs[1] |= ATTR_INVALID;
2508         *dispcurs |= ATTR_INVALID;
2509         curstype = 0;
2510     }
2511     dispcurs = NULL;
2512
2513     /* The normal screen data */
2514     for (i = 0; i < rows; i++) {
2515         unsigned long *ldata;
2516         int lattr;
2517         int idx, dirty_line, dirty_run;
2518         unsigned long attr = 0;
2519         int updated_line = 0;
2520         int start = 0;
2521         int ccount = 0;
2522         int last_run_dirty = 0;
2523
2524         scrpos.y = i + disptop;
2525         ldata = lineptr(scrpos.y);
2526         lattr = (ldata[cols] & LATTR_MODE);
2527
2528         idx = i * (cols + 1);
2529         dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2530         disptext[idx + cols] = ldata[cols];
2531
2532         for (j = 0; j < cols; j++, idx++) {
2533             unsigned long tattr, tchar;
2534             unsigned long *d = ldata + j;
2535             int break_run;
2536             scrpos.x = j;
2537
2538             tchar = (*d & (CHAR_MASK | CSET_MASK));
2539             tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2540             switch (tchar & CSET_MASK) {
2541               case ATTR_ASCII:
2542                 tchar = unitab_line[tchar & 0xFF];
2543                 break;
2544               case ATTR_LINEDRW:
2545                 tchar = unitab_xterm[tchar & 0xFF];
2546                 break;
2547               case ATTR_SCOACS:  
2548                 tchar = unitab_scoacs[tchar&0xFF]; 
2549                 break;
2550             }
2551             tattr |= (tchar & CSET_MASK);
2552             tchar &= CHAR_MASK;
2553
2554             /* Video reversing things */
2555             tattr = (tattr ^ rv
2556                      ^ (posle(selstart, scrpos) &&
2557                         poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2558
2559             /* 'Real' blinking ? */
2560             if (blink_is_real && (tattr & ATTR_BLINK)) {
2561                 if (has_focus && tblinker) {
2562                     tchar = ' ';
2563                     tattr &= ~CSET_MASK;
2564                     tattr |= ATTR_ACP;
2565                 }
2566                 tattr &= ~ATTR_BLINK;
2567             }
2568
2569             /* Cursor here ? Save the 'background' */
2570             if (i == our_curs_y && j == curs.x) {
2571                 cursor_background = tattr | tchar;
2572                 dispcurs = disptext + idx;
2573             }
2574
2575             if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2576                 dirty_line = TRUE;
2577
2578             break_run = (tattr != attr || j - start >= sizeof(ch));
2579
2580             /* Special hack for VT100 Linedraw glyphs */
2581             if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2582                 && tchar <= 0xBD) break_run = TRUE;
2583
2584             if (!dbcs_screenfont && !dirty_line) {
2585                 if ((tchar | tattr) == disptext[idx])
2586                     break_run = TRUE;
2587                 else if (!dirty_run && ccount == 1)
2588                     break_run = TRUE;
2589             }
2590
2591             if (break_run) {
2592                 if ((dirty_run || last_run_dirty) && ccount > 0) {
2593                     do_text(ctx, start, i, ch, ccount, attr, lattr);
2594                     updated_line = 1;
2595                 }
2596                 start = j;
2597                 ccount = 0;
2598                 attr = tattr;
2599                 if (dbcs_screenfont)
2600                     last_run_dirty = dirty_run;
2601                 dirty_run = dirty_line;
2602             }
2603
2604             if ((tchar | tattr) != disptext[idx])
2605                 dirty_run = TRUE;
2606             ch[ccount++] = (char) tchar;
2607             disptext[idx] = tchar | tattr;
2608
2609             /* If it's a wide char step along to the next one. */
2610             if (tattr & ATTR_WIDE) {
2611                 if (++j < cols) {
2612                     idx++;
2613                     d++;
2614                     /* Cursor is here ? Ouch! */
2615                     if (i == our_curs_y && j == curs.x) {
2616                         cursor_background = *d;
2617                         dispcurs = disptext + idx;
2618                     }
2619                     if (disptext[idx] != *d)
2620                         dirty_run = TRUE;
2621                     disptext[idx] = *d;
2622                 }
2623             }
2624         }
2625         if (dirty_run && ccount > 0) {
2626             do_text(ctx, start, i, ch, ccount, attr, lattr);
2627             updated_line = 1;
2628         }
2629
2630         /* Cursor on this line ? (and changed) */
2631         if (i == our_curs_y && (curstype != cursor || updated_line)) {
2632             ch[0] = (char) (cursor_background & CHAR_MASK);
2633             attr = (cursor_background & ATTR_MASK) | cursor;
2634             do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2635             curstype = cursor;
2636         }
2637     }
2638 }
2639
2640 /*
2641  * Flick the switch that says if blinking things should be shown or hidden.
2642  */
2643
2644 void term_blink(int flg)
2645 {
2646     static long last_blink = 0;
2647     static long last_tblink = 0;
2648     long now, blink_diff;
2649
2650     now = GetTickCount();
2651     blink_diff = now - last_tblink;
2652
2653     /* Make sure the text blinks no more than 2Hz */
2654     if (blink_diff < 0 || blink_diff > 450) {
2655         last_tblink = now;
2656         tblinker = !tblinker;
2657     }
2658
2659     if (flg) {
2660         blinker = 1;
2661         last_blink = now;
2662         return;
2663     }
2664
2665     blink_diff = now - last_blink;
2666
2667     /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2668     if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2669         return;
2670
2671     last_blink = now;
2672     blinker = !blinker;
2673 }
2674
2675 /*
2676  * Invalidate the whole screen so it will be repainted in full.
2677  */
2678 void term_invalidate(void)
2679 {
2680     int i;
2681
2682     for (i = 0; i < rows * (cols + 1); i++)
2683         disptext[i] = ATTR_INVALID;
2684 }
2685
2686 /*
2687  * Paint the window in response to a WM_PAINT message.
2688  */
2689 void term_paint(Context ctx, int l, int t, int r, int b)
2690 {
2691     int i, j, left, top, right, bottom;
2692
2693     left = l / font_width;
2694     right = (r - 1) / font_width;
2695     top = t / font_height;
2696     bottom = (b - 1) / font_height;
2697     for (i = top; i <= bottom && i < rows; i++) {
2698         if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2699             for (j = left; j <= right && j < cols; j++)
2700                 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2701         else
2702             for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2703                 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2704     }
2705
2706     /* This should happen soon enough, also for some reason it sometimes 
2707      * fails to actually do anything when re-sizing ... painting the wrong
2708      * window perhaps ?
2709      do_paint (ctx, FALSE);
2710      */
2711 }
2712
2713 /*
2714  * Attempt to scroll the scrollback. The second parameter gives the
2715  * position we want to scroll to; the first is +1 to denote that
2716  * this position is relative to the beginning of the scrollback, -1
2717  * to denote it is relative to the end, and 0 to denote that it is
2718  * relative to the current position.
2719  */
2720 void term_scroll(int rel, int where)
2721 {
2722     int sbtop = -count234(scrollback);
2723
2724     disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2725     if (disptop < sbtop)
2726         disptop = sbtop;
2727     if (disptop > 0)
2728         disptop = 0;
2729     update_sbar();
2730     term_update();
2731 }
2732
2733 static void clipme(pos top, pos bottom)
2734 {
2735     wchar_t *workbuf;
2736     wchar_t *wbptr;                    /* where next char goes within workbuf */
2737     int wblen = 0;                     /* workbuf len */
2738     int buflen;                        /* amount of memory allocated to workbuf */
2739
2740     buflen = 5120;                     /* Default size */
2741     workbuf = smalloc(buflen * sizeof(wchar_t));
2742     wbptr = workbuf;                   /* start filling here */
2743
2744     while (poslt(top, bottom)) {
2745         int nl = FALSE;
2746         unsigned long *ldata = lineptr(top.y);
2747         pos nlpos;
2748
2749         nlpos.y = top.y;
2750         nlpos.x = cols;
2751
2752         if (!(ldata[cols] & LATTR_WRAPPED)) {
2753             while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2754                     (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2755                      (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2756                    && poslt(top, nlpos))
2757                 decpos(nlpos);
2758             if (poslt(nlpos, bottom))
2759                 nl = TRUE;
2760         }
2761         while (poslt(top, bottom) && poslt(top, nlpos)) {
2762 #if 0
2763             char cbuf[16], *p;
2764             sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2765 #else
2766             wchar_t cbuf[16], *p;
2767             int uc = (ldata[top.x] & 0xFFFF);
2768             int set, c;
2769
2770             if (uc == UCSWIDE) {
2771                 top.x++;
2772                 continue;
2773             }
2774
2775             switch (uc & CSET_MASK) {
2776               case ATTR_LINEDRW:
2777                 if (!cfg.rawcnp) {
2778                     uc = unitab_xterm[uc & 0xFF];
2779                     break;
2780                 }
2781               case ATTR_ASCII:
2782                 uc = unitab_line[uc & 0xFF];
2783                 break;
2784               case ATTR_SCOACS:  
2785                 uc = unitab_scoacs[uc&0xFF]; 
2786                 break;
2787             }
2788             switch (uc & CSET_MASK) {
2789               case ATTR_ACP:
2790                 uc = unitab_font[uc & 0xFF];
2791                 break;
2792               case ATTR_OEMCP:
2793                 uc = unitab_oemcp[uc & 0xFF];
2794                 break;
2795             }
2796
2797             set = (uc & CSET_MASK);
2798             c = (uc & CHAR_MASK);
2799             cbuf[0] = uc;
2800             cbuf[1] = 0;
2801
2802             if (DIRECT_FONT(uc)) {
2803                 if (c >= ' ' && c != 0x7F) {
2804                     unsigned char buf[4];
2805                     WCHAR wbuf[4];
2806                     int rv;
2807                     if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2808                         buf[0] = c;
2809                         buf[1] = (unsigned char) ldata[top.x + 1];
2810                         rv = MultiByteToWideChar(font_codepage,
2811                                                  0, buf, 2, wbuf, 4);
2812                         top.x++;
2813                     } else {
2814                         buf[0] = c;
2815                         rv = MultiByteToWideChar(font_codepage,
2816                                                  0, buf, 1, wbuf, 4);
2817                     }
2818
2819                     if (rv > 0) {
2820                         memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2821                         cbuf[rv] = 0;
2822                     }
2823                 }
2824             }
2825 #endif
2826
2827             for (p = cbuf; *p; p++) {
2828                 /* Enough overhead for trailing NL and nul */
2829                 if (wblen >= buflen - 16) {
2830                     workbuf =
2831                         srealloc(workbuf,
2832                                  sizeof(wchar_t) * (buflen += 100));
2833                     wbptr = workbuf + wblen;
2834                 }
2835                 wblen++;
2836                 *wbptr++ = *p;
2837             }
2838             top.x++;
2839         }
2840         if (nl) {
2841             int i;
2842             for (i = 0; i < sel_nl_sz; i++) {
2843                 wblen++;
2844                 *wbptr++ = sel_nl[i];
2845             }
2846         }
2847         top.y++;
2848         top.x = 0;
2849     }
2850     wblen++;
2851     *wbptr++ = 0;
2852     write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2853     if (buflen > 0)                    /* indicates we allocated this buffer */
2854         sfree(workbuf);
2855 }
2856
2857 void term_copyall(void)
2858 {
2859     pos top;
2860     top.y = -count234(scrollback);
2861     top.x = 0;
2862     clipme(top, curs);
2863 }
2864
2865 /*
2866  * The wordness array is mainly for deciding the disposition of the US-ASCII 
2867  * characters.
2868  */
2869 static int wordtype(int uc)
2870 {
2871     static struct {
2872         int start, end, ctype;
2873     } *wptr, ucs_words[] = {
2874         {
2875         128, 160, 0}, {
2876         161, 191, 1}, {
2877         215, 215, 1}, {
2878         247, 247, 1}, {
2879         0x037e, 0x037e, 1},            /* Greek question mark */
2880         {
2881         0x0387, 0x0387, 1},            /* Greek ano teleia */
2882         {
2883         0x055a, 0x055f, 1},            /* Armenian punctuation */
2884         {
2885         0x0589, 0x0589, 1},            /* Armenian full stop */
2886         {
2887         0x0700, 0x070d, 1},            /* Syriac punctuation */
2888         {
2889         0x104a, 0x104f, 1},            /* Myanmar punctuation */
2890         {
2891         0x10fb, 0x10fb, 1},            /* Georgian punctuation */
2892         {
2893         0x1361, 0x1368, 1},            /* Ethiopic punctuation */
2894         {
2895         0x166d, 0x166e, 1},            /* Canadian Syl. punctuation */
2896         {
2897         0x17d4, 0x17dc, 1},            /* Khmer punctuation */
2898         {
2899         0x1800, 0x180a, 1},            /* Mongolian punctuation */
2900         {
2901         0x2000, 0x200a, 0},            /* Various spaces */
2902         {
2903         0x2070, 0x207f, 2},            /* superscript */
2904         {
2905         0x2080, 0x208f, 2},            /* subscript */
2906         {
2907         0x200b, 0x27ff, 1},            /* punctuation and symbols */
2908         {
2909         0x3000, 0x3000, 0},            /* ideographic space */
2910         {
2911         0x3001, 0x3020, 1},            /* ideographic punctuation */
2912         {
2913         0x303f, 0x309f, 3},            /* Hiragana */
2914         {
2915         0x30a0, 0x30ff, 3},            /* Katakana */
2916         {
2917         0x3300, 0x9fff, 3},            /* CJK Ideographs */
2918         {
2919         0xac00, 0xd7a3, 3},            /* Hangul Syllables */
2920         {
2921         0xf900, 0xfaff, 3},            /* CJK Ideographs */
2922         {
2923         0xfe30, 0xfe6b, 1},            /* punctuation forms */
2924         {
2925         0xff00, 0xff0f, 1},            /* half/fullwidth ASCII */
2926         {
2927         0xff1a, 0xff20, 1},            /* half/fullwidth ASCII */
2928         {
2929         0xff3b, 0xff40, 1},            /* half/fullwidth ASCII */
2930         {
2931         0xff5b, 0xff64, 1},            /* half/fullwidth ASCII */
2932         {
2933         0xfff0, 0xffff, 0},            /* half/fullwidth ASCII */
2934         {
2935         0, 0, 0}
2936     };
2937
2938     uc &= (CSET_MASK | CHAR_MASK);
2939
2940     switch (uc & CSET_MASK) {
2941       case ATTR_LINEDRW:
2942         uc = unitab_xterm[uc & 0xFF];
2943         break;
2944       case ATTR_ASCII:
2945         uc = unitab_line[uc & 0xFF];
2946         break;
2947       case ATTR_SCOACS:  
2948         uc = unitab_scoacs[uc&0xFF]; 
2949         break;
2950     }
2951     switch (uc & CSET_MASK) {
2952       case ATTR_ACP:
2953         uc = unitab_font[uc & 0xFF];
2954         break;
2955       case ATTR_OEMCP:
2956         uc = unitab_oemcp[uc & 0xFF];
2957         break;
2958     }
2959
2960     if (uc < 0x80)
2961         return wordness[uc];
2962
2963     for (wptr = ucs_words; wptr->start; wptr++) {
2964         if (uc >= wptr->start && uc <= wptr->end)
2965             return wptr->ctype;
2966     }
2967
2968     return 2;
2969 }
2970
2971 /*
2972  * Spread the selection outwards according to the selection mode.
2973  */
2974 static pos sel_spread_half(pos p, int dir)
2975 {
2976     unsigned long *ldata;
2977     short wvalue;
2978
2979     ldata = lineptr(p.y);
2980
2981     switch (selmode) {
2982       case SM_CHAR:
2983         /*
2984          * In this mode, every character is a separate unit, except
2985          * for runs of spaces at the end of a non-wrapping line.
2986          */
2987         if (!(ldata[cols] & LATTR_WRAPPED)) {
2988             unsigned long *q = ldata + cols;
2989             while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2990                 q--;
2991             if (q == ldata + cols)
2992                 q--;
2993             if (p.x >= q - ldata)
2994                 p.x = (dir == -1 ? q - ldata : cols - 1);
2995         }
2996         break;
2997       case SM_WORD:
2998         /*
2999          * In this mode, the units are maximal runs of characters
3000          * whose `wordness' has the same value.
3001          */
3002         wvalue = wordtype(ldata[p.x]);
3003         if (dir == +1) {
3004             while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
3005                 p.x++;
3006         } else {
3007             while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
3008                 p.x--;
3009         }
3010         break;
3011       case SM_LINE:
3012         /*
3013          * In this mode, every line is a unit.
3014          */
3015         p.x = (dir == -1 ? 0 : cols - 1);
3016         break;
3017     }
3018     return p;
3019 }
3020
3021 static void sel_spread(void)
3022 {
3023     selstart = sel_spread_half(selstart, -1);
3024     decpos(selend);
3025     selend = sel_spread_half(selend, +1);
3026     incpos(selend);
3027 }
3028
3029 void term_do_paste(void)
3030 {
3031     wchar_t *data;
3032     int len;
3033
3034     get_clip(&data, &len);
3035     if (data) {
3036         wchar_t *p, *q;
3037
3038         if (paste_buffer)
3039             sfree(paste_buffer);
3040         paste_pos = paste_hold = paste_len = 0;
3041         paste_buffer = smalloc(len * sizeof(wchar_t));
3042
3043         p = q = data;
3044         while (p < data + len) {
3045             while (p < data + len &&
3046                    !(p <= data + len - sel_nl_sz &&
3047                      !memcmp(p, sel_nl, sizeof(sel_nl))))
3048                 p++;
3049
3050             {
3051                 int i;
3052                 for (i = 0; i < p - q; i++) {
3053                     paste_buffer[paste_len++] = q[i];
3054                 }
3055             }
3056
3057             if (p <= data + len - sel_nl_sz &&
3058                 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3059                 paste_buffer[paste_len++] = '\r';
3060                 p += sel_nl_sz;
3061             }
3062             q = p;
3063         }
3064
3065         /* Assume a small paste will be OK in one go. */
3066         if (paste_len < 256) {
3067             luni_send(paste_buffer, paste_len);
3068             if (paste_buffer)
3069                 sfree(paste_buffer);
3070             paste_buffer = 0;
3071             paste_pos = paste_hold = paste_len = 0;
3072         }
3073     }
3074     get_clip(NULL, NULL);
3075 }
3076
3077 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3078                 int shift, int ctrl)
3079 {
3080     pos selpoint;
3081     unsigned long *ldata;
3082
3083     if (y < 0)
3084         y = 0;
3085     if (y >= rows)
3086         y = rows - 1;
3087     if (x < 0) {
3088         if (y > 0) {
3089             x = cols - 1;
3090             y--;
3091         } else
3092             x = 0;
3093     }
3094     if (x >= cols)
3095         x = cols - 1;
3096
3097     selpoint.y = y + disptop;
3098     selpoint.x = x;
3099     ldata = lineptr(selpoint.y);
3100     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3101         selpoint.x /= 2;
3102
3103     if (xterm_mouse) {
3104         int encstate = 0, r, c;
3105         char abuf[16];
3106         static int is_down = 0;
3107
3108         switch (b) {
3109           case MBT_LEFT:
3110             encstate = 0x20;           /* left button down */
3111             break;
3112           case MBT_MIDDLE:
3113             encstate = 0x21;
3114             break;
3115           case MBT_RIGHT:
3116             encstate = 0x22;
3117             break;
3118           case MBT_WHEEL_UP:
3119             encstate = 0x60;
3120             break;
3121           case MBT_WHEEL_DOWN:
3122             encstate = 0x61;
3123             break;
3124           default: break;              /* placate gcc warning about enum use */
3125         }
3126         switch (a) {
3127           case MA_DRAG:
3128             if (xterm_mouse == 1)
3129                 return;
3130             encstate += 0x20;
3131             break;
3132           case MA_RELEASE:
3133             encstate = 0x23;
3134             is_down = 0;
3135             break;
3136           case MA_CLICK:
3137             if (is_down == b)
3138                 return;
3139             is_down = b;
3140             break;
3141           default: break;              /* placate gcc warning about enum use */
3142         }
3143         if (shift)
3144             encstate += 0x04;
3145         if (ctrl)
3146             encstate += 0x10;
3147         r = y + 33;
3148         c = x + 33;
3149
3150         sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3151         ldisc_send(abuf, 6);
3152         return;
3153     }
3154
3155     b = translate_button(b);
3156
3157     if (b == MBT_SELECT && a == MA_CLICK) {
3158         deselect();
3159         selstate = ABOUT_TO;
3160         selanchor = selpoint;
3161         selmode = SM_CHAR;
3162     } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3163         deselect();
3164         selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3165         selstate = DRAGGING;
3166         selstart = selanchor = selpoint;
3167         selend = selstart;
3168         incpos(selend);
3169         sel_spread();
3170     } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3171                (b == MBT_EXTEND && a != MA_RELEASE)) {
3172         if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3173             return;
3174         if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3175             if (posdiff(selpoint, selstart) <
3176                 posdiff(selend, selstart) / 2) {
3177                 selanchor = selend;
3178                 decpos(selanchor);
3179             } else {
3180                 selanchor = selstart;
3181             }
3182             selstate = DRAGGING;
3183         }
3184         if (selstate != ABOUT_TO && selstate != DRAGGING)
3185             selanchor = selpoint;
3186         selstate = DRAGGING;
3187         if (poslt(selpoint, selanchor)) {
3188             selstart = selpoint;
3189             selend = selanchor;
3190             incpos(selend);
3191         } else {
3192             selstart = selanchor;
3193             selend = selpoint;
3194             incpos(selend);
3195         }
3196         sel_spread();
3197     } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3198         if (selstate == DRAGGING) {
3199             /*
3200              * We've completed a selection. We now transfer the
3201              * data to the clipboard.
3202              */
3203             clipme(selstart, selend);
3204             selstate = SELECTED;
3205         } else
3206             selstate = NO_SELECTION;
3207     } else if (b == MBT_PASTE
3208                && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3209         term_do_paste();
3210     }
3211
3212     term_update();
3213 }
3214
3215 void term_nopaste()
3216 {
3217     if (paste_len == 0)
3218         return;
3219     sfree(paste_buffer);
3220     paste_buffer = 0;
3221     paste_len = 0;
3222 }
3223
3224 void term_paste()
3225 {
3226     static long last_paste = 0;
3227     long now, paste_diff;
3228
3229     if (paste_len == 0)
3230         return;
3231
3232     /* Don't wait forever to paste */
3233     if (paste_hold) {
3234         now = GetTickCount();
3235         paste_diff = now - last_paste;
3236         if (paste_diff >= 0 && paste_diff < 450)
3237             return;
3238     }
3239     paste_hold = 0;
3240
3241     while (paste_pos < paste_len) {
3242         int n = 0;
3243         while (n + paste_pos < paste_len) {
3244             if (paste_buffer[paste_pos + n++] == '\r')
3245                 break;
3246         }
3247         luni_send(paste_buffer + paste_pos, n);
3248         paste_pos += n;
3249
3250         if (paste_pos < paste_len) {
3251             paste_hold = 1;
3252             return;
3253         }
3254     }
3255     sfree(paste_buffer);
3256     paste_buffer = 0;
3257     paste_len = 0;
3258 }
3259
3260 static void deselect(void)
3261 {
3262     selstate = NO_SELECTION;
3263     selstart.x = selstart.y = selend.x = selend.y = 0;
3264 }
3265
3266 void term_deselect(void)
3267 {
3268     deselect();
3269     term_update();
3270 }
3271
3272 int term_ldisc(int option)
3273 {
3274     if (option == LD_ECHO)
3275         return term_echoing;
3276     if (option == LD_EDIT)
3277         return term_editing;
3278     return FALSE;
3279 }
3280
3281 /*
3282  * from_backend(), to get data from the backend for the terminal.
3283  */
3284 int from_backend(int is_stderr, char *data, int len)
3285 {
3286     while (len--) {
3287         if (inbuf_head >= INBUF_SIZE)
3288             term_out();
3289         inbuf[inbuf_head++] = *data++;
3290     }
3291
3292     /*
3293      * We process all stdout/stderr data immediately we receive it,
3294      * and don't return until it's all gone. Therefore, there's no
3295      * reason at all to return anything other than zero from this
3296      * function.
3297      * 
3298      * This is a slightly suboptimal way to deal with SSH2 - in
3299      * principle, the window mechanism would allow us to continue
3300      * to accept data on forwarded ports and X connections even
3301      * while the terminal processing was going slowly - but we
3302      * can't do the 100% right thing without moving the terminal
3303      * processing into a separate thread, and that might hurt
3304      * portability. So we manage stdout buffering the old SSH1 way:
3305      * if the terminal processing goes slowly, the whole SSH
3306      * connection stops accepting data until it's ready.
3307      * 
3308      * In practice, I can't imagine this causing serious trouble.
3309      */
3310     return 0;
3311 }
3312
3313 /*
3314  * Log session traffic.
3315  */
3316 void logtraffic(unsigned char c, int logmode)
3317 {
3318     if (cfg.logtype > 0) {
3319         if (cfg.logtype == logmode) {
3320             /* deferred open file from pgm start? */
3321             if (!lgfp)
3322                 logfopen();
3323             if (lgfp)
3324                 fputc(c, lgfp);
3325         }
3326     }
3327 }
3328
3329 /* open log file append/overwrite mode */
3330 void logfopen(void)
3331 {
3332     char buf[256];
3333     time_t t;
3334     struct tm *tm;
3335     char writemod[4];
3336
3337     if (!cfg.logtype)
3338         return;
3339     sprintf(writemod, "wb");           /* default to rewrite */
3340     lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3341     if (lgfp) {
3342         int i;
3343         fclose(lgfp);
3344         i = askappend(cfg.logfilename);
3345         if (i == 1)
3346             writemod[0] = 'a';         /* set append mode */
3347         else if (i == 0) {             /* cancelled */
3348             lgfp = NULL;
3349             cfg.logtype = 0;           /* disable logging */
3350             return;
3351         }
3352     }
3353
3354     lgfp = fopen(cfg.logfilename, writemod);
3355     if (lgfp) {                        /* enter into event log */
3356         sprintf(buf, "%s session log (%s mode) to file : ",
3357                 (writemod[0] == 'a') ? "Appending" : "Writing new",
3358                 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3359                  cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3360         /* Make sure we do not exceed the output buffer size */
3361         strncat(buf, cfg.logfilename, 128);
3362         buf[strlen(buf)] = '\0';
3363         logevent(buf);
3364
3365         /* --- write header line iinto log file */
3366         fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3367         time(&t);
3368         tm = localtime(&t);
3369         strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3370         fputs(buf, lgfp);
3371         fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3372     }
3373 }
3374
3375 void logfclose(void)
3376 {
3377     if (lgfp) {
3378         fclose(lgfp);
3379         lgfp = NULL;
3380     }
3381 }