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