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