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