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