]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - terminal.c
Removing one bug, and hunting another
[PuTTY.git] / terminal.c
1 #ifndef macintosh
2 #include <windows.h>
3 #endif /* not macintosh */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include "putty.h"
9
10 #define cfg             (s->cfg)
11 #define back            (s->back)
12 #define rows            (s->rows)
13 #define cols            (s->cols)
14 #define savelines       (s->savelines)
15 #define font_width      (s->font_width)
16 #define font_height     (s->font_height)
17 #define has_focus       (s->has_focus)
18 #define inbuf           (s->inbuf)
19 #define inbuf_head      (s->inbuf_head)
20 #define inbuf_reap      (s->inbuf_reap)
21 #define app_cursor_keys (s->app_cursor_keys)
22 #define app_keypad_keys (s->app_keypad_keys)
23 #define attr_mask       (s->attr_mask)
24
25 #define text            s->ts.text
26 #define scrtop          s->ts.scrtop
27 #define disptop         s->ts.disptop
28 #define sbtop           s->ts.sbtop
29 #define cpos            s->ts.cpos
30 #define disptext        s->ts.disptext
31 #define wanttext        s->ts.wanttext
32 #define alttext         s->ts.alttext
33
34 #define selspace        s->ts.selspace
35
36 #define TSIZE (sizeof(*text))
37 #define fix_cpos  do { cpos = scrtop + curs_y * (cols+1) + curs_x; } while(0)
38
39 #define curr_attr       s->ts.curr_attr
40 #define save_attr       s->ts.save_attr
41
42 #define curs_x          s->ts.curs_x
43 #define curs_y          s->ts.curs_y
44 #define save_x          s->ts.save_x
45 #define save_y          s->ts.save_y
46 #define marg_t          s->ts.marg_t
47 #define marg_b          s->ts.marg_b
48 #define curr_dec_om     s->ts.dec_om
49 #define wrap            s->ts.wrap
50 #define wrapnext        s->ts.wrapnext
51 #define insert          s->ts.insert
52 #define cset            s->ts.cset
53 #define save_cset       s->ts.save_cset
54 #define save_csattr     s->ts.save_csattr
55 #define rvideo          s->ts.rvideo
56 #define cset_attr       s->ts.cset_attr
57
58 #define alt_x           s->ts.alt_x
59 #define alt_y           s->ts.alt_y
60 #define alt_om          s->ts.alt_om
61 #define alt_wrap        s->ts.alt_wrap
62 #define alt_wnext       s->ts.alt_wnext
63 #define alt_ins         s->ts.alt_ins
64 #define alt_cset        s->ts.alt_cset
65 #define alt_t           s->ts.alt_t
66 #define alt_b           s->ts.alt_b
67 #define alt_which       s->ts.alt_which
68
69 #define ARG_DEFAULT -1                 /* if an arg isn't specified */
70 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
71
72 #define esc_args        s->ts.esc_args
73 #define esc_nargs       s->ts.esc_nargs
74 #define esc_query       s->ts.esc_query
75
76 #define osc_strlen      s->ts.osc_strlen
77 #define osc_string      s->ts.osc_string
78 #define osc_w           s->ts.osc_w
79
80 #define tabs            s->ts.tabs
81
82 #define MAXNL 5
83 #define nl_count        s->ts.nl_count
84
85 #define termstate       (s->ts.termstate)
86 #define selstate        (s->ts.selstate)
87 #define selmode         (s->ts.selmode)
88 #define selstart        (s->ts.selstart)
89 #define selend          (s->ts.selend)
90 #define selanchor       (s->ts.selanchor)
91
92 #define curr_wordness   s->ts.wordness
93
94 static unsigned char sel_nl[] = SEL_NL;
95
96 /*
97  * Internal prototypes.
98  */
99 static void do_paint (Session *, int);
100 static void erase_lots (Session *, int, int, int);
101 static void swap_screen (Session *, int);
102 static void update_sbar (Session *);
103 static void deselect (Session *);
104 static void scroll_display(Session *, int, int, int);
105
106 /*
107  * Set up power-on settings for the terminal.
108  */
109 static void power_on(Session *s) {
110     curs_x = curs_y = alt_x = alt_y = save_x = save_y = 0;
111     alt_t = marg_t = 0;
112     if (rows != -1)
113         alt_b = marg_b = rows - 1;
114     else
115         alt_b = marg_b = 0;
116     if (cols != -1) {
117         int i;
118         for (i = 0; i < cols; i++)
119             tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
120     }
121     alt_om = curr_dec_om = cfg.dec_om;
122     alt_wnext = wrapnext = alt_ins = insert = FALSE;
123     alt_wrap = wrap = cfg.wrap_mode;
124     alt_cset = cset = 0;
125     cset_attr[0] = cset_attr[1] = ATTR_ASCII;
126     rvideo = 0;
127     save_attr = curr_attr = ATTR_DEFAULT;
128     app_cursor_keys = cfg.app_cursor;
129     app_keypad_keys = cfg.app_keypad;
130     alt_which = 0;
131     {
132         int i;
133         for (i = 0; i < 256; i++)
134             curr_wordness[i] = cfg.wordness[i];
135     }
136     if (text) {
137         swap_screen (s, 1);
138         erase_lots (s, FALSE, TRUE, TRUE);
139         swap_screen (s, 0);
140         erase_lots (s, FALSE, TRUE, TRUE);
141     }
142 }
143
144 /*
145  * Force a screen update.
146  */
147 void term_update(Session *s) {
148
149     pre_paint(s);
150     do_paint(s, TRUE);
151     post_paint(s);
152     nl_count = 0;
153 }
154
155 /*
156  * Same as power_on(), but an external function.
157  */
158 void term_pwron(Session *s) {
159     power_on(s);
160     fix_cpos;
161     disptop = scrtop;
162     deselect(s);
163     term_update(s);
164 }
165
166 /*
167  * Clear the scrollback.
168  */
169 void term_clrsb(Session *s) {
170     disptop = sbtop = scrtop;
171     update_sbar(s);
172 }
173
174 /*
175  * Initialise the terminal.
176  */
177 void term_init(Session *s) {
178     text = sbtop = scrtop = disptop = cpos = NULL;
179     disptext = wanttext = NULL;
180     tabs = NULL;
181     selspace = NULL;
182     deselect(s);
183     rows = cols = -1;
184     nl_count = 0;
185     power_on(s);
186 }
187
188 /*
189  * Set up the terminal for a given size.
190  */
191 void term_size(Session *s, int newrows, int newcols, int newsavelines) {
192     unsigned long *newtext, *newdisp, *newwant, *newalt;
193     int i, j, crows, ccols;
194
195     if (newrows == rows && newcols == cols && newsavelines == savelines)
196         return;                        /* nothing to do */
197
198     alt_t = marg_t = 0;
199     alt_b = marg_b = newrows - 1;
200
201     newtext = smalloc ((newrows+newsavelines)*(newcols+1)*TSIZE);
202     disptop = newtext + newsavelines*(newcols+1);
203     for (i=0; i<(newrows+newsavelines)*(newcols+1); i++)
204         newtext[i] = ERASE_CHAR;
205     if (rows != -1) {
206         crows = rows + (scrtop - sbtop) / (cols+1);
207         if (crows > newrows+newsavelines)
208             crows = newrows+newsavelines;
209         ccols = (cols < newcols ? cols : newcols);
210         for (i=0; i<crows; i++) {
211             int oldidx = (rows + savelines - crows + i) * (cols+1);
212             int newidx = (newrows + newsavelines - crows + i) * (newcols+1);
213             for (j=0; j<ccols; j++)
214                 newtext[newidx+j] = text[oldidx+j];
215             newtext[newidx+newcols] =
216                 (cols == newcols ? text[oldidx+cols] : 0);
217         }
218         sbtop = disptop - (crows - newrows) * (newcols+1);
219         if (sbtop > disptop)
220             sbtop = disptop;
221     } else
222         sbtop = disptop;
223     scrtop = disptop;
224     sfree (text);
225     text = newtext;
226
227     newdisp = smalloc (newrows*(newcols+1)*TSIZE);
228     for (i=0; i<newrows*(newcols+1); i++)
229         newdisp[i] = ATTR_INVALID;
230     sfree (disptext);
231     disptext = newdisp;
232
233     newwant = smalloc (newrows*(newcols+1)*TSIZE);
234     for (i=0; i<newrows*(newcols+1); i++)
235         newwant[i] = ATTR_INVALID;
236     sfree (wanttext);
237     wanttext = newwant;
238
239     newalt = smalloc (newrows*(newcols+1)*TSIZE);
240     for (i=0; i<newrows*(newcols+1); i++)
241         newalt[i] = ERASE_CHAR;
242     sfree (alttext);
243     alttext = newalt;
244
245     sfree (selspace);
246     selspace = smalloc ( (newrows+newsavelines) * (newcols+sizeof(sel_nl)) );
247
248     tabs = srealloc (tabs, newcols*sizeof(*tabs));
249     {
250         int i;
251         for (i = (cols > 0 ? cols : 0); i < newcols; i++)
252             tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
253     }
254
255     if (rows > 0)
256         curs_y += newrows - rows;
257     if (curs_y < 0)
258         curs_y = 0;
259     if (curs_y >= newrows)
260         curs_y = newrows-1;
261     if (curs_x >= newcols)
262         curs_x = newcols-1;
263     alt_x = alt_y = 0;
264     wrapnext = alt_wnext = FALSE;
265
266     rows = newrows;
267     cols = newcols;
268     savelines = newsavelines;
269     fix_cpos;
270
271     deselect(s);
272     update_sbar(s);
273     term_update(s);
274 }
275
276 /*
277  * Swap screens.
278  */
279 static void swap_screen (Session *s, int which) {
280     int t;
281     unsigned long tt;
282
283     if (which == alt_which)
284         return;
285
286     alt_which = which;
287
288     for (t=0; t<rows*(cols+1); t++) {
289         tt = scrtop[t]; scrtop[t] = alttext[t]; alttext[t] = tt;
290     }
291
292     t = curs_x; curs_x = alt_x; alt_x = t;
293     t = curs_y; curs_y = alt_y; alt_y = t;
294     t = marg_t; marg_t = alt_t; alt_t = t;
295     t = marg_b; marg_b = alt_b; alt_b = t;
296     t = curr_dec_om; curr_dec_om = alt_om; alt_om = t;
297     t = wrap; wrap = alt_wrap; alt_wrap = t;
298     t = wrapnext; wrapnext = alt_wnext; alt_wnext = t;
299     t = insert; insert = alt_ins; alt_ins = t;
300     t = cset; cset = alt_cset; alt_cset = t;
301
302     fix_cpos;
303 }
304
305 /*
306  * Retrieve a character from `inbuf'.
307  */
308 static int inbuf_getc(Session *s) {
309     if (inbuf_head == inbuf_reap)
310         return -1;                     /* EOF */
311     else {
312         int n = inbuf_reap;
313         inbuf_reap = (inbuf_reap+1) & INBUF_MASK;
314         return inbuf[n];
315     }
316 }
317
318 /*
319  * Update the scroll bar.
320  */
321 static void update_sbar(Session *s) {
322     int min;
323
324     min = (sbtop - text) / (cols+1);
325     set_sbar(s, (scrtop - text) / (cols+1) + rows - min,
326              (disptop - text) / (cols+1) - min,
327              rows);
328 }
329
330 /*
331  * Check whether the region bounded by the two pointers intersects
332  * the selection, and de-select the on-screen selection if so.
333  */
334 static void check_selection(Session *s,
335                             unsigned long *from, unsigned long *to) {
336     if (from < selend && selstart < to)
337         deselect(s);
338 }
339
340 /*
341  * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
342  * for backward.) `sb' is TRUE if the scrolling is permitted to
343  * affect the scrollback buffer.
344  */
345 static void scroll (Session *s, int topline, int botline, int lines, int sb) {
346     unsigned long *scroll_top;
347     int scroll_size, size, i;
348
349     scroll_top = scrtop + topline*(cols+1);
350     size = (lines < 0 ? -lines : lines) * (cols+1);
351     scroll_size = (botline - topline + 1) * (cols+1) - size;
352
353     if (lines > 0 && topline == 0 && botline == (rows-1) && sb) {
354         /*
355          * Since we're going to scroll the whole screen upwards,
356          * let's also affect the scrollback buffer.
357          */
358         sbtop -= lines * (cols+1);
359         if (sbtop < text)
360             sbtop = text;
361         scroll_size += scroll_top - sbtop;
362         scroll_top = sbtop;
363         update_sbar(s);
364     }
365
366     if (scroll_size < 0) {
367         size += scroll_size;
368         scroll_size = 0;
369     }
370
371     if (lines > 0) {
372         if (scroll_size)
373             memmove (scroll_top, scroll_top + size, scroll_size*TSIZE);
374         for (i = 0; i < size; i++)
375             scroll_top[i+scroll_size] = ERASE_CHAR;
376         if (selstart > scroll_top &&
377             selstart < scroll_top + size + scroll_size) {
378             selstart -= size;
379             if (selstart < scroll_top)
380                 selstart = scroll_top;
381         }
382         if (selend > scroll_top &&
383             selend < scroll_top + size + scroll_size) {
384             selend -= size;
385             if (selend < scroll_top)
386                 selend = scroll_top;
387         }
388     } else {
389         if (scroll_size)
390             memmove (scroll_top + size, scroll_top, scroll_size*TSIZE);
391         for (i = 0; i < size; i++)
392             scroll_top[i] = ERASE_CHAR;
393         if (selstart > scroll_top &&
394             selstart < scroll_top + size + scroll_size) {
395             selstart += size;
396             if (selstart > scroll_top + size + scroll_size)
397                 selstart = scroll_top + size + scroll_size;
398         }
399         if (selend > scroll_top &&
400             selend < scroll_top + size + scroll_size) {
401             selend += size;
402             if (selend > scroll_top + size + scroll_size)
403                 selend = scroll_top + size + scroll_size;
404         }
405     }
406 #ifdef OPTIMISE_SCROLL
407     scroll_display(s, topline, botline, lines);
408 #endif
409 }
410
411 #ifdef OPTIMISE_SCROLL
412 static void scroll_display(Session *s, int topline, int botline, int lines) {
413     unsigned long *start, *end;
414     int distance, size, i;
415
416     start = disptext + topline * (cols + 1);
417     end = disptext + (botline + 1) * (cols + 1);
418     distance = (lines > 0 ? lines : -lines) * (cols + 1);
419     size = end - start - distance;
420     if (lines > 0) {
421         memmove(start, start + distance, size * TSIZE);
422         for (i = 0; i < distance; i++)
423             (start + size)[i] |= ATTR_INVALID;
424     } else {
425         memmove(start + distance, start, size * TSIZE);
426         for (i = 0; i < distance; i++)
427             start[i] |= ATTR_INVALID;
428     }
429     do_scroll(s, topline, botline, lines);
430 }
431 #endif /* OPTIMISE_SCROLL */
432     
433
434 /*
435  * Move the cursor to a given position, clipping at boundaries. We
436  * may or may not want to clip at the scroll margin: marg_clip is 0
437  * not to, 1 to disallow _passing_ the margins, and 2 to disallow
438  * even _being_ outside the margins.
439  */
440 static void move (Session *s, int x, int y, int marg_clip) {
441     if (x < 0)
442         x = 0;
443     if (x >= cols)
444         x = cols-1;
445     if (marg_clip) {
446         if ((curs_y >= marg_t || marg_clip == 2) && y < marg_t)
447             y = marg_t;
448         if ((curs_y <= marg_b || marg_clip == 2) && y > marg_b)
449             y = marg_b;
450     }
451     if (y < 0)
452         y = 0;
453     if (y >= rows)
454         y = rows-1;
455     curs_x = x;
456     curs_y = y;
457     fix_cpos;
458     wrapnext = FALSE;
459 }
460
461 /*
462  * Save or restore the cursor and SGR mode.
463  */
464 static void save_cursor(Session *s, int save) {
465     if (save) {
466         save_x = curs_x;
467         save_y = curs_y;
468         save_attr = curr_attr;
469         save_cset = cset;
470         save_csattr = cset_attr[cset];
471     } else {
472         curs_x = save_x;
473         curs_y = save_y;
474         curr_attr = save_attr;
475         cset = save_cset;
476         cset_attr[cset] = save_csattr;
477         fix_cpos;
478     }
479 }
480
481 /*
482  * Erase a large portion of the screen: the whole screen, or the
483  * whole line, or parts thereof.
484  */
485 static void erase_lots(Session *s, int line_only, int from_begin, int to_end) {
486     unsigned long *startpos, *endpos;
487
488     if (line_only) {
489         startpos = cpos - curs_x;
490         endpos = startpos + cols+1;
491     } else {
492         startpos = scrtop;
493         endpos = startpos + rows * (cols+1);
494     }
495     if (!from_begin)
496         startpos = cpos;
497     if (!to_end)
498         endpos = cpos;
499     check_selection(s, startpos, endpos);
500     while (startpos < endpos)
501         *startpos++ = ERASE_CHAR;
502 }
503
504 /*
505  * Insert or delete characters within the current line. n is +ve if
506  * insertion is desired, and -ve for deletion.
507  */
508 static void insch(Session *s, int n) {
509     int dir = (n < 0 ? -1 : +1);
510     int m;
511
512     n = (n < 0 ? -n : n);
513     if (n > cols - curs_x)
514         n = cols - curs_x;
515     m = cols - curs_x - n;
516     check_selection(s, cpos, cpos+n);
517     if (dir < 0) {
518         memmove (cpos, cpos+n, m*TSIZE);
519         while (n--)
520             cpos[m++] = ERASE_CHAR;
521     } else {
522         memmove (cpos+n, cpos, m*TSIZE);
523         while (n--)
524             cpos[n] = ERASE_CHAR;
525     }
526 }
527
528 /*
529  * Toggle terminal mode `mode' to state `state'. (`query' indicates
530  * whether the mode is a DEC private one or a normal one.)
531  */
532 static void toggle_mode(Session *s, int mode, int query, int state) {
533     if (query) switch (mode) {
534       case 1:                          /* application cursor keys */
535         app_cursor_keys = state;
536         break;
537       case 3:                          /* 80/132 columns */
538         deselect(s);
539         request_resize(s, state ? 132 : 80, rows);
540         break;
541       case 5:                          /* reverse video */
542         rvideo = state;
543         disptop = scrtop;
544         break;
545       case 6:                          /* DEC origin mode */
546         curr_dec_om = state;
547         break;
548       case 7:                          /* auto wrap */
549         wrap = state;
550         break;
551       case 47:                         /* alternate screen */
552         deselect(s);
553         swap_screen(s, state);
554         disptop = scrtop;
555         break;
556     } else switch (mode) {
557       case 4:                          /* set insert mode */
558         insert = state;
559         break;
560     }
561 }
562
563 /*
564  * Process an OSC sequence: set window title or icon name.
565  */
566 static void do_osc(Session *s) {
567     if (osc_w) {
568         while (osc_strlen--)
569             curr_wordness[(unsigned char)osc_string[osc_strlen]] = esc_args[0];
570     } else {
571         osc_string[osc_strlen] = '\0';
572         switch (esc_args[0]) {
573           case 0:
574           case 1:
575             set_icon (s, osc_string);
576             if (esc_args[0] == 1)
577                 break;
578             /* fall through: parameter 0 means set both */
579           case 2:
580           case 21:
581             set_title(s, osc_string);
582             break;
583         }
584     }
585 }
586
587 /*
588  * Remove everything currently in `inbuf' and stick it up on the
589  * in-memory display. There's a big state machine in here to
590  * process escape sequences...
591  */
592 void term_out(Session *s) {
593     int c;
594     int must_update = FALSE;
595
596     while ( (c = inbuf_getc(s)) != -1) {
597 #ifdef LOG
598         {
599             static FILE *fp = NULL;
600             if (!fp) fp = fopen("putty.log", "wb");
601             if (fp) fputc (c, fp);
602         }
603 #endif
604         switch (termstate) {
605           case TOPLEVEL:
606             do_toplevel:
607             switch (c) {
608               case '\005':             /* terminal type query */
609                 back->send(s, "\033[?1;2c", 7);
610                 break;
611               case '\007':
612                 beep(s);
613                 disptop = scrtop;
614                 must_update = TRUE;
615                 break;
616               case '\b':
617                 if (curs_x == 0 && curs_y > 0)
618                     curs_x = cols-1, curs_y--;
619                 else if (wrapnext)
620                     wrapnext = FALSE;
621                 else
622                     curs_x--;
623                 fix_cpos;
624                 disptop = scrtop;
625                 must_update = TRUE;
626                 break;
627               case '\016':
628                 cset = 1;
629                 break;
630               case '\017':
631                 cset = 0;
632                 break;
633               case '\033':
634                 termstate = SEEN_ESC;
635                 break;
636               case 0233:
637                 termstate = SEEN_CSI;
638                 esc_nargs = 1;
639                 esc_args[0] = ARG_DEFAULT;
640                 esc_query = FALSE;
641                 break;
642               case 0235:
643                 termstate = SEEN_OSC;
644                 esc_args[0] = 0;
645                 break;
646               case '\015':
647                 curs_x = 0;
648                 wrapnext = FALSE;
649                 fix_cpos;
650                 disptop = scrtop;
651                 must_update = TRUE;
652                 break;
653               case '\013':
654               case '\014':
655               case '\012':
656                 if (curs_y == marg_b)
657                     scroll(s, marg_t, marg_b, 1, TRUE);
658                 else if (curs_y < rows-1)
659                     curs_y++;
660                 if (cfg.lfhascr)
661                     curs_x = 0;
662                 fix_cpos;
663                 wrapnext = FALSE;
664                 disptop = scrtop;
665                 nl_count++;
666                 break;
667               case '\t':
668                 do {
669                     curs_x++;
670                 } while (curs_x < cols-1 && !tabs[curs_x]);
671                 if (curs_x >= cols)
672                     curs_x = cols-1;
673                 {
674                     unsigned long *old_cpos = cpos;
675                     fix_cpos;
676                     check_selection(s, old_cpos, cpos);
677                 }
678                 disptop = scrtop;
679                 must_update = TRUE;
680                 break;
681               default:
682                 if (c >= ' ' && c != 0234) {
683                     if (wrapnext) {
684                         cpos[1] = ATTR_WRAPPED;
685                         if (curs_y == marg_b)
686                             scroll(s, marg_t, marg_b, 1, TRUE);
687                         else if (curs_y < rows-1)
688                             curs_y++;
689                         curs_x = 0;
690                         fix_cpos;
691                         wrapnext = FALSE;
692                         nl_count++;
693                     }
694                     if (insert)
695                         insch(s, 1);
696                     check_selection(s, cpos, cpos+1);
697                     *cpos++ = c | curr_attr | 
698                         (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
699                     curs_x++;
700                     if (curs_x == cols) {
701                         cpos--;
702                         curs_x--;
703                         wrapnext = wrap;
704                     }
705                     disptop = scrtop;
706                 }
707             }
708             break;
709           case IGNORE_NEXT:
710             termstate = TOPLEVEL;
711             break;
712           case OSC_MAYBE_ST:
713             /*
714              * This state is virtually identical to SEEN_ESC, with the
715              * exception that we have an OSC sequence in the pipeline,
716              * and _if_ we see a backslash, we process it.
717              */
718             if (c == '\\') {
719                 do_osc(s);
720                 termstate = TOPLEVEL;
721                 break;
722             }
723             /* else fall through */
724           case SEEN_ESC:
725             termstate = TOPLEVEL;
726             switch (c) {
727               case '\005': case '\007': case '\b': case '\016': case '\017':
728               case '\033': case 0233: case 0234: case 0235: case '\015':
729               case '\013': case '\014': case '\012': case '\t':
730                 termstate = TOPLEVEL;
731                 goto do_toplevel;      /* hack... */
732               case ' ':                /* some weird sequence? */
733                 termstate = IGNORE_NEXT;
734                 break;
735               case '[':                /* enter CSI mode */
736                 termstate = SEEN_CSI;
737                 esc_nargs = 1;
738                 esc_args[0] = ARG_DEFAULT;
739                 esc_query = FALSE;
740                 break;
741               case ']':                /* xterm escape sequences */
742                 termstate = SEEN_OSC;
743                 esc_args[0] = 0;
744                 break;
745               case '(':                /* should set GL */
746                 termstate = SET_GL;
747                 break;
748               case ')':                /* should set GR */
749                 termstate = SET_GR;
750                 break;
751               case '7':                /* save cursor */
752                 save_cursor(s, TRUE);
753                 break;
754               case '8':                /* restore cursor */
755                 save_cursor(s, FALSE);
756                 disptop = scrtop;
757                 must_update = TRUE;
758                 break;
759               case '=':
760                 app_keypad_keys = TRUE;
761                 break;
762               case '>':
763                 app_keypad_keys = FALSE;
764                 break;
765               case 'D':                /* exactly equivalent to LF */
766                 if (curs_y == marg_b)
767                     scroll(s, marg_t, marg_b, 1, TRUE);
768                 else if (curs_y < rows-1)
769                     curs_y++;
770                 fix_cpos;
771                 wrapnext = FALSE;
772                 disptop = scrtop;
773                 nl_count++;
774                 break;
775               case 'E':                /* exactly equivalent to CR-LF */
776                 curs_x = 0;
777                 wrapnext = FALSE;
778                 if (curs_y == marg_b)
779                     scroll(s, marg_t, marg_b, 1, TRUE);
780                 else if (curs_y < rows-1)
781                     curs_y++;
782                 fix_cpos;
783                 wrapnext = FALSE;
784                 nl_count++;
785                 disptop = scrtop;
786                 break;
787               case 'M':                /* reverse index - backwards LF */
788                 if (curs_y == marg_t)
789                     scroll(s, marg_t, marg_b, -1, TRUE);
790                 else if (curs_y > 0)
791                     curs_y--;
792                 fix_cpos;
793                 wrapnext = FALSE;
794                 disptop = scrtop;
795                 must_update = TRUE;
796                 break;
797               case 'Z':                /* terminal type query */
798                 back->send(s, "\033[?6c", 5);
799                 break;
800               case 'c':                /* restore power-on settings */
801                 power_on(s);
802                 fix_cpos;
803                 disptop = scrtop;
804                 must_update = TRUE;
805                 break;
806               case '#':                /* ESC # 8 fills screen with Es :-) */
807                 termstate = SEEN_ESCHASH;
808                 break;
809               case 'H':                /* set a tab */
810                 tabs[curs_x] = TRUE;
811                 break;
812             }
813             break;
814           case SEEN_CSI:
815             termstate = TOPLEVEL;      /* default */
816             switch (c) {
817               case '\005': case '\007': case '\b': case '\016': case '\017':
818               case '\033': case 0233: case 0234: case 0235: case '\015':
819               case '\013': case '\014': case '\012': case '\t':
820                 termstate = TOPLEVEL;
821                 goto do_toplevel;      /* hack... */
822               case '0': case '1': case '2': case '3': case '4':
823               case '5': case '6': case '7': case '8': case '9':
824                 if (esc_nargs <= ARGS_MAX) {
825                     if (esc_args[esc_nargs-1] == ARG_DEFAULT)
826                         esc_args[esc_nargs-1] = 0;
827                     esc_args[esc_nargs-1] =
828                         10 * esc_args[esc_nargs-1] + c - '0';
829                 }
830                 termstate = SEEN_CSI;
831                 break;
832               case ';':
833                 if (++esc_nargs <= ARGS_MAX)
834                     esc_args[esc_nargs-1] = ARG_DEFAULT;
835                 termstate = SEEN_CSI;
836                 break;
837               case '?':
838                 esc_query = TRUE;
839                 termstate = SEEN_CSI;
840                 break;
841               case 'A':                /* move up N lines */
842                 move(s, curs_x, curs_y - def(esc_args[0], 1), 1);
843                 disptop = scrtop;
844                 must_update = TRUE;
845                 break;
846               case 'B': case 'e':      /* move down N lines */
847                 move(s, curs_x, curs_y + def(esc_args[0], 1), 1);
848                 disptop = scrtop;
849                 must_update = TRUE;
850                 break;
851               case 'C': case 'a':      /* move right N cols */
852                 move(s, curs_x + def(esc_args[0], 1), curs_y, 1);
853                 disptop = scrtop;
854                 must_update = TRUE;
855                 break;
856               case 'D':                /* move left N cols */
857                 move(s, curs_x - def(esc_args[0], 1), curs_y, 1);
858                 disptop = scrtop;
859                 must_update = TRUE;
860                 break;
861               case 'E':                /* move down N lines and CR */
862                 move(s, 0, curs_y + def(esc_args[0], 1), 1);
863                 disptop = scrtop;
864                 must_update = TRUE;
865                 break;
866               case 'F':                /* move up N lines and CR */
867                 move(s, 0, curs_y - def(esc_args[0], 1), 1);
868                 disptop = scrtop;
869                 must_update = TRUE;
870                 break;
871               case 'G': case '`':      /* set horizontal posn */
872                 move(s, def(esc_args[0], 1) - 1, curs_y, 0);
873                 disptop = scrtop;
874                 must_update = TRUE;
875                 break;
876               case 'd':                /* set vertical posn */
877                 move(s, curs_x, (curr_dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
878                       (curr_dec_om ? 2 : 0));
879                 disptop = scrtop;
880                 must_update = TRUE;
881                 break;
882               case 'H': case 'f':      /* set horz and vert posns at once */
883                 if (esc_nargs < 2)
884                     esc_args[1] = ARG_DEFAULT;
885                 move(s, def(esc_args[1], 1) - 1,
886                      (curr_dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
887                      (curr_dec_om ? 2 : 0));
888                 disptop = scrtop;
889                 must_update = TRUE;
890                 break;
891               case 'J':                /* erase screen or parts of it */
892                 {
893                     unsigned int i = def(esc_args[0], 0) + 1;
894                     if (i > 3)
895                         i = 0;
896                     erase_lots(s, FALSE, !!(i & 2), !!(i & 1));
897                 }
898                 disptop = scrtop;
899                 must_update = TRUE;
900                 break;
901               case 'K':                /* erase line or parts of it */
902                 {
903                     unsigned int i = def(esc_args[0], 0) + 1;
904                     if (i > 3)
905                         i = 0;
906                     erase_lots(s, TRUE, !!(i & 2), !!(i & 1));
907                 }
908                 disptop = scrtop;
909                 must_update = TRUE;
910                 break;
911               case 'L':                /* insert lines */
912                 if (curs_y <= marg_b)
913                     scroll(s, curs_y, marg_b, -def(esc_args[0], 1), FALSE);
914                 disptop = scrtop;
915                 must_update = TRUE;
916                 break;
917               case 'M':                /* delete lines */
918                 if (curs_y <= marg_b)
919                     scroll(s, curs_y, marg_b, def(esc_args[0], 1), FALSE);
920                 disptop = scrtop;
921                 must_update = TRUE;
922                 break;
923               case '@':                /* insert chars */
924                 insch(s, def(esc_args[0], 1));
925                 disptop = scrtop;
926                 must_update = TRUE;
927                 break;
928               case 'P':                /* delete chars */
929                 insch(s, -def(esc_args[0], 1));
930                 disptop = scrtop;
931                 must_update = TRUE;
932                 break;
933               case 'c':                /* terminal type query */
934                 back->send(s, "\033[?6c", 5);
935                 break;
936               case 'n':                /* cursor position query */
937                 if (esc_args[0] == 6) {
938                     char buf[32];
939                     sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1);
940                     back->send(s, buf, strlen(buf));
941                 }
942                 break;
943               case 'h':                /* toggle a mode to high */
944                 toggle_mode(s, esc_args[0], esc_query, TRUE);
945                 break;
946               case 'l':                /* toggle a mode to low */
947                 toggle_mode(s, esc_args[0], esc_query, FALSE);
948                 break;
949               case 'g':                /* clear tabs */
950                 if (esc_nargs == 1) {
951                     if (esc_args[0] == 0) {
952                         tabs[curs_x] = FALSE;
953                     } else if (esc_args[0] == 3) {
954                         int i;
955                         for (i = 0; i < cols; i++)
956                             tabs[i] = FALSE;
957                     }
958                 }
959                 break;
960               case 'r':                /* set scroll margins */
961                 if (esc_nargs <= 2) {
962                     int top, bot;
963                     top = def(esc_args[0], 1) - 1;
964                     if (top < 0)
965                         top = 0;
966                     bot = (esc_nargs <= 1 || esc_args[1] == 0 ? rows :
967                            def(esc_args[1], rows)) - 1;
968                     if (bot >= rows)
969                         bot = rows-1;
970                     if (top <= bot) {
971                         marg_t = top;
972                         marg_b = bot;
973                         curs_x = 0;
974                         /*
975                          * I used to think the cursor should be
976                          * placed at the top of the newly marginned
977                          * area. Apparently not: VMS TPU falls over
978                          * if so.
979                          */
980                         curs_y = 0;
981                         fix_cpos;
982                         disptop = scrtop;
983                         must_update = TRUE;
984                     }
985                 }
986                 break;
987               case 'm':                /* set graphics rendition */
988                 {
989                     int i;
990                     for (i=0; i<esc_nargs; i++) {
991                         switch (def(esc_args[i], 0)) {
992                           case 0:      /* restore defaults */
993                             curr_attr = ATTR_DEFAULT; break;
994                           case 1:      /* enable bold */
995                             curr_attr |= ATTR_BOLD; break;
996                           case 4:      /* enable underline */
997                           case 21:     /* (enable double underline) */
998                             curr_attr |= ATTR_UNDER; break;
999                           case 7:      /* enable reverse video */
1000                             curr_attr |= ATTR_REVERSE; break;
1001                           case 22:     /* disable bold */
1002                             curr_attr &= ~ATTR_BOLD; break;
1003                           case 24:     /* disable underline */
1004                             curr_attr &= ~ATTR_UNDER; break;
1005                           case 27:     /* disable reverse video */
1006                             curr_attr &= ~ATTR_REVERSE; break;
1007                           case 30: case 31: case 32: case 33:
1008                           case 34: case 35: case 36: case 37:
1009                             /* foreground */
1010                             curr_attr &= ~ATTR_FGMASK;
1011                             curr_attr |= (esc_args[i] - 30) << ATTR_FGSHIFT;
1012                             break;
1013                           case 39:     /* default-foreground */
1014                             curr_attr &= ~ATTR_FGMASK;
1015                             curr_attr |= ATTR_DEFFG;
1016                             break;
1017                           case 40: case 41: case 42: case 43:
1018                           case 44: case 45: case 46: case 47:
1019                             /* background */
1020                             curr_attr &= ~ATTR_BGMASK;
1021                             curr_attr |= (esc_args[i] - 40) << ATTR_BGSHIFT;
1022                             break;
1023                           case 49:     /* default-background */
1024                             curr_attr &= ~ATTR_BGMASK;
1025                             curr_attr |= ATTR_DEFBG;
1026                             break;
1027                         }
1028                     }
1029                 }
1030                 break;
1031               case 's':                /* save cursor */
1032                 save_cursor(s, TRUE);
1033                 break;
1034               case 'u':                /* restore cursor */
1035                 save_cursor(s, FALSE);
1036                 disptop = scrtop;
1037                 must_update = TRUE;
1038                 break;
1039               case 't':                /* set page size - ie window height */
1040                 request_resize(s, cols, def(esc_args[0], 24));
1041                 deselect(s);
1042                 break;
1043               case 'X':                /* write N spaces w/o moving cursor */
1044                 {
1045                     int n = def(esc_args[0], 1);
1046                     unsigned long *p = cpos;
1047                     if (n > cols - curs_x)
1048                         n = cols - curs_x;
1049                     check_selection(s, cpos, cpos+n);
1050                     while (n--)
1051                         *p++ = ERASE_CHAR;
1052                     disptop = scrtop;
1053                     must_update = TRUE;
1054                 }
1055                 break;
1056               case 'x':                /* report terminal characteristics */
1057                 {
1058                     char buf[32];
1059                     int i = def(esc_args[0], 0);
1060                     if (i == 0 || i == 1) {
1061                         strcpy (buf, "\033[2;1;1;112;112;1;0x");
1062                         buf[2] += i;
1063                         back->send(s, buf, 20);
1064                     }
1065                 }
1066                 break;
1067             }
1068             break;
1069           case SET_GL:
1070           case SET_GR:
1071             switch (c) {
1072               case 'A':
1073                 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
1074                 break;
1075               case '0':
1076                 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
1077                 break;
1078               default:                 /* specifically, 'B' */
1079                 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
1080                 break;
1081             }
1082             termstate = TOPLEVEL;
1083             break;
1084           case SEEN_OSC:
1085             osc_w = FALSE;
1086             switch (c) {
1087               case '\005': case '\007': case '\b': case '\016': case '\017':
1088               case '\033': case 0233: case 0234: case 0235: case '\015':
1089               case '\013': case '\014': case '\012': case '\t':
1090                 termstate = TOPLEVEL;
1091                 goto do_toplevel;      /* hack... */
1092               case 'P':                /* Linux palette sequence */
1093                 termstate = SEEN_OSC_P;
1094                 osc_strlen = 0;
1095                 break;
1096               case 'R':                /* Linux palette reset */
1097                 palette_reset(s);
1098                 term_invalidate(s);
1099                 termstate = TOPLEVEL;
1100                 break;
1101               case 'W':                /* word-set */
1102                 termstate = SEEN_OSC_W;
1103                 osc_w = TRUE;
1104                 break;
1105               case '0': case '1': case '2': case '3': case '4':
1106               case '5': case '6': case '7': case '8': case '9':
1107                 esc_args[0] = 10 * esc_args[0] + c - '0';
1108                 break;
1109               case 'L':
1110                 /*
1111                  * Grotty hack to support xterm and DECterm title
1112                  * sequences concurrently.
1113                  */
1114                 if (esc_args[0] == 2) {
1115                     esc_args[0] = 1;
1116                     break;
1117                 }
1118                 /* else fall through */
1119               default:
1120                 termstate = OSC_STRING;
1121                 osc_strlen = 0;
1122             }
1123             break;
1124           case OSC_STRING:
1125             if (c == 0234 || c == '\007') {
1126                 /*
1127                  * These characters terminate the string; ST and BEL
1128                  * terminate the sequence and trigger instant
1129                  * processing of it, whereas ESC goes back to SEEN_ESC
1130                  * mode unless it is followed by \, in which case it is
1131                  * synonymous with ST in the first place.
1132                  */
1133                 do_osc(s);
1134                 termstate = TOPLEVEL;
1135             } else if (c == '\033')
1136                     termstate = OSC_MAYBE_ST;
1137             else if (osc_strlen < OSC_STR_MAX)
1138                 osc_string[osc_strlen++] = c;
1139             break;
1140           case SEEN_OSC_P:
1141             {
1142                 int max = (osc_strlen == 0 ? 21 : 16);
1143                 int val;
1144                 if (c >= '0' && c <= '9')
1145                     val = c - '0';
1146                 else if (c >= 'A' && c <= 'A'+max-10)
1147                     val = c - 'A' + 10;
1148                 else if (c >= 'a' && c <= 'a'+max-10)
1149                     val = c - 'a' + 10;
1150                 else
1151                     termstate = TOPLEVEL;
1152                 osc_string[osc_strlen++] = val;
1153                 if (osc_strlen >= 7) {
1154                     palette_set (s, osc_string[0],
1155                                  osc_string[1] * 16 + osc_string[2],
1156                                  osc_string[3] * 16 + osc_string[4],
1157                                  osc_string[5] * 16 + osc_string[6]);
1158                     termstate = TOPLEVEL;
1159                 }
1160             }
1161             break;
1162           case SEEN_OSC_W:
1163             switch (c) {
1164               case '\005': case '\007': case '\b': case '\016': case '\017':
1165               case '\033': case 0233: case 0234: case 0235: case '\015':
1166               case '\013': case '\014': case '\012': case '\t':
1167                 termstate = TOPLEVEL;
1168                 goto do_toplevel;      /* hack... */
1169               case '0': case '1': case '2': case '3': case '4':
1170               case '5': case '6': case '7': case '8': case '9':
1171                 esc_args[0] = 10 * esc_args[0] + c - '0';
1172                 break;
1173               default:
1174                 termstate = OSC_STRING;
1175                 osc_strlen = 0;
1176             }
1177             break;
1178           case SEEN_ESCHASH:
1179             if (c == '8') {
1180                 unsigned long *p = scrtop;
1181                 int n = rows * (cols+1);
1182                 while (n--)
1183                     *p++ = ATTR_DEFAULT | 'E';
1184                 disptop = scrtop;
1185                 must_update = TRUE;
1186                 check_selection(s, scrtop, scrtop + rows * (cols+1));
1187             }
1188             termstate = TOPLEVEL;
1189             break;
1190         }
1191         check_selection(s, cpos, cpos+1);
1192     }
1193         
1194     if (must_update || nl_count > MAXNL) {
1195         update_sbar(s);
1196         term_update(s);
1197     }
1198 }
1199
1200 /*
1201  * Given a context, update the window. Out of paranoia, we don't
1202  * allow WM_PAINT responses to do scrolling optimisations.
1203  */
1204 static void do_paint (Session *s, int may_optimise){ 
1205     int i, j, start, our_curs_y;
1206     unsigned long attr, rv, cursor;
1207     char ch[1024];
1208
1209     cursor = (has_focus ? ATTR_ACTCURS : ATTR_PASCURS);
1210     rv = (rvideo ? ATTR_REVERSE : 0);
1211     our_curs_y = curs_y + (scrtop - disptop) / (cols+1);
1212
1213     for (i=0; i<rows; i++) {
1214         int idx = i*(cols+1);
1215         for (j=0; j<=cols; j++,idx++) {
1216             unsigned long *d = disptop+idx;
1217             wanttext[idx] = ((*d ^ rv
1218                               ^ (selstart <= d && d < selend ?
1219                                  ATTR_REVERSE : 0)) |
1220                              (i==our_curs_y && j==curs_x ? cursor : 0));
1221         }
1222     }
1223
1224     /*
1225      * We would perform scrolling optimisations in here, if they
1226      * didn't have a nasty tendency to cause the whole sodding
1227      * program to hang for a second at speed-critical moments.
1228      * We'll leave it well alone...
1229      */
1230
1231     for (i=0; i<rows; i++) {
1232         int idx = i*(cols+1);
1233         start = -1;
1234         for (j=0; j<=cols; j++,idx++) {
1235             unsigned long t = wanttext[idx];
1236             int needs_update = (j < cols && t != disptext[idx]);
1237             int keep_going = (start != -1 && needs_update &&
1238                               (t & attr_mask) == attr &&
1239                               j-start < sizeof(ch));
1240             if (start != -1 && !keep_going) {
1241                 do_text(s, start, i, ch, j-start, attr);
1242                 start = -1;
1243             }
1244             if (needs_update) {
1245                 if (start == -1) {
1246                     start = j;
1247                     attr = t & attr_mask;
1248                 }
1249                 ch[j-start] = (char) (t & CHAR_MASK);
1250             }
1251             disptext[idx] = t;
1252         }
1253     }
1254 }
1255
1256 /*
1257  * Invalidate the whole screen so it will be repainted in full.
1258  */
1259 void term_invalidate(Session *s) {
1260     int i;
1261
1262     for (i=0; i<rows*(cols+1); i++)
1263         disptext[i] = ATTR_INVALID;
1264 }
1265
1266 /*
1267  * Paint the window in response to a WM_PAINT message.
1268  */
1269 void term_paint(Session *s, int l, int t, int r, int b) {
1270     int i, j, left, top, right, bottom;
1271
1272     left = l / font_width;
1273     right = (r - 1) / font_width;
1274     top = t / font_height;
1275     bottom = (b - 1) / font_height;
1276     for (i = top; i <= bottom && i < rows ; i++)
1277       for (j = left; j <= right && j < cols ; j++)
1278             disptext[i*(cols+1)+j] = ATTR_INVALID;
1279
1280     do_paint (s, FALSE);
1281 }
1282
1283 /*
1284  * Attempt to scroll the scrollback. The second parameter gives the
1285  * position we want to scroll to; the first is +1 to denote that
1286  * this position is relative to the beginning of the scrollback, -1
1287  * to denote it is relative to the end, and 0 to denote that it is
1288  * relative to the current position.
1289  */
1290 void term_scroll(Session *s, int rel, int where) {
1291     int n = where * (cols+1);
1292 #ifdef OPTIMISE_SCROLL
1293     unsigned long *olddisptop = disptop;
1294     int shift;
1295 #endif /* OPTIMISE_SCROLL */
1296
1297     disptop = (rel < 0 ? scrtop :
1298                rel > 0 ? sbtop : disptop) + n;
1299     if (disptop < sbtop)
1300         disptop = sbtop;
1301     if (disptop > scrtop)
1302         disptop = scrtop;
1303     update_sbar(s);
1304 #ifdef OPTIMISE_SCROLL
1305     shift = (disptop - olddisptop) / (cols + 1);
1306     if (shift < rows && shift > -rows)
1307         scroll_display(s, 0, rows - 1, shift);
1308 #endif /* OPTIMISE_SCROLL */
1309     term_update(s);
1310 }
1311
1312 /*
1313  * Spread the selection outwards according to the selection mode.
1314  */
1315 static unsigned long *sel_spread_half(Session *s, unsigned long *p, int dir) {
1316     unsigned long *linestart, *lineend;
1317     int x;
1318     short wvalue;
1319
1320     x = (p - text) % (cols+1);
1321     linestart = p - x;
1322     lineend = linestart + cols;
1323
1324     switch (selmode) {
1325       case SM_CHAR:
1326         /*
1327          * In this mode, every character is a separate unit, except
1328          * for runs of spaces at the end of a non-wrapping line.
1329          */
1330         if (!(linestart[cols] & ATTR_WRAPPED)) {
1331             unsigned long *q = lineend;
1332             while (q > linestart && (q[-1] & CHAR_MASK) == 0x20)
1333                 q--;
1334             if (q == lineend)
1335                 q--;
1336             if (p >= q)
1337                 p = (dir == -1 ? q : lineend - 1);
1338         }
1339         break;
1340       case SM_WORD:
1341         /*
1342          * In this mode, the units are maximal runs of characters
1343          * whose `wordness' has the same value.
1344          */
1345         wvalue = curr_wordness[*p & CHAR_MASK];
1346         if (dir == +1) {
1347             while (p < lineend && curr_wordness[p[1] & CHAR_MASK] == wvalue)
1348                 p++;
1349         } else {
1350             while (p > linestart && curr_wordness[p[-1] & CHAR_MASK] == wvalue)
1351                 p--;
1352         }
1353         break;
1354       case SM_LINE:
1355         /*
1356          * In this mode, every line is a unit.
1357          */
1358         p = (dir == -1 ? linestart : lineend - 1);
1359         break;
1360     }
1361     return p;
1362 }
1363
1364 static void sel_spread(Session *s) {
1365     selstart = sel_spread_half(s, selstart, -1);
1366     selend = sel_spread_half(s, selend - 1, +1) + 1;
1367 }
1368
1369 void term_mouse(Session *s, Mouse_Button b, Mouse_Action a, int x, int y) {
1370     unsigned long *selpoint;
1371     
1372     if (x < 0) {
1373         x = cols - 1;
1374         y--;
1375     } else if (x >= cols)
1376         x = cols - 1;
1377
1378     selpoint = disptop + y * (cols + 1) + x;
1379     if (selpoint < sbtop)
1380         selpoint = sbtop;
1381     else if (selpoint > scrtop + rows * (cols + 1) - 1)
1382         /* XXX put this in a variable? */
1383         selpoint = scrtop + rows * (cols + 1) - 1;
1384
1385     if (b == MB_SELECT && a == MA_CLICK) {
1386         deselect(s);
1387         selstate = ABOUT_TO;
1388         selanchor = selpoint;
1389         selmode = SM_CHAR;
1390     } else if (b == MB_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
1391         deselect(s);
1392         selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
1393         selstate = DRAGGING;
1394         selstart = selanchor = selpoint;
1395         selend = selstart + 1;
1396         sel_spread(s);
1397     } else if ((b == MB_SELECT && a == MA_DRAG) ||
1398                (b == MB_EXTEND && a != MA_RELEASE)) {
1399         if (selstate == ABOUT_TO && selanchor == selpoint)
1400             return;
1401         if (b == MB_EXTEND && a != MA_DRAG && selstate == SELECTED) {
1402             if (selpoint-selstart < (selend-selstart)/2)
1403                 selanchor = selend - 1;
1404             else
1405                 selanchor = selstart;
1406             selstate = DRAGGING;
1407         }
1408         if (selstate != ABOUT_TO && selstate != DRAGGING)
1409             selanchor = selpoint;
1410         selstate = DRAGGING;
1411         if (selpoint < selanchor) {
1412             selstart = selpoint;
1413             selend = selanchor + 1;
1414         } else {
1415             selstart = selanchor;
1416             selend = selpoint + 1;
1417         }
1418         sel_spread(s);
1419     } else if ((b == MB_SELECT || b == MB_EXTEND) && a == MA_RELEASE)
1420         if (selstate == DRAGGING) {
1421             if (cfg.implicit_copy)
1422                 term_copy(s);
1423             selstate = SELECTED;
1424         } else
1425             selstate = NO_SELECTION;
1426     else if (b == MB_PASTE && (a==MA_CLICK || a==MA_2CLK || a==MA_3CLK))
1427         term_paste(s);
1428     term_update(s);
1429 }
1430
1431 /*
1432  * We've completed a selection. We now transfer the
1433  * data to the clipboard.
1434  */
1435 void term_copy(Session *s) {
1436     unsigned char *p = selspace;
1437     unsigned long *q = selstart;
1438
1439     while (q < selend) {
1440         int nl = FALSE;
1441         unsigned long *lineend = q - (q-text) % (cols+1) + cols;
1442         unsigned long *nlpos = lineend;
1443
1444         if (!(*nlpos & ATTR_WRAPPED)) {
1445             while ((nlpos[-1] & CHAR_MASK) == 0x20 && nlpos > q)
1446                 nlpos--;
1447             if (nlpos < selend)
1448                 nl = TRUE;
1449         }
1450         while (q < nlpos && q < selend)
1451             *p++ = (unsigned char) (*q++ & CHAR_MASK);
1452         if (nl) {
1453             int i;
1454             for (i=0; i<sizeof(sel_nl); i++)
1455                 *p++ = sel_nl[i];
1456         }
1457         q = lineend + 1;       /* start of next line */
1458     }
1459     write_clip(selspace, p - selspace);
1460 }
1461
1462 void term_paste(Session *s) {
1463     char *data;
1464     int len;
1465
1466     get_clip((void **) &data, &len);
1467     if (data) {
1468         char *p, *q;
1469         p = q = data;
1470         while (p < data+len) {
1471             while (p < data+len &&
1472                    !(p <= data+len-sizeof(sel_nl) &&
1473                      !memcmp(p, sel_nl, sizeof(sel_nl))))
1474                 p++;
1475             back->send(s, q, p-q);
1476             if (p <= data+len-sizeof(sel_nl) &&
1477                 !memcmp(p, sel_nl, sizeof(sel_nl))) {
1478                 back->send(s, "\015", 1);
1479                 p += sizeof(sel_nl);
1480             }
1481             q = p;
1482         }
1483     }
1484     get_clip(NULL, NULL);
1485 }
1486
1487 /*
1488  * Find out if there's a selection.
1489  */
1490 int term_hasselection(Session *s) {
1491
1492     return selstate == SELECTED;
1493 }
1494
1495 static void deselect(Session *s) {
1496     selstate = NO_SELECTION;
1497     selstart = selend = scrtop;
1498 }
1499
1500 void term_deselect(Session *s) {
1501     deselect(s);
1502     term_update(s);
1503 }