]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - terminal.c
69713680b932943ef92c00343cb85515fe4232eb
[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 ANSI('L', '='):
1953                         compatibility(OTHER);
1954                         use_bce = (esc_args[0] <= 0);
1955                         erase_char = ERASE_CHAR;
1956                         if (use_bce)
1957                             erase_char = (' ' | ATTR_ASCII |
1958                                          (curr_attr & 
1959                                           (ATTR_FGMASK | ATTR_BGMASK)));
1960                         break;
1961                       case ANSI('E', '='):
1962                         compatibility(OTHER);
1963                         blink_is_real = (esc_args[0] >= 1);
1964                         break;
1965                       case ANSI('p', '"'):
1966                         /* Allow the host to make this emulator a 'perfect' VT102.
1967                          * This first appeared in the VT220, but we do need to get 
1968                          * back to PuTTY mode so I won't check it.
1969                          *
1970                          * The arg in 40..42,50 are a PuTTY extension.
1971                          * The 2nd arg, 8bit vs 7bit is not checked.
1972                          *
1973                          * Setting VT102 mode should also change the Fkeys to
1974                          * generate PF* codes as a real VT102 has no Fkeys.
1975                          * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1976                          * send nothing.
1977                          *
1978                          * Note ESC c will NOT change this!
1979                          */
1980
1981                         switch (esc_args[0]) {
1982                           case 61:
1983                             compatibility_level &= ~TM_VTXXX;
1984                             compatibility_level |= TM_VT102;
1985                             break;
1986                           case 62:
1987                             compatibility_level &= ~TM_VTXXX;
1988                             compatibility_level |= TM_VT220;
1989                             break;
1990
1991                           default:
1992                             if (esc_args[0] > 60 && esc_args[0] < 70)
1993                                 compatibility_level |= TM_VTXXX;
1994                             break;
1995
1996                           case 40:
1997                             compatibility_level &= TM_VTXXX;
1998                             break;
1999                           case 41:
2000                             compatibility_level = TM_PUTTY;
2001                             break;
2002                           case 42:
2003                             compatibility_level = TM_SCOANSI;
2004                             break;
2005
2006                           case ARG_DEFAULT:
2007                             compatibility_level = TM_PUTTY;
2008                             break;
2009                           case 50:
2010                             break;
2011                         }
2012
2013                         /* Change the response to CSI c */
2014                         if (esc_args[0] == 50) {
2015                             int i;
2016                             char lbuf[64];
2017                             strcpy(id_string, "\033[?");
2018                             for (i = 1; i < esc_nargs; i++) {
2019                                 if (i != 1)
2020                                     strcat(id_string, ";");
2021                                 sprintf(lbuf, "%d", esc_args[i]);
2022                                 strcat(id_string, lbuf);
2023                             }
2024                             strcat(id_string, "c");
2025                         }
2026 #if 0
2027                         /* Is this a good idea ? 
2028                          * Well we should do a soft reset at this point ...
2029                          */
2030                         if (!has_compat(VT420) && has_compat(VT100)) {
2031                             if (reset_132)
2032                                 request_resize(132, 24, 1);
2033                             else
2034                                 request_resize(80, 24, 1);
2035                         }
2036 #endif
2037                         break;
2038                     }
2039                 break;
2040               case SEEN_OSC:
2041                 osc_w = FALSE;
2042                 switch (c) {
2043                   case 'P':            /* Linux palette sequence */
2044                     termstate = SEEN_OSC_P;
2045                     osc_strlen = 0;
2046                     break;
2047                   case 'R':            /* Linux palette reset */
2048                     palette_reset();
2049                     term_invalidate();
2050                     termstate = TOPLEVEL;
2051                     break;
2052                   case 'W':            /* word-set */
2053                     termstate = SEEN_OSC_W;
2054                     osc_w = TRUE;
2055                     break;
2056                   case '0':
2057                   case '1':
2058                   case '2':
2059                   case '3':
2060                   case '4':
2061                   case '5':
2062                   case '6':
2063                   case '7':
2064                   case '8':
2065                   case '9':
2066                     esc_args[0] = 10 * esc_args[0] + c - '0';
2067                     break;
2068                   case 'L':
2069                     /*
2070                      * Grotty hack to support xterm and DECterm title
2071                      * sequences concurrently.
2072                      */
2073                     if (esc_args[0] == 2) {
2074                         esc_args[0] = 1;
2075                         break;
2076                     }
2077                     /* else fall through */
2078                   default:
2079                     termstate = OSC_STRING;
2080                     osc_strlen = 0;
2081                 }
2082                 break;
2083               case OSC_STRING:
2084                 /*
2085                  * This OSC stuff is EVIL. It takes just one character to get into
2086                  * sysline mode and it's not initially obvious how to get out.
2087                  * So I've added CR and LF as string aborts.
2088                  * This shouldn't effect compatibility as I believe embedded 
2089                  * control characters are supposed to be interpreted (maybe?) 
2090                  * and they don't display anything useful anyway.
2091                  *
2092                  * -- RDB
2093                  */
2094                 if (c == '\n' || c == '\r') {
2095                     termstate = TOPLEVEL;
2096                 } else if (c == 0234 || c == '\007') {
2097                     /*
2098                      * These characters terminate the string; ST and BEL
2099                      * terminate the sequence and trigger instant
2100                      * processing of it, whereas ESC goes back to SEEN_ESC
2101                      * mode unless it is followed by \, in which case it is
2102                      * synonymous with ST in the first place.
2103                      */
2104                     do_osc();
2105                     termstate = TOPLEVEL;
2106                 } else if (c == '\033')
2107                     termstate = OSC_MAYBE_ST;
2108                 else if (osc_strlen < OSC_STR_MAX)
2109                     osc_string[osc_strlen++] = c;
2110                 break;
2111               case SEEN_OSC_P:
2112                 {
2113                     int max = (osc_strlen == 0 ? 21 : 16);
2114                     int val;
2115                     if (c >= '0' && c <= '9')
2116                         val = c - '0';
2117                     else if (c >= 'A' && c <= 'A' + max - 10)
2118                         val = c - 'A' + 10;
2119                     else if (c >= 'a' && c <= 'a' + max - 10)
2120                         val = c - 'a' + 10;
2121                     else {
2122                         termstate = TOPLEVEL;
2123                         break;
2124                     }
2125                     osc_string[osc_strlen++] = val;
2126                     if (osc_strlen >= 7) {
2127                         palette_set(osc_string[0],
2128                                     osc_string[1] * 16 + osc_string[2],
2129                                     osc_string[3] * 16 + osc_string[4],
2130                                     osc_string[5] * 16 + osc_string[6]);
2131                         term_invalidate();
2132                         termstate = TOPLEVEL;
2133                     }
2134                 }
2135                 break;
2136               case SEEN_OSC_W:
2137                 switch (c) {
2138                   case '0':
2139                   case '1':
2140                   case '2':
2141                   case '3':
2142                   case '4':
2143                   case '5':
2144                   case '6':
2145                   case '7':
2146                   case '8':
2147                   case '9':
2148                     esc_args[0] = 10 * esc_args[0] + c - '0';
2149                     break;
2150                   default:
2151                     termstate = OSC_STRING;
2152                     osc_strlen = 0;
2153                 }
2154                 break;
2155               case VT52_ESC:
2156                 termstate = TOPLEVEL;
2157                 seen_disp_event = TRUE;
2158                 switch (c) {
2159                   case 'A':
2160                     move(curs.x, curs.y - 1, 1);
2161                     break;
2162                   case 'B':
2163                     move(curs.x, curs.y + 1, 1);
2164                     break;
2165                   case 'C':
2166                     move(curs.x + 1, curs.y, 1);
2167                     break;
2168                   case 'D':
2169                     move(curs.x - 1, curs.y, 1);
2170                     break;
2171                     /*
2172                      * From the VT100 Manual
2173                      * NOTE: The special graphics characters in the VT100
2174                      *       are different from those in the VT52
2175                      *
2176                      * From VT102 manual:
2177                      *       137 _  Blank             - Same
2178                      *       140 `  Reserved          - Humm.
2179                      *       141 a  Solid rectangle   - Similar
2180                      *       142 b  1/                - Top half of fraction for the
2181                      *       143 c  3/                - subscript numbers below.
2182                      *       144 d  5/
2183                      *       145 e  7/
2184                      *       146 f  Degrees           - Same
2185                      *       147 g  Plus or minus     - Same
2186                      *       150 h  Right arrow
2187                      *       151 i  Ellipsis (dots)
2188                      *       152 j  Divide by
2189                      *       153 k  Down arrow
2190                      *       154 l  Bar at scan 0
2191                      *       155 m  Bar at scan 1
2192                      *       156 n  Bar at scan 2
2193                      *       157 o  Bar at scan 3     - Similar
2194                      *       160 p  Bar at scan 4     - Similar
2195                      *       161 q  Bar at scan 5     - Similar
2196                      *       162 r  Bar at scan 6     - Same
2197                      *       163 s  Bar at scan 7     - Similar
2198                      *       164 t  Subscript 0
2199                      *       165 u  Subscript 1
2200                      *       166 v  Subscript 2
2201                      *       167 w  Subscript 3
2202                      *       170 x  Subscript 4
2203                      *       171 y  Subscript 5
2204                      *       172 z  Subscript 6
2205                      *       173 {  Subscript 7
2206                      *       174 |  Subscript 8
2207                      *       175 }  Subscript 9
2208                      *       176 ~  Paragraph
2209                      *
2210                      */
2211                   case 'F':
2212                     cset_attr[cset = 0] = ATTR_LINEDRW;
2213                     break;
2214                   case 'G':
2215                     cset_attr[cset = 0] = ATTR_ASCII;
2216                     break;
2217                   case 'H':
2218                     move(0, 0, 0);
2219                     break;
2220                   case 'I':
2221                     if (curs.y == 0)
2222                         scroll(0, rows - 1, -1, TRUE);
2223                     else if (curs.y > 0)
2224                         curs.y--;
2225                     fix_cpos;
2226                     wrapnext = FALSE;
2227                     break;
2228                   case 'J':
2229                     erase_lots(FALSE, FALSE, TRUE);
2230                     disptop = 0;
2231                     break;
2232                   case 'K':
2233                     erase_lots(TRUE, FALSE, TRUE);
2234                     break;
2235 #if 0
2236                   case 'V':
2237                     /* XXX Print cursor line */
2238                     break;
2239                   case 'W':
2240                     /* XXX Start controller mode */
2241                     break;
2242                   case 'X':
2243                     /* XXX Stop controller mode */
2244                     break;
2245 #endif
2246                   case 'Y':
2247                     termstate = VT52_Y1;
2248                     break;
2249                   case 'Z':
2250                     ldisc_send("\033/Z", 3);
2251                     break;
2252                   case '=':
2253                     app_keypad_keys = TRUE;
2254                     break;
2255                   case '>':
2256                     app_keypad_keys = FALSE;
2257                     break;
2258                   case '<':
2259                     /* XXX This should switch to VT100 mode not current or default
2260                      *     VT mode. But this will only have effect in a VT220+
2261                      *     emulation.
2262                      */
2263                     vt52_mode = FALSE;
2264                     blink_is_real = cfg.blinktext;
2265                     break;
2266 #if 0
2267                   case '^':
2268                     /* XXX Enter auto print mode */
2269                     break;
2270                   case '_':
2271                     /* XXX Exit auto print mode */
2272                     break;
2273                   case ']':
2274                     /* XXX Print screen */
2275                     break;
2276 #endif
2277
2278 #ifdef VT52_PLUS
2279                   case 'E':
2280                     /* compatibility(ATARI) */
2281                     move(0, 0, 0);
2282                     erase_lots(FALSE, FALSE, TRUE);
2283                     disptop = 0;
2284                     break;
2285                   case 'L':
2286                     /* compatibility(ATARI) */
2287                     if (curs.y <= marg_b)
2288                         scroll(curs.y, marg_b, -1, FALSE);
2289                     break;
2290                   case 'M':
2291                     /* compatibility(ATARI) */
2292                     if (curs.y <= marg_b)
2293                         scroll(curs.y, marg_b, 1, TRUE);
2294                     break;
2295                   case 'b':
2296                     /* compatibility(ATARI) */
2297                     termstate = VT52_FG;
2298                     break;
2299                   case 'c':
2300                     /* compatibility(ATARI) */
2301                     termstate = VT52_BG;
2302                     break;
2303                   case 'd':
2304                     /* compatibility(ATARI) */
2305                     erase_lots(FALSE, TRUE, FALSE);
2306                     disptop = 0;
2307                     break;
2308                   case 'e':
2309                     /* compatibility(ATARI) */
2310                     cursor_on = TRUE;
2311                     break;
2312                   case 'f':
2313                     /* compatibility(ATARI) */
2314                     cursor_on = FALSE;
2315                     break;
2316                     /* case 'j': Save cursor position - broken on ST */
2317                     /* case 'k': Restore cursor position */
2318                   case 'l':
2319                     /* compatibility(ATARI) */
2320                     erase_lots(TRUE, TRUE, TRUE);
2321                     curs.x = 0;
2322                     wrapnext = FALSE;
2323                     fix_cpos;
2324                     break;
2325                   case 'o':
2326                     /* compatibility(ATARI) */
2327                     erase_lots(TRUE, TRUE, FALSE);
2328                     break;
2329                   case 'p':
2330                     /* compatibility(ATARI) */
2331                     curr_attr |= ATTR_REVERSE;
2332                     break;
2333                   case 'q':
2334                     /* compatibility(ATARI) */
2335                     curr_attr &= ~ATTR_REVERSE;
2336                     break;
2337                   case 'v':            /* wrap Autowrap on - Wyse style */
2338                     /* compatibility(ATARI) */
2339                     wrap = 1;
2340                     break;
2341                   case 'w':            /* Autowrap off */
2342                     /* compatibility(ATARI) */
2343                     wrap = 0;
2344                     break;
2345
2346                   case 'R':
2347                     /* compatibility(OTHER) */
2348                     vt52_bold = FALSE;
2349                     curr_attr = ATTR_DEFAULT;
2350                     if (use_bce)
2351                         erase_char = (' ' | ATTR_ASCII |
2352                                      (curr_attr & 
2353                                       (ATTR_FGMASK | ATTR_BGMASK)));
2354                     break;
2355                   case 'S':
2356                     /* compatibility(VI50) */
2357                     curr_attr |= ATTR_UNDER;
2358                     break;
2359                   case 'W':
2360                     /* compatibility(VI50) */
2361                     curr_attr &= ~ATTR_UNDER;
2362                     break;
2363                   case 'U':
2364                     /* compatibility(VI50) */
2365                     vt52_bold = TRUE;
2366                     curr_attr |= ATTR_BOLD;
2367                     break;
2368                   case 'T':
2369                     /* compatibility(VI50) */
2370                     vt52_bold = FALSE;
2371                     curr_attr &= ~ATTR_BOLD;
2372                     break;
2373 #endif
2374                 }
2375                 break;
2376               case VT52_Y1:
2377                 termstate = VT52_Y2;
2378                 move(curs.x, c - ' ', 0);
2379                 break;
2380               case VT52_Y2:
2381                 termstate = TOPLEVEL;
2382                 move(c - ' ', curs.y, 0);
2383                 break;
2384
2385 #ifdef VT52_PLUS
2386               case VT52_FG:
2387                 termstate = TOPLEVEL;
2388                 curr_attr &= ~ATTR_FGMASK;
2389                 curr_attr &= ~ATTR_BOLD;
2390                 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2391                 if ((c & 0x8) || vt52_bold)
2392                     curr_attr |= ATTR_BOLD;
2393
2394                 if (use_bce)
2395                     erase_char = (' ' | ATTR_ASCII |
2396                                  (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2397                 break;
2398               case VT52_BG:
2399                 termstate = TOPLEVEL;
2400                 curr_attr &= ~ATTR_BGMASK;
2401                 curr_attr &= ~ATTR_BLINK;
2402                 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2403
2404                 /* Note: bold background */
2405                 if (c & 0x8)
2406                     curr_attr |= ATTR_BLINK;
2407
2408                 if (use_bce)
2409                     erase_char = (' ' | ATTR_ASCII |
2410                                  (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2411                 break;
2412 #endif
2413               default: break;          /* placate gcc warning about enum use */
2414             }
2415         if (selstate != NO_SELECTION) {
2416             pos cursplus = curs;
2417             incpos(cursplus);
2418             check_selection(curs, cursplus);
2419         }
2420     }
2421     inbuf_head = 0;
2422 }
2423
2424 #if 0
2425 /*
2426  * Compare two lines to determine whether they are sufficiently
2427  * alike to scroll-optimise one to the other. Return the degree of
2428  * similarity.
2429  */
2430 static int linecmp(unsigned long *a, unsigned long *b)
2431 {
2432     int i, n;
2433
2434     for (i = n = 0; i < cols; i++)
2435         n += (*a++ == *b++);
2436     return n;
2437 }
2438 #endif
2439
2440 /*
2441  * Given a context, update the window. Out of paranoia, we don't
2442  * allow WM_PAINT responses to do scrolling optimisations.
2443  */
2444 static void do_paint(Context ctx, int may_optimise)
2445 {
2446     int i, j, our_curs_y;
2447     unsigned long rv, cursor;
2448     pos scrpos;
2449     char ch[1024];
2450     long cursor_background = ERASE_CHAR;
2451     long ticks;
2452
2453     /*
2454      * Check the visual bell state.
2455      */
2456     if (in_vbell) {
2457         ticks = GetTickCount();
2458         if (ticks - vbell_timeout >= 0)
2459             in_vbell = FALSE;
2460     }
2461
2462     rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2463
2464     /* Depends on:
2465      * screen array, disptop, scrtop,
2466      * selection, rv, 
2467      * cfg.blinkpc, blink_is_real, tblinker, 
2468      * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2469      */
2470
2471     /* Has the cursor position or type changed ? */
2472     if (cursor_on) {
2473         if (has_focus) {
2474             if (blinker || !cfg.blink_cur)
2475                 cursor = TATTR_ACTCURS;
2476             else
2477                 cursor = 0;
2478         } else
2479             cursor = TATTR_PASCURS;
2480         if (wrapnext)
2481             cursor |= TATTR_RIGHTCURS;
2482     } else
2483         cursor = 0;
2484     our_curs_y = curs.y - disptop;
2485
2486     if (dispcurs && (curstype != cursor ||
2487                      dispcurs !=
2488                      disptext + our_curs_y * (cols + 1) + curs.x)) {
2489         if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2490             dispcurs[-1] |= ATTR_INVALID;
2491         if ((*dispcurs & ATTR_WIDE))
2492             dispcurs[1] |= ATTR_INVALID;
2493         *dispcurs |= ATTR_INVALID;
2494         curstype = 0;
2495     }
2496     dispcurs = NULL;
2497
2498     /* The normal screen data */
2499     for (i = 0; i < rows; i++) {
2500         unsigned long *ldata;
2501         int lattr;
2502         int idx, dirty_line, dirty_run;
2503         unsigned long attr = 0;
2504         int updated_line = 0;
2505         int start = 0;
2506         int ccount = 0;
2507         int last_run_dirty = 0;
2508
2509         scrpos.y = i + disptop;
2510         ldata = lineptr(scrpos.y);
2511         lattr = (ldata[cols] & LATTR_MODE);
2512
2513         idx = i * (cols + 1);
2514         dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2515         disptext[idx + cols] = ldata[cols];
2516
2517         for (j = 0; j < cols; j++, idx++) {
2518             unsigned long tattr, tchar;
2519             unsigned long *d = ldata + j;
2520             int break_run;
2521             scrpos.x = j;
2522
2523             tchar = (*d & (CHAR_MASK | CSET_MASK));
2524             tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2525             switch (tchar & CSET_MASK) {
2526               case ATTR_ASCII:
2527                 tchar = unitab_line[tchar & 0xFF];
2528                 break;
2529               case ATTR_LINEDRW:
2530                 tchar = unitab_xterm[tchar & 0xFF];
2531                 break;
2532               case ATTR_SCOACS:  
2533                 tchar = unitab_scoacs[tchar&0xFF]; 
2534                 break;
2535             }
2536             tattr |= (tchar & CSET_MASK);
2537             tchar &= CHAR_MASK;
2538
2539             /* Video reversing things */
2540             tattr = (tattr ^ rv
2541                      ^ (posle(selstart, scrpos) &&
2542                         poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2543
2544             /* 'Real' blinking ? */
2545             if (blink_is_real && (tattr & ATTR_BLINK)) {
2546                 if (has_focus && tblinker) {
2547                     tchar = ' ';
2548                     tattr &= ~CSET_MASK;
2549                     tattr |= ATTR_ACP;
2550                 }
2551                 tattr &= ~ATTR_BLINK;
2552             }
2553
2554             /* Cursor here ? Save the 'background' */
2555             if (i == our_curs_y && j == curs.x) {
2556                 cursor_background = tattr | tchar;
2557                 dispcurs = disptext + idx;
2558             }
2559
2560             if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2561                 dirty_line = TRUE;
2562
2563             break_run = (tattr != attr || j - start >= sizeof(ch));
2564
2565             /* Special hack for VT100 Linedraw glyphs */
2566             if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2567                 && tchar <= 0xBD) break_run = TRUE;
2568
2569             if (!dbcs_screenfont && !dirty_line) {
2570                 if ((tchar | tattr) == disptext[idx])
2571                     break_run = TRUE;
2572                 else if (!dirty_run && ccount == 1)
2573                     break_run = TRUE;
2574             }
2575
2576             if (break_run) {
2577                 if ((dirty_run || last_run_dirty) && ccount > 0) {
2578                     do_text(ctx, start, i, ch, ccount, attr, lattr);
2579                     updated_line = 1;
2580                 }
2581                 start = j;
2582                 ccount = 0;
2583                 attr = tattr;
2584                 if (dbcs_screenfont)
2585                     last_run_dirty = dirty_run;
2586                 dirty_run = dirty_line;
2587             }
2588
2589             if ((tchar | tattr) != disptext[idx])
2590                 dirty_run = TRUE;
2591             ch[ccount++] = (char) tchar;
2592             disptext[idx] = tchar | tattr;
2593
2594             /* If it's a wide char step along to the next one. */
2595             if (tattr & ATTR_WIDE) {
2596                 if (++j < cols) {
2597                     idx++;
2598                     d++;
2599                     /* Cursor is here ? Ouch! */
2600                     if (i == our_curs_y && j == curs.x) {
2601                         cursor_background = *d;
2602                         dispcurs = disptext + idx;
2603                     }
2604                     if (disptext[idx] != *d)
2605                         dirty_run = TRUE;
2606                     disptext[idx] = *d;
2607                 }
2608             }
2609         }
2610         if (dirty_run && ccount > 0) {
2611             do_text(ctx, start, i, ch, ccount, attr, lattr);
2612             updated_line = 1;
2613         }
2614
2615         /* Cursor on this line ? (and changed) */
2616         if (i == our_curs_y && (curstype != cursor || updated_line)) {
2617             ch[0] = (char) (cursor_background & CHAR_MASK);
2618             attr = (cursor_background & ATTR_MASK) | cursor;
2619             do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2620             curstype = cursor;
2621         }
2622     }
2623 }
2624
2625 /*
2626  * Flick the switch that says if blinking things should be shown or hidden.
2627  */
2628
2629 void term_blink(int flg)
2630 {
2631     static long last_blink = 0;
2632     static long last_tblink = 0;
2633     long now, blink_diff;
2634
2635     now = GetTickCount();
2636     blink_diff = now - last_tblink;
2637
2638     /* Make sure the text blinks no more than 2Hz */
2639     if (blink_diff < 0 || blink_diff > 450) {
2640         last_tblink = now;
2641         tblinker = !tblinker;
2642     }
2643
2644     if (flg) {
2645         blinker = 1;
2646         last_blink = now;
2647         return;
2648     }
2649
2650     blink_diff = now - last_blink;
2651
2652     /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2653     if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2654         return;
2655
2656     last_blink = now;
2657     blinker = !blinker;
2658 }
2659
2660 /*
2661  * Invalidate the whole screen so it will be repainted in full.
2662  */
2663 void term_invalidate(void)
2664 {
2665     int i;
2666
2667     for (i = 0; i < rows * (cols + 1); i++)
2668         disptext[i] = ATTR_INVALID;
2669 }
2670
2671 /*
2672  * Paint the window in response to a WM_PAINT message.
2673  */
2674 void term_paint(Context ctx, int l, int t, int r, int b)
2675 {
2676     int i, j, left, top, right, bottom;
2677
2678     left = l / font_width;
2679     right = (r - 1) / font_width;
2680     top = t / font_height;
2681     bottom = (b - 1) / font_height;
2682     for (i = top; i <= bottom && i < rows; i++) {
2683         if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2684             for (j = left; j <= right && j < cols; j++)
2685                 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2686         else
2687             for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2688                 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2689     }
2690
2691     /* This should happen soon enough, also for some reason it sometimes 
2692      * fails to actually do anything when re-sizing ... painting the wrong
2693      * window perhaps ?
2694      do_paint (ctx, FALSE);
2695      */
2696 }
2697
2698 /*
2699  * Attempt to scroll the scrollback. The second parameter gives the
2700  * position we want to scroll to; the first is +1 to denote that
2701  * this position is relative to the beginning of the scrollback, -1
2702  * to denote it is relative to the end, and 0 to denote that it is
2703  * relative to the current position.
2704  */
2705 void term_scroll(int rel, int where)
2706 {
2707     int sbtop = -count234(scrollback);
2708
2709     disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2710     if (disptop < sbtop)
2711         disptop = sbtop;
2712     if (disptop > 0)
2713         disptop = 0;
2714     update_sbar();
2715     term_update();
2716 }
2717
2718 static void clipme(pos top, pos bottom)
2719 {
2720     wchar_t *workbuf;
2721     wchar_t *wbptr;                    /* where next char goes within workbuf */
2722     int wblen = 0;                     /* workbuf len */
2723     int buflen;                        /* amount of memory allocated to workbuf */
2724
2725     buflen = 5120;                     /* Default size */
2726     workbuf = smalloc(buflen * sizeof(wchar_t));
2727     wbptr = workbuf;                   /* start filling here */
2728
2729     while (poslt(top, bottom)) {
2730         int nl = FALSE;
2731         unsigned long *ldata = lineptr(top.y);
2732         pos nlpos;
2733
2734         nlpos.y = top.y;
2735         nlpos.x = cols;
2736
2737         if (!(ldata[cols] & LATTR_WRAPPED)) {
2738             while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2739                     (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2740                      (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2741                    && poslt(top, nlpos))
2742                 decpos(nlpos);
2743             if (poslt(nlpos, bottom))
2744                 nl = TRUE;
2745         }
2746         while (poslt(top, bottom) && poslt(top, nlpos)) {
2747 #if 0
2748             char cbuf[16], *p;
2749             sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2750 #else
2751             wchar_t cbuf[16], *p;
2752             int uc = (ldata[top.x] & 0xFFFF);
2753             int set, c;
2754
2755             if (uc == UCSWIDE) {
2756                 top.x++;
2757                 continue;
2758             }
2759
2760             switch (uc & CSET_MASK) {
2761               case ATTR_LINEDRW:
2762                 if (!cfg.rawcnp) {
2763                     uc = unitab_xterm[uc & 0xFF];
2764                     break;
2765                 }
2766               case ATTR_ASCII:
2767                 uc = unitab_line[uc & 0xFF];
2768                 break;
2769               case ATTR_SCOACS:  
2770                 uc = unitab_scoacs[uc&0xFF]; 
2771                 break;
2772             }
2773             switch (uc & CSET_MASK) {
2774               case ATTR_ACP:
2775                 uc = unitab_font[uc & 0xFF];
2776                 break;
2777               case ATTR_OEMCP:
2778                 uc = unitab_oemcp[uc & 0xFF];
2779                 break;
2780             }
2781
2782             set = (uc & CSET_MASK);
2783             c = (uc & CHAR_MASK);
2784             cbuf[0] = uc;
2785             cbuf[1] = 0;
2786
2787             if (DIRECT_FONT(uc)) {
2788                 if (c >= ' ' && c != 0x7F) {
2789                     unsigned char buf[4];
2790                     WCHAR wbuf[4];
2791                     int rv;
2792                     if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2793                         buf[0] = c;
2794                         buf[1] = (unsigned char) ldata[top.x + 1];
2795                         rv = MultiByteToWideChar(font_codepage,
2796                                                  0, buf, 2, wbuf, 4);
2797                         top.x++;
2798                     } else {
2799                         buf[0] = c;
2800                         rv = MultiByteToWideChar(font_codepage,
2801                                                  0, buf, 1, wbuf, 4);
2802                     }
2803
2804                     if (rv > 0) {
2805                         memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2806                         cbuf[rv] = 0;
2807                     }
2808                 }
2809             }
2810 #endif
2811
2812             for (p = cbuf; *p; p++) {
2813                 /* Enough overhead for trailing NL and nul */
2814                 if (wblen >= buflen - 16) {
2815                     workbuf =
2816                         srealloc(workbuf,
2817                                  sizeof(wchar_t) * (buflen += 100));
2818                     wbptr = workbuf + wblen;
2819                 }
2820                 wblen++;
2821                 *wbptr++ = *p;
2822             }
2823             top.x++;
2824         }
2825         if (nl) {
2826             int i;
2827             for (i = 0; i < sel_nl_sz; i++) {
2828                 wblen++;
2829                 *wbptr++ = sel_nl[i];
2830             }
2831         }
2832         top.y++;
2833         top.x = 0;
2834     }
2835     wblen++;
2836     *wbptr++ = 0;
2837     write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2838     if (buflen > 0)                    /* indicates we allocated this buffer */
2839         sfree(workbuf);
2840 }
2841
2842 void term_copyall(void)
2843 {
2844     pos top;
2845     top.y = -count234(scrollback);
2846     top.x = 0;
2847     clipme(top, curs);
2848 }
2849
2850 /*
2851  * The wordness array is mainly for deciding the disposition of the US-ASCII 
2852  * characters.
2853  */
2854 static int wordtype(int uc)
2855 {
2856     static struct {
2857         int start, end, ctype;
2858     } *wptr, ucs_words[] = {
2859         {
2860         128, 160, 0}, {
2861         161, 191, 1}, {
2862         215, 215, 1}, {
2863         247, 247, 1}, {
2864         0x037e, 0x037e, 1},            /* Greek question mark */
2865         {
2866         0x0387, 0x0387, 1},            /* Greek ano teleia */
2867         {
2868         0x055a, 0x055f, 1},            /* Armenian punctuation */
2869         {
2870         0x0589, 0x0589, 1},            /* Armenian full stop */
2871         {
2872         0x0700, 0x070d, 1},            /* Syriac punctuation */
2873         {
2874         0x104a, 0x104f, 1},            /* Myanmar punctuation */
2875         {
2876         0x10fb, 0x10fb, 1},            /* Georgian punctuation */
2877         {
2878         0x1361, 0x1368, 1},            /* Ethiopic punctuation */
2879         {
2880         0x166d, 0x166e, 1},            /* Canadian Syl. punctuation */
2881         {
2882         0x17d4, 0x17dc, 1},            /* Khmer punctuation */
2883         {
2884         0x1800, 0x180a, 1},            /* Mongolian punctuation */
2885         {
2886         0x2000, 0x200a, 0},            /* Various spaces */
2887         {
2888         0x2070, 0x207f, 2},            /* superscript */
2889         {
2890         0x2080, 0x208f, 2},            /* subscript */
2891         {
2892         0x200b, 0x27ff, 1},            /* punctuation and symbols */
2893         {
2894         0x3000, 0x3000, 0},            /* ideographic space */
2895         {
2896         0x3001, 0x3020, 1},            /* ideographic punctuation */
2897         {
2898         0x303f, 0x309f, 3},            /* Hiragana */
2899         {
2900         0x30a0, 0x30ff, 3},            /* Katakana */
2901         {
2902         0x3300, 0x9fff, 3},            /* CJK Ideographs */
2903         {
2904         0xac00, 0xd7a3, 3},            /* Hangul Syllables */
2905         {
2906         0xf900, 0xfaff, 3},            /* CJK Ideographs */
2907         {
2908         0xfe30, 0xfe6b, 1},            /* punctuation forms */
2909         {
2910         0xff00, 0xff0f, 1},            /* half/fullwidth ASCII */
2911         {
2912         0xff1a, 0xff20, 1},            /* half/fullwidth ASCII */
2913         {
2914         0xff3b, 0xff40, 1},            /* half/fullwidth ASCII */
2915         {
2916         0xff5b, 0xff64, 1},            /* half/fullwidth ASCII */
2917         {
2918         0xfff0, 0xffff, 0},            /* half/fullwidth ASCII */
2919         {
2920         0, 0, 0}
2921     };
2922
2923     uc &= (CSET_MASK | CHAR_MASK);
2924
2925     switch (uc & CSET_MASK) {
2926       case ATTR_LINEDRW:
2927         uc = unitab_xterm[uc & 0xFF];
2928         break;
2929       case ATTR_ASCII:
2930         uc = unitab_line[uc & 0xFF];
2931         break;
2932       case ATTR_SCOACS:  
2933         uc = unitab_scoacs[uc&0xFF]; 
2934         break;
2935     }
2936     switch (uc & CSET_MASK) {
2937       case ATTR_ACP:
2938         uc = unitab_font[uc & 0xFF];
2939         break;
2940       case ATTR_OEMCP:
2941         uc = unitab_oemcp[uc & 0xFF];
2942         break;
2943     }
2944
2945     if (uc < 0x80)
2946         return wordness[uc];
2947
2948     for (wptr = ucs_words; wptr->start; wptr++) {
2949         if (uc >= wptr->start && uc <= wptr->end)
2950             return wptr->ctype;
2951     }
2952
2953     return 2;
2954 }
2955
2956 /*
2957  * Spread the selection outwards according to the selection mode.
2958  */
2959 static pos sel_spread_half(pos p, int dir)
2960 {
2961     unsigned long *ldata;
2962     short wvalue;
2963
2964     ldata = lineptr(p.y);
2965
2966     switch (selmode) {
2967       case SM_CHAR:
2968         /*
2969          * In this mode, every character is a separate unit, except
2970          * for runs of spaces at the end of a non-wrapping line.
2971          */
2972         if (!(ldata[cols] & LATTR_WRAPPED)) {
2973             unsigned long *q = ldata + cols;
2974             while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2975                 q--;
2976             if (q == ldata + cols)
2977                 q--;
2978             if (p.x >= q - ldata)
2979                 p.x = (dir == -1 ? q - ldata : cols - 1);
2980         }
2981         break;
2982       case SM_WORD:
2983         /*
2984          * In this mode, the units are maximal runs of characters
2985          * whose `wordness' has the same value.
2986          */
2987         wvalue = wordtype(ldata[p.x]);
2988         if (dir == +1) {
2989             while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
2990                 p.x++;
2991         } else {
2992             while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
2993                 p.x--;
2994         }
2995         break;
2996       case SM_LINE:
2997         /*
2998          * In this mode, every line is a unit.
2999          */
3000         p.x = (dir == -1 ? 0 : cols - 1);
3001         break;
3002     }
3003     return p;
3004 }
3005
3006 static void sel_spread(void)
3007 {
3008     selstart = sel_spread_half(selstart, -1);
3009     decpos(selend);
3010     selend = sel_spread_half(selend, +1);
3011     incpos(selend);
3012 }
3013
3014 void term_do_paste(void)
3015 {
3016     wchar_t *data;
3017     int len;
3018
3019     get_clip(&data, &len);
3020     if (data) {
3021         wchar_t *p, *q;
3022
3023         if (paste_buffer)
3024             sfree(paste_buffer);
3025         paste_pos = paste_hold = paste_len = 0;
3026         paste_buffer = smalloc(len * sizeof(wchar_t));
3027
3028         p = q = data;
3029         while (p < data + len) {
3030             while (p < data + len &&
3031                    !(p <= data + len - sel_nl_sz &&
3032                      !memcmp(p, sel_nl, sizeof(sel_nl))))
3033                 p++;
3034
3035             {
3036                 int i;
3037                 for (i = 0; i < p - q; i++) {
3038                     paste_buffer[paste_len++] = q[i];
3039                 }
3040             }
3041
3042             if (p <= data + len - sel_nl_sz &&
3043                 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3044                 paste_buffer[paste_len++] = '\r';
3045                 p += sel_nl_sz;
3046             }
3047             q = p;
3048         }
3049
3050         /* Assume a small paste will be OK in one go. */
3051         if (paste_len < 256) {
3052             luni_send(paste_buffer, paste_len);
3053             if (paste_buffer)
3054                 sfree(paste_buffer);
3055             paste_buffer = 0;
3056             paste_pos = paste_hold = paste_len = 0;
3057         }
3058     }
3059     get_clip(NULL, NULL);
3060 }
3061
3062 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3063                 int shift, int ctrl)
3064 {
3065     pos selpoint;
3066     unsigned long *ldata;
3067
3068     if (y < 0)
3069         y = 0;
3070     if (y >= rows)
3071         y = rows - 1;
3072     if (x < 0) {
3073         if (y > 0) {
3074             x = cols - 1;
3075             y--;
3076         } else
3077             x = 0;
3078     }
3079     if (x >= cols)
3080         x = cols - 1;
3081
3082     selpoint.y = y + disptop;
3083     selpoint.x = x;
3084     ldata = lineptr(selpoint.y);
3085     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3086         selpoint.x /= 2;
3087
3088     if (xterm_mouse) {
3089         int encstate = 0, r, c;
3090         char abuf[16];
3091         static int is_down = 0;
3092
3093         switch (b) {
3094           case MBT_LEFT:
3095             encstate = 0x20;           /* left button down */
3096             break;
3097           case MBT_MIDDLE:
3098             encstate = 0x21;
3099             break;
3100           case MBT_RIGHT:
3101             encstate = 0x22;
3102             break;
3103           case MBT_WHEEL_UP:
3104             encstate = 0x60;
3105             break;
3106           case MBT_WHEEL_DOWN:
3107             encstate = 0x61;
3108             break;
3109           default: break;              /* placate gcc warning about enum use */
3110         }
3111         switch (a) {
3112           case MA_DRAG:
3113             if (xterm_mouse == 1)
3114                 return;
3115             encstate += 0x20;
3116             break;
3117           case MA_RELEASE:
3118             encstate = 0x23;
3119             is_down = 0;
3120             break;
3121           case MA_CLICK:
3122             if (is_down == b)
3123                 return;
3124             is_down = b;
3125             break;
3126           default: break;              /* placate gcc warning about enum use */
3127         }
3128         if (shift)
3129             encstate += 0x04;
3130         if (ctrl)
3131             encstate += 0x10;
3132         r = y + 33;
3133         c = x + 33;
3134
3135         sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3136         ldisc_send(abuf, 6);
3137         return;
3138     }
3139
3140     b = translate_button(b);
3141
3142     if (b == MBT_SELECT && a == MA_CLICK) {
3143         deselect();
3144         selstate = ABOUT_TO;
3145         selanchor = selpoint;
3146         selmode = SM_CHAR;
3147     } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3148         deselect();
3149         selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3150         selstate = DRAGGING;
3151         selstart = selanchor = selpoint;
3152         selend = selstart;
3153         incpos(selend);
3154         sel_spread();
3155     } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3156                (b == MBT_EXTEND && a != MA_RELEASE)) {
3157         if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3158             return;
3159         if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3160             if (posdiff(selpoint, selstart) <
3161                 posdiff(selend, selstart) / 2) {
3162                 selanchor = selend;
3163                 decpos(selanchor);
3164             } else {
3165                 selanchor = selstart;
3166             }
3167             selstate = DRAGGING;
3168         }
3169         if (selstate != ABOUT_TO && selstate != DRAGGING)
3170             selanchor = selpoint;
3171         selstate = DRAGGING;
3172         if (poslt(selpoint, selanchor)) {
3173             selstart = selpoint;
3174             selend = selanchor;
3175             incpos(selend);
3176         } else {
3177             selstart = selanchor;
3178             selend = selpoint;
3179             incpos(selend);
3180         }
3181         sel_spread();
3182     } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3183         if (selstate == DRAGGING) {
3184             /*
3185              * We've completed a selection. We now transfer the
3186              * data to the clipboard.
3187              */
3188             clipme(selstart, selend);
3189             selstate = SELECTED;
3190         } else
3191             selstate = NO_SELECTION;
3192     } else if (b == MBT_PASTE
3193                && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3194         term_do_paste();
3195     }
3196
3197     term_update();
3198 }
3199
3200 void term_nopaste()
3201 {
3202     if (paste_len == 0)
3203         return;
3204     sfree(paste_buffer);
3205     paste_buffer = 0;
3206     paste_len = 0;
3207 }
3208
3209 void term_paste()
3210 {
3211     static long last_paste = 0;
3212     long now, paste_diff;
3213
3214     if (paste_len == 0)
3215         return;
3216
3217     /* Don't wait forever to paste */
3218     if (paste_hold) {
3219         now = GetTickCount();
3220         paste_diff = now - last_paste;
3221         if (paste_diff >= 0 && paste_diff < 450)
3222             return;
3223     }
3224     paste_hold = 0;
3225
3226     while (paste_pos < paste_len) {
3227         int n = 0;
3228         while (n + paste_pos < paste_len) {
3229             if (paste_buffer[paste_pos + n++] == '\r')
3230                 break;
3231         }
3232         luni_send(paste_buffer + paste_pos, n);
3233         paste_pos += n;
3234
3235         if (paste_pos < paste_len) {
3236             paste_hold = 1;
3237             return;
3238         }
3239     }
3240     sfree(paste_buffer);
3241     paste_buffer = 0;
3242     paste_len = 0;
3243 }
3244
3245 static void deselect(void)
3246 {
3247     selstate = NO_SELECTION;
3248     selstart.x = selstart.y = selend.x = selend.y = 0;
3249 }
3250
3251 void term_deselect(void)
3252 {
3253     deselect();
3254     term_update();
3255 }
3256
3257 int term_ldisc(int option)
3258 {
3259     if (option == LD_ECHO)
3260         return term_echoing;
3261     if (option == LD_EDIT)
3262         return term_editing;
3263     return FALSE;
3264 }
3265
3266 /*
3267  * from_backend(), to get data from the backend for the terminal.
3268  */
3269 void from_backend(int is_stderr, char *data, int len)
3270 {
3271     while (len--) {
3272         if (inbuf_head >= INBUF_SIZE)
3273             term_out();
3274         inbuf[inbuf_head++] = *data++;
3275     }
3276 }
3277
3278 /*
3279  * Log session traffic.
3280  */
3281 void logtraffic(unsigned char c, int logmode)
3282 {
3283     if (cfg.logtype > 0) {
3284         if (cfg.logtype == logmode) {
3285             /* deferred open file from pgm start? */
3286             if (!lgfp)
3287                 logfopen();
3288             if (lgfp)
3289                 fputc(c, lgfp);
3290         }
3291     }
3292 }
3293
3294 /* open log file append/overwrite mode */
3295 void logfopen(void)
3296 {
3297     char buf[256];
3298     time_t t;
3299     struct tm *tm;
3300     char writemod[4];
3301
3302     if (!cfg.logtype)
3303         return;
3304     sprintf(writemod, "wb");           /* default to rewrite */
3305     lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3306     if (lgfp) {
3307         int i;
3308         fclose(lgfp);
3309         i = askappend(cfg.logfilename);
3310         if (i == 1)
3311             writemod[0] = 'a';         /* set append mode */
3312         else if (i == 0) {             /* cancelled */
3313             lgfp = NULL;
3314             cfg.logtype = 0;           /* disable logging */
3315             return;
3316         }
3317     }
3318
3319     lgfp = fopen(cfg.logfilename, writemod);
3320     if (lgfp) {                        /* enter into event log */
3321         sprintf(buf, "%s session log (%s mode) to file : ",
3322                 (writemod[0] == 'a') ? "Appending" : "Writing new",
3323                 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3324                  cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3325         /* Make sure we do not exceed the output buffer size */
3326         strncat(buf, cfg.logfilename, 128);
3327         buf[strlen(buf)] = '\0';
3328         logevent(buf);
3329
3330         /* --- write header line iinto log file */
3331         fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3332         time(&t);
3333         tm = localtime(&t);
3334         strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3335         fputs(buf, lgfp);
3336         fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3337     }
3338 }
3339
3340 void logfclose(void)
3341 {
3342     if (lgfp) {
3343         fclose(lgfp);
3344         lgfp = NULL;
3345     }
3346 }