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