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