]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - terminal.c
c3a4e2cba2e1d75b2fc2d300632002257622bda8
[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 CL_ANSIMIN      0x0001  /* Codes in all ANSI like terminals. */
13 #define CL_VT100        0x0002  /* VT100 */
14 #define CL_VT100AVO     0x0004  /* VT100 +AVO; 132x24 (not 132x14) & attrs */
15 #define CL_VT102        0x0008  /* VT102 */
16 #define CL_VT220        0x0010  /* VT220 */
17 #define CL_VT320        0x0020  /* VT320 */
18 #define CL_VT420        0x0040  /* VT420 */
19 #define CL_VT510        0x0080  /* VT510, NB VT510 includes ANSI */
20 #define CL_VT340TEXT    0x0100  /* VT340 extensions that appear in the VT420 */
21 #define CL_SCOANSI      0x1000  /* SCOANSI not in ANSIMIN. */
22 #define CL_ANSI         0x2000  /* ANSI ECMA-48 not in the VT100..VT420 */
23 #define CL_OTHER        0x4000  /* Others, Xterm, linux, putty, dunno, etc */
24
25 #define TM_VT100        (CL_ANSIMIN|CL_VT100)
26 #define TM_VT100AVO     (TM_VT100|CL_VT100AVO)
27 #define TM_VT102        (TM_VT100AVO|CL_VT102)
28 #define TM_VT220        (TM_VT102|CL_VT220)
29 #define TM_VTXXX        (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
30 #define TM_SCOANSI      (CL_ANSIMIN|CL_SCOANSI)
31
32 #define TM_PUTTY        (0xFFFF)
33
34 #define compatibility(x) \
35     if ( ((CL_##x)&compatibility_level) == 0 ) {        \
36        termstate=TOPLEVEL;                              \
37        break;                                           \
38     }
39 #define compatibility2(x,y) \
40     if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
41        termstate=TOPLEVEL;                              \
42        break;                                           \
43     }
44
45 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
46
47 static int compatibility_level = TM_PUTTY;
48
49 static tree234 *scrollback;            /* lines scrolled off top of screen */
50 static tree234 *screen;                /* lines on primary screen */
51 static tree234 *alt_screen;            /* lines on alternate screen */
52 static int disptop;                    /* distance scrolled back (0 or -ve) */
53
54 static unsigned long *cpos;            /* cursor position (convenience) */
55
56 static unsigned long *disptext;        /* buffer of text on real screen */
57 static unsigned long *wanttext;        /* buffer of text we want on screen */
58
59 #define VBELL_TIMEOUT 100              /* millisecond len of visual bell */
60
61 struct beeptime {
62     struct beeptime *next;
63     long ticks;
64 };
65 static struct beeptime *beephead, *beeptail;
66 int nbeeps;
67 int beep_overloaded;
68 long lastbeep;
69
70 static unsigned char *selspace;        /* buffer for building selections in */
71
72 #define TSIZE (sizeof(unsigned long))
73 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
74
75 static unsigned long curr_attr, save_attr;
76 static unsigned long erase_char = ERASE_CHAR;
77
78 typedef struct {
79     int y, x;
80 } pos;
81 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
82 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
83 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
84 #define posdiff(p1,p2) ( ((p2).y - (p1).y) * (cols+1) + (p2).x - (p1).x )
85 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
86 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
87
88 static pos curs;                       /* cursor */
89 static pos savecurs;                   /* saved cursor position */
90 static int marg_t, marg_b;             /* scroll margins */
91 static int dec_om;                     /* DEC origin mode flag */
92 static int wrap, wrapnext;             /* wrap flags */
93 static int insert;                     /* insert-mode flag */
94 static int cset;                       /* 0 or 1: which char set */
95 static int save_cset, save_csattr;     /* saved with cursor position */
96 static int rvideo;                     /* global reverse video flag */
97 static int rvbell_timeout;             /* for ESC[?5hESC[?5l vbell */
98 static int cursor_on;                  /* cursor enabled flag */
99 static int reset_132;                  /* Flag ESC c resets to 80 cols */
100 static int use_bce;                    /* Use Background coloured erase */
101 static int blinker;                    /* When blinking is the cursor on ? */
102 static int tblinker;                   /* When the blinking text is on */
103 static int blink_is_real;              /* Actually blink blinking text */
104 static int term_echoing;               /* Does terminal want local echo? */
105 static int term_editing;               /* Does terminal want local edit? */
106
107 static int xterm_mouse;         /* send mouse messages to app */
108
109 static unsigned long cset_attr[2];
110
111 /*
112  * Saved settings on the alternate screen.
113  */
114 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
115 static int alt_t, alt_b;
116 static int alt_which;
117
118 #define ARGS_MAX 32                    /* max # of esc sequence arguments */
119 #define ARG_DEFAULT 0                  /* if an arg isn't specified */
120 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
121 static int esc_args[ARGS_MAX];
122 static int esc_nargs;
123 static int esc_query;
124 #define ANSI(x,y)       ((x)+((y)<<8))
125 #define ANSI_QUE(x)     ANSI(x,TRUE)
126
127 #define OSC_STR_MAX 2048
128 static int osc_strlen;
129 static char osc_string[OSC_STR_MAX+1];
130 static int osc_w;
131
132 static char id_string[1024] = "\033[?6c";
133
134 static unsigned char *tabs;
135
136 static enum {
137     TOPLEVEL,
138     SEEN_ESC,
139     SEEN_CSI,
140     SEEN_OSC,
141     SEEN_OSC_W,
142
143     DO_CTRLS,
144
145     IGNORE_NEXT,
146     SET_GL, SET_GR,
147     SEEN_OSC_P,
148     OSC_STRING, OSC_MAYBE_ST,
149     SEEN_ESCHASH,
150     VT52_ESC,
151     VT52_Y1,
152     VT52_Y2
153 } termstate;
154
155 static enum {
156     NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
157 } selstate;
158 static enum {
159     SM_CHAR, SM_WORD, SM_LINE
160 } selmode;
161 static pos selstart, selend, selanchor;
162
163 static short wordness[256] = {
164     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 01 */
165     0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2, 2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1, /* 23 */
166     1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2, /* 45 */
167     1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1, /* 67 */
168     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 89 */
169     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* AB */
170     2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2, /* CD */
171     2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2, /* EF */
172 };
173
174 static unsigned char sel_nl[] = SEL_NL;
175 static char * paste_buffer = 0;
176 static int paste_len, paste_pos, paste_hold;
177
178 /*
179  * Internal prototypes.
180  */
181 static void do_paint (Context, int);
182 static void erase_lots (int, int, int);
183 static void swap_screen (int);
184 static void update_sbar (void);
185 static void deselect (void);
186 /* log session to file stuff ... */
187 static FILE *lgfp = NULL;
188 static void logtraffic(unsigned char c, int logmode);
189
190 /*
191  * Retrieve a line of the screen or of the scrollback, according to
192  * whether the y coordinate is non-negative or negative
193  * (respectively).
194  */
195 unsigned long *lineptr(int y, int lineno) {
196     unsigned long *line, lineattrs;
197     tree234 *whichtree;
198     int i, treeindex, oldlen;
199
200     if (y >= 0) {
201         whichtree = screen;
202         treeindex = y;
203     } else {
204         whichtree = scrollback;
205         treeindex = y + count234(scrollback);
206     }
207     line = index234(whichtree, treeindex);
208
209     /* We assume that we don't screw up and retrieve something out of range. */
210     assert(line != NULL);
211
212     if (line[0] != cols) {
213         /*
214          * This line is the wrong length, which probably means it
215          * hasn't been accessed since a resize. Resize it now.
216          */
217         oldlen = line[0];
218         lineattrs = line[oldlen+1];
219         delpos234(whichtree, treeindex);
220         line = srealloc(line, TSIZE * (2+cols));
221         line[0] = cols;
222         for (i = oldlen; i < cols; i++)
223             line[i+1] = ERASE_CHAR;
224         line[cols+1] = lineattrs & LATTR_MODE;
225         addpos234(whichtree, line, treeindex);
226     }
227
228     return line+1;
229 }
230 #define lineptr(x) lineptr(x,__LINE__)
231 /*
232  * Set up power-on settings for the terminal.
233  */
234 static void power_on(void) {
235     curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
236     alt_t = marg_t = 0;
237     if (rows != -1)
238         alt_b = marg_b = rows - 1;
239     else
240         alt_b = marg_b = 0;
241     if (cols != -1) {
242         int i;
243         for (i = 0; i < cols; i++)
244             tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
245     }
246     alt_om = dec_om = cfg.dec_om;
247     alt_wnext = wrapnext = alt_ins = insert = FALSE;
248     alt_wrap = wrap = cfg.wrap_mode;
249     alt_cset = cset = 0;
250     cset_attr[0] = cset_attr[1] = ATTR_ASCII;
251     rvideo = 0;
252     in_vbell = FALSE;
253     cursor_on = 1;
254     save_attr = curr_attr = ATTR_DEFAULT;
255     term_editing = term_echoing = FALSE;
256     ldisc_send(NULL, 0);               /* cause ldisc to notice changes */
257     app_cursor_keys = cfg.app_cursor;
258     app_keypad_keys = cfg.app_keypad;
259     use_bce = cfg.bce;
260     blink_is_real = cfg.blinktext;
261     erase_char = ERASE_CHAR;
262     alt_which = 0;
263     {
264         int i;
265         for (i = 0; i < 256; i++)
266             wordness[i] = cfg.wordness[i];
267     }
268     if (screen) {
269         swap_screen (1);
270         erase_lots (FALSE, TRUE, TRUE);
271         swap_screen (0);
272         erase_lots (FALSE, TRUE, TRUE);
273     }
274 }
275
276 /*
277  * Force a screen update.
278  */
279 void term_update(void) {
280     Context ctx;
281     ctx = get_ctx();
282     if (ctx) {
283         if ( (seen_key_event && (cfg.scroll_on_key)) ||
284              (seen_disp_event && (cfg.scroll_on_disp)) ) {
285             disptop = 0;               /* return to main screen */
286             seen_disp_event = seen_key_event = 0;
287             update_sbar();
288         }
289         do_paint (ctx, TRUE);
290         sys_cursor(curs.x, curs.y - disptop);
291         free_ctx (ctx);
292     }
293 }
294
295 /*
296  * Same as power_on(), but an external function.
297  */
298 void term_pwron(void) {
299     power_on();
300     fix_cpos;
301     disptop = 0;
302     deselect();
303     term_update();
304 }
305
306 /*
307  * Clear the scrollback.
308  */
309 void term_clrsb(void) {
310     unsigned long *line;
311     disptop = 0;
312     while ((line = delpos234(scrollback, 0)) != NULL) {
313         sfree(line);
314     }
315     update_sbar();
316 }
317
318 /*
319  * Initialise the terminal.
320  */
321 void term_init(void) {
322     screen = alt_screen = scrollback = NULL;
323     disptop = 0;
324     disptext = wanttext = NULL;
325     tabs = NULL;
326     selspace = NULL;
327     deselect();
328     rows = cols = -1;
329     power_on();
330     beephead = beeptail = NULL;
331     nbeeps = 0;
332     lastbeep = FALSE;
333     beep_overloaded = FALSE;
334 }
335
336 /*
337  * Set up the terminal for a given size.
338  */
339 void term_size(int newrows, int newcols, int newsavelines) {
340     tree234 *newsb, *newscreen, *newalt;
341     unsigned long *newdisp, *newwant, *oldline, *line;
342     int i, j, ccols;
343     int sblen;
344     int save_alt_which = alt_which;
345
346     if (newrows == rows && newcols == cols && newsavelines == savelines)
347         return;                        /* nothing to do */
348
349     deselect();
350     swap_screen(0);
351
352     alt_t = marg_t = 0;
353     alt_b = marg_b = newrows - 1;
354
355     if (rows == -1) {
356         scrollback = newtree234(NULL);
357         screen = newtree234(NULL);
358         rows = 0;
359     }
360
361     /*
362      * Resize the screen and scrollback. We only need to shift
363      * lines around within our data structures, because lineptr()
364      * will take care of resizing each individual line if
365      * necessary. So:
366      * 
367      *  - If the new screen and the old screen differ in length, we
368      *    must shunt some lines in from the scrollback or out to
369      *    the scrollback.
370      * 
371      *  - If doing that fails to provide us with enough material to
372      *    fill the new screen (i.e. the number of rows needed in
373      *    the new screen exceeds the total number in the previous
374      *    screen+scrollback), we must invent some blank lines to
375      *    cover the gap.
376      * 
377      *  - Then, if the new scrollback length is less than the
378      *    amount of scrollback we actually have, we must throw some
379      *    away.
380      */
381     sblen = count234(scrollback);
382     /* Do this loop to expand the screen if newrows > rows */
383     for (i = rows; i < newrows; i++) {
384         if (sblen > 0) {
385             line = delpos234(scrollback, --sblen);
386         } else {
387             line = smalloc(TSIZE * (newcols+2));
388             line[0] = newcols;
389             for (j = 0; j <= newcols; j++)
390                 line[j+1] = ERASE_CHAR;
391         }
392         addpos234(screen, line, 0);
393     }
394     /* Do this loop to shrink the screen if newrows < rows */
395     for (i = newrows; i < rows; i++) {
396         line = delpos234(screen, 0);
397         addpos234(scrollback, line, sblen++);
398     }
399     assert(count234(screen) == newrows);
400     while (sblen > newsavelines) {
401         line = delpos234(scrollback, 0);
402         sfree(line);
403         sblen--;
404     }
405     assert(count234(scrollback) <= newsavelines);
406     disptop = 0;
407
408     newdisp = smalloc (newrows*(newcols+1)*TSIZE);
409     for (i=0; i<newrows*(newcols+1); i++)
410         newdisp[i] = ATTR_INVALID;
411     sfree (disptext);
412     disptext = newdisp;
413
414     newwant = smalloc (newrows*(newcols+1)*TSIZE);
415     for (i=0; i<newrows*(newcols+1); i++)
416         newwant[i] = ATTR_INVALID;
417     sfree (wanttext);
418     wanttext = newwant;
419
420     newalt = newtree234(NULL);
421     for (i=0; i<newrows; i++) {
422         line = smalloc(TSIZE * (newcols+2));
423         line[0] = newcols;
424         for (j = 0; j <= newcols; j++)
425             line[j+1] = erase_char;
426         addpos234(newalt, line, i);
427     }
428     if (alt_screen) {
429         while (NULL != (line = delpos234(alt_screen, 0)))
430             sfree(line);
431         freetree234(alt_screen);
432     }
433     alt_screen = newalt;
434
435     sfree (selspace);
436     selspace = smalloc ( (newrows+newsavelines) * (newcols+sizeof(sel_nl)) );
437
438     tabs = srealloc (tabs, newcols*sizeof(*tabs));
439     {
440         int i;
441         for (i = (cols > 0 ? cols : 0); i < newcols; i++)
442             tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
443     }
444
445     if (rows > 0)
446         curs.y += newrows - rows;
447     if (curs.y < 0)
448         curs.y = 0;
449     if (curs.y >= newrows)
450         curs.y = newrows-1;
451     if (curs.x >= newcols)
452         curs.x = newcols-1;
453     alt_x = alt_y = 0;
454     wrapnext = alt_wnext = FALSE;
455
456     rows = newrows;
457     cols = newcols;
458     savelines = newsavelines;
459     fix_cpos;
460
461     swap_screen(save_alt_which);
462
463     update_sbar();
464     term_update();
465 }
466
467 /*
468  * Swap screens.
469  */
470 static void swap_screen (int which) {
471     int t;
472     tree234 *ttr;
473
474     if (which == alt_which)
475         return;
476
477     alt_which = which;
478
479     ttr = alt_screen; alt_screen = screen; screen = ttr;
480     t = curs.x; curs.x = alt_x; alt_x = t;
481     t = curs.y; curs.y = alt_y; alt_y = t;
482     t = marg_t; marg_t = alt_t; alt_t = t;
483     t = marg_b; marg_b = alt_b; alt_b = t;
484     t = dec_om; dec_om = alt_om; alt_om = t;
485     t = wrap; wrap = alt_wrap; alt_wrap = t;
486     t = wrapnext; wrapnext = alt_wnext; alt_wnext = t;
487     t = insert; insert = alt_ins; alt_ins = t;
488     t = cset; cset = alt_cset; alt_cset = t;
489
490     fix_cpos;
491 }
492
493 /*
494  * Update the scroll bar.
495  */
496 static void update_sbar(void) {
497     int nscroll;
498
499     nscroll = count234(scrollback);
500
501     set_sbar (nscroll + rows, nscroll + disptop, rows);
502 }
503
504 /*
505  * Check whether the region bounded by the two pointers intersects
506  * the scroll region, and de-select the on-screen selection if so.
507  */
508 static void check_selection (pos from, pos to) {
509     if (poslt(from, selend) && poslt(selstart, to))
510         deselect();
511 }
512
513 /*
514  * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
515  * for backward.) `sb' is TRUE if the scrolling is permitted to
516  * affect the scrollback buffer.
517  * 
518  * NB this function invalidates all pointers into lines of the
519  * screen data structures. In particular, you MUST call fix_cpos
520  * after calling scroll() and before doing anything else that
521  * uses the cpos shortcut pointer.
522  */
523 static void scroll (int topline, int botline, int lines, int sb) {
524     unsigned long *line, *line2;
525     int i;
526
527     if (topline != 0 || alt_which != 0)
528         sb = FALSE;
529
530     if (lines < 0) {
531         while (lines < 0) {
532             line = delpos234(screen, botline);
533             for (i = 0; i < cols; i++)
534                 line[i+1] = erase_char;
535             line[cols+1] = 0;
536             addpos234(screen, line, topline);
537
538             if (selstart.y >= topline && selstart.y <= botline) {
539                 selstart.y++;
540                 if (selstart.y > botline) {
541                     selstart.y = botline;
542                     selstart.x = 0;
543                 }
544             }
545             if (selend.y >= topline && selend.y <= botline) {
546                 selend.y++;
547                 if (selend.y > botline) {
548                     selend.y = botline;
549                     selend.x = 0;
550                 }
551             }
552
553             lines++;
554         }
555     } else {
556         while (lines > 0) {
557             line = delpos234(screen, topline);
558             if (sb && savelines > 0) {
559                 int sblen = count234(scrollback);
560                 /*
561                  * We must add this line to the scrollback. We'll
562                  * remove a line from the top of the scrollback to
563                  * replace it, or allocate a new one if the
564                  * scrollback isn't full.
565                  */
566                 if (sblen == savelines) {
567                     sblen--, line2 = delpos234(scrollback, 0);
568                 } else {
569                     line2 = smalloc(TSIZE * (cols+2));
570                     line2[0] = cols;
571                 }
572                 addpos234(scrollback, line, sblen);
573                 line = line2;
574             }
575             for (i = 0; i < cols; i++)
576                 line[i+1] = erase_char;
577             line[cols+1] = 0;
578             addpos234(screen, line, botline);
579
580             if (selstart.y >= topline && selstart.y <= botline) {
581                 selstart.y--;
582                 if (selstart.y < topline) {
583                     selstart.y = topline;
584                     selstart.x = 0;
585                 }
586             }
587             if (selend.y >= topline && selend.y <= botline) {
588                 selend.y--;
589                 if (selend.y < topline) {
590                     selend.y = topline;
591                     selend.x = 0;
592                 }
593             }
594
595             lines--;
596         }
597     }
598 }
599
600 /*
601  * Move the cursor to a given position, clipping at boundaries. We
602  * may or may not want to clip at the scroll margin: marg_clip is 0
603  * not to, 1 to disallow _passing_ the margins, and 2 to disallow
604  * even _being_ outside the margins.
605  */
606 static void move (int x, int y, int marg_clip) {
607     if (x < 0)
608         x = 0;
609     if (x >= cols)
610         x = cols-1;
611     if (marg_clip) {
612         if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
613             y = marg_t;
614         if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
615             y = marg_b;
616     }
617     if (y < 0)
618         y = 0;
619     if (y >= rows)
620         y = rows-1;
621     curs.x = x;
622     curs.y = y;
623     fix_cpos;
624     wrapnext = FALSE;
625 }
626
627 /*
628  * Save or restore the cursor and SGR mode.
629  */
630 static void save_cursor(int save) {
631     if (save) {
632         savecurs = curs;
633         save_attr = curr_attr;
634         save_cset = cset;
635         save_csattr = cset_attr[cset];
636     } else {
637         curs = savecurs;
638         /* Make sure the window hasn't shrunk since the save */
639         if (curs.x >= cols) curs.x = cols-1;
640         if (curs.y >= rows) curs.y = rows-1;
641
642         curr_attr = save_attr;
643         cset = save_cset;
644         cset_attr[cset] = save_csattr;
645         fix_cpos;
646         if (use_bce) erase_char = (' ' |(curr_attr&(ATTR_FGMASK|ATTR_BGMASK)));
647     }
648 }
649
650 /*
651  * Erase a large portion of the screen: the whole screen, or the
652  * whole line, or parts thereof.
653  */
654 static void erase_lots (int line_only, int from_begin, int to_end) {
655     pos start, end;
656     int erase_lattr;
657     unsigned long *ldata;
658
659     if (line_only) {
660         start.y = curs.y;
661         start.x = 0;
662         end.y = curs.y + 1;
663         end.x = 0;
664         erase_lattr = FALSE;
665     } else {
666         start.y = 0;
667         start.x = 0;
668         end.y = rows;
669         end.x = 0;
670         erase_lattr = TRUE;
671     }
672     if (!from_begin) {
673         start = curs;
674     }
675     if (!to_end) {
676         end = curs;
677     }
678     check_selection (start, end);
679
680     /* Clear screen also forces a full window redraw, just in case. */
681     if (start.y == 0 && start.x == 0 && end.y == rows)
682        term_invalidate();
683
684     ldata = lineptr(start.y);
685     while (poslt(start, end)) {
686         if (start.y == cols && !erase_lattr)
687             ldata[start.x] &= ~ATTR_WRAPPED;
688         else
689             ldata[start.x] = erase_char;
690         if (incpos(start) && start.y < rows)
691             ldata = lineptr(start.y);
692     }
693 }
694
695 /*
696  * Insert or delete characters within the current line. n is +ve if
697  * insertion is desired, and -ve for deletion.
698  */
699 static void insch (int n) {
700     int dir = (n < 0 ? -1 : +1);
701     int m;
702     pos cursplus;
703     unsigned long *ldata;
704
705     n = (n < 0 ? -n : n);
706     if (n > cols - curs.x)
707         n = cols - curs.x;
708     m = cols - curs.x - n;
709     cursplus.y = curs.y;
710     cursplus.x = curs.x + n;
711     check_selection (curs, cursplus);
712     ldata = lineptr(curs.y);
713     if (dir < 0) {
714         memmove (ldata + curs.x, ldata + curs.x + n, m*TSIZE);
715         while (n--)
716             ldata[curs.x + m++] = erase_char;
717     } else {
718         memmove (ldata + curs.x + n, ldata + curs.x, m*TSIZE);
719         while (n--)
720             ldata[curs.x + n] = erase_char;
721     }
722 }
723
724 /*
725  * Toggle terminal mode `mode' to state `state'. (`query' indicates
726  * whether the mode is a DEC private one or a normal one.)
727  */
728 static void toggle_mode (int mode, int query, int state) {
729     long ticks;
730
731     if (query) switch (mode) {
732       case 1:                          /* application cursor keys */
733         app_cursor_keys = state;
734         break;
735       case 2:                          /* VT52 mode */
736         vt52_mode = !state;
737         break;
738       case 3:                          /* 80/132 columns */
739         deselect();
740         request_resize (state ? 132 : 80, rows, 1);
741         reset_132 = state;
742         break;
743       case 5:                          /* reverse video */
744         /*
745          * Toggle reverse video. If we receive an OFF within the
746          * visual bell timeout period after an ON, we trigger an
747          * effective visual bell, so that ESC[?5hESC[?5l will
748          * always be an actually _visible_ visual bell.
749          */
750         ticks = GetTickCount();
751         if (rvideo && !state &&        /* we're turning it off */
752             ticks < rvbell_timeout) {  /* and it's not long since it was turned on */
753             in_vbell = TRUE;           /* we may clear rvideo but we set in_vbell */
754             if (vbell_timeout < rvbell_timeout)   /* don't move vbell end forward */
755                 vbell_timeout = rvbell_timeout;   /* vbell end is at least then */
756         } else if (!rvideo && state) {
757             /* This is an ON, so we notice the time and save it. */
758             rvbell_timeout = ticks + VBELL_TIMEOUT;
759         }
760         rvideo = state;
761         seen_disp_event = TRUE;
762         if (state) term_update();
763         break;
764       case 6:                          /* DEC origin mode */
765         dec_om = state;
766         break;
767       case 7:                          /* auto wrap */
768         wrap = state;
769         break;
770       case 8:                          /* auto key repeat */
771         repeat_off = !state;
772         break;
773       case 10:                         /* set local edit mode */
774         term_editing = state;
775         ldisc_send(NULL, 0);           /* cause ldisc to notice changes */
776         break;
777       case 25:                         /* enable/disable cursor */
778         compatibility2(OTHER,VT220);
779         cursor_on = state;
780         seen_disp_event = TRUE;
781         break;
782       case 47:                         /* alternate screen */
783         compatibility(OTHER);
784         deselect();
785         swap_screen (state);
786         disptop = 0;
787         break;
788       case 1000:                       /* xterm mouse 1 */
789         xterm_mouse = state ? 1 : 0;
790         set_raw_mouse_mode(state);
791         break;
792       case 1002:                       /* xterm mouse 2 */
793         xterm_mouse = state ? 2: 0;
794         set_raw_mouse_mode(state);
795         break;
796     } else switch (mode) {
797       case 4:                          /* set insert mode */
798         compatibility(VT102);
799         insert = state;
800         break;
801       case 12:                         /* set echo mode */
802         term_echoing = !state;
803         ldisc_send(NULL, 0);           /* cause ldisc to notice changes */
804         break;
805       case 20:                         /* Return sends ... */
806         cr_lf_return = state;
807         break;
808     }
809 }
810
811 /*
812  * Process an OSC sequence: set window title or icon name.
813  */
814 static void do_osc(void) {
815     if (osc_w) {
816         while (osc_strlen--)
817             wordness[(unsigned char)osc_string[osc_strlen]] = esc_args[0];
818     } else {
819         osc_string[osc_strlen] = '\0';
820         switch (esc_args[0]) {
821           case 0:
822           case 1:
823             set_icon (osc_string);
824             if (esc_args[0] == 1)
825                 break;
826             /* fall through: parameter 0 means set both */
827           case 2:
828           case 21:
829             set_title (osc_string);
830             break;
831         }
832     }
833 }
834
835 /*
836  * Remove everything currently in `inbuf' and stick it up on the
837  * in-memory display. There's a big state machine in here to
838  * process escape sequences...
839  */
840 void term_out(void) {
841     int c, inbuf_reap;
842
843     for(inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++)
844     {
845         c = inbuf[inbuf_reap];
846
847         /*
848          * Optionally log the session traffic to a file. Useful for
849          * debugging and possibly also useful for actual logging.
850          */
851         logtraffic((unsigned char)c, LGTYP_DEBUG);
852
853         /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
854          * be able to display 8-bit characters, but I'll let that go 'cause
855          * of i18n.
856          */
857         if( ( (c&0x60) == 0 || c == '\177') && 
858              termstate < DO_CTRLS &&
859             ( (c&0x80) == 0 || has_compat(VT220))) {
860             switch (c) {
861               case '\005':             /* terminal type query */
862                 /* Strictly speaking this is VT100 but a VT100 defaults to
863                  * no response. Other terminals respond at their option.
864                  *
865                  * Don't put a CR in the default string as this tends to
866                  * upset some weird software.
867                  *
868                  * An xterm returns "xterm" (5 characters)
869                  */
870                 compatibility(ANSIMIN);
871                 {
872                     char abuf[256], *s, *d;
873                     int state=0;
874                     for(s=cfg.answerback, d=abuf; *s; s++) {
875                         if (state)
876                         {
877                             if (*s >= 'a' && *s <= 'z')
878                                 *d++ = (*s - ('a'-1));
879                             else if ((*s >='@' && *s<='_') ||
880                                      *s == '?' || (*s&0x80))
881                                 *d++ = ('@'^*s);
882                             else if (*s == '~')
883                                 *d++ = '^';
884                             state = 0;
885                         }
886                         else if (*s == '^') {
887                             state = 1;
888                         }
889                         else
890                             *d++ = xlat_kbd2tty((unsigned char)*s);
891                     }
892                     ldisc_send (abuf, d-abuf);
893                 }
894                 break;
895               case '\007':
896                 {
897                     struct beeptime *newbeep;
898                     long ticks;
899
900                     ticks = GetTickCount();
901
902                     if (!beep_overloaded) {
903                         newbeep = smalloc(sizeof(struct beeptime));
904                         newbeep->ticks = ticks;
905                         newbeep->next = NULL;
906                         if (!beephead)
907                             beephead = newbeep;
908                         else
909                             beeptail->next = newbeep;
910                         beeptail = newbeep;
911                         nbeeps++;
912                     }
913
914                     /*
915                      * Throw out any beeps that happened more than
916                      * t seconds ago.
917                      */
918                     while (beephead &&
919                            beephead->ticks < ticks - cfg.bellovl_t) {
920                         struct beeptime *tmp = beephead;
921                         beephead = tmp->next;
922                         sfree(tmp);
923                         if (!beephead)
924                             beeptail = NULL;
925                         nbeeps--;
926                     }
927
928                     if (cfg.bellovl && beep_overloaded &&
929                         ticks-lastbeep >= cfg.bellovl_s) {
930                         /*
931                          * If we're currently overloaded and the
932                          * last beep was more than s seconds ago,
933                          * leave overload mode.
934                          */
935                         beep_overloaded = FALSE;
936                     } else if (cfg.bellovl && !beep_overloaded &&
937                                nbeeps >= cfg.bellovl_n) {
938                         /*
939                          * Now, if we have n or more beeps
940                          * remaining in the queue, go into overload
941                          * mode.
942                          */
943                         beep_overloaded = TRUE;
944                     }
945                     lastbeep = ticks;
946
947                     /*
948                      * Perform an actual beep if we're not overloaded.
949                      */
950                     if ((!cfg.bellovl || !beep_overloaded) && cfg.beep != 0) {
951                         if (cfg.beep != 2)
952                             beep(cfg.beep);
953                         else if(cfg.beep == 2) {
954                             in_vbell = TRUE;
955                             vbell_timeout = ticks + VBELL_TIMEOUT;
956                             term_update();
957                         }
958                     }
959                     disptop = 0;
960                 }
961                 break;
962               case '\b':
963                 if (curs.x == 0 && curs.y == 0)
964                     ;
965                 else if (curs.x == 0 && curs.y > 0)
966                     curs.x = cols-1, curs.y--;
967                 else if (wrapnext)
968                     wrapnext = FALSE;
969                 else
970                     curs.x--;
971                 fix_cpos;
972                 seen_disp_event = TRUE;
973                 break;
974               case '\016':
975                 compatibility(VT100);
976                 cset = 1;
977                 break;
978               case '\017':
979                 compatibility(VT100);
980                 cset = 0;
981                 break;
982               case '\033':
983                 if (vt52_mode) 
984                    termstate = VT52_ESC;
985                 else {
986                     compatibility(ANSIMIN);
987                     termstate = SEEN_ESC;
988                 }
989                 break;
990               case 0233:
991                 compatibility(VT220);
992                 termstate = SEEN_CSI;
993                 esc_nargs = 1;
994                 esc_args[0] = ARG_DEFAULT;
995                 esc_query = FALSE;
996                 break;
997               case 0235:
998                 compatibility(VT220);
999                 termstate = SEEN_OSC;
1000                 esc_args[0] = 0;
1001                 break;
1002               case '\r':
1003                 curs.x = 0;
1004                 wrapnext = FALSE;
1005                 fix_cpos;
1006                 seen_disp_event = TRUE;
1007                 paste_hold = 0;
1008                 logtraffic((unsigned char)c,LGTYP_ASCII);
1009                 break;
1010               case '\013':
1011               case '\014':
1012                 compatibility(VT100);
1013               case '\n':
1014                 if (curs.y == marg_b)
1015                     scroll (marg_t, marg_b, 1, TRUE);
1016                 else if (curs.y < rows-1)
1017                     curs.y++;
1018                 if (cfg.lfhascr)
1019                     curs.x = 0;
1020                 fix_cpos;
1021                 wrapnext = FALSE;
1022                 seen_disp_event = 1;
1023                 paste_hold = 0;
1024                 logtraffic((unsigned char)c,LGTYP_ASCII);
1025                 break;
1026               case '\t':
1027                 {
1028                     pos old_curs = curs;
1029                     unsigned long *ldata = lineptr(curs.y);
1030
1031                     do {
1032                         curs.x++;
1033                     } while (curs.x < cols-1 && !tabs[curs.x]);
1034
1035                     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
1036                     {
1037                         if (curs.x >= cols/2)
1038                             curs.x = cols/2-1;
1039                     }
1040                     else
1041                     {
1042                         if (curs.x >= cols)
1043                             curs.x = cols-1;
1044                     }
1045
1046                     fix_cpos;
1047                     check_selection (old_curs, curs);
1048                 }
1049                 seen_disp_event = TRUE;
1050                 break;
1051               case '\177': /* Destructive backspace
1052                               This does nothing on a real VT100 */
1053                 compatibility(OTHER);
1054                 if (curs.x && !wrapnext) curs.x--;
1055                 wrapnext = FALSE;
1056                 fix_cpos;
1057                 *cpos = (' ' | curr_attr | ATTR_ASCII);
1058                 break;
1059             }
1060         }
1061         else switch (termstate) {
1062           case TOPLEVEL:
1063             /* Only graphic characters get this far, ctrls are stripped above */
1064             if (wrapnext && wrap) {
1065                 cpos[1] |= ATTR_WRAPPED;
1066                 if (curs.y == marg_b)
1067                     scroll (marg_t, marg_b, 1, TRUE);
1068                 else if (curs.y < rows-1)
1069                     curs.y++;
1070                 curs.x = 0;
1071                 fix_cpos;
1072                 wrapnext = FALSE;
1073             }
1074             if (insert)
1075                 insch (1);
1076             if (selstate != NO_SELECTION) {
1077                 pos cursplus = curs;
1078                 incpos(cursplus);
1079                 check_selection (curs, cursplus);
1080             }
1081             switch (cset_attr[cset]) {
1082                 /*
1083                  * Linedraw characters are different from 'ESC ( B'
1084                  * only for a small range. For ones outside that
1085                  * range, make sure we use the same font as well as
1086                  * the same encoding.
1087                  */
1088             case ATTR_LINEDRW:
1089                 if (c<0x5f || c>0x7F)
1090                     *cpos++ = xlat_tty2scr((unsigned char)c) | curr_attr |
1091                               ATTR_ASCII;
1092                 else if (c==0x5F)
1093                     *cpos++ = ' ' | curr_attr | ATTR_ASCII;
1094                 else
1095                     *cpos++ = ((unsigned char)c) | curr_attr | ATTR_LINEDRW;
1096                 break;
1097             case ATTR_GBCHR:
1098                 /* If UK-ASCII, make the '#' a LineDraw Pound */
1099                 if (c == '#') {
1100                     *cpos++ = '}' | curr_attr | ATTR_LINEDRW;
1101                     break;
1102                 }
1103                 /*FALLTHROUGH*/
1104             default:
1105                 *cpos = xlat_tty2scr((unsigned char)c) | curr_attr |
1106                     (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
1107                 logtraffic((unsigned char)c, LGTYP_ASCII);
1108                 cpos++;
1109                 break;
1110             }
1111             curs.x++;
1112             if (curs.x == cols) {
1113                 cpos--;
1114                 curs.x--;
1115                 wrapnext = TRUE;
1116             }
1117             seen_disp_event = 1;
1118             break;
1119
1120           case IGNORE_NEXT:
1121             termstate = TOPLEVEL;
1122             break;
1123           case OSC_MAYBE_ST:
1124             /*
1125              * This state is virtually identical to SEEN_ESC, with the
1126              * exception that we have an OSC sequence in the pipeline,
1127              * and _if_ we see a backslash, we process it.
1128              */
1129             if (c == '\\') {
1130                 do_osc();
1131                 termstate = TOPLEVEL;
1132                 break;
1133             }
1134             /* else fall through */
1135           case SEEN_ESC:
1136             termstate = TOPLEVEL;
1137             switch (c) {
1138               case ' ':                /* some weird sequence? */
1139                 compatibility(VT220);
1140                 termstate = IGNORE_NEXT;
1141                 break;
1142               case '[':                /* enter CSI mode */
1143                 termstate = SEEN_CSI;
1144                 esc_nargs = 1;
1145                 esc_args[0] = ARG_DEFAULT;
1146                 esc_query = FALSE;
1147                 break;
1148               case ']':                /* xterm escape sequences */
1149                 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1150                 compatibility(OTHER);
1151                 termstate = SEEN_OSC;
1152                 esc_args[0] = 0;
1153                 break;
1154               case '(':                /* should set GL */
1155                 compatibility(VT100);
1156                 termstate = SET_GL;
1157                 break;
1158               case ')':                /* should set GR */
1159                 compatibility(VT100);
1160                 termstate = SET_GR;
1161                 break;
1162               case '7':                /* save cursor */
1163                 compatibility(VT100);
1164                 save_cursor (TRUE);
1165                 break;
1166               case '8':                /* restore cursor */
1167                 compatibility(VT100);
1168                 save_cursor (FALSE);
1169                 seen_disp_event = TRUE;
1170                 break;
1171               case '=':
1172                 compatibility(VT100);
1173                 app_keypad_keys = TRUE;
1174                 break;
1175               case '>':
1176                 compatibility(VT100);
1177                 app_keypad_keys = FALSE;
1178                 break;
1179               case 'D':                /* exactly equivalent to LF */
1180                 compatibility(VT100);
1181                 if (curs.y == marg_b)
1182                     scroll (marg_t, marg_b, 1, TRUE);
1183                 else if (curs.y < rows-1)
1184                     curs.y++;
1185                 fix_cpos;
1186                 wrapnext = FALSE;
1187                 seen_disp_event = TRUE;
1188                 break;
1189               case 'E':                /* exactly equivalent to CR-LF */
1190                 compatibility(VT100);
1191                 curs.x = 0;
1192                 if (curs.y == marg_b)
1193                     scroll (marg_t, marg_b, 1, TRUE);
1194                 else if (curs.y < rows-1)
1195                     curs.y++;
1196                 fix_cpos;
1197                 wrapnext = FALSE;
1198                 seen_disp_event = TRUE;
1199                 break;
1200               case 'M':                /* reverse index - backwards LF */
1201                 compatibility(VT100);
1202                 if (curs.y == marg_t)
1203                     scroll (marg_t, marg_b, -1, TRUE);
1204                 else if (curs.y > 0)
1205                     curs.y--;
1206                 fix_cpos;
1207                 wrapnext = FALSE;
1208                 seen_disp_event = TRUE;
1209                 break;
1210               case 'Z':                /* terminal type query */
1211                 compatibility(VT100);
1212                 ldisc_send (id_string, strlen(id_string));
1213                 break;
1214               case 'c':                /* restore power-on settings */
1215                 compatibility(VT100);
1216                 power_on();
1217                 if (reset_132) {
1218                     request_resize (80, rows, 1);
1219                     reset_132 = 0;
1220                 }
1221                 fix_cpos;
1222                 disptop = 0;
1223                 seen_disp_event = TRUE;
1224                 break;
1225               case '#':                /* ESC # 8 fills screen with Es :-) */
1226                 compatibility(VT100);
1227                 termstate = SEEN_ESCHASH;
1228                 break;
1229               case 'H':                /* set a tab */
1230                 compatibility(VT100);
1231                 tabs[curs.x] = TRUE;
1232                 break;
1233             }
1234             break;
1235           case SEEN_CSI:
1236             termstate = TOPLEVEL;      /* default */
1237             if( isdigit(c) )
1238             {
1239                 if (esc_nargs <= ARGS_MAX) {
1240                     if (esc_args[esc_nargs-1] == ARG_DEFAULT)
1241                         esc_args[esc_nargs-1] = 0;
1242                     esc_args[esc_nargs-1] =
1243                         10 * esc_args[esc_nargs-1] + c - '0';
1244                 }
1245                 termstate = SEEN_CSI;
1246             }
1247             else if( c == ';' )
1248             {
1249                 if (++esc_nargs <= ARGS_MAX)
1250                     esc_args[esc_nargs-1] = ARG_DEFAULT;
1251                 termstate = SEEN_CSI;
1252             }
1253             else if( c < '@' )
1254             {
1255                 if( esc_query )     esc_query = -1;
1256                 else if( c == '?' ) esc_query = TRUE;
1257                 else                esc_query = c;
1258                 termstate = SEEN_CSI;
1259             }
1260             else switch (ANSI(c,esc_query)) {
1261               case 'A':                /* move up N lines */
1262                 move (curs.x, curs.y - def(esc_args[0], 1), 1);
1263                 seen_disp_event = TRUE;
1264                 break;
1265               case 'e':      /* move down N lines */
1266                 compatibility(ANSI);
1267               case 'B':
1268                 move (curs.x, curs.y + def(esc_args[0], 1), 1);
1269                 seen_disp_event = TRUE;
1270                 break;
1271               case 'a':      /* move right N cols */
1272                 compatibility(ANSI);
1273               case ANSI('c', '>'):      /* report xterm version */
1274                 compatibility(OTHER);
1275                 /* this reports xterm version 136 so that VIM can
1276                  use the drag messages from the mouse reporting */
1277                 ldisc_send("\033[>0;136;0c", 11);
1278                 break;
1279               case 'C':
1280                 move (curs.x + def(esc_args[0], 1), curs.y, 1);
1281                 seen_disp_event = TRUE;
1282                 break;
1283               case 'D':                /* move left N cols */
1284                 move (curs.x - def(esc_args[0], 1), curs.y, 1);
1285                 seen_disp_event = TRUE;
1286                 break;
1287               case 'E':                /* move down N lines and CR */
1288                 compatibility(ANSI);
1289                 move (0, curs.y + def(esc_args[0], 1), 1);
1290                 seen_disp_event = TRUE;
1291                 break;
1292               case 'F':                /* move up N lines and CR */
1293                 compatibility(ANSI);
1294                 move (0, curs.y - def(esc_args[0], 1), 1);
1295                 seen_disp_event = TRUE;
1296                 break;
1297               case 'G': case '`':      /* set horizontal posn */
1298                 compatibility(ANSI);
1299                 move (def(esc_args[0], 1) - 1, curs.y, 0);
1300                 seen_disp_event = TRUE;
1301                 break;
1302               case 'd':                /* set vertical posn */
1303                 compatibility(ANSI);
1304                 move (curs.x, (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
1305                       (dec_om ? 2 : 0));
1306                 seen_disp_event = TRUE;
1307                 break;
1308               case 'H': case 'f':      /* set horz and vert posns at once */
1309                 if (esc_nargs < 2)
1310                     esc_args[1] = ARG_DEFAULT;
1311                 move (def(esc_args[1], 1) - 1,
1312                       (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
1313                       (dec_om ? 2 : 0));
1314                 seen_disp_event = TRUE;
1315                 break;
1316               case 'J':                /* erase screen or parts of it */
1317                 {
1318                     unsigned int i = def(esc_args[0], 0) + 1;
1319                     if (i > 3)
1320                         i = 0;
1321                     erase_lots(FALSE, !!(i & 2), !!(i & 1));
1322                 }
1323                 disptop = 0;
1324                 seen_disp_event = TRUE;
1325                 break;
1326               case 'K':                /* erase line or parts of it */
1327                 {
1328                     unsigned int i = def(esc_args[0], 0) + 1;
1329                     if (i > 3)
1330                         i = 0;
1331                     erase_lots(TRUE, !!(i & 2), !!(i & 1));
1332                 }
1333                 seen_disp_event = TRUE;
1334                 break;
1335               case 'L':                /* insert lines */
1336                 compatibility(VT102);
1337                 if (curs.y <= marg_b)
1338                     scroll (curs.y, marg_b, -def(esc_args[0], 1), FALSE);
1339                 fix_cpos;
1340                 seen_disp_event = TRUE;
1341                 break;
1342               case 'M':                /* delete lines */
1343                 compatibility(VT102);
1344                 if (curs.y <= marg_b)
1345                     scroll (curs.y, marg_b, def(esc_args[0], 1), TRUE);
1346                 fix_cpos;
1347                 seen_disp_event = TRUE;
1348                 break;
1349               case '@':                /* insert chars */
1350                 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1351                 compatibility(VT102);   
1352                 insch (def(esc_args[0], 1));
1353                 seen_disp_event = TRUE;
1354                 break;
1355               case 'P':                /* delete chars */
1356                 compatibility(VT102);   
1357                 insch (-def(esc_args[0], 1));
1358                 seen_disp_event = TRUE;
1359                 break;
1360               case 'c':                /* terminal type query */
1361                 compatibility(VT100);
1362                 /* This is the response for a VT102 */
1363                 ldisc_send (id_string, strlen(id_string));
1364                 break;
1365               case 'n':                /* cursor position query */
1366                 if (esc_args[0] == 6) {
1367                     char buf[32];
1368                     sprintf (buf, "\033[%d;%dR", curs.y + 1, curs.x + 1);
1369                     ldisc_send (buf, strlen(buf));
1370                 }
1371                 else if (esc_args[0] == 5) {
1372                     ldisc_send ("\033[0n", 4);
1373                 }
1374                 break;
1375               case 'h':                /* toggle modes to high */
1376               case ANSI_QUE('h'):
1377                 compatibility(VT100);
1378                 {
1379                     int i;
1380                     for (i=0; i<esc_nargs; i++)
1381                         toggle_mode (esc_args[i], esc_query, TRUE);
1382                 }
1383                 break;
1384               case 'l':                /* toggle modes to low */
1385               case ANSI_QUE('l'):
1386                 compatibility(VT100);
1387                 {
1388                     int i;
1389                     for (i=0; i<esc_nargs; i++)
1390                         toggle_mode (esc_args[i], esc_query, FALSE);
1391                 }
1392                 break;
1393               case 'g':                /* clear tabs */
1394                 compatibility(VT100);
1395                 if (esc_nargs == 1) {
1396                     if (esc_args[0] == 0) {
1397                         tabs[curs.x] = FALSE;
1398                     } else if (esc_args[0] == 3) {
1399                         int i;
1400                         for (i = 0; i < cols; i++)
1401                             tabs[i] = FALSE;
1402                     }
1403                 }
1404                 break;
1405               case 'r':                /* set scroll margins */
1406                 compatibility(VT100);
1407                 if (esc_nargs <= 2) {
1408                     int top, bot;
1409                     top = def(esc_args[0], 1) - 1;
1410                     bot = (esc_nargs <= 1 || esc_args[1] == 0 ? rows :
1411                            def(esc_args[1], rows)) - 1;
1412                     if (bot >= rows)
1413                         bot = rows-1;
1414                     /* VTTEST Bug 9 - if region is less than 2 lines
1415                      * don't change region.
1416                      */
1417                     if (bot-top > 0) {
1418                         marg_t = top;
1419                         marg_b = bot;
1420                         curs.x = 0;
1421                         /*
1422                          * I used to think the cursor should be
1423                          * placed at the top of the newly marginned
1424                          * area. Apparently not: VMS TPU falls over
1425                          * if so.
1426                          *
1427                          * Well actually it should for Origin mode - RDB
1428                          */
1429                         curs.y = (dec_om ? marg_t : 0);
1430                         fix_cpos;
1431                         seen_disp_event = TRUE;
1432                     }
1433                 }
1434                 break;
1435               case 'm':                /* set graphics rendition */
1436                 {
1437                     /* 
1438                      * A VT100 without the AVO only had one attribute, either
1439                      * underline or reverse video depending on the cursor type,
1440                      * this was selected by CSI 7m.
1441                      *
1442                      * case 2:
1443                      *  This is DIM on the VT100-AVO and VT102
1444                      * case 5:
1445                      *  This is BLINK on the VT100-AVO and VT102+
1446                      * case 8:
1447                      *  This is INVIS on the VT100-AVO and VT102
1448                      * case 21:
1449                      *  This like 22 disables BOLD, DIM and INVIS
1450                      *
1451                      * The ANSI colours appear on any terminal that has colour
1452                      * (obviously) but the interaction between sgr0 and the
1453                      * colours varies but is usually related to the background
1454                      * colour erase item.
1455                      * The interaction between colour attributes and the mono
1456                      * ones is also very implementation dependent.
1457                      *
1458                      * The 39 and 49 attributes are likely to be unimplemented.
1459                      */
1460                     int i;
1461                     for (i=0; i<esc_nargs; i++) {
1462                         switch (def(esc_args[i], 0)) {
1463                           case 0:      /* restore defaults */
1464                             curr_attr = ATTR_DEFAULT; break;
1465                           case 1:      /* enable bold */
1466                             compatibility(VT100AVO);
1467                             curr_attr |= ATTR_BOLD; break;
1468                           case 21:     /* (enable double underline) */
1469                             compatibility(OTHER);
1470                           case 4:      /* enable underline */
1471                             compatibility(VT100AVO);
1472                             curr_attr |= ATTR_UNDER; break;
1473                           case 5:      /* enable blink */
1474                             compatibility(VT100AVO);
1475                             curr_attr |= ATTR_BLINK; break;
1476                           case 7:      /* enable reverse video */
1477                             curr_attr |= ATTR_REVERSE; break;
1478                           case 22:     /* disable bold */
1479                             compatibility2(OTHER,VT220);
1480                             curr_attr &= ~ATTR_BOLD; break;
1481                           case 24:     /* disable underline */
1482                             compatibility2(OTHER,VT220);
1483                             curr_attr &= ~ATTR_UNDER; break;
1484                           case 25:     /* disable blink */
1485                             compatibility2(OTHER,VT220);
1486                             curr_attr &= ~ATTR_BLINK; break;
1487                           case 27:     /* disable reverse video */
1488                             compatibility2(OTHER,VT220);
1489                             curr_attr &= ~ATTR_REVERSE; break;
1490                           case 30: case 31: case 32: case 33:
1491                           case 34: case 35: case 36: case 37:
1492                             /* foreground */
1493                             curr_attr &= ~ATTR_FGMASK;
1494                             curr_attr |= (esc_args[i] - 30) << ATTR_FGSHIFT;
1495                             break;
1496                           case 39:     /* default-foreground */
1497                             curr_attr &= ~ATTR_FGMASK;
1498                             curr_attr |= ATTR_DEFFG;
1499                             break;
1500                           case 40: case 41: case 42: case 43:
1501                           case 44: case 45: case 46: case 47:
1502                             /* background */
1503                             curr_attr &= ~ATTR_BGMASK;
1504                             curr_attr |= (esc_args[i] - 40) << ATTR_BGSHIFT;
1505                             break;
1506                           case 49:     /* default-background */
1507                             curr_attr &= ~ATTR_BGMASK;
1508                             curr_attr |= ATTR_DEFBG;
1509                             break;
1510                         }
1511                     }
1512                     if (use_bce) 
1513                        erase_char = 
1514                             (' '|
1515                               (curr_attr&(ATTR_FGMASK|ATTR_BGMASK|ATTR_BLINK))
1516                             );
1517                 }
1518                 break;
1519               case 's':                /* save cursor */
1520                 save_cursor (TRUE);
1521                 break;
1522               case 'u':                /* restore cursor */
1523                 save_cursor (FALSE);
1524                 seen_disp_event = TRUE;
1525                 break;
1526               case 't':                /* set page size - ie window height */
1527                 /*
1528                  * VT340/VT420 sequence DECSLPP, DEC only allows values
1529                  *  24/25/36/48/72/144 other emulators (eg dtterm) use
1530                  * illegal values (eg first arg 1..9) for window changing 
1531                  * and reports.
1532                  */
1533                 compatibility(VT340TEXT);
1534                 if (esc_nargs<=1 && (esc_args[0]<1 || esc_args[0]>=24)) {
1535                     request_resize (cols, def(esc_args[0], 24), 0);
1536                     deselect();
1537                 }
1538                 break;
1539               case ANSI('|', '*'):
1540                 /* VT420 sequence DECSNLS
1541                  * Set number of lines on screen
1542                  * VT420 uses VGA like hardware and can support any size in
1543                  * reasonable range (24..49 AIUI) with no default specified.
1544                  */
1545                 compatibility(VT420);
1546                 if (esc_nargs==1 && esc_args[0]>0) {
1547                     request_resize (cols, def(esc_args[0], cfg.height), 0);
1548                     deselect();
1549                 }
1550                 break;
1551               case ANSI('|', '$'):
1552                 /* VT340/VT420 sequence DECSCPP
1553                  * Set number of columns per page
1554                  * Docs imply range is only 80 or 132, but I'll allow any.
1555                  */
1556                 compatibility(VT340TEXT);
1557                 if (esc_nargs<=1) {
1558                     request_resize (def(esc_args[0], cfg.width), rows, 0);
1559                     deselect();
1560                 }
1561                 break;
1562               case 'X':                /* write N spaces w/o moving cursor */
1563                 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1564                 compatibility(ANSIMIN);
1565                 {
1566                     int n = def(esc_args[0], 1);
1567                     pos cursplus;
1568                     unsigned long *p = cpos;
1569                     if (n > cols - curs.x)
1570                         n = cols - curs.x;
1571                     cursplus = curs;
1572                     cursplus.x += n;
1573                     check_selection (curs, cursplus);
1574                     while (n--)
1575                         *p++ = erase_char;
1576                     seen_disp_event = TRUE;
1577                 }
1578                 break;
1579               case 'x':                /* report terminal characteristics */
1580                 compatibility(VT100);
1581                 {
1582                     char buf[32];
1583                     int i = def(esc_args[0], 0);
1584                     if (i == 0 || i == 1) {
1585                         strcpy (buf, "\033[2;1;1;112;112;1;0x");
1586                         buf[2] += i;
1587                         ldisc_send (buf, 20);
1588                     }
1589                 }
1590                 break;
1591               case ANSI('L','='):
1592                 compatibility(OTHER);
1593                 use_bce = (esc_args[0]<=0);
1594                 erase_char = ERASE_CHAR;
1595                 if (use_bce)
1596                     erase_char = (' '|(curr_attr&(ATTR_FGMASK|ATTR_BGMASK)));
1597                 break;
1598               case ANSI('E','='):
1599                 compatibility(OTHER);
1600                 blink_is_real = (esc_args[0]>=1);
1601                 break;
1602               case ANSI('p','"'):
1603                 /* Allow the host to make this emulator a 'perfect' VT102.
1604                  * This first appeared in the VT220, but we do need to get 
1605                  * back to PuTTY mode so I won't check it.
1606                  *
1607                  * The arg in 40..42 are a PuTTY extension.
1608                  * The 2nd arg, 8bit vs 7bit is not checked.
1609                  *
1610                  * Setting VT102 mode should also change the Fkeys to
1611                  * generate PF* codes as a real VT102 has no Fkeys.
1612                  * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1613                  * send nothing.
1614                  *
1615                  * Note ESC c will NOT change this!
1616                  */
1617
1618                 switch (esc_args[0]) {
1619                 case 61: compatibility_level &= ~TM_VTXXX;
1620                          compatibility_level |=  TM_VT102;      break;
1621                 case 62: compatibility_level &= ~TM_VTXXX;
1622                          compatibility_level |=  TM_VT220;      break;
1623
1624                 default: if( esc_args[0] > 60 && esc_args[0] < 70 )
1625                             compatibility_level |= TM_VTXXX;    
1626                          break;
1627
1628                 case 40: compatibility_level &=  TM_VTXXX;      break;
1629                 case 41: compatibility_level  =  TM_PUTTY;      break;
1630                 case 42: compatibility_level  =  TM_SCOANSI;    break;
1631
1632                 case ARG_DEFAULT: 
1633                          compatibility_level  =  TM_PUTTY;      break;
1634                 case 50: break;
1635                 }
1636
1637                 /* Change the response to CSI c */
1638                 if (esc_args[0] == 50) {
1639                    int i;
1640                    char lbuf[64];
1641                    strcpy(id_string, "\033[?");
1642                    for (i=1; i<esc_nargs; i++) {
1643                       if (i!=1) strcat(id_string, ";");
1644                       sprintf(lbuf, "%d", esc_args[i]);
1645                       strcat(id_string, lbuf);
1646                    }
1647                    strcat(id_string, "c");
1648                 }
1649
1650 #if 0
1651                 /* Is this a good idea ? 
1652                  * Well we should do a soft reset at this point ...
1653                  */
1654                 if (!has_compat(VT420) && has_compat(VT100)) {
1655                     if (reset_132) request_resize (132, 24, 1);
1656                     else           request_resize ( 80, 24, 1);
1657                 }
1658 #endif
1659                 break;
1660             }
1661             break;
1662           case SET_GL:
1663           case SET_GR:
1664             /* VT100 only here, checked above */
1665             switch (c) {
1666               case 'A':
1667                 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
1668                 break;
1669               case '0':
1670                 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
1671                 break;
1672               case 'B':
1673               default:                 /* specifically, 'B' */
1674                 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
1675                 break;
1676             }
1677             if( !has_compat(VT220) || c != '%' )
1678                 termstate = TOPLEVEL;
1679             break;
1680           case SEEN_OSC:
1681             osc_w = FALSE;
1682             switch (c) {
1683               case 'P':                /* Linux palette sequence */
1684                 termstate = SEEN_OSC_P;
1685                 osc_strlen = 0;
1686                 break;
1687               case 'R':                /* Linux palette reset */
1688                 palette_reset();
1689                 term_invalidate();
1690                 termstate = TOPLEVEL;
1691                 break;
1692               case 'W':                /* word-set */
1693                 termstate = SEEN_OSC_W;
1694                 osc_w = TRUE;
1695                 break;
1696               case '0': case '1': case '2': case '3': case '4':
1697               case '5': case '6': case '7': case '8': case '9':
1698                 esc_args[0] = 10 * esc_args[0] + c - '0';
1699                 break;
1700               case 'L':
1701                 /*
1702                  * Grotty hack to support xterm and DECterm title
1703                  * sequences concurrently.
1704                  */
1705                 if (esc_args[0] == 2) {
1706                     esc_args[0] = 1;
1707                     break;
1708                 }
1709                 /* else fall through */
1710               default:
1711                 termstate = OSC_STRING;
1712                 osc_strlen = 0;
1713             }
1714             break;
1715           case OSC_STRING:
1716             /*
1717              * This OSC stuff is EVIL. It takes just one character to get into
1718              * sysline mode and it's not initially obvious how to get out.
1719              * So I've added CR and LF as string aborts.
1720              * This shouldn't effect compatibility as I believe embedded 
1721              * control characters are supposed to be interpreted (maybe?) 
1722              * and they don't display anything useful anyway.
1723              *
1724              * -- RDB
1725              */
1726             if (c == '\n' || c == '\r') {
1727                 termstate = TOPLEVEL;
1728             } else if (c == 0234 || c == '\007' ) {
1729                 /*
1730                  * These characters terminate the string; ST and BEL
1731                  * terminate the sequence and trigger instant
1732                  * processing of it, whereas ESC goes back to SEEN_ESC
1733                  * mode unless it is followed by \, in which case it is
1734                  * synonymous with ST in the first place.
1735                  */
1736                 do_osc();
1737                 termstate = TOPLEVEL;
1738             } else if (c == '\033')
1739                     termstate = OSC_MAYBE_ST;
1740             else if (osc_strlen < OSC_STR_MAX)
1741                 osc_string[osc_strlen++] = c;
1742             break;
1743           case SEEN_OSC_P:
1744             {
1745                 int max = (osc_strlen == 0 ? 21 : 16);
1746                 int val;
1747                 if (c >= '0' && c <= '9')
1748                     val = c - '0';
1749                 else if (c >= 'A' && c <= 'A'+max-10)
1750                     val = c - 'A' + 10;
1751                 else if (c >= 'a' && c <= 'a'+max-10)
1752                     val = c - 'a' + 10;
1753                 else
1754                     termstate = TOPLEVEL;
1755                 osc_string[osc_strlen++] = val;
1756                 if (osc_strlen >= 7) {
1757                     palette_set (osc_string[0],
1758                                  osc_string[1] * 16 + osc_string[2],
1759                                  osc_string[3] * 16 + osc_string[4],
1760                                  osc_string[5] * 16 + osc_string[6]);
1761                     term_invalidate();
1762                     termstate = TOPLEVEL;
1763                 }
1764             }
1765             break;
1766           case SEEN_OSC_W:
1767             switch (c) {
1768               case '0': case '1': case '2': case '3': case '4':
1769               case '5': case '6': case '7': case '8': case '9':
1770                 esc_args[0] = 10 * esc_args[0] + c - '0';
1771                 break;
1772               default:
1773                 termstate = OSC_STRING;
1774                 osc_strlen = 0;
1775             }
1776             break;
1777           case SEEN_ESCHASH:
1778             {
1779                 unsigned long nlattr;
1780                 unsigned long *ldata;
1781                 int i, j;
1782                 pos scrtop, scrbot;
1783
1784                 switch (c) {
1785                 case '8':
1786                     for (i = 0; i < rows; i++) {
1787                         ldata = lineptr(i);
1788                         for (j = 0; j < cols; j++)
1789                             ldata[j] = ATTR_DEFAULT | 'E';
1790                         ldata[cols] = 0;
1791                     }
1792                     disptop = 0;
1793                     seen_disp_event = TRUE;
1794                     scrtop.x = scrtop.y = 0;
1795                     scrbot.x = 0; scrbot.y = rows;
1796                     check_selection (scrtop, scrbot);
1797                     break;
1798
1799                 case '3': nlattr = LATTR_TOP; goto lattr_common;
1800                 case '4': nlattr = LATTR_BOT; goto lattr_common;
1801                 case '5': nlattr = LATTR_NORM; goto lattr_common;
1802                 case '6': nlattr = LATTR_WIDE;
1803                     lattr_common:
1804
1805                     ldata = lineptr(curs.y);
1806                     ldata[cols] &= ~LATTR_MODE;
1807                     ldata[cols] |=  nlattr;
1808                 }
1809             }
1810             termstate = TOPLEVEL;
1811             break;
1812           case VT52_ESC:
1813             termstate = TOPLEVEL;
1814             seen_disp_event = TRUE;
1815             switch (c) {
1816               case 'A':
1817                 move (curs.x, curs.y - 1, 1);
1818                 break;
1819               case 'B':
1820                 move (curs.x, curs.y + 1, 1);
1821                 break;
1822               case 'C':
1823                 move (curs.x + 1, curs.y, 1);
1824                 break;
1825               case 'D':
1826                 move (curs.x - 1, curs.y, 1);
1827                 break;
1828               case 'F':
1829                 cset_attr[cset=0] = ATTR_LINEDRW;
1830                 break;
1831               case 'G':
1832                 cset_attr[cset=0] = ATTR_ASCII;
1833                 break;
1834               case 'H':
1835                 move (0, 0, 0);
1836                 break;
1837               case 'I':
1838                 if (curs.y == 0)
1839                     scroll (0, rows-1, -1, TRUE);
1840                 else if (curs.y > 0)
1841                     curs.y--;
1842                 fix_cpos;
1843                 wrapnext = FALSE;
1844                 break;
1845               case 'J':
1846                 erase_lots(FALSE, FALSE, TRUE);
1847                 disptop = 0;
1848                 break;
1849               case 'K':
1850                 erase_lots(TRUE, FALSE, TRUE);
1851                 break;
1852               case 'V':
1853                 /* XXX Print cursor line */
1854                 break;
1855               case 'W':
1856                 /* XXX Start controller mode */
1857                 break;
1858               case 'X':
1859                 /* XXX Stop controller mode */
1860                 break;
1861               case 'Y':
1862                 termstate = VT52_Y1;
1863                 break;
1864               case 'Z':
1865                 ldisc_send ("\033/Z", 3);
1866                 break;
1867               case '=':
1868                 app_keypad_keys = TRUE;
1869                 break;
1870               case '>':
1871                 app_keypad_keys = FALSE;
1872                 break;
1873               case '<':
1874                 /* XXX This should switch to VT100 mode not current or default
1875                  *     VT mode. But this will only have effect in a VT220+
1876                  *     emulation.
1877                  */
1878                 vt52_mode = FALSE;
1879                 break;
1880               case '^':
1881                 /* XXX Enter auto print mode */
1882                 break;
1883               case '_':
1884                 /* XXX Exit auto print mode */
1885                 break;
1886               case ']':
1887                 /* XXX Print screen */
1888                 break;
1889             }
1890             break;
1891           case VT52_Y1:
1892             termstate = VT52_Y2;
1893             move(curs.x, c-' ', 0);
1894             break;
1895           case VT52_Y2:
1896             termstate = TOPLEVEL;
1897             move(c-' ', curs.y, 0);
1898             break;
1899         }
1900         if (selstate != NO_SELECTION) {
1901             pos cursplus = curs;
1902             incpos(cursplus);
1903             check_selection (curs, cursplus);
1904         }
1905     }
1906     inbuf_head = 0;
1907 }
1908
1909 /*
1910  * Compare two lines to determine whether they are sufficiently
1911  * alike to scroll-optimise one to the other. Return the degree of
1912  * similarity.
1913  */
1914 static int linecmp (unsigned long *a, unsigned long *b) {
1915     int i, n;
1916
1917     for (i=n=0; i < cols; i++)
1918         n += (*a++ == *b++);
1919     return n;
1920 }
1921
1922 /*
1923  * Given a context, update the window. Out of paranoia, we don't
1924  * allow WM_PAINT responses to do scrolling optimisations.
1925  */
1926 static void do_paint (Context ctx, int may_optimise) {
1927     int i, j, start, our_curs_y;
1928     unsigned long attr, rv, cursor;
1929     pos scrpos;
1930     char ch[1024];
1931     long ticks;
1932
1933     /*
1934      * Check the visual bell state.
1935      */
1936     if (in_vbell) {
1937         ticks = GetTickCount();
1938         if (ticks - vbell_timeout >= 0)
1939             in_vbell = FALSE;
1940     }
1941
1942     /* Depends on:
1943      * screen array, disptop, scrtop,
1944      * selection, rv, 
1945      * cfg.blinkpc, blink_is_real, tblinker, 
1946      * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus
1947      */
1948     if (cursor_on) {
1949         if (has_focus) {
1950             if (blinker || !cfg.blink_cur)
1951                 cursor = ATTR_ACTCURS;
1952             else
1953                 cursor = 0;
1954         }
1955         else
1956             cursor = ATTR_PASCURS;
1957         if (wrapnext)
1958             cursor |= ATTR_RIGHTCURS;
1959     }
1960     else
1961         cursor = 0;
1962     rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
1963     our_curs_y = curs.y - disptop;
1964
1965     for (i=0; i<rows; i++) {
1966         unsigned long *ldata;
1967         int lattr;
1968         scrpos.y = i + disptop;
1969         ldata = lineptr(scrpos.y);
1970         lattr = (ldata[cols] & LATTR_MODE);
1971         for (j=0; j<=cols; j++) {
1972             unsigned long d = ldata[j];
1973             int idx = i*(cols+1)+j;
1974             scrpos.x = j;
1975             
1976             wanttext[idx] = lattr | (((d &~ ATTR_WRAPPED) ^ rv
1977                                       ^ (posle(selstart, scrpos) &&
1978                                          poslt(scrpos, selend) ?
1979                                          ATTR_REVERSE : 0)) |
1980                                      (i==our_curs_y && j==curs.x ? cursor : 0));
1981             if (blink_is_real) {
1982                 if (has_focus && tblinker && (wanttext[idx]&ATTR_BLINK) )
1983                 {
1984                     wanttext[idx] &= ATTR_MASK;
1985                     wanttext[idx] += ' ';
1986                 }
1987                 wanttext[idx] &= ~ATTR_BLINK;
1988             }
1989         }
1990     }
1991
1992     /*
1993      * We would perform scrolling optimisations in here, if they
1994      * didn't have a nasty tendency to cause the whole sodding
1995      * program to hang for a second at speed-critical moments.
1996      * We'll leave it well alone...
1997      */
1998
1999     for (i=0; i<rows; i++) {
2000         int idx = i*(cols+1);
2001         int lattr = (wanttext[idx+cols] & LATTR_MODE);
2002         start = -1;
2003         for (j=0; j<=cols; j++,idx++) {
2004             unsigned long t = wanttext[idx];
2005             int needs_update = (j < cols && t != disptext[idx]);
2006             int keep_going = (start != -1 && needs_update &&
2007                               (t & ATTR_MASK) == attr &&
2008                               j-start < sizeof(ch));
2009             if (start != -1 && !keep_going) {
2010                 do_text (ctx, start, i, ch, j-start, attr, lattr);
2011                 start = -1;
2012             }
2013             if (needs_update) {
2014                 if (start == -1) {
2015                     start = j;
2016                     attr = t & ATTR_MASK;
2017                 }
2018                 ch[j-start] = (char) (t & CHAR_MASK);
2019             }
2020             disptext[idx] = t;
2021         }
2022     }
2023 }
2024
2025 /*
2026  * Flick the switch that says if blinking things should be shown or hidden.
2027  */
2028
2029 void term_blink(int flg) {
2030     static long last_blink = 0;
2031     static long last_tblink = 0;
2032     long now, blink_diff;
2033
2034     now = GetTickCount();
2035     blink_diff = now-last_tblink;
2036
2037     /* Make sure the text blinks no more than 2Hz */
2038     if (blink_diff<0 || blink_diff>450)
2039     {
2040         last_tblink = now;
2041         tblinker = !tblinker;
2042     }
2043
2044     if (flg) {
2045         blinker = 1;
2046         last_blink = now;
2047         return;
2048     } 
2049
2050     blink_diff = now-last_blink;
2051
2052     /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2053     if (blink_diff>=0 && blink_diff<(long)GetCaretBlinkTime())
2054        return;
2055  
2056     last_blink = now;
2057     blinker = !blinker;
2058 }
2059
2060 /*
2061  * Invalidate the whole screen so it will be repainted in full.
2062  */
2063 void term_invalidate(void) {
2064     int i;
2065
2066     for (i=0; i<rows*(cols+1); i++)
2067         disptext[i] = ATTR_INVALID;
2068 }
2069
2070 /*
2071  * Paint the window in response to a WM_PAINT message.
2072  */
2073 void term_paint (Context ctx, int l, int t, int r, int b) {
2074     int i, j, left, top, right, bottom;
2075
2076     left = l / font_width;
2077     right = (r - 1) / font_width;
2078     top = t / font_height;
2079     bottom = (b - 1) / font_height;
2080     for (i = top; i <= bottom && i < rows ; i++)
2081     {
2082         if ( (disptext[i*(cols+1)+cols]&LATTR_MODE) == LATTR_NORM)
2083             for (j = left; j <= right && j < cols ; j++)
2084                 disptext[i*(cols+1)+j] = ATTR_INVALID;
2085         else
2086             for (j = left/2; j <= right/2+1 && j < cols ; j++)
2087                 disptext[i*(cols+1)+j] = ATTR_INVALID;
2088     }
2089
2090     /* This should happen soon enough, also for some reason it sometimes 
2091      * fails to actually do anything when re-sizing ... painting the wrong
2092      * window perhaps ?
2093     do_paint (ctx, FALSE);
2094     */
2095 }
2096
2097 /*
2098  * Attempt to scroll the scrollback. The second parameter gives the
2099  * position we want to scroll to; the first is +1 to denote that
2100  * this position is relative to the beginning of the scrollback, -1
2101  * to denote it is relative to the end, and 0 to denote that it is
2102  * relative to the current position.
2103  */
2104 void term_scroll (int rel, int where) {
2105     int sbtop = -count234(scrollback);
2106
2107     disptop = (rel < 0 ? 0 :
2108                rel > 0 ? sbtop : disptop) + where;
2109     if (disptop < sbtop)
2110         disptop = sbtop;
2111     if (disptop > 0)
2112         disptop = 0;
2113     update_sbar();
2114     term_update();
2115 }
2116
2117 static void clipme(pos top, pos bottom, char *workbuf) {
2118     char *wbptr;                /* where next char goes within workbuf */
2119     int wblen = 0;              /* workbuf len */
2120     int buflen;                 /* amount of memory allocated to workbuf */
2121
2122     if ( workbuf != NULL ) {    /* user supplied buffer? */
2123         buflen = -1;            /* assume buffer passed in is big enough */
2124         wbptr = workbuf;        /* start filling here */
2125     }
2126     else
2127         buflen = 0;             /* No data is available yet */
2128
2129     while (poslt(top, bottom)) {
2130         int nl = FALSE;
2131         unsigned long *ldata = lineptr(top.y);
2132         pos nlpos;
2133
2134         nlpos.y = top.y;
2135         nlpos.x = cols;
2136
2137         if (!(ldata[cols] & ATTR_WRAPPED)) {
2138             while ((ldata[nlpos.x-1] & CHAR_MASK) == 0x20 && poslt(top, nlpos))
2139                 decpos(nlpos);
2140             if (poslt(nlpos, bottom))
2141                 nl = TRUE;
2142         }
2143         while (poslt(top, bottom) && poslt(top, nlpos)) {
2144             int ch = (ldata[top.x] & CHAR_MASK);
2145             int set = (ldata[top.x] & CSET_MASK);
2146
2147             /* VT Specials -> ISO8859-1 for Cut&Paste */
2148             static const unsigned char poorman2[] =
2149 "* # HTFFCRLF\xB0 \xB1 NLVT+ + + + + - - - - - + + + + | <=>=PI!=\xA3 \xB7 ";
2150
2151             if (set && !cfg.rawcnp) {
2152                 if (set == ATTR_LINEDRW && ch >= 0x60 && ch < 0x7F) {
2153                     int x;
2154                     if ((x = poorman2[2*(ch-0x60)+1]) == ' ')
2155                         x = 0;
2156                     ch = (x<<8) + poorman2[2*(ch-0x60)];
2157                 }
2158             }
2159
2160             while(ch != 0) {
2161                 if (cfg.rawcnp || !!(ch&0xE0)) {
2162                     if ( wblen == buflen )
2163                     {
2164                         workbuf = srealloc(workbuf, buflen += 100);
2165                         wbptr = workbuf + wblen;
2166                     }
2167                     wblen++;
2168                     *wbptr++ = (unsigned char) ch;
2169                 }
2170                 ch>>=8;
2171             }
2172             top.x++;
2173         }
2174         if (nl) {
2175             int i;
2176             for (i=0; i<sizeof(sel_nl); i++)
2177             {
2178                 if ( wblen == buflen )
2179                 {
2180                     workbuf = srealloc(workbuf, buflen += 100);
2181                     wbptr = workbuf + wblen;
2182                 }
2183                 wblen++;
2184                 *wbptr++ = sel_nl[i];
2185             }
2186         }
2187         top.y++;
2188         top.x = 0;
2189     }
2190     write_clip (workbuf, wblen, FALSE); /* transfer to clipboard */
2191     if ( buflen > 0 )   /* indicates we allocated this buffer */
2192         sfree(workbuf);
2193
2194 }
2195 void term_copyall (void) {
2196     pos top;
2197     top.y = -count234(scrollback);
2198     top.x = 0;
2199     clipme(top, curs, NULL /* dynamic allocation */);
2200 }
2201
2202 /*
2203  * Spread the selection outwards according to the selection mode.
2204  */
2205 static pos sel_spread_half (pos p, int dir) {
2206     unsigned long *ldata;
2207     short wvalue;
2208
2209     ldata = lineptr(p.y);
2210
2211     switch (selmode) {
2212       case SM_CHAR:
2213         /*
2214          * In this mode, every character is a separate unit, except
2215          * for runs of spaces at the end of a non-wrapping line.
2216          */
2217         if (!(ldata[cols] & ATTR_WRAPPED)) {
2218             unsigned long *q = ldata+cols;
2219             while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2220                 q--;
2221             if (q == ldata+cols)
2222                 q--;
2223             if (p.x >= q-ldata)
2224                 p.x = (dir == -1 ? q-ldata : cols - 1);
2225         }
2226         break;
2227       case SM_WORD:
2228         /*
2229          * In this mode, the units are maximal runs of characters
2230          * whose `wordness' has the same value.
2231          */
2232         wvalue = wordness[ldata[p.x] & CHAR_MASK];
2233         if (dir == +1) {
2234             while (p.x < cols && wordness[ldata[p.x+1] & CHAR_MASK] == wvalue)
2235                 p.x++;
2236         } else {
2237             while (p.x > 0 && wordness[ldata[p.x-1] & CHAR_MASK] == wvalue)
2238                 p.x--;
2239         }
2240         break;
2241       case SM_LINE:
2242         /*
2243          * In this mode, every line is a unit.
2244          */
2245         p.x = (dir == -1 ? 0 : cols - 1);
2246         break;
2247     }
2248     return p;
2249 }
2250
2251 static void sel_spread (void) {
2252     selstart = sel_spread_half (selstart, -1);
2253     decpos(selend);
2254     selend = sel_spread_half (selend, +1);
2255     incpos(selend);
2256 }
2257
2258 void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y,
2259                  int shift, int ctrl) {
2260     pos selpoint;
2261     unsigned long *ldata;
2262     
2263     if (y<0) y = 0;
2264     if (y>=rows) y = rows-1;
2265     if (x<0) {
2266         if (y > 0) {
2267             x = cols-1;
2268             y--;
2269         } else
2270             x = 0;
2271     }
2272     if (x>=cols) x = cols-1;
2273
2274     selpoint.y = y + disptop;
2275     selpoint.x = x;
2276     ldata = lineptr(selpoint.y);
2277     if ((ldata[cols]&LATTR_MODE) != LATTR_NORM)
2278         selpoint.x /= 2;
2279
2280     if (xterm_mouse) {
2281         int encstate = 0, r, c;
2282         char abuf[16];
2283         static int is_down = 0;
2284
2285         switch(b) {
2286           case MBT_LEFT:
2287             encstate = 0x20;    /* left button down */
2288             break;
2289           case MBT_MIDDLE:
2290             encstate = 0x21;
2291             break;
2292           case MBT_RIGHT:
2293             encstate = 0x22;
2294             break;
2295           case MBT_WHEEL_UP:
2296             encstate = 0x60;
2297             break;
2298           case MBT_WHEEL_DOWN:
2299             encstate = 0x61;
2300             break;
2301         }
2302         switch(a) {
2303           case MA_DRAG:
2304             if (xterm_mouse == 1)
2305                 return;
2306             encstate += 0x20;
2307             break;
2308           case MA_RELEASE:
2309             encstate = 0x23;
2310             is_down = 0;
2311             break;
2312           case MA_CLICK:
2313             if (is_down == b)
2314                 return;
2315             is_down = b;
2316             break;
2317         }
2318         if (shift)
2319             encstate += 0x04;
2320         if (ctrl)
2321             encstate += 0x10;
2322         r = y + 33;
2323         c = x + 33;
2324
2325         sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
2326         ldisc_send(abuf, 6);
2327         return;
2328     }
2329
2330     b = translate_button(b);
2331
2332     if (b == MBT_SELECT && a == MA_CLICK) {
2333         deselect();
2334         selstate = ABOUT_TO;
2335         selanchor = selpoint;
2336         selmode = SM_CHAR;
2337     } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
2338         deselect();
2339         selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
2340         selstate = DRAGGING;
2341         selstart = selanchor = selpoint;
2342         selend = selstart;
2343         incpos(selend);
2344         sel_spread();
2345     } else if ((b == MBT_SELECT && a == MA_DRAG) ||
2346                (b == MBT_EXTEND && a != MA_RELEASE)) {
2347         if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
2348             return;
2349         if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
2350             if (posdiff(selpoint,selstart) < posdiff(selend,selstart)/2) {
2351                 selanchor = selend;
2352                 decpos(selanchor);
2353             } else {
2354                 selanchor = selstart;
2355             }
2356             selstate = DRAGGING;
2357         }
2358         if (selstate != ABOUT_TO && selstate != DRAGGING)
2359             selanchor = selpoint;
2360         selstate = DRAGGING;
2361         if (poslt(selpoint, selanchor)) {
2362             selstart = selpoint;
2363             selend = selanchor;
2364             incpos(selend);
2365         } else {
2366             selstart = selanchor;
2367             selend = selpoint;
2368             incpos(selend);
2369         }
2370         sel_spread();
2371     } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
2372         if (selstate == DRAGGING) {
2373             /*
2374              * We've completed a selection. We now transfer the
2375              * data to the clipboard.
2376              */
2377             clipme(selstart, selend, selspace);
2378             selstate = SELECTED;
2379         } else
2380             selstate = NO_SELECTION;
2381     } else if (b == MBT_PASTE && (a==MA_CLICK || a==MA_2CLK || a==MA_3CLK)) {
2382         char *data;
2383         int len;
2384
2385         get_clip((void **) &data, &len);
2386         if (data) {
2387             char *p, *q;
2388
2389             if (paste_buffer) sfree(paste_buffer);
2390             paste_pos = paste_hold = paste_len = 0;
2391             paste_buffer = smalloc(len);
2392
2393             p = q = data;
2394             while (p < data+len) {
2395                 while (p < data+len &&
2396                        !(p <= data+len-sizeof(sel_nl) &&
2397                          !memcmp(p, sel_nl, sizeof(sel_nl))))
2398                     p++;
2399
2400                 {
2401                     int i;
2402                     unsigned char c;
2403                     for(i=0;i<p-q;i++)
2404                     {
2405                         c=xlat_kbd2tty(q[i]);
2406                         paste_buffer[paste_len++] = c;
2407                     }
2408                 }
2409
2410                 if (p <= data+len-sizeof(sel_nl) &&
2411                     !memcmp(p, sel_nl, sizeof(sel_nl))) {
2412                     paste_buffer[paste_len++] = '\r';
2413                     p += sizeof(sel_nl);
2414                 }
2415                 q = p;
2416             }
2417
2418             /* Assume a small paste will be OK in one go. */
2419             if (paste_len<256) {
2420                 ldisc_send (paste_buffer, paste_len);
2421                 if (paste_buffer) sfree(paste_buffer);
2422                 paste_buffer = 0;
2423                 paste_pos = paste_hold = paste_len = 0;
2424             }
2425         }
2426         get_clip(NULL, NULL);
2427     }
2428
2429     term_update();
2430 }
2431
2432 void term_nopaste() {
2433     if(paste_len == 0) return;
2434     sfree(paste_buffer);
2435     paste_buffer = 0;
2436     paste_len = 0;
2437 }
2438
2439 void term_paste() {
2440     static long last_paste = 0;
2441     long now, paste_diff;
2442
2443     if(paste_len == 0) return;
2444
2445     /* Don't wait forever to paste */
2446     if(paste_hold) {
2447         now = GetTickCount();
2448         paste_diff = now-last_paste;
2449         if (paste_diff>=0 && paste_diff<450)
2450             return;
2451     }
2452     paste_hold = 0;
2453
2454     while(paste_pos<paste_len)
2455     {
2456         int n = 0;
2457         while (n + paste_pos < paste_len) {
2458             if (paste_buffer[paste_pos + n++] == '\r')
2459                 break;
2460         }
2461         ldisc_send (paste_buffer+paste_pos, n);
2462         paste_pos += n;
2463
2464         if (paste_pos < paste_len) {
2465             paste_hold = 1;
2466             return;
2467         }
2468     }
2469     sfree(paste_buffer);
2470     paste_buffer = 0;
2471     paste_len = 0;
2472 }
2473
2474 static void deselect (void) {
2475     selstate = NO_SELECTION;
2476     selstart.x = selstart.y = selend.x = selend.y = 0;
2477 }
2478
2479 void term_deselect (void) {
2480     deselect();
2481     term_update();
2482 }
2483
2484 int term_ldisc(int option) {
2485     if (option == LD_ECHO) return term_echoing;
2486     if (option == LD_EDIT) return term_editing;
2487     return FALSE;
2488 }
2489
2490 /*
2491  * from_backend(), to get data from the backend for the terminal.
2492  */
2493 void from_backend(int is_stderr, char *data, int len) {
2494     while (len--) {
2495         if (inbuf_head >= INBUF_SIZE)
2496             term_out();
2497         inbuf[inbuf_head++] = *data++;
2498     }
2499 }
2500
2501 /*
2502  * Log session traffic.
2503  */
2504 void logtraffic(unsigned char c, int logmode) {
2505     if (cfg.logtype > 0) {
2506         if (cfg.logtype == logmode) {
2507             /* deferred open file from pgm start? */
2508             if (!lgfp) logfopen();
2509             if (lgfp) fputc (c, lgfp);
2510         }
2511     }
2512 }
2513
2514 /* open log file append/overwrite mode */
2515 void logfopen(void) {
2516     char buf[256];
2517     time_t t;
2518     struct tm *tm;
2519     char writemod[4];
2520
2521     if (!cfg.logtype)
2522         return;
2523     sprintf (writemod, "wb");          /* default to rewrite */
2524     lgfp = fopen(cfg.logfilename, "r");  /* file already present? */
2525     if (lgfp) {
2526         int i;
2527         fclose(lgfp);
2528         i = askappend(cfg.logfilename);
2529         if (i == 1)
2530             writemod[0] = 'a';         /* set append mode */
2531         else if (i == 0) {             /* cancelled */
2532             lgfp = NULL;
2533             cfg.logtype = 0;           /* disable logging */
2534             return;
2535         }
2536     }
2537
2538     lgfp = fopen(cfg.logfilename, writemod);
2539     if (lgfp) { /* enter into event log */
2540         sprintf(buf, "%s session log (%s mode) to file : ",
2541                 (writemod[0] == 'a') ? "Appending" : "Writing new",
2542                 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
2543                  cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>")  );
2544         /* Make sure we do not exceed the output buffer size */
2545         strncat (buf, cfg.logfilename, 128);
2546         buf[strlen(buf)] = '\0';
2547         logevent(buf);
2548
2549         /* --- write header line iinto log file */
2550         fputs ("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
2551         time(&t);
2552         tm = localtime(&t);
2553         strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
2554         fputs (buf, lgfp);
2555         fputs (" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
2556     }
2557 }
2558
2559 void logfclose (void) {
2560     if (lgfp) { fclose(lgfp); lgfp = NULL; }
2561 }