Miscellaneous fixes to try to make other compilers happier
[u/mdw/putty] / terminal.c
CommitLineData
374330e2 1#include <windows.h>
5b80d07f 2#ifndef AUTO_WINSOCK
3#ifdef WINSOCK_TWO
4#include <winsock2.h>
5#else
6#include <winsock.h>
7#endif
8#endif
374330e2 9
10#include <stdio.h>
11#include <stdlib.h>
12
13#include "putty.h"
14
c9def1b8 15#define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
e14a5a13 16#define CL_VT100 0x0002 /* VT100 */
17#define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
18#define CL_VT102 0x0008 /* VT102 */
19#define CL_VT220 0x0010 /* VT220 */
20#define CL_VT320 0x0020 /* VT320 */
21#define CL_VT420 0x0040 /* VT420 */
22#define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
23#define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
c9def1b8 24#define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
25#define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
26#define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
e14a5a13 27
28#define TM_ANSIMIN (CL_ANSIMIN)
c9def1b8 29#define TM_VT100 (CL_ANSIMIN|CL_VT100)
30#define TM_VT100AVO (TM_VT100|CL_VT100AVO)
31#define TM_VT102 (TM_VT100AVO|CL_VT102)
32#define TM_VT220 (TM_VT102|CL_VT220)
33#define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
34#define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
35
36#define TM_PUTTY (0xFFFF)
e14a5a13 37
38#define compatibility(x) \
39 if ( ((CL_##x)&compatibility_level) == 0 ) { \
40 termstate=TOPLEVEL; \
41 break; \
42 }
c9def1b8 43#define compatibility2(x,y) \
44 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
45 termstate=TOPLEVEL; \
46 break; \
47 }
e14a5a13 48
49#define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
50
51static int compatibility_level = TM_PUTTY;
52
53
374330e2 54static unsigned long *text; /* buffer of text on terminal screen */
55static unsigned long *scrtop; /* top of working screen */
56static unsigned long *disptop; /* top of displayed screen */
57static unsigned long *sbtop; /* top of scrollback */
c9def1b8 58static unsigned long *sbbot; /* furthest extent of scrollback */
374330e2 59static unsigned long *cpos; /* cursor position (convenience) */
60static unsigned long *disptext; /* buffer of text on real screen */
61static unsigned long *wanttext; /* buffer of text we want on screen */
62static unsigned long *alttext; /* buffer of text on alt. screen */
63
64static unsigned char *selspace; /* buffer for building selections in */
65
66#define TSIZE (sizeof(*text))
67#define fix_cpos do { cpos = scrtop + curs_y * (cols+1) + curs_x; } while(0)
68
69static unsigned long curr_attr, save_attr;
e14a5a13 70static unsigned long erase_char = ERASE_CHAR;
374330e2 71
72static int curs_x, curs_y; /* cursor */
73static int save_x, save_y; /* saved cursor position */
74static int marg_t, marg_b; /* scroll margins */
75static int dec_om; /* DEC origin mode flag */
76static int wrap, wrapnext; /* wrap flags */
77static int insert; /* insert-mode flag */
78static int cset; /* 0 or 1: which char set */
79static int save_cset, save_csattr; /* saved with cursor position */
80static int rvideo; /* global reverse video flag */
e14a5a13 81static int cursor_on; /* cursor enabled flag */
82static int reset_132; /* Flag ESC c resets to 80 cols */
83static int use_bce; /* Use Background coloured erase */
84static int blinker; /* When blinking is the cursor on ? */
c9def1b8 85static int tblinker; /* When the blinking text is on */
86static int blink_is_real; /* Actually blink blinking text */
374330e2 87
88static unsigned long cset_attr[2];
89
90/*
91 * Saved settings on the alternate screen.
92 */
93static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
94static int alt_t, alt_b;
95static int alt_which;
96
97#define ARGS_MAX 32 /* max # of esc sequence arguments */
064916ea 98#define ARG_DEFAULT 0 /* if an arg isn't specified */
374330e2 99#define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
100static int esc_args[ARGS_MAX];
101static int esc_nargs;
102static int esc_query;
8b811b12 103#define ANSI(x,y) ((x)+((y)<<8))
104#define ANSI_QUE(x) ANSI(x,TRUE)
374330e2 105
106#define OSC_STR_MAX 2048
107static int osc_strlen;
108static char osc_string[OSC_STR_MAX+1];
109static int osc_w;
110
c9def1b8 111static char id_string[1024] = "\033[?6c";
112
374330e2 113static unsigned char *tabs;
114
374330e2 115static enum {
cabfd08c 116 TOPLEVEL,
117 SEEN_ESC,
118 SEEN_CSI,
119 SEEN_OSC,
120 SEEN_OSC_W,
121
122 DO_CTRLS,
123
124 IGNORE_NEXT,
125 SET_GL, SET_GR,
126 SEEN_OSC_P,
127 OSC_STRING, OSC_MAYBE_ST,
e14a5a13 128 SEEN_ESCHASH,
129 VT52_ESC,
130 VT52_Y1,
131 VT52_Y2
374330e2 132} termstate;
133
134static enum {
135 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
136} selstate;
137static enum {
138 SM_CHAR, SM_WORD, SM_LINE
139} selmode;
140static unsigned long *selstart, *selend, *selanchor;
141
142static short wordness[256] = {
143 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 */
144 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 */
145 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 */
146 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 */
147 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 */
148 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 */
149 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 */
150 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 */
151};
152
153static unsigned char sel_nl[] = SEL_NL;
c9def1b8 154static char * paste_buffer = 0;
155static int paste_len, paste_pos, paste_hold;
374330e2 156
157/*
158 * Internal prototypes.
159 */
160static void do_paint (Context, int);
161static void erase_lots (int, int, int);
162static void swap_screen (int);
163static void update_sbar (void);
164static void deselect (void);
165
166/*
167 * Set up power-on settings for the terminal.
168 */
169static void power_on(void) {
170 curs_x = curs_y = alt_x = alt_y = save_x = save_y = 0;
171 alt_t = marg_t = 0;
172 if (rows != -1)
173 alt_b = marg_b = rows - 1;
174 else
175 alt_b = marg_b = 0;
176 if (cols != -1) {
177 int i;
178 for (i = 0; i < cols; i++)
179 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
180 }
181 alt_om = dec_om = cfg.dec_om;
182 alt_wnext = wrapnext = alt_ins = insert = FALSE;
183 alt_wrap = wrap = cfg.wrap_mode;
184 alt_cset = cset = 0;
185 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
186 rvideo = 0;
e14a5a13 187 cursor_on = 1;
374330e2 188 save_attr = curr_attr = ATTR_DEFAULT;
189 app_cursor_keys = cfg.app_cursor;
190 app_keypad_keys = cfg.app_keypad;
c9def1b8 191 use_bce = cfg.bce;
192 blink_is_real = cfg.blinktext;
e14a5a13 193 erase_char = ERASE_CHAR;
374330e2 194 alt_which = 0;
195 {
196 int i;
197 for (i = 0; i < 256; i++)
198 wordness[i] = cfg.wordness[i];
199 }
200 if (text) {
201 swap_screen (1);
202 erase_lots (FALSE, TRUE, TRUE);
203 swap_screen (0);
204 erase_lots (FALSE, TRUE, TRUE);
205 }
206}
207
208/*
209 * Force a screen update.
210 */
211void term_update(void) {
212 Context ctx;
213 ctx = get_ctx();
214 if (ctx) {
c9def1b8 215 if ( (seen_key_event && (cfg.scroll_on_key)) ||
216 (seen_disp_event && (!cfg.scroll_on_key)) ) {
cabfd08c 217 disptop = scrtop;
218 seen_disp_event = seen_key_event = 0;
219 }
374330e2 220 do_paint (ctx, TRUE);
ec8679e9 221 sys_cursor(curs_x, curs_y + (scrtop - disptop) / (cols+1));
374330e2 222 free_ctx (ctx);
374330e2 223 }
224}
225
226/*
227 * Same as power_on(), but an external function.
228 */
229void term_pwron(void) {
230 power_on();
231 fix_cpos;
232 disptop = scrtop;
233 deselect();
234 term_update();
235}
236
237/*
238 * Clear the scrollback.
239 */
240void term_clrsb(void) {
241 disptop = sbtop = scrtop;
242 update_sbar();
243}
244
245/*
246 * Initialise the terminal.
247 */
248void term_init(void) {
c9def1b8 249 text = sbtop = sbbot = scrtop = disptop = cpos = NULL;
374330e2 250 disptext = wanttext = NULL;
251 tabs = NULL;
252 selspace = NULL;
253 deselect();
254 rows = cols = -1;
374330e2 255 power_on();
256}
257
258/*
259 * Set up the terminal for a given size.
260 */
261void term_size(int newrows, int newcols, int newsavelines) {
262 unsigned long *newtext, *newdisp, *newwant, *newalt;
263 int i, j, crows, ccols;
264
d6832394 265 int save_alt_which = alt_which;
266
374330e2 267 if (newrows == rows && newcols == cols && newsavelines == savelines)
268 return; /* nothing to do */
269
d6832394 270 deselect();
271 swap_screen(0);
272
374330e2 273 alt_t = marg_t = 0;
274 alt_b = marg_b = newrows - 1;
275
276 newtext = smalloc ((newrows+newsavelines)*(newcols+1)*TSIZE);
c9def1b8 277 sbbot = newtext + newsavelines*(newcols+1);
374330e2 278 for (i=0; i<(newrows+newsavelines)*(newcols+1); i++)
c9def1b8 279 newtext[i] = erase_char;
374330e2 280 if (rows != -1) {
281 crows = rows + (scrtop - sbtop) / (cols+1);
282 if (crows > newrows+newsavelines)
283 crows = newrows+newsavelines;
c9def1b8 284 if (newrows>crows)
285 disptop = newtext;
286 else
287 disptop = newtext + (crows-newrows)*(newcols+1);
374330e2 288 ccols = (cols < newcols ? cols : newcols);
289 for (i=0; i<crows; i++) {
c9def1b8 290 int oldidx = (rows - crows + i) * (cols+1);
291 int newidx = (newrows - crows + i) * (newcols+1);
292
374330e2 293 for (j=0; j<ccols; j++)
c9def1b8 294 disptop[newidx+j] = scrtop[oldidx+j];
295 disptop[newidx+newcols] =
296 (cols == newcols ? scrtop[oldidx+cols]
297 : (scrtop[oldidx+cols]&LATTR_MODE));
374330e2 298 }
c9def1b8 299 sbtop = newtext;
300 } else {
301 sbtop = disptop = newtext;
302 }
374330e2 303 scrtop = disptop;
304 sfree (text);
305 text = newtext;
306
307 newdisp = smalloc (newrows*(newcols+1)*TSIZE);
308 for (i=0; i<newrows*(newcols+1); i++)
309 newdisp[i] = ATTR_INVALID;
310 sfree (disptext);
311 disptext = newdisp;
312
313 newwant = smalloc (newrows*(newcols+1)*TSIZE);
314 for (i=0; i<newrows*(newcols+1); i++)
315 newwant[i] = ATTR_INVALID;
316 sfree (wanttext);
317 wanttext = newwant;
318
319 newalt = smalloc (newrows*(newcols+1)*TSIZE);
320 for (i=0; i<newrows*(newcols+1); i++)
e14a5a13 321 newalt[i] = erase_char;
374330e2 322 sfree (alttext);
323 alttext = newalt;
324
325 sfree (selspace);
326 selspace = smalloc ( (newrows+newsavelines) * (newcols+sizeof(sel_nl)) );
327
328 tabs = srealloc (tabs, newcols*sizeof(*tabs));
329 {
330 int i;
331 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
332 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
333 }
334
335 if (rows > 0)
336 curs_y += newrows - rows;
337 if (curs_y < 0)
338 curs_y = 0;
339 if (curs_y >= newrows)
340 curs_y = newrows-1;
341 if (curs_x >= newcols)
342 curs_x = newcols-1;
343 alt_x = alt_y = 0;
344 wrapnext = alt_wnext = FALSE;
345
346 rows = newrows;
347 cols = newcols;
348 savelines = newsavelines;
349 fix_cpos;
350
d6832394 351 swap_screen(save_alt_which);
352
374330e2 353 update_sbar();
354 term_update();
355}
356
357/*
358 * Swap screens.
359 */
360static void swap_screen (int which) {
361 int t;
362 unsigned long tt;
363
364 if (which == alt_which)
365 return;
366
367 alt_which = which;
368
369 for (t=0; t<rows*(cols+1); t++) {
370 tt = scrtop[t]; scrtop[t] = alttext[t]; alttext[t] = tt;
371 }
372
373 t = curs_x; curs_x = alt_x; alt_x = t;
374 t = curs_y; curs_y = alt_y; alt_y = t;
375 t = marg_t; marg_t = alt_t; alt_t = t;
376 t = marg_b; marg_b = alt_b; alt_b = t;
377 t = dec_om; dec_om = alt_om; alt_om = t;
378 t = wrap; wrap = alt_wrap; alt_wrap = t;
379 t = wrapnext; wrapnext = alt_wnext; alt_wnext = t;
380 t = insert; insert = alt_ins; alt_ins = t;
381 t = cset; cset = alt_cset; alt_cset = t;
382
383 fix_cpos;
384}
385
386/*
374330e2 387 * Update the scroll bar.
388 */
389static void update_sbar(void) {
390 int min;
391
392 min = (sbtop - text) / (cols+1);
393 set_sbar ((scrtop - text) / (cols+1) + rows - min,
394 (disptop - text) / (cols+1) - min,
395 rows);
396}
397
398/*
399 * Check whether the region bounded by the two pointers intersects
400 * the scroll region, and de-select the on-screen selection if so.
401 */
402static void check_selection (unsigned long *from, unsigned long *to) {
403 if (from < selend && selstart < to)
404 deselect();
405}
406
407/*
408 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
409 * for backward.) `sb' is TRUE if the scrolling is permitted to
410 * affect the scrollback buffer.
411 */
412static void scroll (int topline, int botline, int lines, int sb) {
413 unsigned long *scroll_top;
c9def1b8 414 unsigned long *newscr;
374330e2 415 int scroll_size, size, i;
c9def1b8 416 int scrtop_is_disptop = (scrtop==disptop);
417static int recursive = 0;
418
419 /* Only scroll more than the window if we're doing a 10% scroll */
420 if (!recursive && lines > botline - topline + 1)
421 lines = botline - topline + 1;
374330e2 422
423 scroll_top = scrtop + topline*(cols+1);
424 size = (lines < 0 ? -lines : lines) * (cols+1);
425 scroll_size = (botline - topline + 1) * (cols+1) - size;
426
cabfd08c 427 if (lines > 0 && topline == 0 && alt_which == 0 && sb) {
374330e2 428 /*
cabfd08c 429 * Since we're going to scroll the top line and we're on the
c9def1b8 430 * scrolling screen let's also effect the scrollback buffer.
431 *
432 * This is normally done by moving the position the screen
433 * painter reads from to reduce the amount of memory copying
434 * required.
374330e2 435 */
c9def1b8 436 if (scroll_size >= 0 && !recursive) {
437 newscr = scrtop + lines * (cols+1);
438
439 if (newscr > sbbot && botline == rows-1) {
440 /* We've hit the bottom of memory, so we have to do a
441 * physical scroll. But instead of just 1 line do it
442 * by 10% of the available memory.
443 *
444 * If the scroll region isn't the whole screen then we can't
445 * do this as it stands. We would need to recover the bottom
446 * of the screen from the scroll buffer after being sure that
447 * it doesn't get deleted.
448 */
449
450 i = (rows+savelines)/10;
451
452 /* Make it simple and ensure safe recursion */
453 if ( i<savelines-1) {
454 recursive ++;
455 scroll(topline, botline, i, sb);
456 recursive --;
457
458 newscr = scrtop - i * (cols+1);
459 if (scrtop_is_disptop) disptop = newscr;
460 scrtop = newscr;
461 }
462
463 newscr = scrtop + lines * (cols+1);
464 }
465
466 if (newscr <= sbbot) {
467 if (scrtop_is_disptop) disptop = newscr;
468 scrtop = newscr;
469
470 if (botline == rows-1 )
471 for (i = 0; i < size; i++)
472 scrtop[i+scroll_size] = erase_char;
473
474 update_sbar();
475 fix_cpos;
476
477 if (botline != rows-1) {
478 /* This fastscroll only works for full window scrolls.
479 * If the caller wanted a partial one we have to reverse
480 * scroll the bottom of the screen.
481 */
482 scroll(botline-lines+1, rows-1, -lines, 0);
483 }
484 return ;
485 }
486
487 /* If we can't scroll by memory remapping do it physically.
488 * But rather than expensivly doing the scroll buffer just
489 * scroll the screen. All it means is that sometimes we choose
490 * to not add lines from a scroll region to the scroll buffer.
491 */
492
493 if (savelines <= 400) {
494 sbtop -= lines * (cols+1);
495 if (sbtop < text)
496 sbtop = text;
497 scroll_size += scroll_top - sbtop;
498 scroll_top = sbtop;
499
500 update_sbar();
501 }
502 } else {
503 /* Ho hum, expensive scroll required. */
504
505 sbtop -= lines * (cols+1);
506 if (sbtop < text)
507 sbtop = text;
508 scroll_size += scroll_top - sbtop;
509 scroll_top = sbtop;
510
511 update_sbar();
512 }
374330e2 513 }
514
515 if (scroll_size < 0) {
516 size += scroll_size;
517 scroll_size = 0;
518 }
519
520 if (lines > 0) {
521 if (scroll_size)
522 memmove (scroll_top, scroll_top + size, scroll_size*TSIZE);
523 for (i = 0; i < size; i++)
e14a5a13 524 scroll_top[i+scroll_size] = erase_char;
374330e2 525 if (selstart > scroll_top &&
526 selstart < scroll_top + size + scroll_size) {
527 selstart -= size;
528 if (selstart < scroll_top)
529 selstart = scroll_top;
530 }
531 if (selend > scroll_top &&
532 selend < scroll_top + size + scroll_size) {
533 selend -= size;
534 if (selend < scroll_top)
535 selend = scroll_top;
536 }
c9def1b8 537 if (scrtop_is_disptop)
538 disptop = scrtop;
539 else
540 if (disptop > scroll_top &&
541 disptop < scroll_top + size + scroll_size) {
542 disptop -= size;
543 if (disptop < scroll_top)
544 disptop = scroll_top;
545 }
374330e2 546 } else {
547 if (scroll_size)
548 memmove (scroll_top + size, scroll_top, scroll_size*TSIZE);
549 for (i = 0; i < size; i++)
e14a5a13 550 scroll_top[i] = erase_char;
374330e2 551 if (selstart > scroll_top &&
552 selstart < scroll_top + size + scroll_size) {
553 selstart += size;
554 if (selstart > scroll_top + size + scroll_size)
555 selstart = scroll_top + size + scroll_size;
556 }
557 if (selend > scroll_top &&
558 selend < scroll_top + size + scroll_size) {
559 selend += size;
560 if (selend > scroll_top + size + scroll_size)
561 selend = scroll_top + size + scroll_size;
562 }
c9def1b8 563 if (scrtop_is_disptop)
564 disptop = scrtop;
565 else if (disptop > scroll_top &&
566 disptop < scroll_top + size + scroll_size) {
567 disptop += size;
568 if (disptop > scroll_top + size + scroll_size)
569 disptop = scroll_top + size + scroll_size;
570 }
374330e2 571 }
374330e2 572}
573
574/*
575 * Move the cursor to a given position, clipping at boundaries. We
576 * may or may not want to clip at the scroll margin: marg_clip is 0
577 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
578 * even _being_ outside the margins.
579 */
580static void move (int x, int y, int marg_clip) {
581 if (x < 0)
582 x = 0;
583 if (x >= cols)
584 x = cols-1;
585 if (marg_clip) {
586 if ((curs_y >= marg_t || marg_clip == 2) && y < marg_t)
587 y = marg_t;
588 if ((curs_y <= marg_b || marg_clip == 2) && y > marg_b)
589 y = marg_b;
590 }
591 if (y < 0)
592 y = 0;
593 if (y >= rows)
594 y = rows-1;
595 curs_x = x;
596 curs_y = y;
597 fix_cpos;
598 wrapnext = FALSE;
599}
600
601/*
602 * Save or restore the cursor and SGR mode.
603 */
604static void save_cursor(int save) {
605 if (save) {
606 save_x = curs_x;
607 save_y = curs_y;
608 save_attr = curr_attr;
609 save_cset = cset;
610 save_csattr = cset_attr[cset];
611 } else {
612 curs_x = save_x;
613 curs_y = save_y;
c9def1b8 614 /* Make sure the window hasn't shrunk since the save */
615 if (curs_x >= cols) curs_x = cols-1;
616 if (curs_y >= rows) curs_y = rows-1;
617
374330e2 618 curr_attr = save_attr;
619 cset = save_cset;
620 cset_attr[cset] = save_csattr;
621 fix_cpos;
e14a5a13 622 if (use_bce) erase_char = (' ' |(curr_attr&(ATTR_FGMASK|ATTR_BGMASK)));
374330e2 623 }
624}
625
626/*
627 * Erase a large portion of the screen: the whole screen, or the
628 * whole line, or parts thereof.
629 */
630static void erase_lots (int line_only, int from_begin, int to_end) {
631 unsigned long *startpos, *endpos;
632
633 if (line_only) {
634 startpos = cpos - curs_x;
c9def1b8 635 endpos = startpos + cols;
636 /* I've removed the +1 so that the Wide screen stuff is not
637 * removed when it shouldn't be.
638 */
374330e2 639 } else {
640 startpos = scrtop;
641 endpos = startpos + rows * (cols+1);
642 }
643 if (!from_begin)
644 startpos = cpos;
645 if (!to_end)
e14a5a13 646 endpos = cpos+1;
374330e2 647 check_selection (startpos, endpos);
e14a5a13 648
649 /* Clear screen also forces a full window redraw, just in case. */
650 if (startpos == scrtop && endpos == scrtop + rows * (cols+1))
651 term_invalidate();
652
374330e2 653 while (startpos < endpos)
e14a5a13 654 *startpos++ = erase_char;
374330e2 655}
656
657/*
658 * Insert or delete characters within the current line. n is +ve if
659 * insertion is desired, and -ve for deletion.
660 */
661static void insch (int n) {
662 int dir = (n < 0 ? -1 : +1);
663 int m;
664
665 n = (n < 0 ? -n : n);
666 if (n > cols - curs_x)
667 n = cols - curs_x;
668 m = cols - curs_x - n;
669 check_selection (cpos, cpos+n);
670 if (dir < 0) {
671 memmove (cpos, cpos+n, m*TSIZE);
672 while (n--)
e14a5a13 673 cpos[m++] = erase_char;
374330e2 674 } else {
675 memmove (cpos+n, cpos, m*TSIZE);
676 while (n--)
e14a5a13 677 cpos[n] = erase_char;
374330e2 678 }
679}
680
681/*
682 * Toggle terminal mode `mode' to state `state'. (`query' indicates
683 * whether the mode is a DEC private one or a normal one.)
684 */
685static void toggle_mode (int mode, int query, int state) {
686 if (query) switch (mode) {
687 case 1: /* application cursor keys */
688 app_cursor_keys = state;
689 break;
e14a5a13 690 case 2: /* VT52 mode */
691 vt52_mode = !state;
692 break;
374330e2 693 case 3: /* 80/132 columns */
694 deselect();
e14a5a13 695 request_resize (state ? 132 : 80, rows, 1);
696 reset_132 = state;
374330e2 697 break;
698 case 5: /* reverse video */
699 rvideo = state;
cabfd08c 700 seen_disp_event = TRUE;
e14a5a13 701 if (state) term_update();
374330e2 702 break;
703 case 6: /* DEC origin mode */
704 dec_om = state;
705 break;
706 case 7: /* auto wrap */
707 wrap = state;
708 break;
c9def1b8 709 case 8: /* auto key repeat */
710 repeat_off = !state;
711 break;
e14a5a13 712 case 25: /* enable/disable cursor */
713 compatibility(VT220);
714 cursor_on = state;
715 seen_disp_event = TRUE;
716 break;
374330e2 717 case 47: /* alternate screen */
e14a5a13 718 compatibility(OTHER);
374330e2 719 deselect();
720 swap_screen (state);
721 disptop = scrtop;
722 break;
723 } else switch (mode) {
724 case 4: /* set insert mode */
e14a5a13 725 compatibility(VT102);
374330e2 726 insert = state;
727 break;
e14a5a13 728 case 12: /* set echo mode */
729 /*
730 * This may be very good in smcup and rmcup (or smkx & rmkx) if you
731 * have a long RTT and the telnet client/daemon doesn't understand
732 * linemode.
733 *
734 * DONT send TS_RECHO/TS_LECHO; the telnet daemon tries to fix the
735 * tty and _really_ confuses some programs.
736 */
737 compatibility(VT220);
738 ldisc = (state? &ldisc_simple : &ldisc_term);
739 break;
c9def1b8 740 case 20: /* Return sends ... */
741 cr_lf_return = state;
742 break;
374330e2 743 }
744}
745
746/*
747 * Process an OSC sequence: set window title or icon name.
748 */
749static void do_osc(void) {
750 if (osc_w) {
751 while (osc_strlen--)
752 wordness[(unsigned char)osc_string[osc_strlen]] = esc_args[0];
753 } else {
754 osc_string[osc_strlen] = '\0';
755 switch (esc_args[0]) {
756 case 0:
757 case 1:
758 set_icon (osc_string);
759 if (esc_args[0] == 1)
760 break;
761 /* fall through: parameter 0 means set both */
762 case 2:
763 case 21:
764 set_title (osc_string);
765 break;
766 }
767 }
768}
769
770/*
771 * Remove everything currently in `inbuf' and stick it up on the
772 * in-memory display. There's a big state machine in here to
773 * process escape sequences...
774 */
775void term_out(void) {
c9def1b8 776 int c, inbuf_reap;
777
778static int beep_overload = 0;
779 int beep_count = 0;
780
781 for(inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++)
782 {
783 c = inbuf[inbuf_reap];
374330e2 784
c9def1b8 785 /*
5fd04f07 786 * Optionally log the session traffic to a file. Useful for
787 * debugging and possibly also useful for actual logging.
788 */
789 if (logfile) {
374330e2 790 static FILE *fp = NULL;
5fd04f07 791 if (!fp) fp = fopen(logfile, "wb");
374330e2 792 if (fp) fputc (c, fp);
793 }
e14a5a13 794 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
795 * be able to display 8-bit characters, but I'll let that go 'cause
796 * of i18n.
797 */
c9def1b8 798 if( ( (c&0x60) == 0 || c == '\177') &&
799 termstate < DO_CTRLS &&
e14a5a13 800 ( (c&0x80) == 0 || has_compat(VT220))) {
374330e2 801 switch (c) {
802 case '\005': /* terminal type query */
e14a5a13 803 /* Strictly speaking this is VT100 but a VT100 defaults to
804 * no response. Other terminals respond at their option.
805 *
806 * Don't put a CR in the default string as this tends to
807 * upset some weird software.
808 *
809 * An xterm returns "xterm" (5 characters)
810 */
811 compatibility(OTHER);
812 ldisc->send ("PuTTY", 5);
374330e2 813 break;
814 case '\007':
c9def1b8 815 beep_count++;
816 if(beep_count>6) beep_overload=1;
374330e2 817 disptop = scrtop;
374330e2 818 break;
819 case '\b':
c9def1b8 820 if (curs_x == 0 && curs_y == 0)
821 ;
822 else if (curs_x == 0 && curs_y > 0)
374330e2 823 curs_x = cols-1, curs_y--;
824 else if (wrapnext)
825 wrapnext = FALSE;
826 else
827 curs_x--;
828 fix_cpos;
cabfd08c 829 seen_disp_event = TRUE;
374330e2 830 break;
831 case '\016':
e14a5a13 832 compatibility(VT100);
374330e2 833 cset = 1;
834 break;
835 case '\017':
e14a5a13 836 compatibility(VT100);
374330e2 837 cset = 0;
838 break;
839 case '\033':
e14a5a13 840 if (vt52_mode)
841 termstate = VT52_ESC;
842 else {
843 compatibility(ANSIMIN);
844 termstate = SEEN_ESC;
845 }
374330e2 846 break;
847 case 0233:
e14a5a13 848 compatibility(VT220);
374330e2 849 termstate = SEEN_CSI;
850 esc_nargs = 1;
851 esc_args[0] = ARG_DEFAULT;
852 esc_query = FALSE;
853 break;
854 case 0235:
e14a5a13 855 compatibility(VT220);
374330e2 856 termstate = SEEN_OSC;
857 esc_args[0] = 0;
858 break;
859 case '\r':
860 curs_x = 0;
861 wrapnext = FALSE;
862 fix_cpos;
cabfd08c 863 seen_disp_event = TRUE;
c9def1b8 864 paste_hold = 0;
374330e2 865 break;
866 case '\013':
867 case '\014':
e14a5a13 868 compatibility(VT100);
374330e2 869 case '\n':
870 if (curs_y == marg_b)
871 scroll (marg_t, marg_b, 1, TRUE);
872 else if (curs_y < rows-1)
873 curs_y++;
cabfd08c 874 if (cfg.lfhascr)
875 curs_x = 0;
374330e2 876 fix_cpos;
877 wrapnext = FALSE;
cabfd08c 878 seen_disp_event = 1;
c9def1b8 879 paste_hold = 0;
374330e2 880 break;
881 case '\t':
374330e2 882 {
883 unsigned long *old_cpos = cpos;
c9def1b8 884 unsigned long *p = scrtop + curs_y * (cols+1) + cols;
885
886 do {
887 curs_x++;
888 } while (curs_x < cols-1 && !tabs[curs_x]);
889
890 if ((*p & LATTR_MODE) != LATTR_NORM)
891 {
892 if (curs_x >= cols/2)
893 curs_x = cols/2-1;
894 }
895 else
896 {
897 if (curs_x >= cols)
898 curs_x = cols-1;
899 }
900
374330e2 901 fix_cpos;
902 check_selection (old_cpos, cpos);
903 }
cabfd08c 904 seen_disp_event = TRUE;
374330e2 905 break;
cabfd08c 906 }
907 }
908 else switch (termstate) {
909 case TOPLEVEL:
8b811b12 910 /* Only graphic characters get this far, ctrls are stripped above */
911 if (wrapnext) {
c9def1b8 912 cpos[1] |= ATTR_WRAPPED;
8b811b12 913 if (curs_y == marg_b)
914 scroll (marg_t, marg_b, 1, TRUE);
915 else if (curs_y < rows-1)
916 curs_y++;
917 curs_x = 0;
918 fix_cpos;
919 wrapnext = FALSE;
8b811b12 920 }
921 if (insert)
922 insch (1);
e14a5a13 923 if (selstate != NO_SELECTION)
924 check_selection (cpos, cpos+1);
925 switch (cset_attr[cset]) {
926 /* Linedraw characters are different from 'ESC ( B' only
927 * for a small range, for ones outside that range make sure
928 * we use the same font as well as the same encoding.
929 */
930 case ATTR_LINEDRW:
931 if (c<0x60 || c>0x7F)
932 *cpos++ = xlat_tty2scr((unsigned char)c) | curr_attr |
933 ATTR_ASCII;
934 else
935 *cpos++ = ((unsigned char)c) | curr_attr | ATTR_LINEDRW;
936 break;
937 default:
938 *cpos++ = xlat_tty2scr((unsigned char)c) | curr_attr |
939 (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
940 break;
941 }
8b811b12 942 curs_x++;
943 if (curs_x == cols) {
944 cpos--;
945 curs_x--;
946 wrapnext = wrap;
374330e2 947 }
8b811b12 948 seen_disp_event = 1;
374330e2 949 break;
cabfd08c 950
374330e2 951 case IGNORE_NEXT:
952 termstate = TOPLEVEL;
953 break;
954 case OSC_MAYBE_ST:
955 /*
956 * This state is virtually identical to SEEN_ESC, with the
957 * exception that we have an OSC sequence in the pipeline,
958 * and _if_ we see a backslash, we process it.
959 */
960 if (c == '\\') {
961 do_osc();
962 termstate = TOPLEVEL;
963 break;
964 }
965 /* else fall through */
966 case SEEN_ESC:
967 termstate = TOPLEVEL;
968 switch (c) {
374330e2 969 case ' ': /* some weird sequence? */
e14a5a13 970 compatibility(VT220);
374330e2 971 termstate = IGNORE_NEXT;
972 break;
973 case '[': /* enter CSI mode */
974 termstate = SEEN_CSI;
975 esc_nargs = 1;
976 esc_args[0] = ARG_DEFAULT;
977 esc_query = FALSE;
978 break;
979 case ']': /* xterm escape sequences */
e14a5a13 980 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
981 compatibility(OTHER);
374330e2 982 termstate = SEEN_OSC;
983 esc_args[0] = 0;
984 break;
985 case '(': /* should set GL */
e14a5a13 986 compatibility(VT100);
374330e2 987 termstate = SET_GL;
988 break;
989 case ')': /* should set GR */
e14a5a13 990 compatibility(VT100);
374330e2 991 termstate = SET_GR;
992 break;
993 case '7': /* save cursor */
e14a5a13 994 compatibility(VT100);
374330e2 995 save_cursor (TRUE);
996 break;
997 case '8': /* restore cursor */
e14a5a13 998 compatibility(VT100);
374330e2 999 save_cursor (FALSE);
cabfd08c 1000 seen_disp_event = TRUE;
374330e2 1001 break;
1002 case '=':
e14a5a13 1003 compatibility(VT100);
374330e2 1004 app_keypad_keys = TRUE;
1005 break;
1006 case '>':
e14a5a13 1007 compatibility(VT100);
374330e2 1008 app_keypad_keys = FALSE;
1009 break;
1010 case 'D': /* exactly equivalent to LF */
e14a5a13 1011 compatibility(VT100);
374330e2 1012 if (curs_y == marg_b)
1013 scroll (marg_t, marg_b, 1, TRUE);
1014 else if (curs_y < rows-1)
1015 curs_y++;
1016 fix_cpos;
1017 wrapnext = FALSE;
cabfd08c 1018 seen_disp_event = TRUE;
374330e2 1019 break;
1020 case 'E': /* exactly equivalent to CR-LF */
e14a5a13 1021 compatibility(VT100);
374330e2 1022 curs_x = 0;
374330e2 1023 if (curs_y == marg_b)
1024 scroll (marg_t, marg_b, 1, TRUE);
1025 else if (curs_y < rows-1)
1026 curs_y++;
1027 fix_cpos;
1028 wrapnext = FALSE;
cabfd08c 1029 seen_disp_event = TRUE;
374330e2 1030 break;
1031 case 'M': /* reverse index - backwards LF */
e14a5a13 1032 compatibility(VT100);
374330e2 1033 if (curs_y == marg_t)
1034 scroll (marg_t, marg_b, -1, TRUE);
1035 else if (curs_y > 0)
1036 curs_y--;
1037 fix_cpos;
1038 wrapnext = FALSE;
cabfd08c 1039 seen_disp_event = TRUE;
374330e2 1040 break;
1041 case 'Z': /* terminal type query */
e14a5a13 1042 compatibility(VT100);
c9def1b8 1043 ldisc->send (id_string, strlen(id_string));
374330e2 1044 break;
1045 case 'c': /* restore power-on settings */
e14a5a13 1046 compatibility(VT100);
374330e2 1047 power_on();
e14a5a13 1048 if (reset_132) {
1049 request_resize (80, rows, 1);
1050 reset_132 = 0;
1051 }
374330e2 1052 fix_cpos;
1053 disptop = scrtop;
cabfd08c 1054 seen_disp_event = TRUE;
374330e2 1055 break;
1056 case '#': /* ESC # 8 fills screen with Es :-) */
e14a5a13 1057 compatibility(VT100);
374330e2 1058 termstate = SEEN_ESCHASH;
1059 break;
1060 case 'H': /* set a tab */
e14a5a13 1061 compatibility(VT100);
374330e2 1062 tabs[curs_x] = TRUE;
1063 break;
1064 }
1065 break;
1066 case SEEN_CSI:
1067 termstate = TOPLEVEL; /* default */
8b811b12 1068 if( isdigit(c) )
1069 {
374330e2 1070 if (esc_nargs <= ARGS_MAX) {
1071 if (esc_args[esc_nargs-1] == ARG_DEFAULT)
1072 esc_args[esc_nargs-1] = 0;
1073 esc_args[esc_nargs-1] =
1074 10 * esc_args[esc_nargs-1] + c - '0';
1075 }
1076 termstate = SEEN_CSI;
8b811b12 1077 }
1078 else if( c == ';' )
1079 {
374330e2 1080 if (++esc_nargs <= ARGS_MAX)
1081 esc_args[esc_nargs-1] = ARG_DEFAULT;
1082 termstate = SEEN_CSI;
8b811b12 1083 }
1084 else if( c < '@' )
1085 {
1086 if( esc_query ) esc_query = -1;
1087 else if( c == '?' ) esc_query = TRUE;
1088 else esc_query = c;
374330e2 1089 termstate = SEEN_CSI;
8b811b12 1090 }
1091 else switch (ANSI(c,esc_query)) {
374330e2 1092 case 'A': /* move up N lines */
1093 move (curs_x, curs_y - def(esc_args[0], 1), 1);
cabfd08c 1094 seen_disp_event = TRUE;
374330e2 1095 break;
e14a5a13 1096 case 'e': /* move down N lines */
1097 compatibility(ANSI);
1098 case 'B':
374330e2 1099 move (curs_x, curs_y + def(esc_args[0], 1), 1);
cabfd08c 1100 seen_disp_event = TRUE;
374330e2 1101 break;
e14a5a13 1102 case 'a': /* move right N cols */
1103 compatibility(ANSI);
1104 case 'C':
374330e2 1105 move (curs_x + def(esc_args[0], 1), curs_y, 1);
cabfd08c 1106 seen_disp_event = TRUE;
374330e2 1107 break;
1108 case 'D': /* move left N cols */
1109 move (curs_x - def(esc_args[0], 1), curs_y, 1);
cabfd08c 1110 seen_disp_event = TRUE;
374330e2 1111 break;
1112 case 'E': /* move down N lines and CR */
e14a5a13 1113 compatibility(ANSI);
374330e2 1114 move (0, curs_y + def(esc_args[0], 1), 1);
cabfd08c 1115 seen_disp_event = TRUE;
374330e2 1116 break;
1117 case 'F': /* move up N lines and CR */
e14a5a13 1118 compatibility(ANSI);
374330e2 1119 move (0, curs_y - def(esc_args[0], 1), 1);
cabfd08c 1120 seen_disp_event = TRUE;
374330e2 1121 break;
1122 case 'G': case '`': /* set horizontal posn */
e14a5a13 1123 compatibility(ANSI);
374330e2 1124 move (def(esc_args[0], 1) - 1, curs_y, 0);
cabfd08c 1125 seen_disp_event = TRUE;
374330e2 1126 break;
1127 case 'd': /* set vertical posn */
e14a5a13 1128 compatibility(ANSI);
374330e2 1129 move (curs_x, (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
1130 (dec_om ? 2 : 0));
cabfd08c 1131 seen_disp_event = TRUE;
374330e2 1132 break;
1133 case 'H': case 'f': /* set horz and vert posns at once */
1134 if (esc_nargs < 2)
1135 esc_args[1] = ARG_DEFAULT;
1136 move (def(esc_args[1], 1) - 1,
1137 (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
1138 (dec_om ? 2 : 0));
cabfd08c 1139 seen_disp_event = TRUE;
374330e2 1140 break;
1141 case 'J': /* erase screen or parts of it */
1142 {
1143 unsigned int i = def(esc_args[0], 0) + 1;
1144 if (i > 3)
1145 i = 0;
1146 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1147 }
1148 disptop = scrtop;
cabfd08c 1149 seen_disp_event = TRUE;
374330e2 1150 break;
1151 case 'K': /* erase line or parts of it */
1152 {
1153 unsigned int i = def(esc_args[0], 0) + 1;
1154 if (i > 3)
1155 i = 0;
1156 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1157 }
cabfd08c 1158 seen_disp_event = TRUE;
374330e2 1159 break;
1160 case 'L': /* insert lines */
e14a5a13 1161 compatibility(VT102);
374330e2 1162 if (curs_y <= marg_b)
1163 scroll (curs_y, marg_b, -def(esc_args[0], 1), FALSE);
cabfd08c 1164 seen_disp_event = TRUE;
374330e2 1165 break;
1166 case 'M': /* delete lines */
e14a5a13 1167 compatibility(VT102);
374330e2 1168 if (curs_y <= marg_b)
cabfd08c 1169 scroll (curs_y, marg_b, def(esc_args[0], 1), TRUE);
1170 seen_disp_event = TRUE;
374330e2 1171 break;
1172 case '@': /* insert chars */
e14a5a13 1173 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1174 compatibility(VT102);
374330e2 1175 insch (def(esc_args[0], 1));
cabfd08c 1176 seen_disp_event = TRUE;
374330e2 1177 break;
1178 case 'P': /* delete chars */
e14a5a13 1179 compatibility(VT102);
374330e2 1180 insch (-def(esc_args[0], 1));
cabfd08c 1181 seen_disp_event = TRUE;
374330e2 1182 break;
1183 case 'c': /* terminal type query */
e14a5a13 1184 compatibility(VT100);
1185 /* This is the response for a VT102 */
c9def1b8 1186 ldisc->send (id_string, strlen(id_string));
374330e2 1187 break;
1188 case 'n': /* cursor position query */
1189 if (esc_args[0] == 6) {
1190 char buf[32];
1191 sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1);
5bc238bb 1192 ldisc->send (buf, strlen(buf));
374330e2 1193 }
e14a5a13 1194 else if (esc_args[0] == 5) {
e14a5a13 1195 ldisc->send ("\033[0n", 4);
1196 }
374330e2 1197 break;
e14a5a13 1198 case 'h': /* toggle modes to high */
8b811b12 1199 case ANSI_QUE('h'):
e14a5a13 1200 compatibility(VT100);
1201 {
1202 int i;
1203 for (i=0; i<esc_nargs; i++)
1204 toggle_mode (esc_args[i], esc_query, TRUE);
1205 }
374330e2 1206 break;
e14a5a13 1207 case 'l': /* toggle modes to low */
8b811b12 1208 case ANSI_QUE('l'):
e14a5a13 1209 compatibility(VT100);
1210 {
1211 int i;
1212 for (i=0; i<esc_nargs; i++)
1213 toggle_mode (esc_args[i], esc_query, FALSE);
1214 }
374330e2 1215 break;
1216 case 'g': /* clear tabs */
e14a5a13 1217 compatibility(VT100);
374330e2 1218 if (esc_nargs == 1) {
1219 if (esc_args[0] == 0) {
1220 tabs[curs_x] = FALSE;
1221 } else if (esc_args[0] == 3) {
1222 int i;
1223 for (i = 0; i < cols; i++)
1224 tabs[i] = FALSE;
1225 }
1226 }
1227 break;
1228 case 'r': /* set scroll margins */
e14a5a13 1229 compatibility(VT100);
8b811b12 1230 if (esc_nargs <= 2) {
374330e2 1231 int top, bot;
7e394092 1232 top = def(esc_args[0], 1) - 1;
1233 bot = (esc_nargs <= 1 || esc_args[1] == 0 ? rows :
1234 def(esc_args[1], rows)) - 1;
374330e2 1235 if (bot >= rows)
1236 bot = rows-1;
7e394092 1237 /* VTTEST Bug 9 - if region is less than 2 lines
1238 * don't change region.
1239 */
1240 if (bot-top > 1) {
374330e2 1241 marg_t = top;
1242 marg_b = bot;
1243 curs_x = 0;
1244 /*
1245 * I used to think the cursor should be
1246 * placed at the top of the newly marginned
1247 * area. Apparently not: VMS TPU falls over
1248 * if so.
e14a5a13 1249 *
1250 * Well actually it should for Origin mode - RDB
374330e2 1251 */
e14a5a13 1252 curs_y = (dec_om ? marg_t : 0);
374330e2 1253 fix_cpos;
cabfd08c 1254 seen_disp_event = TRUE;
374330e2 1255 }
1256 }
1257 break;
1258 case 'm': /* set graphics rendition */
1259 {
e14a5a13 1260 /*
1261 * A VT100 without the AVO only had one attribute, either
1262 * underline or reverse video depending on the cursor type,
1263 * this was selected by CSI 7m.
1264 *
1265 * case 2:
1266 * This is DIM on the VT100-AVO and VT102
1267 * case 5:
1268 * This is BLINK on the VT100-AVO and VT102+
1269 * case 8:
1270 * This is INVIS on the VT100-AVO and VT102
1271 * case 21:
1272 * This like 22 disables BOLD, DIM and INVIS
1273 *
1274 * The ANSI colours appear on any terminal that has colour
1275 * (obviously) but the interaction between sgr0 and the
1276 * colours varies but is usually related to the background
1277 * colour erase item.
1278 * The interaction between colour attributes and the mono
1279 * ones is also very implementation dependent.
1280 *
1281 * The 39 and 49 attributes are likely to be unimplemented.
1282 */
374330e2 1283 int i;
1284 for (i=0; i<esc_nargs; i++) {
1285 switch (def(esc_args[i], 0)) {
1286 case 0: /* restore defaults */
1287 curr_attr = ATTR_DEFAULT; break;
1288 case 1: /* enable bold */
e14a5a13 1289 compatibility(VT100AVO);
374330e2 1290 curr_attr |= ATTR_BOLD; break;
374330e2 1291 case 21: /* (enable double underline) */
e14a5a13 1292 compatibility(OTHER);
1293 case 4: /* enable underline */
1294 compatibility(VT100AVO);
374330e2 1295 curr_attr |= ATTR_UNDER; break;
e14a5a13 1296 case 5: /* enable blink */
1297 compatibility(VT100AVO);
1298 curr_attr |= ATTR_BLINK; break;
374330e2 1299 case 7: /* enable reverse video */
1300 curr_attr |= ATTR_REVERSE; break;
1301 case 22: /* disable bold */
e14a5a13 1302 compatibility(VT220);
374330e2 1303 curr_attr &= ~ATTR_BOLD; break;
1304 case 24: /* disable underline */
e14a5a13 1305 compatibility(VT220);
374330e2 1306 curr_attr &= ~ATTR_UNDER; break;
e14a5a13 1307 case 25: /* disable blink */
1308 compatibility(VT220);
1309 curr_attr &= ~ATTR_BLINK; break;
374330e2 1310 case 27: /* disable reverse video */
e14a5a13 1311 compatibility(VT220);
374330e2 1312 curr_attr &= ~ATTR_REVERSE; break;
1313 case 30: case 31: case 32: case 33:
1314 case 34: case 35: case 36: case 37:
1315 /* foreground */
1316 curr_attr &= ~ATTR_FGMASK;
1317 curr_attr |= (esc_args[i] - 30) << ATTR_FGSHIFT;
1318 break;
1319 case 39: /* default-foreground */
1320 curr_attr &= ~ATTR_FGMASK;
1321 curr_attr |= ATTR_DEFFG;
1322 break;
1323 case 40: case 41: case 42: case 43:
1324 case 44: case 45: case 46: case 47:
1325 /* background */
1326 curr_attr &= ~ATTR_BGMASK;
1327 curr_attr |= (esc_args[i] - 40) << ATTR_BGSHIFT;
1328 break;
1329 case 49: /* default-background */
1330 curr_attr &= ~ATTR_BGMASK;
1331 curr_attr |= ATTR_DEFBG;
1332 break;
1333 }
1334 }
e14a5a13 1335 if (use_bce)
c9def1b8 1336 erase_char =
1337 (' '|
1338 (curr_attr&(ATTR_FGMASK|ATTR_BGMASK|ATTR_BLINK))
1339 );
374330e2 1340 }
1341 break;
1342 case 's': /* save cursor */
1343 save_cursor (TRUE);
1344 break;
1345 case 'u': /* restore cursor */
1346 save_cursor (FALSE);
cabfd08c 1347 seen_disp_event = TRUE;
374330e2 1348 break;
1349 case 't': /* set page size - ie window height */
e14a5a13 1350 /*
1351 * VT340/VT420 sequence DECSLPP, DEC only allows values
1352 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1353 * illegal values (eg first arg 1..9) for window changing
1354 * and reports.
1355 */
1356 compatibility(VT340TEXT);
1357 if (esc_nargs<=1 && (esc_args[0]<1 || esc_args[0]>=24)) {
c9def1b8 1358 request_resize (cols, def(esc_args[0], 24), 0);
e14a5a13 1359 deselect();
1360 }
1361 break;
1362 case ANSI('|', '*'):
1363 /* VT420 sequence DECSNLS
1364 * Set number of lines on screen
1365 * VT420 uses VGA like hardware and can support any size in
1366 * reasonable range (24..49 AIUI) with no default specified.
1367 */
1368 compatibility(VT420);
c9def1b8 1369 if (esc_nargs==1 && esc_args[0]>0) {
1370 request_resize (cols, def(esc_args[0], cfg.height), 0);
e14a5a13 1371 deselect();
1372 }
1373 break;
1374 case ANSI('|', '$'):
1375 /* VT340/VT420 sequence DECSCPP
1376 * Set number of columns per page
1377 * Docs imply range is only 80 or 132, but I'll allow any.
1378 */
1379 compatibility(VT340TEXT);
1380 if (esc_nargs<=1) {
c9def1b8 1381 request_resize (def(esc_args[0], cfg.width), rows, 0);
e14a5a13 1382 deselect();
1383 }
374330e2 1384 break;
1385 case 'X': /* write N spaces w/o moving cursor */
e14a5a13 1386 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
c9def1b8 1387 compatibility(ANSIMIN);
374330e2 1388 {
1389 int n = def(esc_args[0], 1);
1390 unsigned long *p = cpos;
1391 if (n > cols - curs_x)
1392 n = cols - curs_x;
1393 check_selection (cpos, cpos+n);
1394 while (n--)
e14a5a13 1395 *p++ = erase_char;
cabfd08c 1396 seen_disp_event = TRUE;
374330e2 1397 }
1398 break;
1399 case 'x': /* report terminal characteristics */
e14a5a13 1400 compatibility(VT100);
374330e2 1401 {
1402 char buf[32];
1403 int i = def(esc_args[0], 0);
1404 if (i == 0 || i == 1) {
1405 strcpy (buf, "\033[2;1;1;112;112;1;0x");
1406 buf[2] += i;
5bc238bb 1407 ldisc->send (buf, 20);
374330e2 1408 }
1409 }
1410 break;
e14a5a13 1411 case ANSI('L','='):
1412 compatibility(OTHER);
1413 use_bce = (esc_args[0]<=0);
1414 erase_char = ERASE_CHAR;
1415 if (use_bce)
1416 erase_char = (' '|(curr_attr&(ATTR_FGMASK|ATTR_BGMASK)));
1417 break;
c9def1b8 1418 case ANSI('E','='):
1419 compatibility(OTHER);
1420 blink_is_real = (esc_args[0]>=1);
1421 break;
e14a5a13 1422 case ANSI('p','"'):
1423 /* Allow the host to make this emulator a 'perfect' VT102.
1424 * This first appeared in the VT220, but we do need to get
1425 * back to PuTTY mode so I won't check it.
1426 *
1427 * The arg == 60 is a PuTTY extension.
1428 * The 2nd arg, 8bit vs 7bit is not obeyed.
1429 *
1430 * Setting VT102 mode should also change the Fkeys to
1431 * generate PF* codes as a real VT102 has no Fkeys.
1432 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1433 * send nothing.
1434 *
1435 * Note ESC c will NOT change this!
1436 */
1437
1438 if (esc_args[0] == 61) compatibility_level = TM_VT102;
1439 else if (esc_args[0] == 60) compatibility_level = TM_ANSIMIN;
1440 else compatibility_level = TM_PUTTY;
1441 break;
374330e2 1442 }
1443 break;
1444 case SET_GL:
1445 case SET_GR:
e14a5a13 1446 /* VT100 only here, checked above */
374330e2 1447 switch (c) {
1448 case 'A':
1449 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
1450 break;
1451 case '0':
1452 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
1453 break;
e14a5a13 1454 case 'B':
374330e2 1455 default: /* specifically, 'B' */
1456 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
1457 break;
1458 }
e14a5a13 1459 if( !has_compat(VT220) || c != '%' )
8b811b12 1460 termstate = TOPLEVEL;
374330e2 1461 break;
1462 case SEEN_OSC:
1463 osc_w = FALSE;
1464 switch (c) {
374330e2 1465 case 'P': /* Linux palette sequence */
1466 termstate = SEEN_OSC_P;
1467 osc_strlen = 0;
1468 break;
1469 case 'R': /* Linux palette reset */
1470 palette_reset();
1471 term_invalidate();
1472 termstate = TOPLEVEL;
1473 break;
1474 case 'W': /* word-set */
1475 termstate = SEEN_OSC_W;
1476 osc_w = TRUE;
1477 break;
1478 case '0': case '1': case '2': case '3': case '4':
1479 case '5': case '6': case '7': case '8': case '9':
1480 esc_args[0] = 10 * esc_args[0] + c - '0';
1481 break;
1482 case 'L':
1483 /*
1484 * Grotty hack to support xterm and DECterm title
1485 * sequences concurrently.
1486 */
1487 if (esc_args[0] == 2) {
1488 esc_args[0] = 1;
1489 break;
1490 }
1491 /* else fall through */
1492 default:
1493 termstate = OSC_STRING;
1494 osc_strlen = 0;
1495 }
1496 break;
1497 case OSC_STRING:
8b811b12 1498 /*
1499 * This OSC stuff is EVIL. It takes just one character to get into
1500 * sysline mode and it's not initially obvious how to get out.
1501 * So I've added CR and LF as string aborts.
1502 * This shouldn't effect compatibility as I believe embedded
1503 * control characters are supposed to be interpreted (maybe?)
1504 * and they don't display anything useful anyway.
1505 *
1506 * -- RDB
1507 */
1508 if (c == '\n' || c == '\r') {
1509 termstate = TOPLEVEL;
1510 } else if (c == 0234 || c == '\007' ) {
374330e2 1511 /*
1512 * These characters terminate the string; ST and BEL
1513 * terminate the sequence and trigger instant
1514 * processing of it, whereas ESC goes back to SEEN_ESC
1515 * mode unless it is followed by \, in which case it is
1516 * synonymous with ST in the first place.
1517 */
1518 do_osc();
1519 termstate = TOPLEVEL;
1520 } else if (c == '\033')
1521 termstate = OSC_MAYBE_ST;
1522 else if (osc_strlen < OSC_STR_MAX)
1523 osc_string[osc_strlen++] = c;
1524 break;
1525 case SEEN_OSC_P:
1526 {
1527 int max = (osc_strlen == 0 ? 21 : 16);
1528 int val;
1529 if (c >= '0' && c <= '9')
1530 val = c - '0';
1531 else if (c >= 'A' && c <= 'A'+max-10)
1532 val = c - 'A' + 10;
1533 else if (c >= 'a' && c <= 'a'+max-10)
1534 val = c - 'a' + 10;
1535 else
1536 termstate = TOPLEVEL;
1537 osc_string[osc_strlen++] = val;
1538 if (osc_strlen >= 7) {
1539 palette_set (osc_string[0],
1540 osc_string[1] * 16 + osc_string[2],
1541 osc_string[3] * 16 + osc_string[4],
1542 osc_string[5] * 16 + osc_string[6]);
1543 term_invalidate();
1544 termstate = TOPLEVEL;
1545 }
1546 }
1547 break;
1548 case SEEN_OSC_W:
1549 switch (c) {
374330e2 1550 case '0': case '1': case '2': case '3': case '4':
1551 case '5': case '6': case '7': case '8': case '9':
1552 esc_args[0] = 10 * esc_args[0] + c - '0';
1553 break;
1554 default:
1555 termstate = OSC_STRING;
1556 osc_strlen = 0;
1557 }
1558 break;
1559 case SEEN_ESCHASH:
c9def1b8 1560 {
1561 unsigned long *p;
1562 unsigned long nlattr;
1563 int n;
1564
1565 switch (c) {
1566 case '8':
1567 p = scrtop;
1568 n = rows * (cols+1);
1569 while (n--)
1570 *p++ = ATTR_DEFAULT | 'E';
1571 disptop = scrtop;
1572 seen_disp_event = TRUE;
1573 check_selection (scrtop, scrtop + rows * (cols+1));
1574 break;
1575
1576 case '3': nlattr = LATTR_TOP; if(0) {
1577 case '4': nlattr = LATTR_BOT; } if(0) {
1578 case '5': nlattr = LATTR_NORM; } if(0) {
1579 case '6': nlattr = LATTR_WIDE; }
1580
1581 p = scrtop + curs_y * (cols+1) + cols;
1582 *p &= ~LATTR_MODE;
1583 *p |= nlattr;
1584 }
374330e2 1585 }
1586 termstate = TOPLEVEL;
1587 break;
e14a5a13 1588 case VT52_ESC:
1589 termstate = TOPLEVEL;
1590 seen_disp_event = TRUE;
1591 switch (c) {
1592 case 'A':
1593 move (curs_x, curs_y - 1, 1);
1594 break;
1595 case 'B':
1596 move (curs_x, curs_y + 1, 1);
1597 break;
1598 case 'C':
1599 move (curs_x + 1, curs_y, 1);
1600 break;
1601 case 'D':
1602 move (curs_x - 1, curs_y, 1);
1603 break;
1604 case 'F':
1605 cset_attr[cset=0] = ATTR_LINEDRW;
1606 break;
1607 case 'G':
1608 cset_attr[cset=0] = ATTR_ASCII;
1609 break;
1610 case 'H':
1611 move (0, 0, 0);
1612 break;
1613 case 'I':
1614 if (curs_y == 0)
1615 scroll (0, rows-1, -1, TRUE);
1616 else if (curs_y > 0)
1617 curs_y--;
1618 fix_cpos;
1619 wrapnext = FALSE;
1620 break;
1621 case 'J':
1622 erase_lots(FALSE, FALSE, TRUE);
1623 disptop = scrtop;
1624 break;
1625 case 'K':
1626 erase_lots(TRUE, FALSE, TRUE);
1627 break;
1628 case 'V':
1629 /* XXX Print cursor line */
1630 break;
1631 case 'W':
1632 /* XXX Start controller mode */
1633 break;
1634 case 'X':
1635 /* XXX Stop controller mode */
1636 break;
1637 case 'Y':
1638 termstate = VT52_Y1;
1639 break;
1640 case 'Z':
1641 ldisc->send ("\033/Z", 3);
1642 break;
1643 case '=':
1644 app_cursor_keys = TRUE;
1645 break;
1646 case '>':
1647 app_cursor_keys = FALSE;
1648 break;
1649 case '<':
1650 /* XXX This should switch to VT100 mode not current or default
1651 * VT mode. But this will only have effect in a VT220+
1652 * emulation.
1653 */
1654 vt52_mode = FALSE;
1655 break;
1656 case '^':
1657 /* XXX Enter auto print mode */
1658 break;
1659 case '_':
1660 /* XXX Exit auto print mode */
1661 break;
1662 case ']':
1663 /* XXX Print screen */
1664 break;
1665 }
1666 break;
1667 case VT52_Y1:
1668 termstate = VT52_Y2;
1669 move(curs_x, c-' ', 0);
1670 break;
1671 case VT52_Y2:
1672 termstate = TOPLEVEL;
1673 move(c-' ', curs_y, 0);
1674 break;
374330e2 1675 }
e14a5a13 1676 if (selstate != NO_SELECTION)
1677 check_selection (cpos, cpos+1);
374330e2 1678 }
c9def1b8 1679 inbuf_head = 0;
1680
1681 if (beep_overload)
1682 {
1683 if(!beep_count) beep_overload=0;
1684 }
1685 else if(beep_count && beep_count<5 && cfg.beep)
1686 beep(beep_count/3);
374330e2 1687}
1688
1689/*
1690 * Compare two lines to determine whether they are sufficiently
1691 * alike to scroll-optimise one to the other. Return the degree of
1692 * similarity.
1693 */
1694static int linecmp (unsigned long *a, unsigned long *b) {
1695 int i, n;
1696
1697 for (i=n=0; i < cols; i++)
1698 n += (*a++ == *b++);
1699 return n;
1700}
1701
1702/*
1703 * Given a context, update the window. Out of paranoia, we don't
1704 * allow WM_PAINT responses to do scrolling optimisations.
1705 */
ec8679e9 1706static void do_paint (Context ctx, int may_optimise){
374330e2 1707 int i, j, start, our_curs_y;
1708 unsigned long attr, rv, cursor;
1709 char ch[1024];
1710
e14a5a13 1711 if (cursor_on) {
1712 if (has_focus) {
217dceef 1713 if (blinker || !cfg.blink_cur)
1714 cursor = ATTR_ACTCURS;
1715 else
1716 cursor = 0;
e14a5a13 1717 }
1718 else
1719 cursor = ATTR_PASCURS;
1720 }
1721 else cursor = 0;
374330e2 1722 rv = (rvideo ? ATTR_REVERSE : 0);
1723 our_curs_y = curs_y + (scrtop - disptop) / (cols+1);
1724
1725 for (i=0; i<rows; i++) {
1726 int idx = i*(cols+1);
c9def1b8 1727 int lattr = (disptop[idx+cols] & LATTR_MODE);
374330e2 1728 for (j=0; j<=cols; j++,idx++) {
1729 unsigned long *d = disptop+idx;
c9def1b8 1730 wanttext[idx] = lattr | ((*d ^ rv
374330e2 1731 ^ (selstart <= d && d < selend ?
1732 ATTR_REVERSE : 0)) |
1733 (i==our_curs_y && j==curs_x ? cursor : 0));
c9def1b8 1734
1735 if (blink_is_real) {
1736 if (has_focus && tblinker && (wanttext[idx]&ATTR_BLINK) )
1737 {
1738 wanttext[idx] &= ATTR_MASK;
1739 wanttext[idx] += ' ';
1740 }
1741 wanttext[idx] &= ~ATTR_BLINK;
1742 }
374330e2 1743 }
1744 }
1745
1746 /*
1747 * We would perform scrolling optimisations in here, if they
1748 * didn't have a nasty tendency to cause the whole sodding
1749 * program to hang for a second at speed-critical moments.
1750 * We'll leave it well alone...
1751 */
1752
1753 for (i=0; i<rows; i++) {
1754 int idx = i*(cols+1);
c9def1b8 1755 int lattr = (wanttext[idx+cols] & LATTR_MODE);
374330e2 1756 start = -1;
1757 for (j=0; j<=cols; j++,idx++) {
1758 unsigned long t = wanttext[idx];
1759 int needs_update = (j < cols && t != disptext[idx]);
1760 int keep_going = (start != -1 && needs_update &&
1761 (t & ATTR_MASK) == attr &&
1762 j-start < sizeof(ch));
1763 if (start != -1 && !keep_going) {
c9def1b8 1764 do_text (ctx, start, i, ch, j-start, attr, lattr);
374330e2 1765 start = -1;
1766 }
1767 if (needs_update) {
1768 if (start == -1) {
1769 start = j;
1770 attr = t & ATTR_MASK;
1771 }
1772 ch[j-start] = (char) (t & CHAR_MASK);
1773 }
1774 disptext[idx] = t;
1775 }
1776 }
1777}
1778
1779/*
e14a5a13 1780 * Flick the switch that says if blinking things should be shown or hidden.
1781 */
1782
1783void term_blink(int flg) {
1784static long last_blink = 0;
c9def1b8 1785static long last_tblink = 0;
e14a5a13 1786 long now, blink_diff;
1787
c9def1b8 1788 now = GetTickCount();
1789 blink_diff = now-last_tblink;
1790
1791 /* Make sure the text blinks no more than 2Hz */
1792 if (blink_diff<0 || blink_diff>450)
1793 {
1794 last_tblink = now;
1795 tblinker = !tblinker;
1796 }
1797
e14a5a13 1798 if (flg) {
1799 blinker = 1;
c9def1b8 1800 last_blink = now;
e14a5a13 1801 return;
1802 }
e14a5a13 1803
1804 blink_diff = now-last_blink;
1805
1806 /* Make sure the cursor blinks no more than 2Hz */
1807 if (blink_diff>=0 && blink_diff<450)
1808 return;
1809
1810 last_blink = now;
1811 blinker = !blinker;
1812}
1813
1814/*
374330e2 1815 * Invalidate the whole screen so it will be repainted in full.
1816 */
1817void term_invalidate(void) {
1818 int i;
1819
1820 for (i=0; i<rows*(cols+1); i++)
1821 disptext[i] = ATTR_INVALID;
1822}
1823
1824/*
1825 * Paint the window in response to a WM_PAINT message.
1826 */
1827void term_paint (Context ctx, int l, int t, int r, int b) {
1828 int i, j, left, top, right, bottom;
1829
1830 left = l / font_width;
1831 right = (r - 1) / font_width;
1832 top = t / font_height;
1833 bottom = (b - 1) / font_height;
e42ad027 1834 for (i = top; i <= bottom && i < rows ; i++)
c9def1b8 1835 {
1836 if ( (disptext[i*(cols+1)+cols]&LATTR_MODE) == LATTR_NORM)
1837 for (j = left; j <= right && j < cols ; j++)
1838 disptext[i*(cols+1)+j] = ATTR_INVALID;
1839 else
1840 for (j = left/2; j <= right/2+1 && j < cols ; j++)
1841 disptext[i*(cols+1)+j] = ATTR_INVALID;
1842 }
374330e2 1843
e14a5a13 1844 /* This should happen soon enough, also for some reason it sometimes
1845 * fails to actually do anything when re-sizing ... painting the wrong
1846 * window perhaps ?
374330e2 1847 do_paint (ctx, FALSE);
e14a5a13 1848 */
374330e2 1849}
1850
1851/*
1852 * Attempt to scroll the scrollback. The second parameter gives the
1853 * position we want to scroll to; the first is +1 to denote that
1854 * this position is relative to the beginning of the scrollback, -1
1855 * to denote it is relative to the end, and 0 to denote that it is
1856 * relative to the current position.
1857 */
1858void term_scroll (int rel, int where) {
1859 int n = where * (cols+1);
1860
1861 disptop = (rel < 0 ? scrtop :
1862 rel > 0 ? sbtop : disptop) + n;
1863 if (disptop < sbtop)
1864 disptop = sbtop;
1865 if (disptop > scrtop)
1866 disptop = scrtop;
1867 update_sbar();
1868 term_update();
1869}
1870
1871/*
1872 * Spread the selection outwards according to the selection mode.
1873 */
1874static unsigned long *sel_spread_half (unsigned long *p, int dir) {
1875 unsigned long *linestart, *lineend;
1876 int x;
1877 short wvalue;
1878
1879 x = (p - text) % (cols+1);
1880 linestart = p - x;
1881 lineend = linestart + cols;
1882
1883 switch (selmode) {
1884 case SM_CHAR:
1885 /*
1886 * In this mode, every character is a separate unit, except
1887 * for runs of spaces at the end of a non-wrapping line.
1888 */
1889 if (!(linestart[cols] & ATTR_WRAPPED)) {
1890 unsigned long *q = lineend;
1891 while (q > linestart && (q[-1] & CHAR_MASK) == 0x20)
1892 q--;
1893 if (q == lineend)
1894 q--;
1895 if (p >= q)
1896 p = (dir == -1 ? q : lineend - 1);
1897 }
1898 break;
1899 case SM_WORD:
1900 /*
1901 * In this mode, the units are maximal runs of characters
1902 * whose `wordness' has the same value.
1903 */
1904 wvalue = wordness[*p & CHAR_MASK];
1905 if (dir == +1) {
1906 while (p < lineend && wordness[p[1] & CHAR_MASK] == wvalue)
1907 p++;
1908 } else {
1909 while (p > linestart && wordness[p[-1] & CHAR_MASK] == wvalue)
1910 p--;
1911 }
1912 break;
1913 case SM_LINE:
1914 /*
1915 * In this mode, every line is a unit.
1916 */
1917 p = (dir == -1 ? linestart : lineend - 1);
1918 break;
1919 }
1920 return p;
1921}
1922
1923static void sel_spread (void) {
1924 selstart = sel_spread_half (selstart, -1);
1925 selend = sel_spread_half (selend - 1, +1) + 1;
1926}
1927
1928void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) {
37508af4 1929 unsigned long *selpoint;
1930
1931 if (y<0) y = 0;
1932 if (y>=rows) y = rows-1;
094ed2a6 1933 if (x<0) {
1934 if (y > 0) {
1935 x = cols-1;
1936 y--;
1937 } else
1938 x = 0;
1939 }
37508af4 1940 if (x>=cols) x = cols-1;
1941
c9def1b8 1942 selpoint = disptop + y * (cols+1);
1943 if ((selpoint[cols]&LATTR_MODE) != LATTR_NORM)
1944 selpoint += x/2;
1945 else
1946 selpoint += x;
374330e2 1947
1948 if (b == MB_SELECT && a == MA_CLICK) {
1949 deselect();
1950 selstate = ABOUT_TO;
1951 selanchor = selpoint;
1952 selmode = SM_CHAR;
1953 } else if (b == MB_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
1954 deselect();
1955 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
1956 selstate = DRAGGING;
1957 selstart = selanchor = selpoint;
1958 selend = selstart + 1;
1959 sel_spread();
1960 } else if ((b == MB_SELECT && a == MA_DRAG) ||
1961 (b == MB_EXTEND && a != MA_RELEASE)) {
1962 if (selstate == ABOUT_TO && selanchor == selpoint)
1963 return;
1964 if (b == MB_EXTEND && a != MA_DRAG && selstate == SELECTED) {
1965 if (selpoint-selstart < (selend-selstart)/2)
1966 selanchor = selend - 1;
1967 else
1968 selanchor = selstart;
1969 selstate = DRAGGING;
1970 }
1971 if (selstate != ABOUT_TO && selstate != DRAGGING)
1972 selanchor = selpoint;
1973 selstate = DRAGGING;
1974 if (selpoint < selanchor) {
1975 selstart = selpoint;
1976 selend = selanchor + 1;
1977 } else {
1978 selstart = selanchor;
1979 selend = selpoint + 1;
1980 }
1981 sel_spread();
1982 } else if ((b == MB_SELECT || b == MB_EXTEND) && a == MA_RELEASE) {
1983 if (selstate == DRAGGING) {
1984 /*
1985 * We've completed a selection. We now transfer the
1986 * data to the clipboard.
1987 */
1988 unsigned char *p = selspace;
1989 unsigned long *q = selstart;
1990
1991 while (q < selend) {
1992 int nl = FALSE;
1993 unsigned long *lineend = q - (q-text) % (cols+1) + cols;
1994 unsigned long *nlpos = lineend;
1995
1996 if (!(*nlpos & ATTR_WRAPPED)) {
1997 while ((nlpos[-1] & CHAR_MASK) == 0x20 && nlpos > q)
1998 nlpos--;
1999 if (nlpos < selend)
2000 nl = TRUE;
2001 }
2002 while (q < nlpos && q < selend)
2003 *p++ = (unsigned char) (*q++ & CHAR_MASK);
2004 if (nl) {
2005 int i;
2006 for (i=0; i<sizeof(sel_nl); i++)
2007 *p++ = sel_nl[i];
2008 }
2009 q = lineend + 1; /* start of next line */
2010 }
f0df44da 2011 write_clip (selspace, p - selspace, FALSE);
374330e2 2012 selstate = SELECTED;
2013 } else
2014 selstate = NO_SELECTION;
2015 } else if (b == MB_PASTE && (a==MA_CLICK || a==MA_2CLK || a==MA_3CLK)) {
2016 char *data;
2017 int len;
2018
2019 get_clip((void **) &data, &len);
2020 if (data) {
2021 char *p, *q;
c9def1b8 2022
2023 if (paste_buffer) sfree(paste_buffer);
2024 paste_pos = paste_hold = paste_len = 0;
2025 paste_buffer = smalloc(len);
2026
374330e2 2027 p = q = data;
2028 while (p < data+len) {
2029 while (p < data+len &&
2030 !(p <= data+len-sizeof(sel_nl) &&
2031 !memcmp(p, sel_nl, sizeof(sel_nl))))
2032 p++;
14963b8f 2033
2034 {
2035 int i;
2036 unsigned char c;
2037 for(i=0;i<p-q;i++)
2038 {
2039 c=xlat_kbd2tty(q[i]);
c9def1b8 2040 paste_buffer[paste_len++] = c;
14963b8f 2041 }
2042 }
2043
374330e2 2044 if (p <= data+len-sizeof(sel_nl) &&
2045 !memcmp(p, sel_nl, sizeof(sel_nl))) {
c9def1b8 2046 paste_buffer[paste_len++] = '\r';
374330e2 2047 p += sizeof(sel_nl);
2048 }
2049 q = p;
2050 }
c9def1b8 2051
2052 /* Assume a small paste will be OK in one go. */
2053 if (paste_len<256) {
2054 ldisc->send (paste_buffer, paste_len);
2055 if (paste_buffer) sfree(paste_buffer);
2056 paste_buffer = 0;
2057 paste_pos = paste_hold = paste_len = 0;
2058 }
374330e2 2059 }
2060 get_clip(NULL, NULL);
2061 }
2062
2063 term_update();
2064}
2065
c9def1b8 2066void term_nopaste() {
2067 if(paste_len == 0) return;
2068 sfree(paste_buffer);
2069 paste_buffer = 0;
2070 paste_len = 0;
2071}
2072
2073void term_paste() {
2074static long last_paste = 0;
2075 long now, paste_diff;
2076
2077 if(paste_len == 0) return;
2078
2079 /* Don't wait forever to paste */
2080 if(paste_hold) {
2081 now = GetTickCount();
2082 paste_diff = now-last_paste;
2083 if (paste_diff>=0 && paste_diff<450)
2084 return;
2085 }
2086 paste_hold = 0;
2087
2088 while(paste_pos<paste_len)
2089 {
2090 char c = paste_buffer[paste_pos++];
2091 ldisc->send (&c, 1);
2092
2093 if (c =='\r') {
2094 paste_hold = 1;
2095 return;
2096 }
2097 }
2098 sfree(paste_buffer);
2099 paste_buffer = 0;
2100 paste_len = 0;
2101}
2102
374330e2 2103static void deselect (void) {
2104 selstate = NO_SELECTION;
2105 selstart = selend = scrtop;
2106}
2107
2108void term_deselect (void) {
2109 deselect();
2110 term_update();
2111}