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