Fix flaky scrollbar update when scrollback was reset to bottom.
[sgt/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 VT52_PLUS
13
14 #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
15 #define CL_VT100 0x0002 /* VT100 */
16 #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
17 #define CL_VT102 0x0008 /* VT102 */
18 #define CL_VT220 0x0010 /* VT220 */
19 #define CL_VT320 0x0020 /* VT320 */
20 #define CL_VT420 0x0040 /* VT420 */
21 #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
22 #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
23 #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
24 #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
25 #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
26
27 #define TM_VT100 (CL_ANSIMIN|CL_VT100)
28 #define TM_VT100AVO (TM_VT100|CL_VT100AVO)
29 #define TM_VT102 (TM_VT100AVO|CL_VT102)
30 #define TM_VT220 (TM_VT102|CL_VT220)
31 #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
32 #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
33
34 #define TM_PUTTY (0xFFFF)
35
36 #define compatibility(x) \
37 if ( ((CL_##x)&compatibility_level) == 0 ) { \
38 termstate=TOPLEVEL; \
39 break; \
40 }
41 #define compatibility2(x,y) \
42 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
43 termstate=TOPLEVEL; \
44 break; \
45 }
46
47 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
48
49 static int compatibility_level = TM_PUTTY;
50
51 static tree234 *scrollback; /* lines scrolled off top of screen */
52 static tree234 *screen; /* lines on primary screen */
53 static tree234 *alt_screen; /* lines on alternate screen */
54 static int disptop; /* distance scrolled back (0 or -ve) */
55
56 static unsigned long *cpos; /* cursor position (convenience) */
57
58 static unsigned long *disptext; /* buffer of text on real screen */
59 static unsigned long *dispcurs; /* location of cursor on real screen */
60 static unsigned long curstype; /* type of cursor on real screen */
61
62 #define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
63
64 struct beeptime {
65 struct beeptime *next;
66 long ticks;
67 };
68 static struct beeptime *beephead, *beeptail;
69 int nbeeps;
70 int beep_overloaded;
71 long lastbeep;
72
73 #define TSIZE (sizeof(unsigned long))
74 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
75
76 static unsigned long curr_attr, save_attr;
77 static unsigned long erase_char = ERASE_CHAR;
78
79 typedef struct {
80 int y, x;
81 } pos;
82 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
83 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
84 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
85 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
86 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
87 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
88
89 static pos curs; /* cursor */
90 static pos savecurs; /* saved cursor position */
91 static int marg_t, marg_b; /* scroll margins */
92 static int dec_om; /* DEC origin mode flag */
93 static int wrap, wrapnext; /* wrap flags */
94 static int insert; /* insert-mode flag */
95 static int cset; /* 0 or 1: which char set */
96 static int save_cset, save_csattr; /* saved with cursor position */
97 static int save_utf; /* saved with cursor position */
98 static int rvideo; /* global reverse video flag */
99 static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
100 static int cursor_on; /* cursor enabled flag */
101 static int reset_132; /* Flag ESC c resets to 80 cols */
102 static int use_bce; /* Use Background coloured erase */
103 static int blinker; /* When blinking is the cursor on ? */
104 static int tblinker; /* When the blinking text is on */
105 static int blink_is_real; /* Actually blink blinking text */
106 static int term_echoing; /* Does terminal want local echo? */
107 static int term_editing; /* Does terminal want local edit? */
108 static int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */
109 static int vt52_bold; /* Force bold on non-bold colours */
110 static int utf_state; /* Is there a pending UTF-8 character */
111 static int utf_char; /* and what is it so far. */
112 static int utf_size; /* The size of the UTF character. */
113
114 static int xterm_mouse; /* send mouse messages to app */
115
116 static unsigned long cset_attr[2];
117
118 /*
119 * Saved settings on the alternate screen.
120 */
121 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
122 static int alt_t, alt_b;
123 static int alt_which;
124
125 #define ARGS_MAX 32 /* max # of esc sequence arguments */
126 #define ARG_DEFAULT 0 /* if an arg isn't specified */
127 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
128 static int esc_args[ARGS_MAX];
129 static int esc_nargs;
130 static int esc_query;
131 #define ANSI(x,y) ((x)+((y)<<8))
132 #define ANSI_QUE(x) ANSI(x,TRUE)
133
134 #define OSC_STR_MAX 2048
135 static int osc_strlen;
136 static char osc_string[OSC_STR_MAX + 1];
137 static int osc_w;
138
139 static char id_string[1024] = "\033[?6c";
140
141 static unsigned char *tabs;
142
143 static enum {
144 TOPLEVEL,
145 SEEN_ESC,
146 SEEN_CSI,
147 SEEN_OSC,
148 SEEN_OSC_W,
149
150 DO_CTRLS,
151
152 SEEN_OSC_P,
153 OSC_STRING, OSC_MAYBE_ST,
154 VT52_ESC,
155 VT52_Y1,
156 VT52_Y2,
157 VT52_FG,
158 VT52_BG
159 } termstate;
160
161 static enum {
162 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
163 } selstate;
164 static enum {
165 SM_CHAR, SM_WORD, SM_LINE
166 } selmode;
167 static pos selstart, selend, selanchor;
168
169 static short wordness[256] = {
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
172 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
173 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
174 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
175 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
176 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
177 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
179 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
181 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
182 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
183 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
184 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
185 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
186 };
187
188 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
189 static wchar_t sel_nl[] = SEL_NL;
190 static wchar_t *paste_buffer = 0;
191 static int paste_len, paste_pos, paste_hold;
192
193 /*
194 * Internal prototypes.
195 */
196 static void do_paint(Context, int);
197 static void erase_lots(int, int, int);
198 static void swap_screen(int);
199 static void update_sbar(void);
200 static void deselect(void);
201 /* log session to file stuff ... */
202 static FILE *lgfp = NULL;
203 static void logtraffic(unsigned char c, int logmode);
204 static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm);
205
206 /*
207 * Resize a line to make it `cols' columns wide.
208 */
209 unsigned long *resizeline(unsigned long *line, int cols)
210 {
211 int i, oldlen;
212 unsigned long lineattrs;
213
214 if (line[0] != (unsigned long)cols) {
215 /*
216 * This line is the wrong length, which probably means it
217 * hasn't been accessed since a resize. Resize it now.
218 */
219 oldlen = line[0];
220 lineattrs = line[oldlen + 1];
221 line = srealloc(line, TSIZE * (2 + cols));
222 line[0] = cols;
223 for (i = oldlen; i < cols; i++)
224 line[i + 1] = ERASE_CHAR;
225 line[cols + 1] = lineattrs & LATTR_MODE;
226 }
227
228 return line;
229 }
230
231 /*
232 * Retrieve a line of the screen or of the scrollback, according to
233 * whether the y coordinate is non-negative or negative
234 * (respectively).
235 */
236 unsigned long *lineptr(int y, int lineno)
237 {
238 unsigned long *line, *newline;
239 tree234 *whichtree;
240 int treeindex;
241
242 if (y >= 0) {
243 whichtree = screen;
244 treeindex = y;
245 } else {
246 whichtree = scrollback;
247 treeindex = y + count234(scrollback);
248 }
249 line = index234(whichtree, treeindex);
250
251 /* We assume that we don't screw up and retrieve something out of range. */
252 assert(line != NULL);
253
254 newline = resizeline(line, cols);
255 if (newline != line) {
256 delpos234(whichtree, treeindex);
257 addpos234(whichtree, newline, treeindex);
258 line = newline;
259 }
260
261 return line + 1;
262 }
263
264 #define lineptr(x) lineptr(x,__LINE__)
265 /*
266 * Set up power-on settings for the terminal.
267 */
268 static void power_on(void)
269 {
270 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
271 alt_t = marg_t = 0;
272 if (rows != -1)
273 alt_b = marg_b = rows - 1;
274 else
275 alt_b = marg_b = 0;
276 if (cols != -1) {
277 int i;
278 for (i = 0; i < cols; i++)
279 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
280 }
281 alt_om = dec_om = cfg.dec_om;
282 alt_wnext = wrapnext = alt_ins = insert = FALSE;
283 alt_wrap = wrap = cfg.wrap_mode;
284 alt_cset = cset = 0;
285 alt_utf = utf = 0;
286 alt_sco_acs = sco_acs = 0;
287 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
288 rvideo = 0;
289 in_vbell = FALSE;
290 cursor_on = 1;
291 big_cursor = 0;
292 save_attr = curr_attr = ATTR_DEFAULT;
293 term_editing = term_echoing = FALSE;
294 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
295 app_cursor_keys = cfg.app_cursor;
296 app_keypad_keys = cfg.app_keypad;
297 use_bce = cfg.bce;
298 blink_is_real = cfg.blinktext;
299 erase_char = ERASE_CHAR;
300 alt_which = 0;
301 {
302 int i;
303 for (i = 0; i < 256; i++)
304 wordness[i] = cfg.wordness[i];
305 }
306 if (screen) {
307 swap_screen(1);
308 erase_lots(FALSE, TRUE, TRUE);
309 swap_screen(0);
310 erase_lots(FALSE, TRUE, TRUE);
311 }
312 }
313
314 /*
315 * Force a screen update.
316 */
317 void term_update(void)
318 {
319 Context ctx;
320 ctx = get_ctx();
321 if (ctx) {
322 int need_sbar_update = seen_disp_event;
323 if ((seen_key_event && (cfg.scroll_on_key)) ||
324 (seen_disp_event && (cfg.scroll_on_disp))) {
325 disptop = 0; /* return to main screen */
326 seen_disp_event = seen_key_event = 0;
327 need_sbar_update = TRUE;
328 }
329 if (need_sbar_update)
330 update_sbar();
331 do_paint(ctx, TRUE);
332 sys_cursor(curs.x, curs.y - disptop);
333 free_ctx(ctx);
334 }
335 }
336
337 /*
338 * Same as power_on(), but an external function.
339 */
340 void term_pwron(void)
341 {
342 power_on();
343 fix_cpos;
344 disptop = 0;
345 deselect();
346 term_update();
347 }
348
349 /*
350 * Clear the scrollback.
351 */
352 void term_clrsb(void)
353 {
354 unsigned long *line;
355 disptop = 0;
356 while ((line = delpos234(scrollback, 0)) != NULL) {
357 sfree(line);
358 }
359 update_sbar();
360 }
361
362 /*
363 * Initialise the terminal.
364 */
365 void term_init(void)
366 {
367 screen = alt_screen = scrollback = NULL;
368 disptop = 0;
369 disptext = dispcurs = NULL;
370 tabs = NULL;
371 deselect();
372 rows = cols = -1;
373 power_on();
374 beephead = beeptail = NULL;
375 nbeeps = 0;
376 lastbeep = FALSE;
377 beep_overloaded = FALSE;
378 }
379
380 /*
381 * Set up the terminal for a given size.
382 */
383 void term_size(int newrows, int newcols, int newsavelines)
384 {
385 tree234 *newalt;
386 unsigned long *newdisp, *line;
387 int i, j;
388 int sblen;
389 int save_alt_which = alt_which;
390
391 if (newrows == rows && newcols == cols && newsavelines == savelines)
392 return; /* nothing to do */
393
394 deselect();
395 swap_screen(0);
396
397 alt_t = marg_t = 0;
398 alt_b = marg_b = newrows - 1;
399
400 if (rows == -1) {
401 scrollback = newtree234(NULL);
402 screen = newtree234(NULL);
403 rows = 0;
404 }
405
406 /*
407 * Resize the screen and scrollback. We only need to shift
408 * lines around within our data structures, because lineptr()
409 * will take care of resizing each individual line if
410 * necessary. So:
411 *
412 * - If the new screen and the old screen differ in length, we
413 * must shunt some lines in from the scrollback or out to
414 * the scrollback.
415 *
416 * - If doing that fails to provide us with enough material to
417 * fill the new screen (i.e. the number of rows needed in
418 * the new screen exceeds the total number in the previous
419 * screen+scrollback), we must invent some blank lines to
420 * cover the gap.
421 *
422 * - Then, if the new scrollback length is less than the
423 * amount of scrollback we actually have, we must throw some
424 * away.
425 */
426 sblen = count234(scrollback);
427 /* Do this loop to expand the screen if newrows > rows */
428 for (i = rows; i < newrows; i++) {
429 if (sblen > 0) {
430 line = delpos234(scrollback, --sblen);
431 } else {
432 line = smalloc(TSIZE * (newcols + 2));
433 line[0] = newcols;
434 for (j = 0; j <= newcols; j++)
435 line[j + 1] = ERASE_CHAR;
436 }
437 addpos234(screen, line, 0);
438 }
439 /* Do this loop to shrink the screen if newrows < rows */
440 for (i = newrows; i < rows; i++) {
441 line = delpos234(screen, 0);
442 addpos234(scrollback, line, sblen++);
443 }
444 assert(count234(screen) == newrows);
445 while (sblen > newsavelines) {
446 line = delpos234(scrollback, 0);
447 sfree(line);
448 sblen--;
449 }
450 assert(count234(scrollback) <= newsavelines);
451 disptop = 0;
452
453 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
454 for (i = 0; i < newrows * (newcols + 1); i++)
455 newdisp[i] = ATTR_INVALID;
456 sfree(disptext);
457 disptext = newdisp;
458 dispcurs = NULL;
459
460 newalt = newtree234(NULL);
461 for (i = 0; i < newrows; i++) {
462 line = smalloc(TSIZE * (newcols + 2));
463 line[0] = newcols;
464 for (j = 0; j <= newcols; j++)
465 line[j + 1] = erase_char;
466 addpos234(newalt, line, i);
467 }
468 if (alt_screen) {
469 while (NULL != (line = delpos234(alt_screen, 0)))
470 sfree(line);
471 freetree234(alt_screen);
472 }
473 alt_screen = newalt;
474
475 tabs = srealloc(tabs, newcols * sizeof(*tabs));
476 {
477 int i;
478 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
479 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
480 }
481
482 if (rows > 0)
483 curs.y += newrows - rows;
484 if (curs.y < 0)
485 curs.y = 0;
486 if (curs.y >= newrows)
487 curs.y = newrows - 1;
488 if (curs.x >= newcols)
489 curs.x = newcols - 1;
490 alt_x = alt_y = 0;
491 wrapnext = alt_wnext = FALSE;
492
493 rows = newrows;
494 cols = newcols;
495 savelines = newsavelines;
496 fix_cpos;
497
498 swap_screen(save_alt_which);
499
500 update_sbar();
501 term_update();
502 back->size();
503 }
504
505 /*
506 * Swap screens.
507 */
508 static void swap_screen(int which)
509 {
510 int t;
511 tree234 *ttr;
512
513 if (which == alt_which)
514 return;
515
516 alt_which = which;
517
518 ttr = alt_screen;
519 alt_screen = screen;
520 screen = ttr;
521 t = curs.x;
522 curs.x = alt_x;
523 alt_x = t;
524 t = curs.y;
525 curs.y = alt_y;
526 alt_y = t;
527 t = marg_t;
528 marg_t = alt_t;
529 alt_t = t;
530 t = marg_b;
531 marg_b = alt_b;
532 alt_b = t;
533 t = dec_om;
534 dec_om = alt_om;
535 alt_om = t;
536 t = wrap;
537 wrap = alt_wrap;
538 alt_wrap = t;
539 t = wrapnext;
540 wrapnext = alt_wnext;
541 alt_wnext = t;
542 t = insert;
543 insert = alt_ins;
544 alt_ins = t;
545 t = cset;
546 cset = alt_cset;
547 alt_cset = t;
548 t = utf;
549 utf = alt_utf;
550 alt_utf = t;
551 t = sco_acs;
552 sco_acs = alt_sco_acs;
553 alt_sco_acs = t;
554
555 fix_cpos;
556 }
557
558 /*
559 * Update the scroll bar.
560 */
561 static void update_sbar(void)
562 {
563 int nscroll;
564
565 nscroll = count234(scrollback);
566
567 set_sbar(nscroll + rows, nscroll + disptop, rows);
568 }
569
570 /*
571 * Check whether the region bounded by the two pointers intersects
572 * the scroll region, and de-select the on-screen selection if so.
573 */
574 static void check_selection(pos from, pos to)
575 {
576 if (poslt(from, selend) && poslt(selstart, to))
577 deselect();
578 }
579
580 /*
581 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
582 * for backward.) `sb' is TRUE if the scrolling is permitted to
583 * affect the scrollback buffer.
584 *
585 * NB this function invalidates all pointers into lines of the
586 * screen data structures. In particular, you MUST call fix_cpos
587 * after calling scroll() and before doing anything else that
588 * uses the cpos shortcut pointer.
589 */
590 static void scroll(int topline, int botline, int lines, int sb)
591 {
592 unsigned long *line, *line2;
593 int i, seltop;
594
595 if (topline != 0 || alt_which != 0)
596 sb = FALSE;
597
598 if (lines < 0) {
599 while (lines < 0) {
600 line = delpos234(screen, botline);
601 line = resizeline(line, cols);
602 for (i = 0; i < cols; i++)
603 line[i + 1] = erase_char;
604 line[cols + 1] = 0;
605 addpos234(screen, line, topline);
606
607 if (selstart.y >= topline && selstart.y <= botline) {
608 selstart.y++;
609 if (selstart.y > botline) {
610 selstart.y = botline;
611 selstart.x = 0;
612 }
613 }
614 if (selend.y >= topline && selend.y <= botline) {
615 selend.y++;
616 if (selend.y > botline) {
617 selend.y = botline;
618 selend.x = 0;
619 }
620 }
621
622 lines++;
623 }
624 } else {
625 while (lines > 0) {
626 line = delpos234(screen, topline);
627 if (sb && savelines > 0) {
628 int sblen = count234(scrollback);
629 /*
630 * We must add this line to the scrollback. We'll
631 * remove a line from the top of the scrollback to
632 * replace it, or allocate a new one if the
633 * scrollback isn't full.
634 */
635 if (sblen == savelines) {
636 sblen--, line2 = delpos234(scrollback, 0);
637 } else {
638 line2 = smalloc(TSIZE * (cols + 2));
639 line2[0] = cols;
640 }
641 addpos234(scrollback, line, sblen);
642 line = line2;
643
644 /*
645 * If the user is currently looking at part of the
646 * scrollback, and they haven't enabled any options
647 * that are going to reset the scrollback as a
648 * result of this movement, then the chances are
649 * they'd like to keep looking at the same line. So
650 * we move their viewpoint at the same rate as the
651 * scroll, at least until their viewpoint hits the
652 * top end of the scrollback buffer, at which point
653 * we don't have the choice any more.
654 *
655 * Thanks to Jan Holmen Holsten for the idea and
656 * initial implementation.
657 */
658 if (disptop > -savelines && disptop < 0)
659 disptop--;
660 }
661 line = resizeline(line, cols);
662 for (i = 0; i < cols; i++)
663 line[i + 1] = erase_char;
664 line[cols + 1] = 0;
665 addpos234(screen, line, botline);
666
667 /*
668 * If the selection endpoints move into the scrollback,
669 * we keep them moving until they hit the top. However,
670 * of course, if the line _hasn't_ moved into the
671 * scrollback then we don't do this, and cut them off
672 * at the top of the scroll region.
673 *
674 * This applies to selstart and selend (for an existing
675 * selection), and also selanchor (for one being
676 * selected as we speak).
677 */
678 seltop = sb ? -savelines : 0;
679
680 if (selstart.y >= seltop && selstart.y <= botline) {
681 selstart.y--;
682 if (selstart.y < seltop) {
683 selstart.y = seltop;
684 selstart.x = 0;
685 }
686 }
687 if (selend.y >= seltop && selend.y <= botline) {
688 selend.y--;
689 if (selend.y < seltop) {
690 selend.y = seltop;
691 selend.x = 0;
692 }
693 }
694 if (selanchor.y >= seltop && selanchor.y <= botline) {
695 selanchor.y--;
696 if (selanchor.y < seltop) {
697 selanchor.y = seltop;
698 selanchor.x = 0;
699 }
700 }
701
702 lines--;
703 }
704 }
705 }
706
707 /*
708 * Move the cursor to a given position, clipping at boundaries. We
709 * may or may not want to clip at the scroll margin: marg_clip is 0
710 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
711 * even _being_ outside the margins.
712 */
713 static void move(int x, int y, int marg_clip)
714 {
715 if (x < 0)
716 x = 0;
717 if (x >= cols)
718 x = cols - 1;
719 if (marg_clip) {
720 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
721 y = marg_t;
722 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
723 y = marg_b;
724 }
725 if (y < 0)
726 y = 0;
727 if (y >= rows)
728 y = rows - 1;
729 curs.x = x;
730 curs.y = y;
731 fix_cpos;
732 wrapnext = FALSE;
733 }
734
735 /*
736 * Save or restore the cursor and SGR mode.
737 */
738 static void save_cursor(int save)
739 {
740 if (save) {
741 savecurs = curs;
742 save_attr = curr_attr;
743 save_cset = cset;
744 save_utf = utf;
745 save_csattr = cset_attr[cset];
746 save_sco_acs = sco_acs;
747 } else {
748 curs = savecurs;
749 /* Make sure the window hasn't shrunk since the save */
750 if (curs.x >= cols)
751 curs.x = cols - 1;
752 if (curs.y >= rows)
753 curs.y = rows - 1;
754
755 curr_attr = save_attr;
756 cset = save_cset;
757 utf = save_utf;
758 cset_attr[cset] = save_csattr;
759 sco_acs = save_sco_acs;
760 fix_cpos;
761 if (use_bce)
762 erase_char = (' ' | ATTR_ASCII |
763 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
764 }
765 }
766
767 /*
768 * Erase a large portion of the screen: the whole screen, or the
769 * whole line, or parts thereof.
770 */
771 static void erase_lots(int line_only, int from_begin, int to_end)
772 {
773 pos start, end;
774 int erase_lattr;
775 unsigned long *ldata;
776
777 if (line_only) {
778 start.y = curs.y;
779 start.x = 0;
780 end.y = curs.y + 1;
781 end.x = 0;
782 erase_lattr = FALSE;
783 } else {
784 start.y = 0;
785 start.x = 0;
786 end.y = rows;
787 end.x = 0;
788 erase_lattr = TRUE;
789 }
790 if (!from_begin) {
791 start = curs;
792 }
793 if (!to_end) {
794 end = curs;
795 incpos(end);
796 }
797 check_selection(start, end);
798
799 /* Clear screen also forces a full window redraw, just in case. */
800 if (start.y == 0 && start.x == 0 && end.y == rows)
801 term_invalidate();
802
803 ldata = lineptr(start.y);
804 while (poslt(start, end)) {
805 if (start.x == cols && !erase_lattr)
806 ldata[start.x] &= ~LATTR_WRAPPED;
807 else
808 ldata[start.x] = erase_char;
809 if (incpos(start) && start.y < rows)
810 ldata = lineptr(start.y);
811 }
812 }
813
814 /*
815 * Insert or delete characters within the current line. n is +ve if
816 * insertion is desired, and -ve for deletion.
817 */
818 static void insch(int n)
819 {
820 int dir = (n < 0 ? -1 : +1);
821 int m;
822 pos cursplus;
823 unsigned long *ldata;
824
825 n = (n < 0 ? -n : n);
826 if (n > cols - curs.x)
827 n = cols - curs.x;
828 m = cols - curs.x - n;
829 cursplus.y = curs.y;
830 cursplus.x = curs.x + n;
831 check_selection(curs, cursplus);
832 ldata = lineptr(curs.y);
833 if (dir < 0) {
834 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
835 while (n--)
836 ldata[curs.x + m++] = erase_char;
837 } else {
838 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
839 while (n--)
840 ldata[curs.x + n] = erase_char;
841 }
842 }
843
844 /*
845 * Toggle terminal mode `mode' to state `state'. (`query' indicates
846 * whether the mode is a DEC private one or a normal one.)
847 */
848 static void toggle_mode(int mode, int query, int state)
849 {
850 long ticks;
851
852 if (query)
853 switch (mode) {
854 case 1: /* application cursor keys */
855 app_cursor_keys = state;
856 break;
857 case 2: /* VT52 mode */
858 vt52_mode = !state;
859 if (vt52_mode) {
860 blink_is_real = FALSE;
861 vt52_bold = FALSE;
862 } else {
863 blink_is_real = cfg.blinktext;
864 }
865 break;
866 case 3: /* 80/132 columns */
867 deselect();
868 request_resize(state ? 132 : 80, rows);
869 reset_132 = state;
870 break;
871 case 5: /* reverse video */
872 /*
873 * Toggle reverse video. If we receive an OFF within the
874 * visual bell timeout period after an ON, we trigger an
875 * effective visual bell, so that ESC[?5hESC[?5l will
876 * always be an actually _visible_ visual bell.
877 */
878 ticks = GetTickCount();
879 if (rvideo && !state && /* we're turning it off */
880 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
881 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
882 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
883 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
884 } else if (!rvideo && state) {
885 /* This is an ON, so we notice the time and save it. */
886 rvbell_timeout = ticks + VBELL_TIMEOUT;
887 }
888 rvideo = state;
889 seen_disp_event = TRUE;
890 if (state)
891 term_update();
892 break;
893 case 6: /* DEC origin mode */
894 dec_om = state;
895 break;
896 case 7: /* auto wrap */
897 wrap = state;
898 break;
899 case 8: /* auto key repeat */
900 repeat_off = !state;
901 break;
902 case 10: /* set local edit mode */
903 term_editing = state;
904 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
905 break;
906 case 25: /* enable/disable cursor */
907 compatibility2(OTHER, VT220);
908 cursor_on = state;
909 seen_disp_event = TRUE;
910 break;
911 case 47: /* alternate screen */
912 compatibility(OTHER);
913 deselect();
914 swap_screen(state);
915 disptop = 0;
916 break;
917 case 1000: /* xterm mouse 1 */
918 xterm_mouse = state ? 1 : 0;
919 set_raw_mouse_mode(state);
920 break;
921 case 1002: /* xterm mouse 2 */
922 xterm_mouse = state ? 2 : 0;
923 set_raw_mouse_mode(state);
924 break;
925 } else
926 switch (mode) {
927 case 4: /* set insert mode */
928 compatibility(VT102);
929 insert = state;
930 break;
931 case 12: /* set echo mode */
932 term_echoing = !state;
933 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
934 break;
935 case 20: /* Return sends ... */
936 cr_lf_return = state;
937 break;
938 case 34: /* Make cursor BIG */
939 compatibility2(OTHER, VT220);
940 big_cursor = !state;
941 }
942 }
943
944 /*
945 * Process an OSC sequence: set window title or icon name.
946 */
947 static void do_osc(void)
948 {
949 if (osc_w) {
950 while (osc_strlen--)
951 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
952 } else {
953 osc_string[osc_strlen] = '\0';
954 switch (esc_args[0]) {
955 case 0:
956 case 1:
957 set_icon(osc_string);
958 if (esc_args[0] == 1)
959 break;
960 /* fall through: parameter 0 means set both */
961 case 2:
962 case 21:
963 set_title(osc_string);
964 break;
965 }
966 }
967 }
968
969 /*
970 * Remove everything currently in `inbuf' and stick it up on the
971 * in-memory display. There's a big state machine in here to
972 * process escape sequences...
973 */
974 void term_out(void)
975 {
976 int c, inbuf_reap;
977
978 /*
979 * Optionally log the session traffic to a file. Useful for
980 * debugging and possibly also useful for actual logging.
981 */
982 if (cfg.logtype == LGTYP_DEBUG)
983 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
984 logtraffic((unsigned char) inbuf[inbuf_reap], LGTYP_DEBUG);
985 }
986
987 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
988 c = inbuf[inbuf_reap];
989
990 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
991 * be able to display 8-bit characters, but I'll let that go 'cause
992 * of i18n.
993 */
994
995 /* First see about all those translations. */
996 if (termstate == TOPLEVEL) {
997 if (in_utf)
998 switch (utf_state) {
999 case 0:
1000 if (c < 0x80) {
1001 /* UTF-8 must be stateless so we ignore iso2022. */
1002 if (unitab_ctrl[c] != 0xFF)
1003 c = unitab_ctrl[c];
1004 else c = ((unsigned char)c) | ATTR_ASCII;
1005 break;
1006 } else if ((c & 0xe0) == 0xc0) {
1007 utf_size = utf_state = 1;
1008 utf_char = (c & 0x1f);
1009 } else if ((c & 0xf0) == 0xe0) {
1010 utf_size = utf_state = 2;
1011 utf_char = (c & 0x0f);
1012 } else if ((c & 0xf8) == 0xf0) {
1013 utf_size = utf_state = 3;
1014 utf_char = (c & 0x07);
1015 } else if ((c & 0xfc) == 0xf8) {
1016 utf_size = utf_state = 4;
1017 utf_char = (c & 0x03);
1018 } else if ((c & 0xfe) == 0xfc) {
1019 utf_size = utf_state = 5;
1020 utf_char = (c & 0x01);
1021 } else {
1022 c = UCSERR;
1023 break;
1024 }
1025 continue;
1026 case 1:
1027 case 2:
1028 case 3:
1029 case 4:
1030 case 5:
1031 if ((c & 0xC0) != 0x80) {
1032 inbuf_reap--;
1033 c = UCSERR;
1034 utf_state = 0;
1035 break;
1036 }
1037 utf_char = (utf_char << 6) | (c & 0x3f);
1038 if (--utf_state)
1039 continue;
1040
1041 c = utf_char;
1042
1043 /* Is somebody trying to be evil! */
1044 if (c < 0x80 ||
1045 (c < 0x800 && utf_size >= 2) ||
1046 (c < 0x10000 && utf_size >= 3) ||
1047 (c < 0x200000 && utf_size >= 4) ||
1048 (c < 0x4000000 && utf_size >= 5))
1049 c = UCSERR;
1050
1051 /* Unicode line separator and paragraph separator are CR-LF */
1052 if (c == 0x2028 || c == 0x2029)
1053 c = 0x85;
1054
1055 /* High controls are probably a Baaad idea too. */
1056 if (c < 0xA0)
1057 c = 0xFFFD;
1058
1059 /* The UTF-16 surrogates are not nice either. */
1060 /* The standard give the option of decoding these:
1061 * I don't want to! */
1062 if (c >= 0xD800 && c < 0xE000)
1063 c = UCSERR;
1064
1065 /* ISO 10646 characters now limited to UTF-16 range. */
1066 if (c > 0x10FFFF)
1067 c = UCSERR;
1068
1069 /* This is currently a TagPhobic application.. */
1070 if (c >= 0xE0000 && c <= 0xE007F)
1071 continue;
1072
1073 /* U+FEFF is best seen as a null. */
1074 if (c == 0xFEFF)
1075 continue;
1076 /* But U+FFFE is an error. */
1077 if (c == 0xFFFE || c == 0xFFFF)
1078 c = UCSERR;
1079
1080 /* Oops this is a 16bit implementation */
1081 if (c >= 0x10000)
1082 c = 0xFFFD;
1083 break;
1084 }
1085 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1086 else if(sco_acs &&
1087 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1088 {
1089 if (sco_acs == 2) c ^= 0x80;
1090 c |= ATTR_SCOACS;
1091 } else {
1092 switch (cset_attr[cset]) {
1093 /*
1094 * Linedraw characters are different from 'ESC ( B'
1095 * only for a small range. For ones outside that
1096 * range, make sure we use the same font as well as
1097 * the same encoding.
1098 */
1099 case ATTR_LINEDRW:
1100 if (unitab_ctrl[c] != 0xFF)
1101 c = unitab_ctrl[c];
1102 else
1103 c = ((unsigned char) c) | ATTR_LINEDRW;
1104 break;
1105
1106 case ATTR_GBCHR:
1107 /* If UK-ASCII, make the '#' a LineDraw Pound */
1108 if (c == '#') {
1109 c = '}' | ATTR_LINEDRW;
1110 break;
1111 }
1112 /*FALLTHROUGH*/ case ATTR_ASCII:
1113 if (unitab_ctrl[c] != 0xFF)
1114 c = unitab_ctrl[c];
1115 else
1116 c = ((unsigned char) c) | ATTR_ASCII;
1117 break;
1118 case ATTR_SCOACS:
1119 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1120 break;
1121 }
1122 }
1123 }
1124
1125 /* How about C1 controls ? */
1126 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1127 has_compat(VT220)) {
1128 termstate = SEEN_ESC;
1129 esc_query = FALSE;
1130 c = '@' + (c & 0x1F);
1131 }
1132
1133 /* Or the GL control. */
1134 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1135 if (curs.x && !wrapnext)
1136 curs.x--;
1137 wrapnext = FALSE;
1138 fix_cpos;
1139 *cpos = (' ' | curr_attr | ATTR_ASCII);
1140 } else
1141 /* Or normal C0 controls. */
1142 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1143 switch (c) {
1144 case '\005': /* terminal type query */
1145 /* Strictly speaking this is VT100 but a VT100 defaults to
1146 * no response. Other terminals respond at their option.
1147 *
1148 * Don't put a CR in the default string as this tends to
1149 * upset some weird software.
1150 *
1151 * An xterm returns "xterm" (5 characters)
1152 */
1153 compatibility(ANSIMIN);
1154 {
1155 char abuf[256], *s, *d;
1156 int state = 0;
1157 for (s = cfg.answerback, d = abuf; *s; s++) {
1158 if (state) {
1159 if (*s >= 'a' && *s <= 'z')
1160 *d++ = (*s - ('a' - 1));
1161 else if ((*s >= '@' && *s <= '_') ||
1162 *s == '?' || (*s & 0x80))
1163 *d++ = ('@' ^ *s);
1164 else if (*s == '~')
1165 *d++ = '^';
1166 state = 0;
1167 } else if (*s == '^') {
1168 state = 1;
1169 } else
1170 *d++ = *s;
1171 }
1172 lpage_send(CP_ACP, abuf, d - abuf);
1173 }
1174 break;
1175 case '\007':
1176 {
1177 struct beeptime *newbeep;
1178 long ticks;
1179
1180 ticks = GetTickCount();
1181
1182 if (!beep_overloaded) {
1183 newbeep = smalloc(sizeof(struct beeptime));
1184 newbeep->ticks = ticks;
1185 newbeep->next = NULL;
1186 if (!beephead)
1187 beephead = newbeep;
1188 else
1189 beeptail->next = newbeep;
1190 beeptail = newbeep;
1191 nbeeps++;
1192 }
1193
1194 /*
1195 * Throw out any beeps that happened more than
1196 * t seconds ago.
1197 */
1198 while (beephead &&
1199 beephead->ticks < ticks - cfg.bellovl_t) {
1200 struct beeptime *tmp = beephead;
1201 beephead = tmp->next;
1202 sfree(tmp);
1203 if (!beephead)
1204 beeptail = NULL;
1205 nbeeps--;
1206 }
1207
1208 if (cfg.bellovl && beep_overloaded &&
1209 ticks - lastbeep >= cfg.bellovl_s) {
1210 /*
1211 * If we're currently overloaded and the
1212 * last beep was more than s seconds ago,
1213 * leave overload mode.
1214 */
1215 beep_overloaded = FALSE;
1216 } else if (cfg.bellovl && !beep_overloaded &&
1217 nbeeps >= cfg.bellovl_n) {
1218 /*
1219 * Now, if we have n or more beeps
1220 * remaining in the queue, go into overload
1221 * mode.
1222 */
1223 beep_overloaded = TRUE;
1224 }
1225 lastbeep = ticks;
1226
1227 /*
1228 * Perform an actual beep if we're not overloaded.
1229 */
1230 if (!cfg.bellovl || !beep_overloaded) {
1231 beep(cfg.beep);
1232 if (cfg.beep == BELL_VISUAL) {
1233 in_vbell = TRUE;
1234 vbell_timeout = ticks + VBELL_TIMEOUT;
1235 term_update();
1236 }
1237 }
1238 disptop = 0;
1239 }
1240 break;
1241 case '\b':
1242 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1243 else if (curs.x == 0 && curs.y > 0)
1244 curs.x = cols - 1, curs.y--;
1245 else if (wrapnext)
1246 wrapnext = FALSE;
1247 else
1248 curs.x--;
1249 fix_cpos;
1250 seen_disp_event = TRUE;
1251 break;
1252 case '\016':
1253 compatibility(VT100);
1254 cset = 1;
1255 break;
1256 case '\017':
1257 compatibility(VT100);
1258 cset = 0;
1259 break;
1260 case '\033':
1261 if (vt52_mode)
1262 termstate = VT52_ESC;
1263 else {
1264 compatibility(ANSIMIN);
1265 termstate = SEEN_ESC;
1266 esc_query = FALSE;
1267 }
1268 break;
1269 case '\r':
1270 curs.x = 0;
1271 wrapnext = FALSE;
1272 fix_cpos;
1273 seen_disp_event = TRUE;
1274 paste_hold = 0;
1275 logtraffic((unsigned char) c, LGTYP_ASCII);
1276 break;
1277 case '\014':
1278 if (has_compat(SCOANSI)) {
1279 move(0, 0, 0);
1280 erase_lots(FALSE, FALSE, TRUE);
1281 disptop = 0;
1282 wrapnext = FALSE;
1283 seen_disp_event = 1;
1284 break;
1285 }
1286 case '\013':
1287 compatibility(VT100);
1288 case '\n':
1289 if (curs.y == marg_b)
1290 scroll(marg_t, marg_b, 1, TRUE);
1291 else if (curs.y < rows - 1)
1292 curs.y++;
1293 if (cfg.lfhascr)
1294 curs.x = 0;
1295 fix_cpos;
1296 wrapnext = FALSE;
1297 seen_disp_event = 1;
1298 paste_hold = 0;
1299 logtraffic((unsigned char) c, LGTYP_ASCII);
1300 break;
1301 case '\t':
1302 {
1303 pos old_curs = curs;
1304 unsigned long *ldata = lineptr(curs.y);
1305
1306 do {
1307 curs.x++;
1308 } while (curs.x < cols - 1 && !tabs[curs.x]);
1309
1310 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1311 if (curs.x >= cols / 2)
1312 curs.x = cols / 2 - 1;
1313 } else {
1314 if (curs.x >= cols)
1315 curs.x = cols - 1;
1316 }
1317
1318 fix_cpos;
1319 check_selection(old_curs, curs);
1320 }
1321 seen_disp_event = TRUE;
1322 break;
1323 }
1324 } else
1325 switch (termstate) {
1326 case TOPLEVEL:
1327 /* Only graphic characters get this far, ctrls are stripped above */
1328 if (wrapnext && wrap) {
1329 cpos[1] |= LATTR_WRAPPED;
1330 if (curs.y == marg_b)
1331 scroll(marg_t, marg_b, 1, TRUE);
1332 else if (curs.y < rows - 1)
1333 curs.y++;
1334 curs.x = 0;
1335 fix_cpos;
1336 wrapnext = FALSE;
1337 }
1338 if (insert)
1339 insch(1);
1340 if (selstate != NO_SELECTION) {
1341 pos cursplus = curs;
1342 incpos(cursplus);
1343 check_selection(curs, cursplus);
1344 }
1345 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1346 logtraffic((unsigned char) c, LGTYP_ASCII);
1347 {
1348 extern int wcwidth(wchar_t ucs);
1349 int width = 0;
1350 if (DIRECT_CHAR(c))
1351 width = 1;
1352 if (!width)
1353 width = wcwidth((wchar_t) c);
1354 switch (width) {
1355 case 2:
1356 *cpos++ = c | curr_attr;
1357 if (++curs.x == cols) {
1358 *cpos |= LATTR_WRAPPED;
1359 if (curs.y == marg_b)
1360 scroll(marg_t, marg_b, 1, TRUE);
1361 else if (curs.y < rows - 1)
1362 curs.y++;
1363 curs.x = 0;
1364 fix_cpos;
1365 }
1366 *cpos++ = UCSWIDE | curr_attr;
1367 break;
1368 case 1:
1369 *cpos++ = c | curr_attr;
1370 break;
1371 default:
1372 continue;
1373 }
1374 }
1375 curs.x++;
1376 if (curs.x == cols) {
1377 cpos--;
1378 curs.x--;
1379 wrapnext = TRUE;
1380 if (wrap && vt52_mode) {
1381 cpos[1] |= LATTR_WRAPPED;
1382 if (curs.y == marg_b)
1383 scroll(marg_t, marg_b, 1, TRUE);
1384 else if (curs.y < rows - 1)
1385 curs.y++;
1386 curs.x = 0;
1387 fix_cpos;
1388 wrapnext = FALSE;
1389 }
1390 }
1391 seen_disp_event = 1;
1392 break;
1393
1394 case OSC_MAYBE_ST:
1395 /*
1396 * This state is virtually identical to SEEN_ESC, with the
1397 * exception that we have an OSC sequence in the pipeline,
1398 * and _if_ we see a backslash, we process it.
1399 */
1400 if (c == '\\') {
1401 do_osc();
1402 termstate = TOPLEVEL;
1403 break;
1404 }
1405 /* else fall through */
1406 case SEEN_ESC:
1407 if (c >= ' ' && c <= '/') {
1408 if (esc_query)
1409 esc_query = -1;
1410 else
1411 esc_query = c;
1412 break;
1413 }
1414 termstate = TOPLEVEL;
1415 switch (ANSI(c, esc_query)) {
1416 case '[': /* enter CSI mode */
1417 termstate = SEEN_CSI;
1418 esc_nargs = 1;
1419 esc_args[0] = ARG_DEFAULT;
1420 esc_query = FALSE;
1421 break;
1422 case ']': /* xterm escape sequences */
1423 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1424 compatibility(OTHER);
1425 termstate = SEEN_OSC;
1426 esc_args[0] = 0;
1427 break;
1428 case '7': /* save cursor */
1429 compatibility(VT100);
1430 save_cursor(TRUE);
1431 break;
1432 case '8': /* restore cursor */
1433 compatibility(VT100);
1434 save_cursor(FALSE);
1435 seen_disp_event = TRUE;
1436 break;
1437 case '=':
1438 compatibility(VT100);
1439 app_keypad_keys = TRUE;
1440 break;
1441 case '>':
1442 compatibility(VT100);
1443 app_keypad_keys = FALSE;
1444 break;
1445 case 'D': /* exactly equivalent to LF */
1446 compatibility(VT100);
1447 if (curs.y == marg_b)
1448 scroll(marg_t, marg_b, 1, TRUE);
1449 else if (curs.y < rows - 1)
1450 curs.y++;
1451 fix_cpos;
1452 wrapnext = FALSE;
1453 seen_disp_event = TRUE;
1454 break;
1455 case 'E': /* exactly equivalent to CR-LF */
1456 compatibility(VT100);
1457 curs.x = 0;
1458 if (curs.y == marg_b)
1459 scroll(marg_t, marg_b, 1, TRUE);
1460 else if (curs.y < rows - 1)
1461 curs.y++;
1462 fix_cpos;
1463 wrapnext = FALSE;
1464 seen_disp_event = TRUE;
1465 break;
1466 case 'M': /* reverse index - backwards LF */
1467 compatibility(VT100);
1468 if (curs.y == marg_t)
1469 scroll(marg_t, marg_b, -1, TRUE);
1470 else if (curs.y > 0)
1471 curs.y--;
1472 fix_cpos;
1473 wrapnext = FALSE;
1474 seen_disp_event = TRUE;
1475 break;
1476 case 'Z': /* terminal type query */
1477 compatibility(VT100);
1478 ldisc_send(id_string, strlen(id_string));
1479 break;
1480 case 'c': /* restore power-on settings */
1481 compatibility(VT100);
1482 power_on();
1483 if (reset_132) {
1484 request_resize(80, rows);
1485 reset_132 = 0;
1486 }
1487 fix_cpos;
1488 disptop = 0;
1489 seen_disp_event = TRUE;
1490 break;
1491 case 'H': /* set a tab */
1492 compatibility(VT100);
1493 tabs[curs.x] = TRUE;
1494 break;
1495
1496 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1497 compatibility(VT100);
1498 {
1499 unsigned long *ldata;
1500 int i, j;
1501 pos scrtop, scrbot;
1502
1503 for (i = 0; i < rows; i++) {
1504 ldata = lineptr(i);
1505 for (j = 0; j < cols; j++)
1506 ldata[j] = ATTR_DEFAULT | 'E';
1507 ldata[cols] = 0;
1508 }
1509 disptop = 0;
1510 seen_disp_event = TRUE;
1511 scrtop.x = scrtop.y = 0;
1512 scrbot.x = 0;
1513 scrbot.y = rows;
1514 check_selection(scrtop, scrbot);
1515 }
1516 break;
1517
1518 case ANSI('3', '#'):
1519 case ANSI('4', '#'):
1520 case ANSI('5', '#'):
1521 case ANSI('6', '#'):
1522 compatibility(VT100);
1523 {
1524 unsigned long nlattr;
1525 unsigned long *ldata;
1526 switch (ANSI(c, esc_query)) {
1527 case ANSI('3', '#'):
1528 nlattr = LATTR_TOP;
1529 break;
1530 case ANSI('4', '#'):
1531 nlattr = LATTR_BOT;
1532 break;
1533 case ANSI('5', '#'):
1534 nlattr = LATTR_NORM;
1535 break;
1536 default: /* spiritually case ANSI('6', '#'): */
1537 nlattr = LATTR_WIDE;
1538 break;
1539 }
1540 ldata = lineptr(curs.y);
1541 ldata[cols] &= ~LATTR_MODE;
1542 ldata[cols] |= nlattr;
1543 }
1544 break;
1545
1546 case ANSI('A', '('):
1547 compatibility(VT100);
1548 cset_attr[0] = ATTR_GBCHR;
1549 break;
1550 case ANSI('B', '('):
1551 compatibility(VT100);
1552 cset_attr[0] = ATTR_ASCII;
1553 break;
1554 case ANSI('0', '('):
1555 compatibility(VT100);
1556 cset_attr[0] = ATTR_LINEDRW;
1557 break;
1558 case ANSI('U', '('):
1559 compatibility(OTHER);
1560 cset_attr[0] = ATTR_SCOACS;
1561 break;
1562
1563 case ANSI('A', ')'):
1564 compatibility(VT100);
1565 cset_attr[1] = ATTR_GBCHR;
1566 break;
1567 case ANSI('B', ')'):
1568 compatibility(VT100);
1569 cset_attr[1] = ATTR_ASCII;
1570 break;
1571 case ANSI('0', ')'):
1572 compatibility(VT100);
1573 cset_attr[1] = ATTR_LINEDRW;
1574 break;
1575 case ANSI('U', ')'):
1576 compatibility(OTHER);
1577 cset_attr[1] = ATTR_SCOACS;
1578 break;
1579
1580 case ANSI('8', '%'): /* Old Linux code */
1581 case ANSI('G', '%'):
1582 compatibility(OTHER);
1583 utf = 1;
1584 break;
1585 case ANSI('@', '%'):
1586 compatibility(OTHER);
1587 utf = 0;
1588 break;
1589 }
1590 break;
1591 case SEEN_CSI:
1592 termstate = TOPLEVEL; /* default */
1593 if (isdigit(c)) {
1594 if (esc_nargs <= ARGS_MAX) {
1595 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1596 esc_args[esc_nargs - 1] = 0;
1597 esc_args[esc_nargs - 1] =
1598 10 * esc_args[esc_nargs - 1] + c - '0';
1599 }
1600 termstate = SEEN_CSI;
1601 } else if (c == ';') {
1602 if (++esc_nargs <= ARGS_MAX)
1603 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1604 termstate = SEEN_CSI;
1605 } else if (c < '@') {
1606 if (esc_query)
1607 esc_query = -1;
1608 else if (c == '?')
1609 esc_query = TRUE;
1610 else
1611 esc_query = c;
1612 termstate = SEEN_CSI;
1613 } else
1614 switch (ANSI(c, esc_query)) {
1615 case 'A': /* move up N lines */
1616 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1617 seen_disp_event = TRUE;
1618 break;
1619 case 'e': /* move down N lines */
1620 compatibility(ANSI);
1621 /* FALLTHROUGH */
1622 case 'B':
1623 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1624 seen_disp_event = TRUE;
1625 break;
1626 case ANSI('c', '>'): /* report xterm version */
1627 compatibility(OTHER);
1628 /* this reports xterm version 136 so that VIM can
1629 use the drag messages from the mouse reporting */
1630 ldisc_send("\033[>0;136;0c", 11);
1631 break;
1632 case 'a': /* move right N cols */
1633 compatibility(ANSI);
1634 /* FALLTHROUGH */
1635 case 'C':
1636 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1637 seen_disp_event = TRUE;
1638 break;
1639 case 'D': /* move left N cols */
1640 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1641 seen_disp_event = TRUE;
1642 break;
1643 case 'E': /* move down N lines and CR */
1644 compatibility(ANSI);
1645 move(0, curs.y + def(esc_args[0], 1), 1);
1646 seen_disp_event = TRUE;
1647 break;
1648 case 'F': /* move up N lines and CR */
1649 compatibility(ANSI);
1650 move(0, curs.y - def(esc_args[0], 1), 1);
1651 seen_disp_event = TRUE;
1652 break;
1653 case 'G':
1654 case '`': /* set horizontal posn */
1655 compatibility(ANSI);
1656 move(def(esc_args[0], 1) - 1, curs.y, 0);
1657 seen_disp_event = TRUE;
1658 break;
1659 case 'd': /* set vertical posn */
1660 compatibility(ANSI);
1661 move(curs.x,
1662 (dec_om ? marg_t : 0) + def(esc_args[0],
1663 1) - 1,
1664 (dec_om ? 2 : 0));
1665 seen_disp_event = TRUE;
1666 break;
1667 case 'H':
1668 case 'f': /* set horz and vert posns at once */
1669 if (esc_nargs < 2)
1670 esc_args[1] = ARG_DEFAULT;
1671 move(def(esc_args[1], 1) - 1,
1672 (dec_om ? marg_t : 0) + def(esc_args[0],
1673 1) - 1,
1674 (dec_om ? 2 : 0));
1675 seen_disp_event = TRUE;
1676 break;
1677 case 'J': /* erase screen or parts of it */
1678 {
1679 unsigned int i = def(esc_args[0], 0) + 1;
1680 if (i > 3)
1681 i = 0;
1682 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1683 }
1684 disptop = 0;
1685 seen_disp_event = TRUE;
1686 break;
1687 case 'K': /* erase line or parts of it */
1688 {
1689 unsigned int i = def(esc_args[0], 0) + 1;
1690 if (i > 3)
1691 i = 0;
1692 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1693 }
1694 seen_disp_event = TRUE;
1695 break;
1696 case 'L': /* insert lines */
1697 compatibility(VT102);
1698 if (curs.y <= marg_b)
1699 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1700 FALSE);
1701 fix_cpos;
1702 seen_disp_event = TRUE;
1703 break;
1704 case 'M': /* delete lines */
1705 compatibility(VT102);
1706 if (curs.y <= marg_b)
1707 scroll(curs.y, marg_b, def(esc_args[0], 1),
1708 TRUE);
1709 fix_cpos;
1710 seen_disp_event = TRUE;
1711 break;
1712 case '@': /* insert chars */
1713 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1714 compatibility(VT102);
1715 insch(def(esc_args[0], 1));
1716 seen_disp_event = TRUE;
1717 break;
1718 case 'P': /* delete chars */
1719 compatibility(VT102);
1720 insch(-def(esc_args[0], 1));
1721 seen_disp_event = TRUE;
1722 break;
1723 case 'c': /* terminal type query */
1724 compatibility(VT100);
1725 /* This is the response for a VT102 */
1726 ldisc_send(id_string, strlen(id_string));
1727 break;
1728 case 'n': /* cursor position query */
1729 if (esc_args[0] == 6) {
1730 char buf[32];
1731 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1732 curs.x + 1);
1733 ldisc_send(buf, strlen(buf));
1734 } else if (esc_args[0] == 5) {
1735 ldisc_send("\033[0n", 4);
1736 }
1737 break;
1738 case 'h': /* toggle modes to high */
1739 case ANSI_QUE('h'):
1740 compatibility(VT100);
1741 {
1742 int i;
1743 for (i = 0; i < esc_nargs; i++)
1744 toggle_mode(esc_args[i], esc_query, TRUE);
1745 }
1746 break;
1747 case 'l': /* toggle modes to low */
1748 case ANSI_QUE('l'):
1749 compatibility(VT100);
1750 {
1751 int i;
1752 for (i = 0; i < esc_nargs; i++)
1753 toggle_mode(esc_args[i], esc_query, FALSE);
1754 }
1755 break;
1756 case 'g': /* clear tabs */
1757 compatibility(VT100);
1758 if (esc_nargs == 1) {
1759 if (esc_args[0] == 0) {
1760 tabs[curs.x] = FALSE;
1761 } else if (esc_args[0] == 3) {
1762 int i;
1763 for (i = 0; i < cols; i++)
1764 tabs[i] = FALSE;
1765 }
1766 }
1767 break;
1768 case 'r': /* set scroll margins */
1769 compatibility(VT100);
1770 if (esc_nargs <= 2) {
1771 int top, bot;
1772 top = def(esc_args[0], 1) - 1;
1773 bot = (esc_nargs <= 1
1774 || esc_args[1] ==
1775 0 ? rows : def(esc_args[1], rows)) - 1;
1776 if (bot >= rows)
1777 bot = rows - 1;
1778 /* VTTEST Bug 9 - if region is less than 2 lines
1779 * don't change region.
1780 */
1781 if (bot - top > 0) {
1782 marg_t = top;
1783 marg_b = bot;
1784 curs.x = 0;
1785 /*
1786 * I used to think the cursor should be
1787 * placed at the top of the newly marginned
1788 * area. Apparently not: VMS TPU falls over
1789 * if so.
1790 *
1791 * Well actually it should for Origin mode - RDB
1792 */
1793 curs.y = (dec_om ? marg_t : 0);
1794 fix_cpos;
1795 seen_disp_event = TRUE;
1796 }
1797 }
1798 break;
1799 case 'm': /* set graphics rendition */
1800 {
1801 /*
1802 * A VT100 without the AVO only had one attribute, either
1803 * underline or reverse video depending on the cursor type,
1804 * this was selected by CSI 7m.
1805 *
1806 * case 2:
1807 * This is sometimes DIM, eg on the GIGI and Linux
1808 * case 8:
1809 * This is sometimes INVIS various ANSI.
1810 * case 21:
1811 * This like 22 disables BOLD, DIM and INVIS
1812 *
1813 * The ANSI colours appear on any terminal that has colour
1814 * (obviously) but the interaction between sgr0 and the
1815 * colours varies but is usually related to the background
1816 * colour erase item.
1817 * The interaction between colour attributes and the mono
1818 * ones is also very implementation dependent.
1819 *
1820 * The 39 and 49 attributes are likely to be unimplemented.
1821 */
1822 int i;
1823 for (i = 0; i < esc_nargs; i++) {
1824 switch (def(esc_args[i], 0)) {
1825 case 0: /* restore defaults */
1826 curr_attr = ATTR_DEFAULT;
1827 break;
1828 case 1: /* enable bold */
1829 compatibility(VT100AVO);
1830 curr_attr |= ATTR_BOLD;
1831 break;
1832 case 21: /* (enable double underline) */
1833 compatibility(OTHER);
1834 case 4: /* enable underline */
1835 compatibility(VT100AVO);
1836 curr_attr |= ATTR_UNDER;
1837 break;
1838 case 5: /* enable blink */
1839 compatibility(VT100AVO);
1840 curr_attr |= ATTR_BLINK;
1841 break;
1842 case 7: /* enable reverse video */
1843 curr_attr |= ATTR_REVERSE;
1844 break;
1845 case 10: /* SCO acs off */
1846 compatibility(SCOANSI);
1847 sco_acs = 0; break;
1848 case 11: /* SCO acs on */
1849 compatibility(SCOANSI);
1850 sco_acs = 1; break;
1851 case 12: /* SCO acs on flipped */
1852 compatibility(SCOANSI);
1853 sco_acs = 2; break;
1854 case 22: /* disable bold */
1855 compatibility2(OTHER, VT220);
1856 curr_attr &= ~ATTR_BOLD;
1857 break;
1858 case 24: /* disable underline */
1859 compatibility2(OTHER, VT220);
1860 curr_attr &= ~ATTR_UNDER;
1861 break;
1862 case 25: /* disable blink */
1863 compatibility2(OTHER, VT220);
1864 curr_attr &= ~ATTR_BLINK;
1865 break;
1866 case 27: /* disable reverse video */
1867 compatibility2(OTHER, VT220);
1868 curr_attr &= ~ATTR_REVERSE;
1869 break;
1870 case 30:
1871 case 31:
1872 case 32:
1873 case 33:
1874 case 34:
1875 case 35:
1876 case 36:
1877 case 37:
1878 /* foreground */
1879 curr_attr &= ~ATTR_FGMASK;
1880 curr_attr |=
1881 (esc_args[i] - 30) << ATTR_FGSHIFT;
1882 break;
1883 case 39: /* default-foreground */
1884 curr_attr &= ~ATTR_FGMASK;
1885 curr_attr |= ATTR_DEFFG;
1886 break;
1887 case 40:
1888 case 41:
1889 case 42:
1890 case 43:
1891 case 44:
1892 case 45:
1893 case 46:
1894 case 47:
1895 /* background */
1896 curr_attr &= ~ATTR_BGMASK;
1897 curr_attr |=
1898 (esc_args[i] - 40) << ATTR_BGSHIFT;
1899 break;
1900 case 49: /* default-background */
1901 curr_attr &= ~ATTR_BGMASK;
1902 curr_attr |= ATTR_DEFBG;
1903 break;
1904 }
1905 }
1906 if (use_bce)
1907 erase_char = (' ' | ATTR_ASCII |
1908 (curr_attr &
1909 (ATTR_FGMASK | ATTR_BGMASK)));
1910 }
1911 break;
1912 case 's': /* save cursor */
1913 save_cursor(TRUE);
1914 break;
1915 case 'u': /* restore cursor */
1916 save_cursor(FALSE);
1917 seen_disp_event = TRUE;
1918 break;
1919 case 't': /* set page size - ie window height */
1920 /*
1921 * VT340/VT420 sequence DECSLPP, DEC only allows values
1922 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1923 * illegal values (eg first arg 1..9) for window changing
1924 * and reports.
1925 */
1926 compatibility(VT340TEXT);
1927 if (esc_nargs <= 1
1928 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1929 request_resize(cols, def(esc_args[0], 24));
1930 deselect();
1931 }
1932 break;
1933 case 'S':
1934 compatibility(SCOANSI);
1935 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1936 fix_cpos;
1937 wrapnext = FALSE;
1938 seen_disp_event = TRUE;
1939 break;
1940 case 'T':
1941 compatibility(SCOANSI);
1942 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1943 fix_cpos;
1944 wrapnext = FALSE;
1945 seen_disp_event = TRUE;
1946 break;
1947 case ANSI('|', '*'):
1948 /* VT420 sequence DECSNLS
1949 * Set number of lines on screen
1950 * VT420 uses VGA like hardware and can support any size in
1951 * reasonable range (24..49 AIUI) with no default specified.
1952 */
1953 compatibility(VT420);
1954 if (esc_nargs == 1 && esc_args[0] > 0) {
1955 request_resize(cols, def(esc_args[0], cfg.height));
1956 deselect();
1957 }
1958 break;
1959 case ANSI('|', '$'):
1960 /* VT340/VT420 sequence DECSCPP
1961 * Set number of columns per page
1962 * Docs imply range is only 80 or 132, but I'll allow any.
1963 */
1964 compatibility(VT340TEXT);
1965 if (esc_nargs <= 1) {
1966 request_resize(def(esc_args[0], cfg.width), rows);
1967 deselect();
1968 }
1969 break;
1970 case 'X': /* write N spaces w/o moving cursor */
1971 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1972 compatibility(ANSIMIN);
1973 {
1974 int n = def(esc_args[0], 1);
1975 pos cursplus;
1976 unsigned long *p = cpos;
1977 if (n > cols - curs.x)
1978 n = cols - curs.x;
1979 cursplus = curs;
1980 cursplus.x += n;
1981 check_selection(curs, cursplus);
1982 while (n--)
1983 *p++ = erase_char;
1984 seen_disp_event = TRUE;
1985 }
1986 break;
1987 case 'x': /* report terminal characteristics */
1988 compatibility(VT100);
1989 {
1990 char buf[32];
1991 int i = def(esc_args[0], 0);
1992 if (i == 0 || i == 1) {
1993 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1994 buf[2] += i;
1995 ldisc_send(buf, 20);
1996 }
1997 }
1998 break;
1999 case 'Z': /* BackTab for xterm */
2000 compatibility(OTHER);
2001 {
2002 int i = def(esc_args[0], 1);
2003 pos old_curs = curs;
2004
2005 for(;i>0 && curs.x>0; i--) {
2006 do {
2007 curs.x--;
2008 } while (curs.x >0 && !tabs[curs.x]);
2009 }
2010 fix_cpos;
2011 check_selection(old_curs, curs);
2012 }
2013 break;
2014 case ANSI('L', '='):
2015 compatibility(OTHER);
2016 use_bce = (esc_args[0] <= 0);
2017 erase_char = ERASE_CHAR;
2018 if (use_bce)
2019 erase_char = (' ' | ATTR_ASCII |
2020 (curr_attr &
2021 (ATTR_FGMASK | ATTR_BGMASK)));
2022 break;
2023 case ANSI('E', '='):
2024 compatibility(OTHER);
2025 blink_is_real = (esc_args[0] >= 1);
2026 break;
2027 case ANSI('p', '"'):
2028 /* Allow the host to make this emulator a 'perfect' VT102.
2029 * This first appeared in the VT220, but we do need to get
2030 * back to PuTTY mode so I won't check it.
2031 *
2032 * The arg in 40..42,50 are a PuTTY extension.
2033 * The 2nd arg, 8bit vs 7bit is not checked.
2034 *
2035 * Setting VT102 mode should also change the Fkeys to
2036 * generate PF* codes as a real VT102 has no Fkeys.
2037 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2038 * send nothing.
2039 *
2040 * Note ESC c will NOT change this!
2041 */
2042
2043 switch (esc_args[0]) {
2044 case 61:
2045 compatibility_level &= ~TM_VTXXX;
2046 compatibility_level |= TM_VT102;
2047 break;
2048 case 62:
2049 compatibility_level &= ~TM_VTXXX;
2050 compatibility_level |= TM_VT220;
2051 break;
2052
2053 default:
2054 if (esc_args[0] > 60 && esc_args[0] < 70)
2055 compatibility_level |= TM_VTXXX;
2056 break;
2057
2058 case 40:
2059 compatibility_level &= TM_VTXXX;
2060 break;
2061 case 41:
2062 compatibility_level = TM_PUTTY;
2063 break;
2064 case 42:
2065 compatibility_level = TM_SCOANSI;
2066 break;
2067
2068 case ARG_DEFAULT:
2069 compatibility_level = TM_PUTTY;
2070 break;
2071 case 50:
2072 break;
2073 }
2074
2075 /* Change the response to CSI c */
2076 if (esc_args[0] == 50) {
2077 int i;
2078 char lbuf[64];
2079 strcpy(id_string, "\033[?");
2080 for (i = 1; i < esc_nargs; i++) {
2081 if (i != 1)
2082 strcat(id_string, ";");
2083 sprintf(lbuf, "%d", esc_args[i]);
2084 strcat(id_string, lbuf);
2085 }
2086 strcat(id_string, "c");
2087 }
2088 #if 0
2089 /* Is this a good idea ?
2090 * Well we should do a soft reset at this point ...
2091 */
2092 if (!has_compat(VT420) && has_compat(VT100)) {
2093 if (reset_132)
2094 request_resize(132, 24);
2095 else
2096 request_resize(80, 24);
2097 }
2098 #endif
2099 break;
2100 }
2101 break;
2102 case SEEN_OSC:
2103 osc_w = FALSE;
2104 switch (c) {
2105 case 'P': /* Linux palette sequence */
2106 termstate = SEEN_OSC_P;
2107 osc_strlen = 0;
2108 break;
2109 case 'R': /* Linux palette reset */
2110 palette_reset();
2111 term_invalidate();
2112 termstate = TOPLEVEL;
2113 break;
2114 case 'W': /* word-set */
2115 termstate = SEEN_OSC_W;
2116 osc_w = TRUE;
2117 break;
2118 case '0':
2119 case '1':
2120 case '2':
2121 case '3':
2122 case '4':
2123 case '5':
2124 case '6':
2125 case '7':
2126 case '8':
2127 case '9':
2128 esc_args[0] = 10 * esc_args[0] + c - '0';
2129 break;
2130 case 'L':
2131 /*
2132 * Grotty hack to support xterm and DECterm title
2133 * sequences concurrently.
2134 */
2135 if (esc_args[0] == 2) {
2136 esc_args[0] = 1;
2137 break;
2138 }
2139 /* else fall through */
2140 default:
2141 termstate = OSC_STRING;
2142 osc_strlen = 0;
2143 }
2144 break;
2145 case OSC_STRING:
2146 /*
2147 * This OSC stuff is EVIL. It takes just one character to get into
2148 * sysline mode and it's not initially obvious how to get out.
2149 * So I've added CR and LF as string aborts.
2150 * This shouldn't effect compatibility as I believe embedded
2151 * control characters are supposed to be interpreted (maybe?)
2152 * and they don't display anything useful anyway.
2153 *
2154 * -- RDB
2155 */
2156 if (c == '\n' || c == '\r') {
2157 termstate = TOPLEVEL;
2158 } else if (c == 0234 || c == '\007') {
2159 /*
2160 * These characters terminate the string; ST and BEL
2161 * terminate the sequence and trigger instant
2162 * processing of it, whereas ESC goes back to SEEN_ESC
2163 * mode unless it is followed by \, in which case it is
2164 * synonymous with ST in the first place.
2165 */
2166 do_osc();
2167 termstate = TOPLEVEL;
2168 } else if (c == '\033')
2169 termstate = OSC_MAYBE_ST;
2170 else if (osc_strlen < OSC_STR_MAX)
2171 osc_string[osc_strlen++] = c;
2172 break;
2173 case SEEN_OSC_P:
2174 {
2175 int max = (osc_strlen == 0 ? 21 : 16);
2176 int val;
2177 if (c >= '0' && c <= '9')
2178 val = c - '0';
2179 else if (c >= 'A' && c <= 'A' + max - 10)
2180 val = c - 'A' + 10;
2181 else if (c >= 'a' && c <= 'a' + max - 10)
2182 val = c - 'a' + 10;
2183 else {
2184 termstate = TOPLEVEL;
2185 break;
2186 }
2187 osc_string[osc_strlen++] = val;
2188 if (osc_strlen >= 7) {
2189 palette_set(osc_string[0],
2190 osc_string[1] * 16 + osc_string[2],
2191 osc_string[3] * 16 + osc_string[4],
2192 osc_string[5] * 16 + osc_string[6]);
2193 term_invalidate();
2194 termstate = TOPLEVEL;
2195 }
2196 }
2197 break;
2198 case SEEN_OSC_W:
2199 switch (c) {
2200 case '0':
2201 case '1':
2202 case '2':
2203 case '3':
2204 case '4':
2205 case '5':
2206 case '6':
2207 case '7':
2208 case '8':
2209 case '9':
2210 esc_args[0] = 10 * esc_args[0] + c - '0';
2211 break;
2212 default:
2213 termstate = OSC_STRING;
2214 osc_strlen = 0;
2215 }
2216 break;
2217 case VT52_ESC:
2218 termstate = TOPLEVEL;
2219 seen_disp_event = TRUE;
2220 switch (c) {
2221 case 'A':
2222 move(curs.x, curs.y - 1, 1);
2223 break;
2224 case 'B':
2225 move(curs.x, curs.y + 1, 1);
2226 break;
2227 case 'C':
2228 move(curs.x + 1, curs.y, 1);
2229 break;
2230 case 'D':
2231 move(curs.x - 1, curs.y, 1);
2232 break;
2233 /*
2234 * From the VT100 Manual
2235 * NOTE: The special graphics characters in the VT100
2236 * are different from those in the VT52
2237 *
2238 * From VT102 manual:
2239 * 137 _ Blank - Same
2240 * 140 ` Reserved - Humm.
2241 * 141 a Solid rectangle - Similar
2242 * 142 b 1/ - Top half of fraction for the
2243 * 143 c 3/ - subscript numbers below.
2244 * 144 d 5/
2245 * 145 e 7/
2246 * 146 f Degrees - Same
2247 * 147 g Plus or minus - Same
2248 * 150 h Right arrow
2249 * 151 i Ellipsis (dots)
2250 * 152 j Divide by
2251 * 153 k Down arrow
2252 * 154 l Bar at scan 0
2253 * 155 m Bar at scan 1
2254 * 156 n Bar at scan 2
2255 * 157 o Bar at scan 3 - Similar
2256 * 160 p Bar at scan 4 - Similar
2257 * 161 q Bar at scan 5 - Similar
2258 * 162 r Bar at scan 6 - Same
2259 * 163 s Bar at scan 7 - Similar
2260 * 164 t Subscript 0
2261 * 165 u Subscript 1
2262 * 166 v Subscript 2
2263 * 167 w Subscript 3
2264 * 170 x Subscript 4
2265 * 171 y Subscript 5
2266 * 172 z Subscript 6
2267 * 173 { Subscript 7
2268 * 174 | Subscript 8
2269 * 175 } Subscript 9
2270 * 176 ~ Paragraph
2271 *
2272 */
2273 case 'F':
2274 cset_attr[cset = 0] = ATTR_LINEDRW;
2275 break;
2276 case 'G':
2277 cset_attr[cset = 0] = ATTR_ASCII;
2278 break;
2279 case 'H':
2280 move(0, 0, 0);
2281 break;
2282 case 'I':
2283 if (curs.y == 0)
2284 scroll(0, rows - 1, -1, TRUE);
2285 else if (curs.y > 0)
2286 curs.y--;
2287 fix_cpos;
2288 wrapnext = FALSE;
2289 break;
2290 case 'J':
2291 erase_lots(FALSE, FALSE, TRUE);
2292 disptop = 0;
2293 break;
2294 case 'K':
2295 erase_lots(TRUE, FALSE, TRUE);
2296 break;
2297 #if 0
2298 case 'V':
2299 /* XXX Print cursor line */
2300 break;
2301 case 'W':
2302 /* XXX Start controller mode */
2303 break;
2304 case 'X':
2305 /* XXX Stop controller mode */
2306 break;
2307 #endif
2308 case 'Y':
2309 termstate = VT52_Y1;
2310 break;
2311 case 'Z':
2312 ldisc_send("\033/Z", 3);
2313 break;
2314 case '=':
2315 app_keypad_keys = TRUE;
2316 break;
2317 case '>':
2318 app_keypad_keys = FALSE;
2319 break;
2320 case '<':
2321 /* XXX This should switch to VT100 mode not current or default
2322 * VT mode. But this will only have effect in a VT220+
2323 * emulation.
2324 */
2325 vt52_mode = FALSE;
2326 blink_is_real = cfg.blinktext;
2327 break;
2328 #if 0
2329 case '^':
2330 /* XXX Enter auto print mode */
2331 break;
2332 case '_':
2333 /* XXX Exit auto print mode */
2334 break;
2335 case ']':
2336 /* XXX Print screen */
2337 break;
2338 #endif
2339
2340 #ifdef VT52_PLUS
2341 case 'E':
2342 /* compatibility(ATARI) */
2343 move(0, 0, 0);
2344 erase_lots(FALSE, FALSE, TRUE);
2345 disptop = 0;
2346 break;
2347 case 'L':
2348 /* compatibility(ATARI) */
2349 if (curs.y <= marg_b)
2350 scroll(curs.y, marg_b, -1, FALSE);
2351 break;
2352 case 'M':
2353 /* compatibility(ATARI) */
2354 if (curs.y <= marg_b)
2355 scroll(curs.y, marg_b, 1, TRUE);
2356 break;
2357 case 'b':
2358 /* compatibility(ATARI) */
2359 termstate = VT52_FG;
2360 break;
2361 case 'c':
2362 /* compatibility(ATARI) */
2363 termstate = VT52_BG;
2364 break;
2365 case 'd':
2366 /* compatibility(ATARI) */
2367 erase_lots(FALSE, TRUE, FALSE);
2368 disptop = 0;
2369 break;
2370 case 'e':
2371 /* compatibility(ATARI) */
2372 cursor_on = TRUE;
2373 break;
2374 case 'f':
2375 /* compatibility(ATARI) */
2376 cursor_on = FALSE;
2377 break;
2378 /* case 'j': Save cursor position - broken on ST */
2379 /* case 'k': Restore cursor position */
2380 case 'l':
2381 /* compatibility(ATARI) */
2382 erase_lots(TRUE, TRUE, TRUE);
2383 curs.x = 0;
2384 wrapnext = FALSE;
2385 fix_cpos;
2386 break;
2387 case 'o':
2388 /* compatibility(ATARI) */
2389 erase_lots(TRUE, TRUE, FALSE);
2390 break;
2391 case 'p':
2392 /* compatibility(ATARI) */
2393 curr_attr |= ATTR_REVERSE;
2394 break;
2395 case 'q':
2396 /* compatibility(ATARI) */
2397 curr_attr &= ~ATTR_REVERSE;
2398 break;
2399 case 'v': /* wrap Autowrap on - Wyse style */
2400 /* compatibility(ATARI) */
2401 wrap = 1;
2402 break;
2403 case 'w': /* Autowrap off */
2404 /* compatibility(ATARI) */
2405 wrap = 0;
2406 break;
2407
2408 case 'R':
2409 /* compatibility(OTHER) */
2410 vt52_bold = FALSE;
2411 curr_attr = ATTR_DEFAULT;
2412 if (use_bce)
2413 erase_char = (' ' | ATTR_ASCII |
2414 (curr_attr &
2415 (ATTR_FGMASK | ATTR_BGMASK)));
2416 break;
2417 case 'S':
2418 /* compatibility(VI50) */
2419 curr_attr |= ATTR_UNDER;
2420 break;
2421 case 'W':
2422 /* compatibility(VI50) */
2423 curr_attr &= ~ATTR_UNDER;
2424 break;
2425 case 'U':
2426 /* compatibility(VI50) */
2427 vt52_bold = TRUE;
2428 curr_attr |= ATTR_BOLD;
2429 break;
2430 case 'T':
2431 /* compatibility(VI50) */
2432 vt52_bold = FALSE;
2433 curr_attr &= ~ATTR_BOLD;
2434 break;
2435 #endif
2436 }
2437 break;
2438 case VT52_Y1:
2439 termstate = VT52_Y2;
2440 move(curs.x, c - ' ', 0);
2441 break;
2442 case VT52_Y2:
2443 termstate = TOPLEVEL;
2444 move(c - ' ', curs.y, 0);
2445 break;
2446
2447 #ifdef VT52_PLUS
2448 case VT52_FG:
2449 termstate = TOPLEVEL;
2450 curr_attr &= ~ATTR_FGMASK;
2451 curr_attr &= ~ATTR_BOLD;
2452 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2453 if ((c & 0x8) || vt52_bold)
2454 curr_attr |= ATTR_BOLD;
2455
2456 if (use_bce)
2457 erase_char = (' ' | ATTR_ASCII |
2458 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2459 break;
2460 case VT52_BG:
2461 termstate = TOPLEVEL;
2462 curr_attr &= ~ATTR_BGMASK;
2463 curr_attr &= ~ATTR_BLINK;
2464 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2465
2466 /* Note: bold background */
2467 if (c & 0x8)
2468 curr_attr |= ATTR_BLINK;
2469
2470 if (use_bce)
2471 erase_char = (' ' | ATTR_ASCII |
2472 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2473 break;
2474 #endif
2475 default: break; /* placate gcc warning about enum use */
2476 }
2477 if (selstate != NO_SELECTION) {
2478 pos cursplus = curs;
2479 incpos(cursplus);
2480 check_selection(curs, cursplus);
2481 }
2482 }
2483 inbuf_head = 0;
2484 }
2485
2486 #if 0
2487 /*
2488 * Compare two lines to determine whether they are sufficiently
2489 * alike to scroll-optimise one to the other. Return the degree of
2490 * similarity.
2491 */
2492 static int linecmp(unsigned long *a, unsigned long *b)
2493 {
2494 int i, n;
2495
2496 for (i = n = 0; i < cols; i++)
2497 n += (*a++ == *b++);
2498 return n;
2499 }
2500 #endif
2501
2502 /*
2503 * Given a context, update the window. Out of paranoia, we don't
2504 * allow WM_PAINT responses to do scrolling optimisations.
2505 */
2506 static void do_paint(Context ctx, int may_optimise)
2507 {
2508 int i, j, our_curs_y;
2509 unsigned long rv, cursor;
2510 pos scrpos;
2511 char ch[1024];
2512 long cursor_background = ERASE_CHAR;
2513 long ticks;
2514
2515 /*
2516 * Check the visual bell state.
2517 */
2518 if (in_vbell) {
2519 ticks = GetTickCount();
2520 if (ticks - vbell_timeout >= 0)
2521 in_vbell = FALSE;
2522 }
2523
2524 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2525
2526 /* Depends on:
2527 * screen array, disptop, scrtop,
2528 * selection, rv,
2529 * cfg.blinkpc, blink_is_real, tblinker,
2530 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2531 */
2532
2533 /* Has the cursor position or type changed ? */
2534 if (cursor_on) {
2535 if (has_focus) {
2536 if (blinker || !cfg.blink_cur)
2537 cursor = TATTR_ACTCURS;
2538 else
2539 cursor = 0;
2540 } else
2541 cursor = TATTR_PASCURS;
2542 if (wrapnext)
2543 cursor |= TATTR_RIGHTCURS;
2544 } else
2545 cursor = 0;
2546 our_curs_y = curs.y - disptop;
2547
2548 if (dispcurs && (curstype != cursor ||
2549 dispcurs !=
2550 disptext + our_curs_y * (cols + 1) + curs.x)) {
2551 if (dispcurs > disptext &&
2552 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2553 dispcurs[-1] |= ATTR_INVALID;
2554 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2555 dispcurs[1] |= ATTR_INVALID;
2556 *dispcurs |= ATTR_INVALID;
2557 curstype = 0;
2558 }
2559 dispcurs = NULL;
2560
2561 /* The normal screen data */
2562 for (i = 0; i < rows; i++) {
2563 unsigned long *ldata;
2564 int lattr;
2565 int idx, dirty_line, dirty_run;
2566 unsigned long attr = 0;
2567 int updated_line = 0;
2568 int start = 0;
2569 int ccount = 0;
2570 int last_run_dirty = 0;
2571
2572 scrpos.y = i + disptop;
2573 ldata = lineptr(scrpos.y);
2574 lattr = (ldata[cols] & LATTR_MODE);
2575
2576 idx = i * (cols + 1);
2577 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2578 disptext[idx + cols] = ldata[cols];
2579
2580 for (j = 0; j < cols; j++, idx++) {
2581 unsigned long tattr, tchar;
2582 unsigned long *d = ldata + j;
2583 int break_run;
2584 scrpos.x = j;
2585
2586 tchar = (*d & (CHAR_MASK | CSET_MASK));
2587 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2588 switch (tchar & CSET_MASK) {
2589 case ATTR_ASCII:
2590 tchar = unitab_line[tchar & 0xFF];
2591 break;
2592 case ATTR_LINEDRW:
2593 tchar = unitab_xterm[tchar & 0xFF];
2594 break;
2595 case ATTR_SCOACS:
2596 tchar = unitab_scoacs[tchar&0xFF];
2597 break;
2598 }
2599 tattr |= (tchar & CSET_MASK);
2600 tchar &= CHAR_MASK;
2601 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2602 tattr |= ATTR_WIDE;
2603
2604 /* Video reversing things */
2605 tattr = (tattr ^ rv
2606 ^ (posle(selstart, scrpos) &&
2607 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2608
2609 /* 'Real' blinking ? */
2610 if (blink_is_real && (tattr & ATTR_BLINK)) {
2611 if (has_focus && tblinker) {
2612 tchar = ' ';
2613 tattr &= ~CSET_MASK;
2614 tattr |= ATTR_ACP;
2615 }
2616 tattr &= ~ATTR_BLINK;
2617 }
2618
2619 /*
2620 * Check the font we'll _probably_ be using to see if
2621 * the character is wide when we don't want it to be.
2622 */
2623 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2624 if ((tattr & ATTR_WIDE) == 0 &&
2625 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2626 tattr |= ATTR_NARROW;
2627 } else if (disptext[idx]&ATTR_NARROW)
2628 tattr |= ATTR_NARROW;
2629
2630 /* Cursor here ? Save the 'background' */
2631 if (i == our_curs_y && j == curs.x) {
2632 cursor_background = tattr | tchar;
2633 dispcurs = disptext + idx;
2634 }
2635
2636 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2637 dirty_line = TRUE;
2638
2639 break_run = (tattr != attr || j - start >= sizeof(ch));
2640
2641 /* Special hack for VT100 Linedraw glyphs */
2642 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2643 && tchar <= 0xBD) break_run = TRUE;
2644
2645 if (!dbcs_screenfont && !dirty_line) {
2646 if ((tchar | tattr) == disptext[idx])
2647 break_run = TRUE;
2648 else if (!dirty_run && ccount == 1)
2649 break_run = TRUE;
2650 }
2651
2652 if (break_run) {
2653 if ((dirty_run || last_run_dirty) && ccount > 0) {
2654 do_text(ctx, start, i, ch, ccount, attr, lattr);
2655 updated_line = 1;
2656 }
2657 start = j;
2658 ccount = 0;
2659 attr = tattr;
2660 if (dbcs_screenfont)
2661 last_run_dirty = dirty_run;
2662 dirty_run = dirty_line;
2663 }
2664
2665 if ((tchar | tattr) != disptext[idx])
2666 dirty_run = TRUE;
2667 ch[ccount++] = (char) tchar;
2668 disptext[idx] = tchar | tattr;
2669
2670 /* If it's a wide char step along to the next one. */
2671 if (tattr & ATTR_WIDE) {
2672 if (++j < cols) {
2673 idx++;
2674 d++;
2675 /* Cursor is here ? Ouch! */
2676 if (i == our_curs_y && j == curs.x) {
2677 cursor_background = *d;
2678 dispcurs = disptext + idx;
2679 }
2680 if (disptext[idx] != *d)
2681 dirty_run = TRUE;
2682 disptext[idx] = *d;
2683 }
2684 }
2685 }
2686 if (dirty_run && ccount > 0) {
2687 do_text(ctx, start, i, ch, ccount, attr, lattr);
2688 updated_line = 1;
2689 }
2690
2691 /* Cursor on this line ? (and changed) */
2692 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2693 ch[0] = (char) (cursor_background & CHAR_MASK);
2694 attr = (cursor_background & ATTR_MASK) | cursor;
2695 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2696 curstype = cursor;
2697 }
2698 }
2699 }
2700
2701 /*
2702 * Flick the switch that says if blinking things should be shown or hidden.
2703 */
2704
2705 void term_blink(int flg)
2706 {
2707 static long last_blink = 0;
2708 static long last_tblink = 0;
2709 long now, blink_diff;
2710
2711 now = GetTickCount();
2712 blink_diff = now - last_tblink;
2713
2714 /* Make sure the text blinks no more than 2Hz */
2715 if (blink_diff < 0 || blink_diff > 450) {
2716 last_tblink = now;
2717 tblinker = !tblinker;
2718 }
2719
2720 if (flg) {
2721 blinker = 1;
2722 last_blink = now;
2723 return;
2724 }
2725
2726 blink_diff = now - last_blink;
2727
2728 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2729 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2730 return;
2731
2732 last_blink = now;
2733 blinker = !blinker;
2734 }
2735
2736 /*
2737 * Invalidate the whole screen so it will be repainted in full.
2738 */
2739 void term_invalidate(void)
2740 {
2741 int i;
2742
2743 for (i = 0; i < rows * (cols + 1); i++)
2744 disptext[i] = ATTR_INVALID;
2745 }
2746
2747 /*
2748 * Paint the window in response to a WM_PAINT message.
2749 */
2750 void term_paint(Context ctx, int left, int top, int right, int bottom)
2751 {
2752 int i, j;
2753 if (left < 0) left = 0;
2754 if (top < 0) top = 0;
2755 if (right >= cols) right = cols-1;
2756 if (bottom >= rows) bottom = rows-1;
2757
2758 for (i = top; i <= bottom && i < rows; i++) {
2759 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2760 for (j = left; j <= right && j < cols; j++)
2761 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2762 else
2763 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2764 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2765 }
2766
2767 /* This should happen soon enough, also for some reason it sometimes
2768 * fails to actually do anything when re-sizing ... painting the wrong
2769 * window perhaps ?
2770 */
2771 if (alt_pressed)
2772 do_paint (ctx, FALSE);
2773 }
2774
2775 /*
2776 * Attempt to scroll the scrollback. The second parameter gives the
2777 * position we want to scroll to; the first is +1 to denote that
2778 * this position is relative to the beginning of the scrollback, -1
2779 * to denote it is relative to the end, and 0 to denote that it is
2780 * relative to the current position.
2781 */
2782 void term_scroll(int rel, int where)
2783 {
2784 int sbtop = -count234(scrollback);
2785
2786 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2787 if (disptop < sbtop)
2788 disptop = sbtop;
2789 if (disptop > 0)
2790 disptop = 0;
2791 update_sbar();
2792 term_update();
2793 }
2794
2795 static void clipme(pos top, pos bottom)
2796 {
2797 wchar_t *workbuf;
2798 wchar_t *wbptr; /* where next char goes within workbuf */
2799 int wblen = 0; /* workbuf len */
2800 int buflen; /* amount of memory allocated to workbuf */
2801
2802 buflen = 5120; /* Default size */
2803 workbuf = smalloc(buflen * sizeof(wchar_t));
2804 wbptr = workbuf; /* start filling here */
2805
2806 while (poslt(top, bottom)) {
2807 int nl = FALSE;
2808 unsigned long *ldata = lineptr(top.y);
2809 pos nlpos;
2810
2811 nlpos.y = top.y;
2812 nlpos.x = cols;
2813
2814 if (!(ldata[cols] & LATTR_WRAPPED)) {
2815 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2816 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2817 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2818 && poslt(top, nlpos))
2819 decpos(nlpos);
2820 if (poslt(nlpos, bottom))
2821 nl = TRUE;
2822 }
2823 while (poslt(top, bottom) && poslt(top, nlpos)) {
2824 #if 0
2825 char cbuf[16], *p;
2826 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2827 #else
2828 wchar_t cbuf[16], *p;
2829 int uc = (ldata[top.x] & 0xFFFF);
2830 int set, c;
2831
2832 if (uc == UCSWIDE) {
2833 top.x++;
2834 continue;
2835 }
2836
2837 switch (uc & CSET_MASK) {
2838 case ATTR_LINEDRW:
2839 if (!cfg.rawcnp) {
2840 uc = unitab_xterm[uc & 0xFF];
2841 break;
2842 }
2843 case ATTR_ASCII:
2844 uc = unitab_line[uc & 0xFF];
2845 break;
2846 case ATTR_SCOACS:
2847 uc = unitab_scoacs[uc&0xFF];
2848 break;
2849 }
2850 switch (uc & CSET_MASK) {
2851 case ATTR_ACP:
2852 uc = unitab_font[uc & 0xFF];
2853 break;
2854 case ATTR_OEMCP:
2855 uc = unitab_oemcp[uc & 0xFF];
2856 break;
2857 }
2858
2859 set = (uc & CSET_MASK);
2860 c = (uc & CHAR_MASK);
2861 cbuf[0] = uc;
2862 cbuf[1] = 0;
2863
2864 if (DIRECT_FONT(uc)) {
2865 if (c >= ' ' && c != 0x7F) {
2866 unsigned char buf[4];
2867 WCHAR wbuf[4];
2868 int rv;
2869 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2870 buf[0] = c;
2871 buf[1] = (unsigned char) ldata[top.x + 1];
2872 rv = MultiByteToWideChar(font_codepage,
2873 0, buf, 2, wbuf, 4);
2874 top.x++;
2875 } else {
2876 buf[0] = c;
2877 rv = MultiByteToWideChar(font_codepage,
2878 0, buf, 1, wbuf, 4);
2879 }
2880
2881 if (rv > 0) {
2882 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2883 cbuf[rv] = 0;
2884 }
2885 }
2886 }
2887 #endif
2888
2889 for (p = cbuf; *p; p++) {
2890 /* Enough overhead for trailing NL and nul */
2891 if (wblen >= buflen - 16) {
2892 workbuf =
2893 srealloc(workbuf,
2894 sizeof(wchar_t) * (buflen += 100));
2895 wbptr = workbuf + wblen;
2896 }
2897 wblen++;
2898 *wbptr++ = *p;
2899 }
2900 top.x++;
2901 }
2902 if (nl) {
2903 int i;
2904 for (i = 0; i < sel_nl_sz; i++) {
2905 wblen++;
2906 *wbptr++ = sel_nl[i];
2907 }
2908 }
2909 top.y++;
2910 top.x = 0;
2911 }
2912 wblen++;
2913 *wbptr++ = 0;
2914 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2915 if (buflen > 0) /* indicates we allocated this buffer */
2916 sfree(workbuf);
2917 }
2918
2919 void term_copyall(void)
2920 {
2921 pos top;
2922 top.y = -count234(scrollback);
2923 top.x = 0;
2924 clipme(top, curs);
2925 }
2926
2927 /*
2928 * The wordness array is mainly for deciding the disposition of the US-ASCII
2929 * characters.
2930 */
2931 static int wordtype(int uc)
2932 {
2933 static struct {
2934 int start, end, ctype;
2935 } *wptr, ucs_words[] = {
2936 {
2937 128, 160, 0}, {
2938 161, 191, 1}, {
2939 215, 215, 1}, {
2940 247, 247, 1}, {
2941 0x037e, 0x037e, 1}, /* Greek question mark */
2942 {
2943 0x0387, 0x0387, 1}, /* Greek ano teleia */
2944 {
2945 0x055a, 0x055f, 1}, /* Armenian punctuation */
2946 {
2947 0x0589, 0x0589, 1}, /* Armenian full stop */
2948 {
2949 0x0700, 0x070d, 1}, /* Syriac punctuation */
2950 {
2951 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2952 {
2953 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2954 {
2955 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2956 {
2957 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2958 {
2959 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2960 {
2961 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2962 {
2963 0x2000, 0x200a, 0}, /* Various spaces */
2964 {
2965 0x2070, 0x207f, 2}, /* superscript */
2966 {
2967 0x2080, 0x208f, 2}, /* subscript */
2968 {
2969 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2970 {
2971 0x3000, 0x3000, 0}, /* ideographic space */
2972 {
2973 0x3001, 0x3020, 1}, /* ideographic punctuation */
2974 {
2975 0x303f, 0x309f, 3}, /* Hiragana */
2976 {
2977 0x30a0, 0x30ff, 3}, /* Katakana */
2978 {
2979 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2980 {
2981 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2982 {
2983 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2984 {
2985 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2986 {
2987 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2988 {
2989 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2990 {
2991 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2992 {
2993 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2994 {
2995 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2996 {
2997 0, 0, 0}
2998 };
2999
3000 uc &= (CSET_MASK | CHAR_MASK);
3001
3002 switch (uc & CSET_MASK) {
3003 case ATTR_LINEDRW:
3004 uc = unitab_xterm[uc & 0xFF];
3005 break;
3006 case ATTR_ASCII:
3007 uc = unitab_line[uc & 0xFF];
3008 break;
3009 case ATTR_SCOACS:
3010 uc = unitab_scoacs[uc&0xFF];
3011 break;
3012 }
3013 switch (uc & CSET_MASK) {
3014 case ATTR_ACP:
3015 uc = unitab_font[uc & 0xFF];
3016 break;
3017 case ATTR_OEMCP:
3018 uc = unitab_oemcp[uc & 0xFF];
3019 break;
3020 }
3021
3022 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3023 * fail as there's such a thing as a double width space. :-(
3024 */
3025 if (dbcs_screenfont && font_codepage == line_codepage)
3026 return (uc != ' ');
3027
3028 if (uc < 0x80)
3029 return wordness[uc];
3030
3031 for (wptr = ucs_words; wptr->start; wptr++) {
3032 if (uc >= wptr->start && uc <= wptr->end)
3033 return wptr->ctype;
3034 }
3035
3036 return 2;
3037 }
3038
3039 /*
3040 * Spread the selection outwards according to the selection mode.
3041 */
3042 static pos sel_spread_half(pos p, int dir)
3043 {
3044 unsigned long *ldata;
3045 short wvalue;
3046
3047 ldata = lineptr(p.y);
3048
3049 switch (selmode) {
3050 case SM_CHAR:
3051 /*
3052 * In this mode, every character is a separate unit, except
3053 * for runs of spaces at the end of a non-wrapping line.
3054 */
3055 if (!(ldata[cols] & LATTR_WRAPPED)) {
3056 unsigned long *q = ldata + cols;
3057 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3058 q--;
3059 if (q == ldata + cols)
3060 q--;
3061 if (p.x >= q - ldata)
3062 p.x = (dir == -1 ? q - ldata : cols - 1);
3063 }
3064 break;
3065 case SM_WORD:
3066 /*
3067 * In this mode, the units are maximal runs of characters
3068 * whose `wordness' has the same value.
3069 */
3070 wvalue = wordtype(ldata[p.x]);
3071 if (dir == +1) {
3072 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
3073 p.x++;
3074 } else {
3075 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
3076 p.x--;
3077 }
3078 break;
3079 case SM_LINE:
3080 /*
3081 * In this mode, every line is a unit.
3082 */
3083 p.x = (dir == -1 ? 0 : cols - 1);
3084 break;
3085 }
3086 return p;
3087 }
3088
3089 static void sel_spread(void)
3090 {
3091 selstart = sel_spread_half(selstart, -1);
3092 decpos(selend);
3093 selend = sel_spread_half(selend, +1);
3094 incpos(selend);
3095 }
3096
3097 void term_do_paste(void)
3098 {
3099 wchar_t *data;
3100 int len;
3101
3102 get_clip(&data, &len);
3103 if (data) {
3104 wchar_t *p, *q;
3105
3106 if (paste_buffer)
3107 sfree(paste_buffer);
3108 paste_pos = paste_hold = paste_len = 0;
3109 paste_buffer = smalloc(len * sizeof(wchar_t));
3110
3111 p = q = data;
3112 while (p < data + len) {
3113 while (p < data + len &&
3114 !(p <= data + len - sel_nl_sz &&
3115 !memcmp(p, sel_nl, sizeof(sel_nl))))
3116 p++;
3117
3118 {
3119 int i;
3120 for (i = 0; i < p - q; i++) {
3121 paste_buffer[paste_len++] = q[i];
3122 }
3123 }
3124
3125 if (p <= data + len - sel_nl_sz &&
3126 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3127 paste_buffer[paste_len++] = '\r';
3128 p += sel_nl_sz;
3129 }
3130 q = p;
3131 }
3132
3133 /* Assume a small paste will be OK in one go. */
3134 if (paste_len < 256) {
3135 luni_send(paste_buffer, paste_len);
3136 if (paste_buffer)
3137 sfree(paste_buffer);
3138 paste_buffer = 0;
3139 paste_pos = paste_hold = paste_len = 0;
3140 }
3141 }
3142 get_clip(NULL, NULL);
3143 }
3144
3145 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3146 int shift, int ctrl)
3147 {
3148 pos selpoint;
3149 unsigned long *ldata;
3150 int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
3151
3152 if (y < 0) {
3153 y = 0;
3154 if (a == MA_DRAG && !raw_mouse)
3155 term_scroll(0, -1);
3156 }
3157 if (y >= rows) {
3158 y = rows - 1;
3159 if (a == MA_DRAG && !raw_mouse)
3160 term_scroll(0, +1);
3161 }
3162 if (x < 0) {
3163 if (y > 0) {
3164 x = cols - 1;
3165 y--;
3166 } else
3167 x = 0;
3168 }
3169 if (x >= cols)
3170 x = cols - 1;
3171
3172 selpoint.y = y + disptop;
3173 selpoint.x = x;
3174 ldata = lineptr(selpoint.y);
3175 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3176 selpoint.x /= 2;
3177
3178 if (raw_mouse) {
3179 int encstate = 0, r, c;
3180 char abuf[16];
3181 static int is_down = 0;
3182
3183 switch (b) {
3184 case MBT_LEFT:
3185 encstate = 0x20; /* left button down */
3186 break;
3187 case MBT_MIDDLE:
3188 encstate = 0x21;
3189 break;
3190 case MBT_RIGHT:
3191 encstate = 0x22;
3192 break;
3193 case MBT_WHEEL_UP:
3194 encstate = 0x60;
3195 break;
3196 case MBT_WHEEL_DOWN:
3197 encstate = 0x61;
3198 break;
3199 default: break; /* placate gcc warning about enum use */
3200 }
3201 switch (a) {
3202 case MA_DRAG:
3203 if (xterm_mouse == 1)
3204 return;
3205 encstate += 0x20;
3206 break;
3207 case MA_RELEASE:
3208 encstate = 0x23;
3209 is_down = 0;
3210 break;
3211 case MA_CLICK:
3212 if (is_down == b)
3213 return;
3214 is_down = b;
3215 break;
3216 default: break; /* placate gcc warning about enum use */
3217 }
3218 if (shift)
3219 encstate += 0x04;
3220 if (ctrl)
3221 encstate += 0x10;
3222 r = y + 33;
3223 c = x + 33;
3224
3225 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3226 ldisc_send(abuf, 6);
3227 return;
3228 }
3229
3230 b = translate_button(b);
3231
3232 if (b == MBT_SELECT && a == MA_CLICK) {
3233 deselect();
3234 selstate = ABOUT_TO;
3235 selanchor = selpoint;
3236 selmode = SM_CHAR;
3237 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3238 deselect();
3239 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3240 selstate = DRAGGING;
3241 selstart = selanchor = selpoint;
3242 selend = selstart;
3243 incpos(selend);
3244 sel_spread();
3245 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3246 (b == MBT_EXTEND && a != MA_RELEASE)) {
3247 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3248 return;
3249 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3250 if (posdiff(selpoint, selstart) <
3251 posdiff(selend, selstart) / 2) {
3252 selanchor = selend;
3253 decpos(selanchor);
3254 } else {
3255 selanchor = selstart;
3256 }
3257 selstate = DRAGGING;
3258 }
3259 if (selstate != ABOUT_TO && selstate != DRAGGING)
3260 selanchor = selpoint;
3261 selstate = DRAGGING;
3262 if (poslt(selpoint, selanchor)) {
3263 selstart = selpoint;
3264 selend = selanchor;
3265 incpos(selend);
3266 } else {
3267 selstart = selanchor;
3268 selend = selpoint;
3269 incpos(selend);
3270 }
3271 sel_spread();
3272 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3273 if (selstate == DRAGGING) {
3274 /*
3275 * We've completed a selection. We now transfer the
3276 * data to the clipboard.
3277 */
3278 clipme(selstart, selend);
3279 selstate = SELECTED;
3280 } else
3281 selstate = NO_SELECTION;
3282 } else if (b == MBT_PASTE
3283 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3284 term_do_paste();
3285 }
3286
3287 term_update();
3288 }
3289
3290 void term_nopaste()
3291 {
3292 if (paste_len == 0)
3293 return;
3294 sfree(paste_buffer);
3295 paste_buffer = 0;
3296 paste_len = 0;
3297 }
3298
3299 void term_paste()
3300 {
3301 static long last_paste = 0;
3302 long now, paste_diff;
3303
3304 if (paste_len == 0)
3305 return;
3306
3307 /* Don't wait forever to paste */
3308 if (paste_hold) {
3309 now = GetTickCount();
3310 paste_diff = now - last_paste;
3311 if (paste_diff >= 0 && paste_diff < 450)
3312 return;
3313 }
3314 paste_hold = 0;
3315
3316 while (paste_pos < paste_len) {
3317 int n = 0;
3318 while (n + paste_pos < paste_len) {
3319 if (paste_buffer[paste_pos + n++] == '\r')
3320 break;
3321 }
3322 luni_send(paste_buffer + paste_pos, n);
3323 paste_pos += n;
3324
3325 if (paste_pos < paste_len) {
3326 paste_hold = 1;
3327 return;
3328 }
3329 }
3330 sfree(paste_buffer);
3331 paste_buffer = 0;
3332 paste_len = 0;
3333 }
3334
3335 static void deselect(void)
3336 {
3337 selstate = NO_SELECTION;
3338 selstart.x = selstart.y = selend.x = selend.y = 0;
3339 }
3340
3341 void term_deselect(void)
3342 {
3343 deselect();
3344 term_update();
3345 }
3346
3347 int term_ldisc(int option)
3348 {
3349 if (option == LD_ECHO)
3350 return term_echoing;
3351 if (option == LD_EDIT)
3352 return term_editing;
3353 return FALSE;
3354 }
3355
3356 /*
3357 * from_backend(), to get data from the backend for the terminal.
3358 */
3359 int from_backend(int is_stderr, char *data, int len)
3360 {
3361 while (len--) {
3362 if (inbuf_head >= INBUF_SIZE)
3363 term_out();
3364 inbuf[inbuf_head++] = *data++;
3365 }
3366
3367 /*
3368 * We process all stdout/stderr data immediately we receive it,
3369 * and don't return until it's all gone. Therefore, there's no
3370 * reason at all to return anything other than zero from this
3371 * function.
3372 *
3373 * This is a slightly suboptimal way to deal with SSH2 - in
3374 * principle, the window mechanism would allow us to continue
3375 * to accept data on forwarded ports and X connections even
3376 * while the terminal processing was going slowly - but we
3377 * can't do the 100% right thing without moving the terminal
3378 * processing into a separate thread, and that might hurt
3379 * portability. So we manage stdout buffering the old SSH1 way:
3380 * if the terminal processing goes slowly, the whole SSH
3381 * connection stops accepting data until it's ready.
3382 *
3383 * In practice, I can't imagine this causing serious trouble.
3384 */
3385 return 0;
3386 }
3387
3388 /*
3389 * Log session traffic.
3390 */
3391 void logtraffic(unsigned char c, int logmode)
3392 {
3393 if (cfg.logtype > 0) {
3394 if (cfg.logtype == logmode) {
3395 /* deferred open file from pgm start? */
3396 if (!lgfp)
3397 logfopen();
3398 if (lgfp)
3399 fputc(c, lgfp);
3400 }
3401 }
3402 }
3403
3404 void settimstr(char *ta, int no_sec);
3405 char *subslfcode(char *dest, char *src, char *dstrt);
3406 char *stpncpy(char *dst, const char *src, size_t maxlen);
3407 char timdatbuf[20];
3408 char currlogfilename[FILENAME_MAX];
3409
3410 /* open log file append/overwrite mode */
3411 void logfopen(void)
3412 {
3413 char buf[256];
3414 time_t t;
3415 struct tm tm;
3416 char writemod[4];
3417
3418 if (!cfg.logtype)
3419 return;
3420 sprintf(writemod, "wb"); /* default to rewrite */
3421
3422 time(&t);
3423 tm = *localtime(&t);
3424
3425 /* substitute special codes in file name */
3426 xlatlognam(currlogfilename,cfg.logfilename,cfg.host, &tm);
3427
3428 lgfp = fopen(currlogfilename, "r"); /* file already present? */
3429 if (lgfp) {
3430 int i;
3431 fclose(lgfp);
3432 i = askappend(currlogfilename);
3433 if (i == 1)
3434 writemod[0] = 'a'; /* set append mode */
3435 else if (i == 0) { /* cancelled */
3436 lgfp = NULL;
3437 cfg.logtype = 0; /* disable logging */
3438 return;
3439 }
3440 }
3441
3442 lgfp = fopen(currlogfilename, writemod);
3443 if (lgfp) { /* enter into event log */
3444 sprintf(buf, "%s session log (%s mode) to file : ",
3445 (writemod[0] == 'a') ? "Appending" : "Writing new",
3446 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3447 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3448 /* Make sure we do not exceed the output buffer size */
3449 strncat(buf, currlogfilename, 128);
3450 buf[strlen(buf)] = '\0';
3451 logevent(buf);
3452
3453 /* --- write header line into log file */
3454 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3455 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
3456 fputs(buf, lgfp);
3457 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3458 }
3459 }
3460
3461 void logfclose(void)
3462 {
3463 if (lgfp) {
3464 fclose(lgfp);
3465 lgfp = NULL;
3466 }
3467 }
3468
3469 /*
3470 * translate format codes into time/date strings
3471 * and insert them into log file name
3472 *
3473 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3474 */
3475 static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm) {
3476 char buf[10], *bufp;
3477 int size;
3478 char *ds = d; /* save start pos. */
3479 int len = FILENAME_MAX-1;
3480
3481 while (*s) {
3482 /* Let (bufp, len) be the string to append. */
3483 bufp = buf; /* don't usually override this */
3484 if (*s == '&') {
3485 char c;
3486 s++;
3487 if (*s) switch (c = *s++, tolower(c)) {
3488 case 'y':
3489 size = strftime(buf, sizeof(buf), "%Y", tm);
3490 break;
3491 case 'm':
3492 size = strftime(buf, sizeof(buf), "%m", tm);
3493 break;
3494 case 'd':
3495 size = strftime(buf, sizeof(buf), "%d", tm);
3496 break;
3497 case 't':
3498 size = strftime(buf, sizeof(buf), "%H%M%S", tm);
3499 break;
3500 case 'h':
3501 bufp = hostname;
3502 size = strlen(bufp);
3503 break;
3504 default:
3505 buf[0] = '&';
3506 size = 1;
3507 if (c != '&')
3508 buf[size++] = c;
3509 }
3510 } else {
3511 buf[0] = *s++;
3512 size = 1;
3513 }
3514 if (size > len)
3515 size = len;
3516 memcpy(d, bufp, size);
3517 d += size;
3518 len -= size;
3519 }
3520 *d = '\0';
3521 }