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