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