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