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