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