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