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