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