]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - terminal.c
Only engage a GTK idle function when absolutely necessary, otherwise
[PuTTY.git] / terminal.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4
5 #include <time.h>
6 #include <assert.h>
7 #include "putty.h"
8 #include "tree234.h"
9
10 #define VT52_PLUS
11
12 #define CL_ANSIMIN      0x0001         /* Codes in all ANSI like terminals. */
13 #define CL_VT100        0x0002         /* VT100 */
14 #define CL_VT100AVO     0x0004         /* VT100 +AVO; 132x24 (not 132x14) & attrs */
15 #define CL_VT102        0x0008         /* VT102 */
16 #define CL_VT220        0x0010         /* VT220 */
17 #define CL_VT320        0x0020         /* VT320 */
18 #define CL_VT420        0x0040         /* VT420 */
19 #define CL_VT510        0x0080         /* VT510, NB VT510 includes ANSI */
20 #define CL_VT340TEXT    0x0100         /* VT340 extensions that appear in the VT420 */
21 #define CL_SCOANSI      0x1000         /* SCOANSI not in ANSIMIN. */
22 #define CL_ANSI         0x2000         /* ANSI ECMA-48 not in the VT100..VT420 */
23 #define CL_OTHER        0x4000         /* Others, Xterm, linux, putty, dunno, etc */
24
25 #define TM_VT100        (CL_ANSIMIN|CL_VT100)
26 #define TM_VT100AVO     (TM_VT100|CL_VT100AVO)
27 #define TM_VT102        (TM_VT100AVO|CL_VT102)
28 #define TM_VT220        (TM_VT102|CL_VT220)
29 #define TM_VTXXX        (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
30 #define TM_SCOANSI      (CL_ANSIMIN|CL_SCOANSI)
31
32 #define TM_PUTTY        (0xFFFF)
33
34 #define compatibility(x) \
35     if ( ((CL_##x)&compatibility_level) == 0 ) {        \
36        termstate=TOPLEVEL;                              \
37        break;                                           \
38     }
39 #define compatibility2(x,y) \
40     if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
41        termstate=TOPLEVEL;                              \
42        break;                                           \
43     }
44
45 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
46
47 static int compatibility_level = TM_PUTTY;
48
49 static tree234 *scrollback;            /* lines scrolled off top of screen */
50 static tree234 *screen;                /* lines on primary screen */
51 static tree234 *alt_screen;            /* lines on alternate screen */
52 static int disptop;                    /* distance scrolled back (0 or -ve) */
53
54 static unsigned long *cpos;            /* cursor position (convenience) */
55
56 static unsigned long *disptext;        /* buffer of text on real screen */
57 static unsigned long *dispcurs;        /* location of cursor on real screen */
58 static unsigned long curstype;         /* type of cursor on real screen */
59
60 #define VBELL_TIMEOUT (TICKSPERSEC/10) /* visual bell lasts 1/10 sec */
61
62 struct beeptime {
63     struct beeptime *next;
64     unsigned long ticks;
65 };
66 static struct beeptime *beephead, *beeptail;
67 int nbeeps;
68 int beep_overloaded;
69 long lastbeep;
70
71 #define TSIZE (sizeof(unsigned long))
72 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
73
74 static unsigned long curr_attr, save_attr;
75 static unsigned long erase_char = ERASE_CHAR;
76
77 typedef struct {
78     int y, x;
79 } pos;
80 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
81 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
82 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
83 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
84 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
85 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
86
87 /* Product-order comparisons for rectangular block selection. */
88 #define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
89 #define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
90
91 static bufchain inbuf;                 /* terminal input buffer */
92 static pos curs;                       /* cursor */
93 static pos savecurs;                   /* saved cursor position */
94 static int marg_t, marg_b;             /* scroll margins */
95 static int dec_om;                     /* DEC origin mode flag */
96 static int wrap, wrapnext;             /* wrap flags */
97 static int insert;                     /* insert-mode flag */
98 static int cset;                       /* 0 or 1: which char set */
99 static int save_cset, save_csattr;     /* saved with cursor position */
100 static int save_utf, save_wnext;       /* saved with cursor position */
101 static int rvideo;                     /* global reverse video flag */
102 static unsigned long rvbell_startpoint;/* for ESC[?5hESC[?5l vbell */
103 static int cursor_on;                  /* cursor enabled flag */
104 static int reset_132;                  /* Flag ESC c resets to 80 cols */
105 static int use_bce;                    /* Use Background coloured erase */
106 static int blinker;                    /* When blinking is the cursor on ? */
107 static int tblinker;                   /* When the blinking text is on */
108 static int blink_is_real;              /* Actually blink blinking text */
109 static int term_echoing;               /* Does terminal want local echo? */
110 static int term_editing;               /* Does terminal want local edit? */
111 static int sco_acs, save_sco_acs;      /* CSI 10,11,12m -> OEM charset */
112 static int vt52_bold;                  /* Force bold on non-bold colours */
113 static int utf_state;                  /* Is there a pending UTF-8 character */
114 static int utf_char;                   /* and what is it so far. */
115 static int utf_size;                   /* The size of the UTF character. */
116 static int printing, only_printing;    /* Are we doing ANSI printing? */
117 static int print_state;                /* state of print-end-sequence scan */
118 static bufchain printer_buf;           /* buffered data for printer */
119 static printer_job *print_job;
120
121 static int xterm_mouse;                /* send mouse messages to app */
122
123 static unsigned long cset_attr[2];
124
125 /*
126  * Saved settings on the alternate screen.
127  */
128 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
129 static int alt_t, alt_b;
130 static int alt_which;
131
132 #define ARGS_MAX 32                    /* max # of esc sequence arguments */
133 #define ARG_DEFAULT 0                  /* if an arg isn't specified */
134 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
135 static int esc_args[ARGS_MAX];
136 static int esc_nargs;
137 static int esc_query;
138 #define ANSI(x,y)       ((x)+((y)<<8))
139 #define ANSI_QUE(x)     ANSI(x,TRUE)
140
141 #define OSC_STR_MAX 2048
142 static int osc_strlen;
143 static char osc_string[OSC_STR_MAX + 1];
144 static int osc_w;
145
146 static char id_string[1024] = "\033[?6c";
147
148 static unsigned char *tabs;
149
150 static enum {
151     TOPLEVEL,
152     SEEN_ESC,
153     SEEN_CSI,
154     SEEN_OSC,
155     SEEN_OSC_W,
156
157     DO_CTRLS,
158
159     SEEN_OSC_P,
160     OSC_STRING, OSC_MAYBE_ST,
161     VT52_ESC,
162     VT52_Y1,
163     VT52_Y2,
164     VT52_FG,
165     VT52_BG
166 } termstate;
167
168 static enum {
169     NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
170 } selstate;
171 static enum {
172     LEXICOGRAPHIC, RECTANGULAR
173 } seltype;
174 static enum {
175     SM_CHAR, SM_WORD, SM_LINE
176 } selmode;
177 static pos selstart, selend, selanchor;
178
179 static short wordness[256] = {
180     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
181     0, 0, 0, 0, 0, 0, 0, 0,            /* 01 */
182     0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
183     2, 2, 1, 1, 1, 1, 1, 1,            /* 23 */
184     1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
185     2, 2, 2, 1, 1, 1, 1, 2,            /* 45 */
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, 1,            /* 67 */
188     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
189     1, 1, 1, 1, 1, 1, 1, 1,            /* 89 */
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,            /* AB */
192     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
193     2, 2, 2, 2, 2, 2, 2, 2,            /* CD */
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,            /* EF */
196 };
197
198 #define sel_nl_sz  (sizeof(sel_nl)/sizeof(wchar_t))
199 static wchar_t sel_nl[] = SEL_NL;
200 static wchar_t *paste_buffer = 0;
201 static int paste_len, paste_pos, paste_hold;
202
203 /*
204  * Internal prototypes.
205  */
206 static void do_paint(Context, int);
207 static void erase_lots(int, int, int);
208 static void swap_screen(int);
209 static void update_sbar(void);
210 static void deselect(void);
211 static void term_print_finish(void);
212
213 /*
214  * Resize a line to make it `cols' columns wide.
215  */
216 unsigned long *resizeline(unsigned long *line, int cols)
217 {
218     int i, oldlen;
219     unsigned long lineattrs;
220
221     if (line[0] != (unsigned long)cols) {
222         /*
223          * This line is the wrong length, which probably means it
224          * hasn't been accessed since a resize. Resize it now.
225          */
226         oldlen = line[0];
227         lineattrs = line[oldlen + 1];
228         line = srealloc(line, TSIZE * (2 + cols));
229         line[0] = cols;
230         for (i = oldlen; i < cols; i++)
231             line[i + 1] = ERASE_CHAR;
232         line[cols + 1] = lineattrs & LATTR_MODE;
233     }
234
235     return line;
236 }
237
238 /*
239  * Retrieve a line of the screen or of the scrollback, according to
240  * whether the y coordinate is non-negative or negative
241  * (respectively).
242  */
243 unsigned long *lineptr(int y, int lineno)
244 {
245     unsigned long *line, *newline;
246     tree234 *whichtree;
247     int treeindex;
248
249     if (y >= 0) {
250         whichtree = screen;
251         treeindex = y;
252     } else {
253         whichtree = scrollback;
254         treeindex = y + count234(scrollback);
255     }
256     line = index234(whichtree, treeindex);
257
258     /* We assume that we don't screw up and retrieve something out of range. */
259     assert(line != NULL);
260
261     newline = resizeline(line, cols);
262     if (newline != line) {
263         delpos234(whichtree, treeindex);
264         addpos234(whichtree, newline, treeindex);
265         line = newline;
266     }
267
268     return line + 1;
269 }
270
271 #define lineptr(x) lineptr(x,__LINE__)
272 /*
273  * Set up power-on settings for the terminal.
274  */
275 static void power_on(void)
276 {
277     curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
278     alt_t = marg_t = 0;
279     if (rows != -1)
280         alt_b = marg_b = rows - 1;
281     else
282         alt_b = marg_b = 0;
283     if (cols != -1) {
284         int i;
285         for (i = 0; i < cols; i++)
286             tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
287     }
288     alt_om = dec_om = cfg.dec_om;
289     alt_wnext = wrapnext = alt_ins = insert = FALSE;
290     alt_wrap = wrap = cfg.wrap_mode;
291     alt_cset = cset = 0;
292     alt_utf = utf = 0;
293     alt_sco_acs = sco_acs = 0;
294     cset_attr[0] = cset_attr[1] = ATTR_ASCII;
295     rvideo = 0;
296     in_vbell = FALSE;
297     cursor_on = 1;
298     big_cursor = 0;
299     save_attr = curr_attr = ATTR_DEFAULT;
300     term_editing = term_echoing = FALSE;
301     ldisc_send(NULL, 0, 0);            /* cause ldisc to notice changes */
302     app_cursor_keys = cfg.app_cursor;
303     app_keypad_keys = cfg.app_keypad;
304     use_bce = cfg.bce;
305     blink_is_real = cfg.blinktext;
306     erase_char = ERASE_CHAR;
307     alt_which = 0;
308     term_print_finish();
309     {
310         int i;
311         for (i = 0; i < 256; i++)
312             wordness[i] = cfg.wordness[i];
313     }
314     if (screen) {
315         swap_screen(1);
316         erase_lots(FALSE, TRUE, TRUE);
317         swap_screen(0);
318         erase_lots(FALSE, TRUE, TRUE);
319     }
320 }
321
322 /*
323  * Force a screen update.
324  */
325 void term_update(void)
326 {
327     Context ctx;
328     ctx = get_ctx();
329     if (ctx) {
330         int need_sbar_update = seen_disp_event;
331         if ((seen_key_event && (cfg.scroll_on_key)) ||
332             (seen_disp_event && (cfg.scroll_on_disp))) {
333             disptop = 0;               /* return to main screen */
334             seen_disp_event = seen_key_event = 0;
335             need_sbar_update = TRUE;
336         }
337         if (need_sbar_update)
338             update_sbar();
339         do_paint(ctx, TRUE);
340         sys_cursor(curs.x, curs.y - disptop);
341         free_ctx(ctx);
342     }
343 }
344
345 /*
346  * Same as power_on(), but an external function.
347  */
348 void term_pwron(void)
349 {
350     power_on();
351     fix_cpos;
352     disptop = 0;
353     deselect();
354     term_update();
355 }
356
357 /*
358  * When the user reconfigures us, we need to check the forbidden-
359  * alternate-screen config option, disable raw mouse mode if the
360  * user has disabled mouse reporting, and abandon a print job if
361  * the user has disabled printing.
362  */
363 void term_reconfig(void)
364 {
365     if (cfg.no_alt_screen)
366         swap_screen(0);
367     if (cfg.no_mouse_rep) {
368         xterm_mouse = 0;
369         set_raw_mouse_mode(0);
370     }
371     if (cfg.no_remote_charset) {
372         cset_attr[0] = cset_attr[1] = ATTR_ASCII;
373         sco_acs = alt_sco_acs = 0;
374         utf = 0;
375     }
376     if (!*cfg.printer) {
377         term_print_finish();
378     }
379 }
380
381 /*
382  * Clear the scrollback.
383  */
384 void term_clrsb(void)
385 {
386     unsigned long *line;
387     disptop = 0;
388     while ((line = delpos234(scrollback, 0)) != NULL) {
389         sfree(line);
390     }
391     update_sbar();
392 }
393
394 /*
395  * Initialise the terminal.
396  */
397 void term_init(void)
398 {
399     screen = alt_screen = scrollback = NULL;
400     disptop = 0;
401     disptext = dispcurs = NULL;
402     tabs = NULL;
403     deselect();
404     rows = cols = -1;
405     power_on();
406     beephead = beeptail = NULL;
407     nbeeps = 0;
408     lastbeep = FALSE;
409     beep_overloaded = FALSE;
410 }
411
412 /*
413  * Set up the terminal for a given size.
414  */
415 void term_size(int newrows, int newcols, int newsavelines)
416 {
417     tree234 *newalt;
418     unsigned long *newdisp, *line;
419     int i, j;
420     int sblen;
421     int save_alt_which = alt_which;
422
423     if (newrows == rows && newcols == cols && newsavelines == savelines)
424         return;                        /* nothing to do */
425
426     deselect();
427     swap_screen(0);
428
429     alt_t = marg_t = 0;
430     alt_b = marg_b = newrows - 1;
431
432     if (rows == -1) {
433         scrollback = newtree234(NULL);
434         screen = newtree234(NULL);
435         rows = 0;
436     }
437
438     /*
439      * Resize the screen and scrollback. We only need to shift
440      * lines around within our data structures, because lineptr()
441      * will take care of resizing each individual line if
442      * necessary. So:
443      * 
444      *  - If the new screen and the old screen differ in length, we
445      *    must shunt some lines in from the scrollback or out to
446      *    the scrollback.
447      * 
448      *  - If doing that fails to provide us with enough material to
449      *    fill the new screen (i.e. the number of rows needed in
450      *    the new screen exceeds the total number in the previous
451      *    screen+scrollback), we must invent some blank lines to
452      *    cover the gap.
453      * 
454      *  - Then, if the new scrollback length is less than the
455      *    amount of scrollback we actually have, we must throw some
456      *    away.
457      */
458     sblen = count234(scrollback);
459     /* Do this loop to expand the screen if newrows > rows */
460     for (i = rows; i < newrows; i++) {
461         if (sblen > 0) {
462             line = delpos234(scrollback, --sblen);
463         } else {
464             line = smalloc(TSIZE * (newcols + 2));
465             line[0] = newcols;
466             for (j = 0; j <= newcols; j++)
467                 line[j + 1] = ERASE_CHAR;
468         }
469         addpos234(screen, line, 0);
470     }
471     /* Do this loop to shrink the screen if newrows < rows */
472     for (i = newrows; i < rows; i++) {
473         line = delpos234(screen, 0);
474         addpos234(scrollback, line, sblen++);
475     }
476     assert(count234(screen) == newrows);
477     while (sblen > newsavelines) {
478         line = delpos234(scrollback, 0);
479         sfree(line);
480         sblen--;
481     }
482     assert(count234(scrollback) <= newsavelines);
483     disptop = 0;
484
485     newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
486     for (i = 0; i < newrows * (newcols + 1); i++)
487         newdisp[i] = ATTR_INVALID;
488     sfree(disptext);
489     disptext = newdisp;
490     dispcurs = NULL;
491
492     newalt = newtree234(NULL);
493     for (i = 0; i < newrows; i++) {
494         line = smalloc(TSIZE * (newcols + 2));
495         line[0] = newcols;
496         for (j = 0; j <= newcols; j++)
497             line[j + 1] = erase_char;
498         addpos234(newalt, line, i);
499     }
500     if (alt_screen) {
501         while (NULL != (line = delpos234(alt_screen, 0)))
502             sfree(line);
503         freetree234(alt_screen);
504     }
505     alt_screen = newalt;
506
507     tabs = srealloc(tabs, newcols * sizeof(*tabs));
508     {
509         int i;
510         for (i = (cols > 0 ? cols : 0); i < newcols; i++)
511             tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
512     }
513
514     if (rows > 0)
515         curs.y += newrows - rows;
516     if (curs.y < 0)
517         curs.y = 0;
518     if (curs.y >= newrows)
519         curs.y = newrows - 1;
520     if (curs.x >= newcols)
521         curs.x = newcols - 1;
522     alt_x = alt_y = 0;
523     wrapnext = alt_wnext = FALSE;
524
525     rows = newrows;
526     cols = newcols;
527     savelines = newsavelines;
528     fix_cpos;
529
530     swap_screen(save_alt_which);
531
532     update_sbar();
533     term_update();
534     back->size();
535 }
536
537 /*
538  * Swap screens.
539  */
540 static void swap_screen(int which)
541 {
542     int t;
543     tree234 *ttr;
544
545     if (which == alt_which)
546         return;
547
548     alt_which = which;
549
550     ttr = alt_screen;
551     alt_screen = screen;
552     screen = ttr;
553     t = curs.x;
554     curs.x = alt_x;
555     alt_x = t;
556     t = curs.y;
557     curs.y = alt_y;
558     alt_y = t;
559     t = marg_t;
560     marg_t = alt_t;
561     alt_t = t;
562     t = marg_b;
563     marg_b = alt_b;
564     alt_b = t;
565     t = dec_om;
566     dec_om = alt_om;
567     alt_om = t;
568     t = wrap;
569     wrap = alt_wrap;
570     alt_wrap = t;
571     t = wrapnext;
572     wrapnext = alt_wnext;
573     alt_wnext = t;
574     t = insert;
575     insert = alt_ins;
576     alt_ins = t;
577     t = cset;
578     cset = alt_cset;
579     alt_cset = t;
580     t = utf;
581     utf = alt_utf;
582     alt_utf = t;
583     t = sco_acs;
584     sco_acs = alt_sco_acs;
585     alt_sco_acs = t;
586
587     fix_cpos;
588 }
589
590 /*
591  * Update the scroll bar.
592  */
593 static void update_sbar(void)
594 {
595     int nscroll;
596
597     nscroll = count234(scrollback);
598
599     set_sbar(nscroll + rows, nscroll + disptop, rows);
600 }
601
602 /*
603  * Check whether the region bounded by the two pointers intersects
604  * the scroll region, and de-select the on-screen selection if so.
605  */
606 static void check_selection(pos from, pos to)
607 {
608     if (poslt(from, selend) && poslt(selstart, to))
609         deselect();
610 }
611
612 /*
613  * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
614  * for backward.) `sb' is TRUE if the scrolling is permitted to
615  * affect the scrollback buffer.
616  * 
617  * NB this function invalidates all pointers into lines of the
618  * screen data structures. In particular, you MUST call fix_cpos
619  * after calling scroll() and before doing anything else that
620  * uses the cpos shortcut pointer.
621  */
622 static void scroll(int topline, int botline, int lines, int sb)
623 {
624     unsigned long *line, *line2;
625     int i, seltop;
626
627     if (topline != 0 || alt_which != 0)
628         sb = FALSE;
629
630     if (lines < 0) {
631         while (lines < 0) {
632             line = delpos234(screen, botline);
633             line = resizeline(line, cols);
634             for (i = 0; i < cols; i++)
635                 line[i + 1] = erase_char;
636             line[cols + 1] = 0;
637             addpos234(screen, line, topline);
638
639             if (selstart.y >= topline && selstart.y <= botline) {
640                 selstart.y++;
641                 if (selstart.y > botline) {
642                     selstart.y = botline;
643                     selstart.x = 0;
644                 }
645             }
646             if (selend.y >= topline && selend.y <= botline) {
647                 selend.y++;
648                 if (selend.y > botline) {
649                     selend.y = botline;
650                     selend.x = 0;
651                 }
652             }
653
654             lines++;
655         }
656     } else {
657         while (lines > 0) {
658             line = delpos234(screen, topline);
659             if (sb && savelines > 0) {
660                 int sblen = count234(scrollback);
661                 /*
662                  * We must add this line to the scrollback. We'll
663                  * remove a line from the top of the scrollback to
664                  * replace it, or allocate a new one if the
665                  * scrollback isn't full.
666                  */
667                 if (sblen == savelines) {
668                     sblen--, line2 = delpos234(scrollback, 0);
669                 } else {
670                     line2 = smalloc(TSIZE * (cols + 2));
671                     line2[0] = cols;
672                 }
673                 addpos234(scrollback, line, sblen);
674                 line = line2;
675
676                 /*
677                  * If the user is currently looking at part of the
678                  * scrollback, and they haven't enabled any options
679                  * that are going to reset the scrollback as a
680                  * result of this movement, then the chances are
681                  * they'd like to keep looking at the same line. So
682                  * we move their viewpoint at the same rate as the
683                  * scroll, at least until their viewpoint hits the
684                  * top end of the scrollback buffer, at which point
685                  * we don't have the choice any more.
686                  * 
687                  * Thanks to Jan Holmen Holsten for the idea and
688                  * initial implementation.
689                  */
690                 if (disptop > -savelines && disptop < 0)
691                     disptop--;
692             }
693             line = resizeline(line, cols);
694             for (i = 0; i < cols; i++)
695                 line[i + 1] = erase_char;
696             line[cols + 1] = 0;
697             addpos234(screen, line, botline);
698
699             /*
700              * If the selection endpoints move into the scrollback,
701              * we keep them moving until they hit the top. However,
702              * of course, if the line _hasn't_ moved into the
703              * scrollback then we don't do this, and cut them off
704              * at the top of the scroll region.
705              * 
706              * This applies to selstart and selend (for an existing
707              * selection), and also selanchor (for one being
708              * selected as we speak).
709              */
710             seltop = sb ? -savelines : topline;
711
712             if (selstart.y >= seltop && selstart.y <= botline) {
713                 selstart.y--;
714                 if (selstart.y < seltop) {
715                     selstart.y = seltop;
716                     selstart.x = 0;
717                 }
718             }
719             if (selend.y >= seltop && selend.y <= botline) {
720                 selend.y--;
721                 if (selend.y < seltop) {
722                     selend.y = seltop;
723                     selend.x = 0;
724                 }
725             }
726             if (selanchor.y >= seltop && selanchor.y <= botline) {
727                 selanchor.y--;
728                 if (selanchor.y < seltop) {
729                     selanchor.y = seltop;
730                     selanchor.x = 0;
731                 }
732             }
733
734             lines--;
735         }
736     }
737 }
738
739 /*
740  * Move the cursor to a given position, clipping at boundaries. We
741  * may or may not want to clip at the scroll margin: marg_clip is 0
742  * not to, 1 to disallow _passing_ the margins, and 2 to disallow
743  * even _being_ outside the margins.
744  */
745 static void move(int x, int y, int marg_clip)
746 {
747     if (x < 0)
748         x = 0;
749     if (x >= cols)
750         x = cols - 1;
751     if (marg_clip) {
752         if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
753             y = marg_t;
754         if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
755             y = marg_b;
756     }
757     if (y < 0)
758         y = 0;
759     if (y >= rows)
760         y = rows - 1;
761     curs.x = x;
762     curs.y = y;
763     fix_cpos;
764     wrapnext = FALSE;
765 }
766
767 /*
768  * Save or restore the cursor and SGR mode.
769  */
770 static void save_cursor(int save)
771 {
772     if (save) {
773         savecurs = curs;
774         save_attr = curr_attr;
775         save_cset = cset;
776         save_utf = utf;
777         save_wnext = wrapnext;
778         save_csattr = cset_attr[cset];
779         save_sco_acs = sco_acs;
780     } else {
781         curs = savecurs;
782         /* Make sure the window hasn't shrunk since the save */
783         if (curs.x >= cols)
784             curs.x = cols - 1;
785         if (curs.y >= rows)
786             curs.y = rows - 1;
787
788         curr_attr = save_attr;
789         cset = save_cset;
790         utf = save_utf;
791         wrapnext = save_wnext;
792         /*
793          * wrapnext might reset to False if the x position is no
794          * longer at the rightmost edge.
795          */
796         if (wrapnext && curs.x < cols-1)
797             wrapnext = FALSE;
798         cset_attr[cset] = save_csattr;
799         sco_acs = save_sco_acs;
800         fix_cpos;
801         if (use_bce)
802             erase_char = (' ' | ATTR_ASCII |
803                          (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
804     }
805 }
806
807 /*
808  * Erase a large portion of the screen: the whole screen, or the
809  * whole line, or parts thereof.
810  */
811 static void erase_lots(int line_only, int from_begin, int to_end)
812 {
813     pos start, end;
814     int erase_lattr;
815     unsigned long *ldata;
816
817     if (line_only) {
818         start.y = curs.y;
819         start.x = 0;
820         end.y = curs.y + 1;
821         end.x = 0;
822         erase_lattr = FALSE;
823     } else {
824         start.y = 0;
825         start.x = 0;
826         end.y = rows;
827         end.x = 0;
828         erase_lattr = TRUE;
829     }
830     if (!from_begin) {
831         start = curs;
832     }
833     if (!to_end) {
834         end = curs;
835         incpos(end);
836     }
837     check_selection(start, end);
838
839     /* Clear screen also forces a full window redraw, just in case. */
840     if (start.y == 0 && start.x == 0 && end.y == rows)
841         term_invalidate();
842
843     ldata = lineptr(start.y);
844     while (poslt(start, end)) {
845         if (start.x == cols && !erase_lattr)
846             ldata[start.x] &= ~LATTR_WRAPPED;
847         else
848             ldata[start.x] = erase_char;
849         if (incpos(start) && start.y < rows)
850             ldata = lineptr(start.y);
851     }
852 }
853
854 /*
855  * Insert or delete characters within the current line. n is +ve if
856  * insertion is desired, and -ve for deletion.
857  */
858 static void insch(int n)
859 {
860     int dir = (n < 0 ? -1 : +1);
861     int m;
862     pos cursplus;
863     unsigned long *ldata;
864
865     n = (n < 0 ? -n : n);
866     if (n > cols - curs.x)
867         n = cols - curs.x;
868     m = cols - curs.x - n;
869     cursplus.y = curs.y;
870     cursplus.x = curs.x + n;
871     check_selection(curs, cursplus);
872     ldata = lineptr(curs.y);
873     if (dir < 0) {
874         memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
875         while (n--)
876             ldata[curs.x + m++] = erase_char;
877     } else {
878         memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
879         while (n--)
880             ldata[curs.x + n] = erase_char;
881     }
882 }
883
884 /*
885  * Toggle terminal mode `mode' to state `state'. (`query' indicates
886  * whether the mode is a DEC private one or a normal one.)
887  */
888 static void toggle_mode(int mode, int query, int state)
889 {
890     unsigned long ticks;
891
892     if (query)
893         switch (mode) {
894           case 1:                      /* application cursor keys */
895             app_cursor_keys = state;
896             break;
897           case 2:                      /* VT52 mode */
898             vt52_mode = !state;
899             if (vt52_mode) {
900                 blink_is_real = FALSE;
901                 vt52_bold = FALSE;
902             } else {
903                 blink_is_real = cfg.blinktext;
904             }
905             break;
906           case 3:                      /* 80/132 columns */
907             deselect();
908             if (!cfg.no_remote_resize)
909                 request_resize(state ? 132 : 80, rows);
910             reset_132 = state;
911             break;
912           case 5:                      /* reverse video */
913             /*
914              * Toggle reverse video. If we receive an OFF within the
915              * visual bell timeout period after an ON, we trigger an
916              * effective visual bell, so that ESC[?5hESC[?5l will
917              * always be an actually _visible_ visual bell.
918              */
919             ticks = GETTICKCOUNT();
920             /* turn off a previous vbell to avoid inconsistencies */
921             if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
922                 in_vbell = FALSE;
923             if (rvideo && !state &&    /* we're turning it off... */
924                 (ticks - rvbell_startpoint) < VBELL_TIMEOUT) {  /* ...soon */
925                 /* If there's no vbell timeout already, or this one lasts
926                  * longer, replace vbell_timeout with ours. */
927                 if (!in_vbell ||
928                     (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
929                     vbell_startpoint = rvbell_startpoint;
930                 in_vbell = TRUE;       /* we may clear rvideo but we set in_vbell */
931             } else if (!rvideo && state) {
932                 /* This is an ON, so we notice the time and save it. */
933                 rvbell_startpoint = ticks;
934             }
935             rvideo = state;
936             seen_disp_event = TRUE;
937             if (state)
938                 term_update();
939             break;
940           case 6:                      /* DEC origin mode */
941             dec_om = state;
942             break;
943           case 7:                      /* auto wrap */
944             wrap = state;
945             break;
946           case 8:                      /* auto key repeat */
947             repeat_off = !state;
948             break;
949           case 10:                     /* set local edit mode */
950             term_editing = state;
951             ldisc_send(NULL, 0, 0);    /* cause ldisc to notice changes */
952             break;
953           case 25:                     /* enable/disable cursor */
954             compatibility2(OTHER, VT220);
955             cursor_on = state;
956             seen_disp_event = TRUE;
957             break;
958           case 47:                     /* alternate screen */
959             compatibility(OTHER);
960             deselect();
961             swap_screen(cfg.no_alt_screen ? 0 : state);
962             disptop = 0;
963             break;
964           case 1000:                   /* xterm mouse 1 */
965             xterm_mouse = state ? 1 : 0;
966             set_raw_mouse_mode(state);
967             break;
968           case 1002:                   /* xterm mouse 2 */
969             xterm_mouse = state ? 2 : 0;
970             set_raw_mouse_mode(state);
971             break;
972     } else
973         switch (mode) {
974           case 4:                      /* set insert mode */
975             compatibility(VT102);
976             insert = state;
977             break;
978           case 12:                     /* set echo mode */
979             term_echoing = !state;
980             ldisc_send(NULL, 0, 0);    /* cause ldisc to notice changes */
981             break;
982           case 20:                     /* Return sends ... */
983             cr_lf_return = state;
984             break;
985           case 34:                     /* Make cursor BIG */
986             compatibility2(OTHER, VT220);
987             big_cursor = !state;
988         }
989 }
990
991 /*
992  * Process an OSC sequence: set window title or icon name.
993  */
994 static void do_osc(void)
995 {
996     if (osc_w) {
997         while (osc_strlen--)
998             wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
999     } else {
1000         osc_string[osc_strlen] = '\0';
1001         switch (esc_args[0]) {
1002           case 0:
1003           case 1:
1004             if (!cfg.no_remote_wintitle)
1005                 set_icon(osc_string);
1006             if (esc_args[0] == 1)
1007                 break;
1008             /* fall through: parameter 0 means set both */
1009           case 2:
1010           case 21:
1011             if (!cfg.no_remote_wintitle)
1012                 set_title(osc_string);
1013             break;
1014         }
1015     }
1016 }
1017
1018 /*
1019  * ANSI printing routines.
1020  */
1021 static void term_print_setup(void)
1022 {
1023     bufchain_clear(&printer_buf);
1024     print_job = printer_start_job(cfg.printer);
1025 }
1026 static void term_print_flush(void)
1027 {
1028     void *data;
1029     int len;
1030     int size;
1031     while ((size = bufchain_size(&printer_buf)) > 5) {
1032         bufchain_prefix(&printer_buf, &data, &len);
1033         if (len > size-5)
1034             len = size-5;
1035         printer_job_data(print_job, data, len);
1036         bufchain_consume(&printer_buf, len);
1037     }
1038 }
1039 static void term_print_finish(void)
1040 {
1041     void *data;
1042     int len, size;
1043     char c;
1044
1045     if (!printing && !only_printing)
1046         return;                        /* we need do nothing */
1047
1048     term_print_flush();
1049     while ((size = bufchain_size(&printer_buf)) > 0) {
1050         bufchain_prefix(&printer_buf, &data, &len);
1051         c = *(char *)data;
1052         if (c == '\033' || c == '\233') {
1053             bufchain_consume(&printer_buf, size);
1054             break;
1055         } else {
1056             printer_job_data(print_job, &c, 1);
1057             bufchain_consume(&printer_buf, 1);
1058         }
1059     }
1060     printer_finish_job(print_job);
1061     print_job = NULL;
1062     printing = only_printing = FALSE;
1063 }
1064
1065 /*
1066  * Remove everything currently in `inbuf' and stick it up on the
1067  * in-memory display. There's a big state machine in here to
1068  * process escape sequences...
1069  */
1070 void term_out(void)
1071 {
1072     int c, unget;
1073     unsigned char localbuf[256], *chars;
1074     int nchars = 0;
1075
1076     unget = -1;
1077
1078     chars = NULL;                      /* placate compiler warnings */
1079     while (nchars > 0 || bufchain_size(&inbuf) > 0) {
1080         if (unget == -1) {
1081             if (nchars == 0) {
1082                 void *ret;
1083                 bufchain_prefix(&inbuf, &ret, &nchars);
1084                 if (nchars > sizeof(localbuf))
1085                     nchars = sizeof(localbuf);
1086                 memcpy(localbuf, ret, nchars);
1087                 bufchain_consume(&inbuf, nchars);
1088                 chars = localbuf;
1089                 assert(chars != NULL);
1090             }
1091             c = *chars++;
1092             nchars--;
1093
1094             /*
1095              * Optionally log the session traffic to a file. Useful for
1096              * debugging and possibly also useful for actual logging.
1097              */
1098             if (cfg.logtype == LGTYP_DEBUG)
1099                 logtraffic((unsigned char) c, LGTYP_DEBUG);
1100         } else {
1101             c = unget;
1102             unget = -1;
1103         }
1104
1105         /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1106          * be able to display 8-bit characters, but I'll let that go 'cause
1107          * of i18n.
1108          */
1109
1110         /*
1111          * If we're printing, add the character to the printer
1112          * buffer.
1113          */
1114         if (printing) {
1115             bufchain_add(&printer_buf, &c, 1);
1116
1117             /*
1118              * If we're in print-only mode, we use a much simpler
1119              * state machine designed only to recognise the ESC[4i
1120              * termination sequence.
1121              */
1122             if (only_printing) {
1123                 if (c == '\033')
1124                     print_state = 1;
1125                 else if (c == (unsigned char)'\233')
1126                     print_state = 2;
1127                 else if (c == '[' && print_state == 1)
1128                     print_state = 2;
1129                 else if (c == '4' && print_state == 2)
1130                     print_state = 3;
1131                 else if (c == 'i' && print_state == 3)
1132                     print_state = 4;
1133                 else
1134                     print_state = 0;
1135                 if (print_state == 4) {
1136                     printing = only_printing = FALSE;
1137                     term_print_finish();
1138                 }
1139                 continue;
1140             }
1141         }
1142
1143         /* First see about all those translations. */
1144         if (termstate == TOPLEVEL) {
1145             if (in_utf)
1146                 switch (utf_state) {
1147                   case 0:
1148                     if (c < 0x80) {
1149                         /* UTF-8 must be stateless so we ignore iso2022. */
1150                         if (unitab_ctrl[c] != 0xFF) 
1151                              c = unitab_ctrl[c];
1152                         else c = ((unsigned char)c) | ATTR_ASCII;
1153                         break;
1154                     } else if ((c & 0xe0) == 0xc0) {
1155                         utf_size = utf_state = 1;
1156                         utf_char = (c & 0x1f);
1157                     } else if ((c & 0xf0) == 0xe0) {
1158                         utf_size = utf_state = 2;
1159                         utf_char = (c & 0x0f);
1160                     } else if ((c & 0xf8) == 0xf0) {
1161                         utf_size = utf_state = 3;
1162                         utf_char = (c & 0x07);
1163                     } else if ((c & 0xfc) == 0xf8) {
1164                         utf_size = utf_state = 4;
1165                         utf_char = (c & 0x03);
1166                     } else if ((c & 0xfe) == 0xfc) {
1167                         utf_size = utf_state = 5;
1168                         utf_char = (c & 0x01);
1169                     } else {
1170                         c = UCSERR;
1171                         break;
1172                     }
1173                     continue;
1174                   case 1:
1175                   case 2:
1176                   case 3:
1177                   case 4:
1178                   case 5:
1179                     if ((c & 0xC0) != 0x80) {
1180                         unget = c;
1181                         c = UCSERR;
1182                         utf_state = 0;
1183                         break;
1184                     }
1185                     utf_char = (utf_char << 6) | (c & 0x3f);
1186                     if (--utf_state)
1187                         continue;
1188
1189                     c = utf_char;
1190
1191                     /* Is somebody trying to be evil! */
1192                     if (c < 0x80 ||
1193                         (c < 0x800 && utf_size >= 2) ||
1194                         (c < 0x10000 && utf_size >= 3) ||
1195                         (c < 0x200000 && utf_size >= 4) ||
1196                         (c < 0x4000000 && utf_size >= 5))
1197                         c = UCSERR;
1198
1199                     /* Unicode line separator and paragraph separator are CR-LF */
1200                     if (c == 0x2028 || c == 0x2029)
1201                         c = 0x85;
1202
1203                     /* High controls are probably a Baaad idea too. */
1204                     if (c < 0xA0)
1205                         c = 0xFFFD;
1206
1207                     /* The UTF-16 surrogates are not nice either. */
1208                     /*       The standard give the option of decoding these: 
1209                      *       I don't want to! */
1210                     if (c >= 0xD800 && c < 0xE000)
1211                         c = UCSERR;
1212
1213                     /* ISO 10646 characters now limited to UTF-16 range. */
1214                     if (c > 0x10FFFF)
1215                         c = UCSERR;
1216
1217                     /* This is currently a TagPhobic application.. */
1218                     if (c >= 0xE0000 && c <= 0xE007F)
1219                         continue;
1220
1221                     /* U+FEFF is best seen as a null. */
1222                     if (c == 0xFEFF)
1223                         continue;
1224                     /* But U+FFFE is an error. */
1225                     if (c == 0xFFFE || c == 0xFFFF)
1226                         c = UCSERR;
1227
1228                     /* Oops this is a 16bit implementation */
1229                     if (c >= 0x10000)
1230                         c = 0xFFFD;
1231                     break;
1232             }
1233             /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1234             else if(sco_acs && 
1235                     (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1236             {
1237                if (sco_acs == 2) c ^= 0x80;
1238                c |= ATTR_SCOACS;
1239             } else {
1240                 switch (cset_attr[cset]) {
1241                     /* 
1242                      * Linedraw characters are different from 'ESC ( B'
1243                      * only for a small range. For ones outside that
1244                      * range, make sure we use the same font as well as
1245                      * the same encoding.
1246                      */
1247                   case ATTR_LINEDRW:
1248                     if (unitab_ctrl[c] != 0xFF)
1249                         c = unitab_ctrl[c];
1250                     else
1251                         c = ((unsigned char) c) | ATTR_LINEDRW;
1252                     break;
1253
1254                   case ATTR_GBCHR:
1255                     /* If UK-ASCII, make the '#' a LineDraw Pound */
1256                     if (c == '#') {
1257                         c = '}' | ATTR_LINEDRW;
1258                         break;
1259                     }
1260                   /*FALLTHROUGH*/ case ATTR_ASCII:
1261                     if (unitab_ctrl[c] != 0xFF)
1262                         c = unitab_ctrl[c];
1263                     else
1264                         c = ((unsigned char) c) | ATTR_ASCII;
1265                     break;
1266                 case ATTR_SCOACS:
1267                     if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1268                     break;
1269                 }
1270             }
1271         }
1272
1273         /* How about C1 controls ? */
1274         if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1275             has_compat(VT220)) {
1276             termstate = SEEN_ESC;
1277             esc_query = FALSE;
1278             c = '@' + (c & 0x1F);
1279         }
1280
1281         /* Or the GL control. */
1282         if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1283             if (curs.x && !wrapnext)
1284                 curs.x--;
1285             wrapnext = FALSE;
1286             fix_cpos;
1287             if (!cfg.no_dbackspace)    /* destructive bksp might be disabled */
1288                 *cpos = (' ' | curr_attr | ATTR_ASCII);
1289         } else
1290             /* Or normal C0 controls. */
1291         if ((c & -32) == 0 && termstate < DO_CTRLS) {
1292             switch (c) {
1293               case '\005':             /* terminal type query */
1294                 /* Strictly speaking this is VT100 but a VT100 defaults to
1295                  * no response. Other terminals respond at their option.
1296                  *
1297                  * Don't put a CR in the default string as this tends to
1298                  * upset some weird software.
1299                  *
1300                  * An xterm returns "xterm" (5 characters)
1301                  */
1302                 compatibility(ANSIMIN);
1303                 {
1304                     char abuf[256], *s, *d;
1305                     int state = 0;
1306                     for (s = cfg.answerback, d = abuf; *s; s++) {
1307                         if (state) {
1308                             if (*s >= 'a' && *s <= 'z')
1309                                 *d++ = (*s - ('a' - 1));
1310                             else if ((*s >= '@' && *s <= '_') ||
1311                                      *s == '?' || (*s & 0x80))
1312                                 *d++ = ('@' ^ *s);
1313                             else if (*s == '~')
1314                                 *d++ = '^';
1315                             state = 0;
1316                         } else if (*s == '^') {
1317                             state = 1;
1318                         } else
1319                             *d++ = *s;
1320                     }
1321                     lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
1322                 }
1323                 break;
1324               case '\007':
1325                 {
1326                     struct beeptime *newbeep;
1327                     unsigned long ticks;
1328
1329                     ticks = GETTICKCOUNT();
1330
1331                     if (!beep_overloaded) {
1332                         newbeep = smalloc(sizeof(struct beeptime));
1333                         newbeep->ticks = ticks;
1334                         newbeep->next = NULL;
1335                         if (!beephead)
1336                             beephead = newbeep;
1337                         else
1338                             beeptail->next = newbeep;
1339                         beeptail = newbeep;
1340                         nbeeps++;
1341                     }
1342
1343                     /*
1344                      * Throw out any beeps that happened more than
1345                      * t seconds ago.
1346                      */
1347                     while (beephead &&
1348                            beephead->ticks < ticks - cfg.bellovl_t) {
1349                         struct beeptime *tmp = beephead;
1350                         beephead = tmp->next;
1351                         sfree(tmp);
1352                         if (!beephead)
1353                             beeptail = NULL;
1354                         nbeeps--;
1355                     }
1356
1357                     if (cfg.bellovl && beep_overloaded &&
1358                         ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
1359                         /*
1360                          * If we're currently overloaded and the
1361                          * last beep was more than s seconds ago,
1362                          * leave overload mode.
1363                          */
1364                         beep_overloaded = FALSE;
1365                     } else if (cfg.bellovl && !beep_overloaded &&
1366                                nbeeps >= cfg.bellovl_n) {
1367                         /*
1368                          * Now, if we have n or more beeps
1369                          * remaining in the queue, go into overload
1370                          * mode.
1371                          */
1372                         beep_overloaded = TRUE;
1373                     }
1374                     lastbeep = ticks;
1375
1376                     /*
1377                      * Perform an actual beep if we're not overloaded.
1378                      */
1379                     if (!cfg.bellovl || !beep_overloaded) {
1380                         beep(cfg.beep);
1381                         if (cfg.beep == BELL_VISUAL) {
1382                             in_vbell = TRUE;
1383                             vbell_startpoint = ticks;
1384                             term_update();
1385                         }
1386                     }
1387                     disptop = 0;
1388                 }
1389                 break;
1390               case '\b':
1391                 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1392                 else if (curs.x == 0 && curs.y > 0)
1393                     curs.x = cols - 1, curs.y--;
1394                 else if (wrapnext)
1395                     wrapnext = FALSE;
1396                 else
1397                     curs.x--;
1398                 fix_cpos;
1399                 seen_disp_event = TRUE;
1400                 break;
1401               case '\016':
1402                 compatibility(VT100);
1403                 cset = 1;
1404                 break;
1405               case '\017':
1406                 compatibility(VT100);
1407                 cset = 0;
1408                 break;
1409               case '\033':
1410                 if (vt52_mode)
1411                     termstate = VT52_ESC;
1412                 else {
1413                     compatibility(ANSIMIN);
1414                     termstate = SEEN_ESC;
1415                     esc_query = FALSE;
1416                 }
1417                 break;
1418               case '\r':
1419                 curs.x = 0;
1420                 wrapnext = FALSE;
1421                 fix_cpos;
1422                 seen_disp_event = TRUE;
1423                 paste_hold = 0;
1424                 logtraffic((unsigned char) c, LGTYP_ASCII);
1425                 break;
1426               case '\014':
1427                 if (has_compat(SCOANSI)) {
1428                     move(0, 0, 0);
1429                     erase_lots(FALSE, FALSE, TRUE);
1430                     disptop = 0;
1431                     wrapnext = FALSE;
1432                     seen_disp_event = 1;
1433                     break;
1434                 }
1435               case '\013':
1436                 compatibility(VT100);
1437               case '\n':
1438                 if (curs.y == marg_b)
1439                     scroll(marg_t, marg_b, 1, TRUE);
1440                 else if (curs.y < rows - 1)
1441                     curs.y++;
1442                 if (cfg.lfhascr)
1443                     curs.x = 0;
1444                 fix_cpos;
1445                 wrapnext = FALSE;
1446                 seen_disp_event = 1;
1447                 paste_hold = 0;
1448                 logtraffic((unsigned char) c, LGTYP_ASCII);
1449                 break;
1450               case '\t':
1451                 {
1452                     pos old_curs = curs;
1453                     unsigned long *ldata = lineptr(curs.y);
1454
1455                     do {
1456                         curs.x++;
1457                     } while (curs.x < cols - 1 && !tabs[curs.x]);
1458
1459                     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1460                         if (curs.x >= cols / 2)
1461                             curs.x = cols / 2 - 1;
1462                     } else {
1463                         if (curs.x >= cols)
1464                             curs.x = cols - 1;
1465                     }
1466
1467                     fix_cpos;
1468                     check_selection(old_curs, curs);
1469                 }
1470                 seen_disp_event = TRUE;
1471                 break;
1472             }
1473         } else
1474             switch (termstate) {
1475               case TOPLEVEL:
1476                 /* Only graphic characters get this far, ctrls are stripped above */
1477                 if (wrapnext && wrap) {
1478                     cpos[1] |= LATTR_WRAPPED;
1479                     if (curs.y == marg_b)
1480                         scroll(marg_t, marg_b, 1, TRUE);
1481                     else if (curs.y < rows - 1)
1482                         curs.y++;
1483                     curs.x = 0;
1484                     fix_cpos;
1485                     wrapnext = FALSE;
1486                 }
1487                 if (insert)
1488                     insch(1);
1489                 if (selstate != NO_SELECTION) {
1490                     pos cursplus = curs;
1491                     incpos(cursplus);
1492                     check_selection(curs, cursplus);
1493                 }
1494                 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1495                     logtraffic((unsigned char) c, LGTYP_ASCII);
1496                 {
1497                     extern int wcwidth(wchar_t ucs);
1498                     int width = 0;
1499                     if (DIRECT_CHAR(c))
1500                         width = 1;
1501                     if (!width)
1502                         width = wcwidth((wchar_t) c);
1503                     switch (width) {
1504                       case 2:
1505                         *cpos++ = c | curr_attr;
1506                         if (++curs.x == cols) {
1507                             *cpos |= LATTR_WRAPPED;
1508                             if (curs.y == marg_b)
1509                                 scroll(marg_t, marg_b, 1, TRUE);
1510                             else if (curs.y < rows - 1)
1511                                 curs.y++;
1512                             curs.x = 0;
1513                             fix_cpos;
1514                         }
1515                         *cpos++ = UCSWIDE | curr_attr;
1516                         break;
1517                       case 1:
1518                         *cpos++ = c | curr_attr;
1519                         break;
1520                       default:
1521                         continue;
1522                     }
1523                 }
1524                 curs.x++;
1525                 if (curs.x == cols) {
1526                     cpos--;
1527                     curs.x--;
1528                     wrapnext = TRUE;
1529                     if (wrap && vt52_mode) {
1530                         cpos[1] |= LATTR_WRAPPED;
1531                         if (curs.y == marg_b)
1532                             scroll(marg_t, marg_b, 1, TRUE);
1533                         else if (curs.y < rows - 1)
1534                             curs.y++;
1535                         curs.x = 0;
1536                         fix_cpos;
1537                         wrapnext = FALSE;
1538                     }
1539                 }
1540                 seen_disp_event = 1;
1541                 break;
1542
1543               case OSC_MAYBE_ST:
1544                 /*
1545                  * This state is virtually identical to SEEN_ESC, with the
1546                  * exception that we have an OSC sequence in the pipeline,
1547                  * and _if_ we see a backslash, we process it.
1548                  */
1549                 if (c == '\\') {
1550                     do_osc();
1551                     termstate = TOPLEVEL;
1552                     break;
1553                 }
1554                 /* else fall through */
1555               case SEEN_ESC:
1556                 if (c >= ' ' && c <= '/') {
1557                     if (esc_query)
1558                         esc_query = -1;
1559                     else
1560                         esc_query = c;
1561                     break;
1562                 }
1563                 termstate = TOPLEVEL;
1564                 switch (ANSI(c, esc_query)) {
1565                   case '[':            /* enter CSI mode */
1566                     termstate = SEEN_CSI;
1567                     esc_nargs = 1;
1568                     esc_args[0] = ARG_DEFAULT;
1569                     esc_query = FALSE;
1570                     break;
1571                   case ']':            /* xterm escape sequences */
1572                     /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1573                     compatibility(OTHER);
1574                     termstate = SEEN_OSC;
1575                     esc_args[0] = 0;
1576                     break;
1577                   case '7':            /* save cursor */
1578                     compatibility(VT100);
1579                     save_cursor(TRUE);
1580                     break;
1581                   case '8':            /* restore cursor */
1582                     compatibility(VT100);
1583                     save_cursor(FALSE);
1584                     seen_disp_event = TRUE;
1585                     break;
1586                   case '=':
1587                     compatibility(VT100);
1588                     app_keypad_keys = TRUE;
1589                     break;
1590                   case '>':
1591                     compatibility(VT100);
1592                     app_keypad_keys = FALSE;
1593                     break;
1594                   case 'D':            /* exactly equivalent to LF */
1595                     compatibility(VT100);
1596                     if (curs.y == marg_b)
1597                         scroll(marg_t, marg_b, 1, TRUE);
1598                     else if (curs.y < rows - 1)
1599                         curs.y++;
1600                     fix_cpos;
1601                     wrapnext = FALSE;
1602                     seen_disp_event = TRUE;
1603                     break;
1604                   case 'E':            /* exactly equivalent to CR-LF */
1605                     compatibility(VT100);
1606                     curs.x = 0;
1607                     if (curs.y == marg_b)
1608                         scroll(marg_t, marg_b, 1, TRUE);
1609                     else if (curs.y < rows - 1)
1610                         curs.y++;
1611                     fix_cpos;
1612                     wrapnext = FALSE;
1613                     seen_disp_event = TRUE;
1614                     break;
1615                   case 'M':            /* reverse index - backwards LF */
1616                     compatibility(VT100);
1617                     if (curs.y == marg_t)
1618                         scroll(marg_t, marg_b, -1, TRUE);
1619                     else if (curs.y > 0)
1620                         curs.y--;
1621                     fix_cpos;
1622                     wrapnext = FALSE;
1623                     seen_disp_event = TRUE;
1624                     break;
1625                   case 'Z':            /* terminal type query */
1626                     compatibility(VT100);
1627                     ldisc_send(id_string, strlen(id_string), 0);
1628                     break;
1629                   case 'c':            /* restore power-on settings */
1630                     compatibility(VT100);
1631                     power_on();
1632                     if (reset_132) {
1633                         if (!cfg.no_remote_resize)
1634                             request_resize(80, rows);
1635                         reset_132 = 0;
1636                     }
1637                     fix_cpos;
1638                     disptop = 0;
1639                     seen_disp_event = TRUE;
1640                     break;
1641                   case 'H':            /* set a tab */
1642                     compatibility(VT100);
1643                     tabs[curs.x] = TRUE;
1644                     break;
1645
1646                   case ANSI('8', '#'):  /* ESC # 8 fills screen with Es :-) */
1647                     compatibility(VT100);
1648                     {
1649                         unsigned long *ldata;
1650                         int i, j;
1651                         pos scrtop, scrbot;
1652
1653                         for (i = 0; i < rows; i++) {
1654                             ldata = lineptr(i);
1655                             for (j = 0; j < cols; j++)
1656                                 ldata[j] = ATTR_DEFAULT | 'E';
1657                             ldata[cols] = 0;
1658                         }
1659                         disptop = 0;
1660                         seen_disp_event = TRUE;
1661                         scrtop.x = scrtop.y = 0;
1662                         scrbot.x = 0;
1663                         scrbot.y = rows;
1664                         check_selection(scrtop, scrbot);
1665                     }
1666                     break;
1667
1668                   case ANSI('3', '#'):
1669                   case ANSI('4', '#'):
1670                   case ANSI('5', '#'):
1671                   case ANSI('6', '#'):
1672                     compatibility(VT100);
1673                     {
1674                         unsigned long nlattr;
1675                         unsigned long *ldata;
1676                         switch (ANSI(c, esc_query)) {
1677                           case ANSI('3', '#'):
1678                             nlattr = LATTR_TOP;
1679                             break;
1680                           case ANSI('4', '#'):
1681                             nlattr = LATTR_BOT;
1682                             break;
1683                           case ANSI('5', '#'):
1684                             nlattr = LATTR_NORM;
1685                             break;
1686                           default: /* spiritually case ANSI('6', '#'): */
1687                             nlattr = LATTR_WIDE;
1688                             break;
1689                         }
1690                         ldata = lineptr(curs.y);
1691                         ldata[cols] &= ~LATTR_MODE;
1692                         ldata[cols] |= nlattr;
1693                     }
1694                     break;
1695
1696                   case ANSI('A', '('):
1697                     compatibility(VT100);
1698                     if (!cfg.no_remote_charset)
1699                         cset_attr[0] = ATTR_GBCHR;
1700                     break;
1701                   case ANSI('B', '('):
1702                     compatibility(VT100);
1703                     if (!cfg.no_remote_charset)
1704                         cset_attr[0] = ATTR_ASCII;
1705                     break;
1706                   case ANSI('0', '('):
1707                     compatibility(VT100);
1708                     if (!cfg.no_remote_charset)
1709                         cset_attr[0] = ATTR_LINEDRW;
1710                     break;
1711                   case ANSI('U', '('): 
1712                     compatibility(OTHER);
1713                     if (!cfg.no_remote_charset)
1714                         cset_attr[0] = ATTR_SCOACS; 
1715                     break;
1716
1717                   case ANSI('A', ')'):
1718                     compatibility(VT100);
1719                     if (!cfg.no_remote_charset)
1720                         cset_attr[1] = ATTR_GBCHR;
1721                     break;
1722                   case ANSI('B', ')'):
1723                     compatibility(VT100);
1724                     if (!cfg.no_remote_charset)
1725                         cset_attr[1] = ATTR_ASCII;
1726                     break;
1727                   case ANSI('0', ')'):
1728                     compatibility(VT100);
1729                     if (!cfg.no_remote_charset)
1730                         cset_attr[1] = ATTR_LINEDRW;
1731                     break;
1732                   case ANSI('U', ')'): 
1733                     compatibility(OTHER);
1734                     if (!cfg.no_remote_charset)
1735                         cset_attr[1] = ATTR_SCOACS; 
1736                     break;
1737
1738                   case ANSI('8', '%'):  /* Old Linux code */
1739                   case ANSI('G', '%'):
1740                     compatibility(OTHER);
1741                     if (!cfg.no_remote_charset)
1742                         utf = 1;
1743                     break;
1744                   case ANSI('@', '%'):
1745                     compatibility(OTHER);
1746                     if (!cfg.no_remote_charset)
1747                         utf = 0;
1748                     break;
1749                 }
1750                 break;
1751               case SEEN_CSI:
1752                 termstate = TOPLEVEL;  /* default */
1753                 if (isdigit(c)) {
1754                     if (esc_nargs <= ARGS_MAX) {
1755                         if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1756                             esc_args[esc_nargs - 1] = 0;
1757                         esc_args[esc_nargs - 1] =
1758                             10 * esc_args[esc_nargs - 1] + c - '0';
1759                     }
1760                     termstate = SEEN_CSI;
1761                 } else if (c == ';') {
1762                     if (++esc_nargs <= ARGS_MAX)
1763                         esc_args[esc_nargs - 1] = ARG_DEFAULT;
1764                     termstate = SEEN_CSI;
1765                 } else if (c < '@') {
1766                     if (esc_query)
1767                         esc_query = -1;
1768                     else if (c == '?')
1769                         esc_query = TRUE;
1770                     else
1771                         esc_query = c;
1772                     termstate = SEEN_CSI;
1773                 } else
1774                     switch (ANSI(c, esc_query)) {
1775                       case 'A':       /* move up N lines */
1776                         move(curs.x, curs.y - def(esc_args[0], 1), 1);
1777                         seen_disp_event = TRUE;
1778                         break;
1779                       case 'e':       /* move down N lines */
1780                         compatibility(ANSI);
1781                         /* FALLTHROUGH */
1782                       case 'B':
1783                         move(curs.x, curs.y + def(esc_args[0], 1), 1);
1784                         seen_disp_event = TRUE;
1785                         break;
1786                       case ANSI('c', '>'):      /* report xterm version */
1787                         compatibility(OTHER);
1788                         /* this reports xterm version 136 so that VIM can
1789                            use the drag messages from the mouse reporting */
1790                         ldisc_send("\033[>0;136;0c", 11, 0);
1791                         break;
1792                       case 'a':       /* move right N cols */
1793                         compatibility(ANSI);
1794                         /* FALLTHROUGH */
1795                       case 'C':
1796                         move(curs.x + def(esc_args[0], 1), curs.y, 1);
1797                         seen_disp_event = TRUE;
1798                         break;
1799                       case 'D':       /* move left N cols */
1800                         move(curs.x - def(esc_args[0], 1), curs.y, 1);
1801                         seen_disp_event = TRUE;
1802                         break;
1803                       case 'E':       /* move down N lines and CR */
1804                         compatibility(ANSI);
1805                         move(0, curs.y + def(esc_args[0], 1), 1);
1806                         seen_disp_event = TRUE;
1807                         break;
1808                       case 'F':       /* move up N lines and CR */
1809                         compatibility(ANSI);
1810                         move(0, curs.y - def(esc_args[0], 1), 1);
1811                         seen_disp_event = TRUE;
1812                         break;
1813                       case 'G':
1814                       case '`':       /* set horizontal posn */
1815                         compatibility(ANSI);
1816                         move(def(esc_args[0], 1) - 1, curs.y, 0);
1817                         seen_disp_event = TRUE;
1818                         break;
1819                       case 'd':       /* set vertical posn */
1820                         compatibility(ANSI);
1821                         move(curs.x,
1822                              (dec_om ? marg_t : 0) + def(esc_args[0],
1823                                                          1) - 1,
1824                              (dec_om ? 2 : 0));
1825                         seen_disp_event = TRUE;
1826                         break;
1827                       case 'H':
1828                       case 'f':       /* set horz and vert posns at once */
1829                         if (esc_nargs < 2)
1830                             esc_args[1] = ARG_DEFAULT;
1831                         move(def(esc_args[1], 1) - 1,
1832                              (dec_om ? marg_t : 0) + def(esc_args[0],
1833                                                          1) - 1,
1834                              (dec_om ? 2 : 0));
1835                         seen_disp_event = TRUE;
1836                         break;
1837                       case 'J':       /* erase screen or parts of it */
1838                         {
1839                             unsigned int i = def(esc_args[0], 0) + 1;
1840                             if (i > 3)
1841                                 i = 0;
1842                             erase_lots(FALSE, !!(i & 2), !!(i & 1));
1843                         }
1844                         disptop = 0;
1845                         seen_disp_event = TRUE;
1846                         break;
1847                       case 'K':       /* erase line or parts of it */
1848                         {
1849                             unsigned int i = def(esc_args[0], 0) + 1;
1850                             if (i > 3)
1851                                 i = 0;
1852                             erase_lots(TRUE, !!(i & 2), !!(i & 1));
1853                         }
1854                         seen_disp_event = TRUE;
1855                         break;
1856                       case 'L':       /* insert lines */
1857                         compatibility(VT102);
1858                         if (curs.y <= marg_b)
1859                             scroll(curs.y, marg_b, -def(esc_args[0], 1),
1860                                    FALSE);
1861                         fix_cpos;
1862                         seen_disp_event = TRUE;
1863                         break;
1864                       case 'M':       /* delete lines */
1865                         compatibility(VT102);
1866                         if (curs.y <= marg_b)
1867                             scroll(curs.y, marg_b, def(esc_args[0], 1),
1868                                    TRUE);
1869                         fix_cpos;
1870                         seen_disp_event = TRUE;
1871                         break;
1872                       case '@':       /* insert chars */
1873                         /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1874                         compatibility(VT102);
1875                         insch(def(esc_args[0], 1));
1876                         seen_disp_event = TRUE;
1877                         break;
1878                       case 'P':       /* delete chars */
1879                         compatibility(VT102);
1880                         insch(-def(esc_args[0], 1));
1881                         seen_disp_event = TRUE;
1882                         break;
1883                       case 'c':       /* terminal type query */
1884                         compatibility(VT100);
1885                         /* This is the response for a VT102 */
1886                         ldisc_send(id_string, strlen(id_string), 0);
1887                         break;
1888                       case 'n':       /* cursor position query */
1889                         if (esc_args[0] == 6) {
1890                             char buf[32];
1891                             sprintf(buf, "\033[%d;%dR", curs.y + 1,
1892                                     curs.x + 1);
1893                             ldisc_send(buf, strlen(buf), 0);
1894                         } else if (esc_args[0] == 5) {
1895                             ldisc_send("\033[0n", 4, 0);
1896                         }
1897                         break;
1898                       case 'h':       /* toggle modes to high */
1899                       case ANSI_QUE('h'):
1900                         compatibility(VT100);
1901                         {
1902                             int i;
1903                             for (i = 0; i < esc_nargs; i++)
1904                                 toggle_mode(esc_args[i], esc_query, TRUE);
1905                         }
1906                         break;
1907                       case 'i':
1908                       case ANSI_QUE('i'):
1909                         compatibility(VT100);
1910                         {
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 system blink rate */
3016     if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
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 (is_dbcs_leadbyte(font_codepage, (BYTE) c)) {
3184                         buf[0] = c;
3185                         buf[1] = (unsigned char) ldata[top.x + 1];
3186                         rv = mb_to_wc(font_codepage, 0, buf, 2, wbuf, 4);
3187                         top.x++;
3188                     } else {
3189                         buf[0] = c;
3190                         rv = mb_to_wc(font_codepage, 0, buf, 1, wbuf, 4);
3191                     }
3192
3193                     if (rv > 0) {
3194                         memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3195                         cbuf[rv] = 0;
3196                     }
3197                 }
3198             }
3199 #endif
3200
3201             for (p = cbuf; *p; p++) {
3202                 /* Enough overhead for trailing NL and nul */
3203                 if (wblen >= buflen - 16) {
3204                     workbuf =
3205                         srealloc(workbuf,
3206                                  sizeof(wchar_t) * (buflen += 100));
3207                     wbptr = workbuf + wblen;
3208                 }
3209                 wblen++;
3210                 *wbptr++ = *p;
3211             }
3212             top.x++;
3213         }
3214         if (nl) {
3215             int i;
3216             for (i = 0; i < sel_nl_sz; i++) {
3217                 wblen++;
3218                 *wbptr++ = sel_nl[i];
3219             }
3220         }
3221         top.y++;
3222         top.x = rect ? old_top_x : 0;
3223     }
3224 #if SELECTION_NUL_TERMINATED
3225     wblen++;
3226     *wbptr++ = 0;
3227 #endif
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
3692 #if MULTICLICK_ONLY_EVENT
3693                    || a == MA_2CLK || a == MA_3CLK
3694 #endif
3695                    )) {
3696         request_paste();
3697     }
3698
3699     term_update();
3700 }
3701
3702 void term_nopaste()
3703 {
3704     if (paste_len == 0)
3705         return;
3706     sfree(paste_buffer);
3707     paste_buffer = 0;
3708     paste_len = 0;
3709 }
3710
3711 int term_paste_pending(void)
3712 {
3713     return paste_len != 0;
3714 }
3715
3716 void term_paste()
3717 {
3718     static long last_paste = 0;
3719     long now, paste_diff;
3720
3721     if (paste_len == 0)
3722         return;
3723
3724     /* Don't wait forever to paste */
3725     if (paste_hold) {
3726         now = GETTICKCOUNT();
3727         paste_diff = now - last_paste;
3728         if (paste_diff >= 0 && paste_diff < 450)
3729             return;
3730     }
3731     paste_hold = 0;
3732
3733     while (paste_pos < paste_len) {
3734         int n = 0;
3735         while (n + paste_pos < paste_len) {
3736             if (paste_buffer[paste_pos + n++] == '\r')
3737                 break;
3738         }
3739         luni_send(paste_buffer + paste_pos, n, 0);
3740         paste_pos += n;
3741
3742         if (paste_pos < paste_len) {
3743             paste_hold = 1;
3744             return;
3745         }
3746     }
3747     sfree(paste_buffer);
3748     paste_buffer = 0;
3749     paste_len = 0;
3750 }
3751
3752 static void deselect(void)
3753 {
3754     selstate = NO_SELECTION;
3755     selstart.x = selstart.y = selend.x = selend.y = 0;
3756 }
3757
3758 void term_deselect(void)
3759 {
3760     deselect();
3761     term_update();
3762 }
3763
3764 int term_ldisc(int option)
3765 {
3766     if (option == LD_ECHO)
3767         return term_echoing;
3768     if (option == LD_EDIT)
3769         return term_editing;
3770     return FALSE;
3771 }
3772
3773 /*
3774  * from_backend(), to get data from the backend for the terminal.
3775  */
3776 int from_backend(int is_stderr, char *data, int len)
3777 {
3778     assert(len > 0);
3779
3780     bufchain_add(&inbuf, data, len);
3781
3782     /*
3783      * term_out() always completely empties inbuf. Therefore,
3784      * there's no reason at all to return anything other than zero
3785      * from this function, because there _can't_ be a question of
3786      * the remote side needing to wait until term_out() has cleared
3787      * a backlog.
3788      *
3789      * This is a slightly suboptimal way to deal with SSH2 - in
3790      * principle, the window mechanism would allow us to continue
3791      * to accept data on forwarded ports and X connections even
3792      * while the terminal processing was going slowly - but we
3793      * can't do the 100% right thing without moving the terminal
3794      * processing into a separate thread, and that might hurt
3795      * portability. So we manage stdout buffering the old SSH1 way:
3796      * if the terminal processing goes slowly, the whole SSH
3797      * connection stops accepting data until it's ready.
3798      *
3799      * In practice, I can't imagine this causing serious trouble.
3800      */
3801     return 0;
3802 }