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