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