Placate gcc's `-Wall' warnings.
[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 )
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 */
32874aea 1145 if ((!cfg.bellovl || !beep_overloaded)
1146 && cfg.beep != 0) {
156686ef 1147 if (cfg.beep != 2)
1148 beep(cfg.beep);
32874aea 1149 else if (cfg.beep == 2) {
156686ef 1150 in_vbell = TRUE;
1151 vbell_timeout = ticks + VBELL_TIMEOUT;
1152 term_update();
1153 }
1154 }
4facdf84 1155 disptop = 0;
156686ef 1156 }
374330e2 1157 break;
1158 case '\b':
967a161e 1159 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
4facdf84 1160 else if (curs.x == 0 && curs.y > 0)
32874aea 1161 curs.x = cols - 1, curs.y--;
374330e2 1162 else if (wrapnext)
1163 wrapnext = FALSE;
1164 else
4facdf84 1165 curs.x--;
374330e2 1166 fix_cpos;
cabfd08c 1167 seen_disp_event = TRUE;
374330e2 1168 break;
1169 case '\016':
32874aea 1170 compatibility(VT100);
374330e2 1171 cset = 1;
1172 break;
1173 case '\017':
32874aea 1174 compatibility(VT100);
374330e2 1175 cset = 0;
1176 break;
1177 case '\033':
32874aea 1178 if (vt52_mode)
1179 termstate = VT52_ESC;
e14a5a13 1180 else {
1181 compatibility(ANSIMIN);
1182 termstate = SEEN_ESC;
4eeb7d09 1183 esc_query = FALSE;
e14a5a13 1184 }
374330e2 1185 break;
374330e2 1186 case '\r':
4facdf84 1187 curs.x = 0;
374330e2 1188 wrapnext = FALSE;
1189 fix_cpos;
cabfd08c 1190 seen_disp_event = TRUE;
c9def1b8 1191 paste_hold = 0;
32874aea 1192 logtraffic((unsigned char) c, LGTYP_ASCII);
374330e2 1193 break;
374330e2 1194 case '\014':
1a837633 1195 if (has_compat(SCOANSI)) {
1196 move(0, 0, 0);
1197 erase_lots(FALSE, FALSE, TRUE);
1198 disptop = 0;
1199 wrapnext = FALSE;
1200 seen_disp_event = 1;
1201 break;
1202 }
1203 case '\013':
32874aea 1204 compatibility(VT100);
374330e2 1205 case '\n':
4facdf84 1206 if (curs.y == marg_b)
32874aea 1207 scroll(marg_t, marg_b, 1, TRUE);
1208 else if (curs.y < rows - 1)
4facdf84 1209 curs.y++;
cabfd08c 1210 if (cfg.lfhascr)
4facdf84 1211 curs.x = 0;
374330e2 1212 fix_cpos;
1213 wrapnext = FALSE;
cabfd08c 1214 seen_disp_event = 1;
c9def1b8 1215 paste_hold = 0;
32874aea 1216 logtraffic((unsigned char) c, LGTYP_ASCII);
374330e2 1217 break;
1218 case '\t':
374330e2 1219 {
4facdf84 1220 pos old_curs = curs;
1221 unsigned long *ldata = lineptr(curs.y);
c9def1b8 1222
1223 do {
4facdf84 1224 curs.x++;
32874aea 1225 } while (curs.x < cols - 1 && !tabs[curs.x]);
c9def1b8 1226
32874aea 1227 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1228 if (curs.x >= cols / 2)
1229 curs.x = cols / 2 - 1;
1230 } else {
4facdf84 1231 if (curs.x >= cols)
32874aea 1232 curs.x = cols - 1;
c9def1b8 1233 }
1234
374330e2 1235 fix_cpos;
32874aea 1236 check_selection(old_curs, curs);
374330e2 1237 }
cabfd08c 1238 seen_disp_event = TRUE;
374330e2 1239 break;
cabfd08c 1240 }
32874aea 1241 } else
1242 switch (termstate) {
1243 case TOPLEVEL:
1244 /* Only graphic characters get this far, ctrls are stripped above */
1245 if (wrapnext && wrap) {
4eeb7d09 1246 cpos[1] |= LATTR_WRAPPED;
32874aea 1247 if (curs.y == marg_b)
1248 scroll(marg_t, marg_b, 1, TRUE);
1249 else if (curs.y < rows - 1)
1250 curs.y++;
1251 curs.x = 0;
1252 fix_cpos;
1253 wrapnext = FALSE;
374330e2 1254 }
32874aea 1255 if (insert)
1256 insch(1);
1257 if (selstate != NO_SELECTION) {
1258 pos cursplus = curs;
1259 incpos(cursplus);
1260 check_selection(curs, cursplus);
374330e2 1261 }
4eeb7d09 1262 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1263 logtraffic((unsigned char) c, LGTYP_ASCII);
1264 {
1265 extern int wcwidth(wchar_t ucs);
1266 int width = 0;
1267 if (DIRECT_CHAR(c))
1268 width = 1;
1269 if (!width)
1270 width = wcwidth((wchar_t) c);
1271 switch (width) {
1272 case 2:
1273 if (curs.x + 1 != cols) {
1274 *cpos++ = c | ATTR_WIDE | curr_attr;
1275 *cpos++ = UCSWIDE | curr_attr;
1276 curs.x++;
1277 break;
1278 }
1279 case 1:
1280 *cpos++ = c | curr_attr;
32874aea 1281 break;
4eeb7d09 1282 default:
1283 continue;
32874aea 1284 }
374330e2 1285 }
32874aea 1286 curs.x++;
1287 if (curs.x == cols) {
1288 cpos--;
1289 curs.x--;
1290 wrapnext = TRUE;
4eeb7d09 1291 if (wrap && vt52_mode) {
1292 cpos[1] |= LATTR_WRAPPED;
1293 if (curs.y == marg_b)
1294 scroll(marg_t, marg_b, 1, TRUE);
1295 else if (curs.y < rows - 1)
1296 curs.y++;
1297 curs.x = 0;
1298 fix_cpos;
1299 wrapnext = FALSE;
1300 }
e14a5a13 1301 }
32874aea 1302 seen_disp_event = 1;
374330e2 1303 break;
32874aea 1304
32874aea 1305 case OSC_MAYBE_ST:
1306 /*
1307 * This state is virtually identical to SEEN_ESC, with the
1308 * exception that we have an OSC sequence in the pipeline,
1309 * and _if_ we see a backslash, we process it.
1310 */
1311 if (c == '\\') {
1312 do_osc();
1313 termstate = TOPLEVEL;
1314 break;
e14a5a13 1315 }
32874aea 1316 /* else fall through */
1317 case SEEN_ESC:
4eeb7d09 1318 if (c >= ' ' && c <= '/') {
1319 if (esc_query)
1320 esc_query = -1;
1321 else
1322 esc_query = c;
32874aea 1323 break;
4eeb7d09 1324 }
1325 termstate = TOPLEVEL;
1326 switch (ANSI(c, esc_query)) {
32874aea 1327 case '[': /* enter CSI mode */
1328 termstate = SEEN_CSI;
1329 esc_nargs = 1;
1330 esc_args[0] = ARG_DEFAULT;
1331 esc_query = FALSE;
1332 break;
1333 case ']': /* xterm escape sequences */
1334 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1335 compatibility(OTHER);
1336 termstate = SEEN_OSC;
1337 esc_args[0] = 0;
1338 break;
32874aea 1339 case '7': /* save cursor */
1340 compatibility(VT100);
1341 save_cursor(TRUE);
1342 break;
1343 case '8': /* restore cursor */
1344 compatibility(VT100);
1345 save_cursor(FALSE);
1346 seen_disp_event = TRUE;
1347 break;
1348 case '=':
1349 compatibility(VT100);
1350 app_keypad_keys = TRUE;
1351 break;
1352 case '>':
1353 compatibility(VT100);
1354 app_keypad_keys = FALSE;
1355 break;
1356 case 'D': /* exactly equivalent to LF */
1357 compatibility(VT100);
1358 if (curs.y == marg_b)
1359 scroll(marg_t, marg_b, 1, TRUE);
1360 else if (curs.y < rows - 1)
1361 curs.y++;
1362 fix_cpos;
1363 wrapnext = FALSE;
1364 seen_disp_event = TRUE;
1365 break;
1366 case 'E': /* exactly equivalent to CR-LF */
1367 compatibility(VT100);
1368 curs.x = 0;
1369 if (curs.y == marg_b)
1370 scroll(marg_t, marg_b, 1, TRUE);
1371 else if (curs.y < rows - 1)
1372 curs.y++;
1373 fix_cpos;
1374 wrapnext = FALSE;
1375 seen_disp_event = TRUE;
1376 break;
1377 case 'M': /* reverse index - backwards LF */
1378 compatibility(VT100);
1379 if (curs.y == marg_t)
1380 scroll(marg_t, marg_b, -1, TRUE);
1381 else if (curs.y > 0)
1382 curs.y--;
1383 fix_cpos;
1384 wrapnext = FALSE;
1385 seen_disp_event = TRUE;
1386 break;
1387 case 'Z': /* terminal type query */
1388 compatibility(VT100);
1389 ldisc_send(id_string, strlen(id_string));
1390 break;
1391 case 'c': /* restore power-on settings */
1392 compatibility(VT100);
1393 power_on();
1394 if (reset_132) {
1395 request_resize(80, rows, 1);
1396 reset_132 = 0;
374330e2 1397 }
32874aea 1398 fix_cpos;
1399 disptop = 0;
1400 seen_disp_event = TRUE;
1401 break;
32874aea 1402 case 'H': /* set a tab */
1403 compatibility(VT100);
1404 tabs[curs.x] = TRUE;
1405 break;
4eeb7d09 1406
1407 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1408 compatibility(VT100);
1409 {
1410 unsigned long *ldata;
1411 int i, j;
1412 pos scrtop, scrbot;
1413
1414 for (i = 0; i < rows; i++) {
1415 ldata = lineptr(i);
1416 for (j = 0; j < cols; j++)
1417 ldata[j] = ATTR_DEFAULT | 'E';
1418 ldata[cols] = 0;
1419 }
1420 disptop = 0;
1421 seen_disp_event = TRUE;
1422 scrtop.x = scrtop.y = 0;
1423 scrbot.x = 0;
1424 scrbot.y = rows;
1425 check_selection(scrtop, scrbot);
1426 }
1427 break;
1428
1429 case ANSI('3', '#'):
1430 case ANSI('4', '#'):
1431 case ANSI('5', '#'):
1432 case ANSI('6', '#'):
1433 compatibility(VT100);
1434 {
1435 unsigned long nlattr;
1436 unsigned long *ldata;
1437 switch (ANSI(c, esc_query)) {
1438 case ANSI('3', '#'):
1439 nlattr = LATTR_TOP;
1440 break;
1441 case ANSI('4', '#'):
1442 nlattr = LATTR_BOT;
1443 break;
1444 case ANSI('5', '#'):
1445 nlattr = LATTR_NORM;
1446 break;
2d466ffd 1447 default: /* spiritually case ANSI('6', '#'): */
4eeb7d09 1448 nlattr = LATTR_WIDE;
1449 break;
1450 }
1451 ldata = lineptr(curs.y);
1452 ldata[cols] &= ~LATTR_MODE;
1453 ldata[cols] |= nlattr;
1454 }
1455 break;
1456
1457 case ANSI('A', '('):
1458 compatibility(VT100);
1459 cset_attr[0] = ATTR_GBCHR;
1460 break;
1461 case ANSI('B', '('):
1462 compatibility(VT100);
1463 cset_attr[0] = ATTR_ASCII;
1464 break;
1465 case ANSI('0', '('):
1466 compatibility(VT100);
1467 cset_attr[0] = ATTR_LINEDRW;
1468 break;
1469
1470 case ANSI('A', ')'):
1471 compatibility(VT100);
1472 cset_attr[1] = ATTR_GBCHR;
1473 break;
1474 case ANSI('B', ')'):
1475 compatibility(VT100);
1476 cset_attr[1] = ATTR_ASCII;
1477 break;
1478 case ANSI('0', ')'):
1479 compatibility(VT100);
1480 cset_attr[1] = ATTR_LINEDRW;
1481 break;
1482
1483 case ANSI('8', '%'): /* Old Linux code */
1484 case ANSI('G', '%'):
1485 compatibility(OTHER);
1486 utf = 1;
1487 break;
1488 case ANSI('@', '%'):
1489 compatibility(OTHER);
1490 if (line_codepage != CP_UTF8)
1491 utf = 0;
1492 break;
374330e2 1493 }
1494 break;
32874aea 1495 case SEEN_CSI:
1496 termstate = TOPLEVEL; /* default */
1497 if (isdigit(c)) {
1498 if (esc_nargs <= ARGS_MAX) {
1499 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1500 esc_args[esc_nargs - 1] = 0;
1501 esc_args[esc_nargs - 1] =
1502 10 * esc_args[esc_nargs - 1] + c - '0';
1503 }
1504 termstate = SEEN_CSI;
1505 } else if (c == ';') {
1506 if (++esc_nargs <= ARGS_MAX)
1507 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1508 termstate = SEEN_CSI;
1509 } else if (c < '@') {
1510 if (esc_query)
1511 esc_query = -1;
1512 else if (c == '?')
1513 esc_query = TRUE;
1514 else
1515 esc_query = c;
1516 termstate = SEEN_CSI;
1517 } else
1518 switch (ANSI(c, esc_query)) {
1519 case 'A': /* move up N lines */
1520 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1521 seen_disp_event = TRUE;
1522 break;
1523 case 'e': /* move down N lines */
1524 compatibility(ANSI);
1525 case 'B':
1526 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1527 seen_disp_event = TRUE;
1528 break;
1529 case 'a': /* move right N cols */
1530 compatibility(ANSI);
1531 case ANSI('c', '>'): /* report xterm version */
1532 compatibility(OTHER);
1533 /* this reports xterm version 136 so that VIM can
1534 use the drag messages from the mouse reporting */
1535 ldisc_send("\033[>0;136;0c", 11);
1536 break;
1537 case 'C':
1538 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1539 seen_disp_event = TRUE;
1540 break;
1541 case 'D': /* move left N cols */
1542 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1543 seen_disp_event = TRUE;
1544 break;
1545 case 'E': /* move down N lines and CR */
1546 compatibility(ANSI);
1547 move(0, curs.y + def(esc_args[0], 1), 1);
1548 seen_disp_event = TRUE;
1549 break;
1550 case 'F': /* move up N lines and CR */
1551 compatibility(ANSI);
1552 move(0, curs.y - def(esc_args[0], 1), 1);
1553 seen_disp_event = TRUE;
1554 break;
1555 case 'G':
1556 case '`': /* set horizontal posn */
1557 compatibility(ANSI);
1558 move(def(esc_args[0], 1) - 1, curs.y, 0);
1559 seen_disp_event = TRUE;
1560 break;
1561 case 'd': /* set vertical posn */
1562 compatibility(ANSI);
1563 move(curs.x,
1564 (dec_om ? marg_t : 0) + def(esc_args[0],
1565 1) - 1,
1566 (dec_om ? 2 : 0));
1567 seen_disp_event = TRUE;
1568 break;
1569 case 'H':
1570 case 'f': /* set horz and vert posns at once */
1571 if (esc_nargs < 2)
1572 esc_args[1] = ARG_DEFAULT;
1573 move(def(esc_args[1], 1) - 1,
1574 (dec_om ? marg_t : 0) + def(esc_args[0],
1575 1) - 1,
1576 (dec_om ? 2 : 0));
1577 seen_disp_event = TRUE;
1578 break;
1579 case 'J': /* erase screen or parts of it */
1580 {
1581 unsigned int i = def(esc_args[0], 0) + 1;
1582 if (i > 3)
1583 i = 0;
1584 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1585 }
1586 disptop = 0;
1587 seen_disp_event = TRUE;
1588 break;
1589 case 'K': /* erase line or parts of it */
1590 {
1591 unsigned int i = def(esc_args[0], 0) + 1;
1592 if (i > 3)
1593 i = 0;
1594 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1595 }
1596 seen_disp_event = TRUE;
1597 break;
1598 case 'L': /* insert lines */
1599 compatibility(VT102);
1600 if (curs.y <= marg_b)
1601 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1602 FALSE);
1603 fix_cpos;
1604 seen_disp_event = TRUE;
1605 break;
1606 case 'M': /* delete lines */
1607 compatibility(VT102);
1608 if (curs.y <= marg_b)
1609 scroll(curs.y, marg_b, def(esc_args[0], 1),
1610 TRUE);
1611 fix_cpos;
1612 seen_disp_event = TRUE;
1613 break;
1614 case '@': /* insert chars */
1615 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1616 compatibility(VT102);
1617 insch(def(esc_args[0], 1));
1618 seen_disp_event = TRUE;
1619 break;
1620 case 'P': /* delete chars */
1621 compatibility(VT102);
1622 insch(-def(esc_args[0], 1));
1623 seen_disp_event = TRUE;
1624 break;
1625 case 'c': /* terminal type query */
1626 compatibility(VT100);
1627 /* This is the response for a VT102 */
1628 ldisc_send(id_string, strlen(id_string));
1629 break;
1630 case 'n': /* cursor position query */
1631 if (esc_args[0] == 6) {
1632 char buf[32];
1633 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1634 curs.x + 1);
1635 ldisc_send(buf, strlen(buf));
1636 } else if (esc_args[0] == 5) {
1637 ldisc_send("\033[0n", 4);
1638 }
1639 break;
1640 case 'h': /* toggle modes to high */
1641 case ANSI_QUE('h'):
1642 compatibility(VT100);
1643 {
1644 int i;
1645 for (i = 0; i < esc_nargs; i++)
1646 toggle_mode(esc_args[i], esc_query, TRUE);
1647 }
1648 break;
1649 case 'l': /* toggle modes to low */
1650 case ANSI_QUE('l'):
1651 compatibility(VT100);
1652 {
1653 int i;
1654 for (i = 0; i < esc_nargs; i++)
1655 toggle_mode(esc_args[i], esc_query, FALSE);
1656 }
1657 break;
1658 case 'g': /* clear tabs */
1659 compatibility(VT100);
1660 if (esc_nargs == 1) {
1661 if (esc_args[0] == 0) {
1662 tabs[curs.x] = FALSE;
1663 } else if (esc_args[0] == 3) {
1664 int i;
1665 for (i = 0; i < cols; i++)
1666 tabs[i] = FALSE;
1667 }
1668 }
1669 break;
1670 case 'r': /* set scroll margins */
1671 compatibility(VT100);
1672 if (esc_nargs <= 2) {
1673 int top, bot;
1674 top = def(esc_args[0], 1) - 1;
1675 bot = (esc_nargs <= 1
1676 || esc_args[1] ==
1677 0 ? rows : def(esc_args[1], rows)) - 1;
1678 if (bot >= rows)
1679 bot = rows - 1;
1680 /* VTTEST Bug 9 - if region is less than 2 lines
1681 * don't change region.
1682 */
1683 if (bot - top > 0) {
1684 marg_t = top;
1685 marg_b = bot;
1686 curs.x = 0;
1687 /*
1688 * I used to think the cursor should be
1689 * placed at the top of the newly marginned
1690 * area. Apparently not: VMS TPU falls over
1691 * if so.
1692 *
1693 * Well actually it should for Origin mode - RDB
1694 */
1695 curs.y = (dec_om ? marg_t : 0);
1696 fix_cpos;
1697 seen_disp_event = TRUE;
1698 }
1699 }
1700 break;
1701 case 'm': /* set graphics rendition */
1702 {
1703 /*
1704 * A VT100 without the AVO only had one attribute, either
1705 * underline or reverse video depending on the cursor type,
1706 * this was selected by CSI 7m.
1707 *
1708 * case 2:
4eeb7d09 1709 * This is sometimes DIM, eg on the GIGI and Linux
32874aea 1710 * case 8:
4eeb7d09 1711 * This is sometimes INVIS various ANSI.
32874aea 1712 * case 21:
1713 * This like 22 disables BOLD, DIM and INVIS
1714 *
1715 * The ANSI colours appear on any terminal that has colour
1716 * (obviously) but the interaction between sgr0 and the
1717 * colours varies but is usually related to the background
1718 * colour erase item.
1719 * The interaction between colour attributes and the mono
1720 * ones is also very implementation dependent.
1721 *
1722 * The 39 and 49 attributes are likely to be unimplemented.
1723 */
1724 int i;
1725 for (i = 0; i < esc_nargs; i++) {
1726 switch (def(esc_args[i], 0)) {
1727 case 0: /* restore defaults */
1728 curr_attr = ATTR_DEFAULT;
1729 break;
1730 case 1: /* enable bold */
1731 compatibility(VT100AVO);
1732 curr_attr |= ATTR_BOLD;
1733 break;
1734 case 21: /* (enable double underline) */
1735 compatibility(OTHER);
1736 case 4: /* enable underline */
1737 compatibility(VT100AVO);
1738 curr_attr |= ATTR_UNDER;
1739 break;
1740 case 5: /* enable blink */
1741 compatibility(VT100AVO);
1742 curr_attr |= ATTR_BLINK;
1743 break;
1744 case 7: /* enable reverse video */
1745 curr_attr |= ATTR_REVERSE;
1746 break;
1747 case 22: /* disable bold */
1748 compatibility2(OTHER, VT220);
1749 curr_attr &= ~ATTR_BOLD;
1750 break;
1751 case 24: /* disable underline */
1752 compatibility2(OTHER, VT220);
1753 curr_attr &= ~ATTR_UNDER;
1754 break;
1755 case 25: /* disable blink */
1756 compatibility2(OTHER, VT220);
1757 curr_attr &= ~ATTR_BLINK;
1758 break;
1759 case 27: /* disable reverse video */
1760 compatibility2(OTHER, VT220);
1761 curr_attr &= ~ATTR_REVERSE;
1762 break;
1763 case 30:
1764 case 31:
1765 case 32:
1766 case 33:
1767 case 34:
1768 case 35:
1769 case 36:
1770 case 37:
1771 /* foreground */
1772 curr_attr &= ~ATTR_FGMASK;
1773 curr_attr |=
1774 (esc_args[i] - 30) << ATTR_FGSHIFT;
1775 break;
1776 case 39: /* default-foreground */
1777 curr_attr &= ~ATTR_FGMASK;
1778 curr_attr |= ATTR_DEFFG;
1779 break;
1780 case 40:
1781 case 41:
1782 case 42:
1783 case 43:
1784 case 44:
1785 case 45:
1786 case 46:
1787 case 47:
1788 /* background */
1789 curr_attr &= ~ATTR_BGMASK;
1790 curr_attr |=
1791 (esc_args[i] - 40) << ATTR_BGSHIFT;
1792 break;
1793 case 49: /* default-background */
1794 curr_attr &= ~ATTR_BGMASK;
1795 curr_attr |= ATTR_DEFBG;
1796 break;
1797 }
1798 }
1799 if (use_bce)
1800 erase_char =
1801 (' ' |
1802 (curr_attr &
1803 (ATTR_FGMASK | ATTR_BGMASK |
1804 ATTR_BLINK)));
1805 }
1806 break;
1807 case 's': /* save cursor */
1808 save_cursor(TRUE);
1809 break;
1810 case 'u': /* restore cursor */
1811 save_cursor(FALSE);
1812 seen_disp_event = TRUE;
1813 break;
1814 case 't': /* set page size - ie window height */
374330e2 1815 /*
32874aea 1816 * VT340/VT420 sequence DECSLPP, DEC only allows values
1817 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1818 * illegal values (eg first arg 1..9) for window changing
1819 * and reports.
1820 */
1821 compatibility(VT340TEXT);
1822 if (esc_nargs <= 1
1823 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1824 request_resize(cols, def(esc_args[0], 24), 0);
1825 deselect();
1826 }
1827 break;
1a837633 1828 case 'S':
1829 compatibility(SCOANSI);
1830 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1831 fix_cpos;
1832 wrapnext = FALSE;
1833 seen_disp_event = TRUE;
1834 break;
1835 case 'T':
1836 compatibility(SCOANSI);
1837 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1838 fix_cpos;
1839 wrapnext = FALSE;
1840 seen_disp_event = TRUE;
1841 break;
32874aea 1842 case ANSI('|', '*'):
1843 /* VT420 sequence DECSNLS
1844 * Set number of lines on screen
1845 * VT420 uses VGA like hardware and can support any size in
1846 * reasonable range (24..49 AIUI) with no default specified.
1847 */
1848 compatibility(VT420);
1849 if (esc_nargs == 1 && esc_args[0] > 0) {
1850 request_resize(cols,
1851 def(esc_args[0], cfg.height),
1852 0);
1853 deselect();
1854 }
1855 break;
1856 case ANSI('|', '$'):
1857 /* VT340/VT420 sequence DECSCPP
1858 * Set number of columns per page
1859 * Docs imply range is only 80 or 132, but I'll allow any.
1860 */
1861 compatibility(VT340TEXT);
1862 if (esc_nargs <= 1) {
1863 request_resize(def(esc_args[0], cfg.width),
1864 rows, 0);
1865 deselect();
1866 }
1867 break;
1868 case 'X': /* write N spaces w/o moving cursor */
1869 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1870 compatibility(ANSIMIN);
1871 {
1872 int n = def(esc_args[0], 1);
1873 pos cursplus;
1874 unsigned long *p = cpos;
1875 if (n > cols - curs.x)
1876 n = cols - curs.x;
1877 cursplus = curs;
1878 cursplus.x += n;
1879 check_selection(curs, cursplus);
1880 while (n--)
1881 *p++ = erase_char;
1882 seen_disp_event = TRUE;
1883 }
1884 break;
1885 case 'x': /* report terminal characteristics */
1886 compatibility(VT100);
1887 {
1888 char buf[32];
1889 int i = def(esc_args[0], 0);
1890 if (i == 0 || i == 1) {
1891 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1892 buf[2] += i;
1893 ldisc_send(buf, 20);
1894 }
1895 }
1896 break;
1897 case ANSI('L', '='):
1898 compatibility(OTHER);
1899 use_bce = (esc_args[0] <= 0);
1900 erase_char = ERASE_CHAR;
1901 if (use_bce)
1902 erase_char =
1903 (' ' |
1904 (curr_attr &
1905 (ATTR_FGMASK | ATTR_BGMASK)));
1906 break;
1907 case ANSI('E', '='):
1908 compatibility(OTHER);
1909 blink_is_real = (esc_args[0] >= 1);
1910 break;
1911 case ANSI('p', '"'):
1912 /* Allow the host to make this emulator a 'perfect' VT102.
1913 * This first appeared in the VT220, but we do need to get
1914 * back to PuTTY mode so I won't check it.
e14a5a13 1915 *
4eeb7d09 1916 * The arg in 40..42,50 are a PuTTY extension.
32874aea 1917 * The 2nd arg, 8bit vs 7bit is not checked.
1918 *
1919 * Setting VT102 mode should also change the Fkeys to
1920 * generate PF* codes as a real VT102 has no Fkeys.
1921 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1922 * send nothing.
1923 *
1924 * Note ESC c will NOT change this!
374330e2 1925 */
32874aea 1926
1927 switch (esc_args[0]) {
1928 case 61:
1929 compatibility_level &= ~TM_VTXXX;
1930 compatibility_level |= TM_VT102;
374330e2 1931 break;
32874aea 1932 case 62:
1933 compatibility_level &= ~TM_VTXXX;
1934 compatibility_level |= TM_VT220;
1935 break;
1936
1937 default:
1938 if (esc_args[0] > 60 && esc_args[0] < 70)
1939 compatibility_level |= TM_VTXXX;
1940 break;
1941
1942 case 40:
1943 compatibility_level &= TM_VTXXX;
374330e2 1944 break;
32874aea 1945 case 41:
1946 compatibility_level = TM_PUTTY;
374330e2 1947 break;
32874aea 1948 case 42:
1949 compatibility_level = TM_SCOANSI;
374330e2 1950 break;
32874aea 1951
1952 case ARG_DEFAULT:
1953 compatibility_level = TM_PUTTY;
1954 break;
1955 case 50:
1956 break;
1957 }
1958
1959 /* Change the response to CSI c */
1960 if (esc_args[0] == 50) {
1961 int i;
1962 char lbuf[64];
1963 strcpy(id_string, "\033[?");
1964 for (i = 1; i < esc_nargs; i++) {
1965 if (i != 1)
1966 strcat(id_string, ";");
1967 sprintf(lbuf, "%d", esc_args[i]);
1968 strcat(id_string, lbuf);
1969 }
1970 strcat(id_string, "c");
1971 }
1972#if 0
1973 /* Is this a good idea ?
1974 * Well we should do a soft reset at this point ...
1975 */
1976 if (!has_compat(VT420) && has_compat(VT100)) {
1977 if (reset_132)
1978 request_resize(132, 24, 1);
1979 else
1980 request_resize(80, 24, 1);
374330e2 1981 }
32874aea 1982#endif
1983 break;
374330e2 1984 }
374330e2 1985 break;
32874aea 1986 case SEEN_OSC:
1987 osc_w = FALSE;
1988 switch (c) {
1989 case 'P': /* Linux palette sequence */
1990 termstate = SEEN_OSC_P;
1991 osc_strlen = 0;
1992 break;
1993 case 'R': /* Linux palette reset */
1994 palette_reset();
1995 term_invalidate();
1996 termstate = TOPLEVEL;
1997 break;
1998 case 'W': /* word-set */
1999 termstate = SEEN_OSC_W;
2000 osc_w = TRUE;
2001 break;
2002 case '0':
2003 case '1':
2004 case '2':
2005 case '3':
2006 case '4':
2007 case '5':
2008 case '6':
2009 case '7':
2010 case '8':
2011 case '9':
2012 esc_args[0] = 10 * esc_args[0] + c - '0';
2013 break;
2014 case 'L':
2015 /*
2016 * Grotty hack to support xterm and DECterm title
2017 * sequences concurrently.
2018 */
2019 if (esc_args[0] == 2) {
2020 esc_args[0] = 1;
2021 break;
2022 }
2023 /* else fall through */
2024 default:
2025 termstate = OSC_STRING;
2026 osc_strlen = 0;
e14a5a13 2027 }
2028 break;
32874aea 2029 case OSC_STRING:
2030 /*
2031 * This OSC stuff is EVIL. It takes just one character to get into
2032 * sysline mode and it's not initially obvious how to get out.
2033 * So I've added CR and LF as string aborts.
2034 * This shouldn't effect compatibility as I believe embedded
2035 * control characters are supposed to be interpreted (maybe?)
2036 * and they don't display anything useful anyway.
2037 *
2038 * -- RDB
e14a5a13 2039 */
32874aea 2040 if (c == '\n' || c == '\r') {
2041 termstate = TOPLEVEL;
2042 } else if (c == 0234 || c == '\007') {
2043 /*
2044 * These characters terminate the string; ST and BEL
2045 * terminate the sequence and trigger instant
2046 * processing of it, whereas ESC goes back to SEEN_ESC
2047 * mode unless it is followed by \, in which case it is
2048 * synonymous with ST in the first place.
2049 */
2050 do_osc();
2051 termstate = TOPLEVEL;
2052 } else if (c == '\033')
2053 termstate = OSC_MAYBE_ST;
2054 else if (osc_strlen < OSC_STR_MAX)
2055 osc_string[osc_strlen++] = c;
374330e2 2056 break;
32874aea 2057 case SEEN_OSC_P:
374330e2 2058 {
32874aea 2059 int max = (osc_strlen == 0 ? 21 : 16);
2060 int val;
2061 if (c >= '0' && c <= '9')
2062 val = c - '0';
2063 else if (c >= 'A' && c <= 'A' + max - 10)
2064 val = c - 'A' + 10;
2065 else if (c >= 'a' && c <= 'a' + max - 10)
2066 val = c - 'a' + 10;
2d466ffd 2067 else {
32874aea 2068 termstate = TOPLEVEL;
2d466ffd 2069 break;
2070 }
32874aea 2071 osc_string[osc_strlen++] = val;
2072 if (osc_strlen >= 7) {
2073 palette_set(osc_string[0],
2074 osc_string[1] * 16 + osc_string[2],
2075 osc_string[3] * 16 + osc_string[4],
2076 osc_string[5] * 16 + osc_string[6]);
2077 term_invalidate();
2078 termstate = TOPLEVEL;
374330e2 2079 }
2080 }
2081 break;
32874aea 2082 case SEEN_OSC_W:
2083 switch (c) {
2084 case '0':
2085 case '1':
2086 case '2':
2087 case '3':
2088 case '4':
2089 case '5':
2090 case '6':
2091 case '7':
2092 case '8':
2093 case '9':
2094 esc_args[0] = 10 * esc_args[0] + c - '0';
2095 break;
2096 default:
2097 termstate = OSC_STRING;
2098 osc_strlen = 0;
ec55b220 2099 }
32874aea 2100 break;
32874aea 2101 case VT52_ESC:
374330e2 2102 termstate = TOPLEVEL;
32874aea 2103 seen_disp_event = TRUE;
c9def1b8 2104 switch (c) {
32874aea 2105 case 'A':
2106 move(curs.x, curs.y - 1, 1);
2107 break;
2108 case 'B':
2109 move(curs.x, curs.y + 1, 1);
2110 break;
2111 case 'C':
2112 move(curs.x + 1, curs.y, 1);
2113 break;
2114 case 'D':
2115 move(curs.x - 1, curs.y, 1);
2116 break;
4eeb7d09 2117 /*
2118 * From the VT100 Manual
2119 * NOTE: The special graphics characters in the VT100
2120 * are different from those in the VT52
2121 *
2122 * From VT102 manual:
2123 * 137 _ Blank - Same
2124 * 140 ` Reserved - Humm.
2125 * 141 a Solid rectangle - Similar
2126 * 142 b 1/ - Top half of fraction for the
2127 * 143 c 3/ - subscript numbers below.
2128 * 144 d 5/
2129 * 145 e 7/
2130 * 146 f Degrees - Same
2131 * 147 g Plus or minus - Same
2132 * 150 h Right arrow
2133 * 151 i Ellipsis (dots)
2134 * 152 j Divide by
2135 * 153 k Down arrow
2136 * 154 l Bar at scan 0
2137 * 155 m Bar at scan 1
2138 * 156 n Bar at scan 2
2139 * 157 o Bar at scan 3 - Similar
2140 * 160 p Bar at scan 4 - Similar
2141 * 161 q Bar at scan 5 - Similar
2142 * 162 r Bar at scan 6 - Same
2143 * 163 s Bar at scan 7 - Similar
2144 * 164 t Subscript 0
2145 * 165 u Subscript 1
2146 * 166 v Subscript 2
2147 * 167 w Subscript 3
2148 * 170 x Subscript 4
2149 * 171 y Subscript 5
2150 * 172 z Subscript 6
2151 * 173 { Subscript 7
2152 * 174 | Subscript 8
2153 * 175 } Subscript 9
2154 * 176 ~ Paragraph
2155 *
2156 */
32874aea 2157 case 'F':
2158 cset_attr[cset = 0] = ATTR_LINEDRW;
2159 break;
2160 case 'G':
2161 cset_attr[cset = 0] = ATTR_ASCII;
2162 break;
2163 case 'H':
2164 move(0, 0, 0);
2165 break;
2166 case 'I':
2167 if (curs.y == 0)
2168 scroll(0, rows - 1, -1, TRUE);
2169 else if (curs.y > 0)
2170 curs.y--;
2171 fix_cpos;
2172 wrapnext = FALSE;
2173 break;
2174 case 'J':
2175 erase_lots(FALSE, FALSE, TRUE);
4facdf84 2176 disptop = 0;
c9def1b8 2177 break;
32874aea 2178 case 'K':
2179 erase_lots(TRUE, FALSE, TRUE);
2180 break;
4eeb7d09 2181#if 0
32874aea 2182 case 'V':
2183 /* XXX Print cursor line */
2184 break;
2185 case 'W':
2186 /* XXX Start controller mode */
2187 break;
2188 case 'X':
2189 /* XXX Stop controller mode */
2190 break;
4eeb7d09 2191#endif
32874aea 2192 case 'Y':
2193 termstate = VT52_Y1;
2194 break;
2195 case 'Z':
2196 ldisc_send("\033/Z", 3);
2197 break;
2198 case '=':
2199 app_keypad_keys = TRUE;
2200 break;
2201 case '>':
2202 app_keypad_keys = FALSE;
2203 break;
2204 case '<':
2205 /* XXX This should switch to VT100 mode not current or default
2206 * VT mode. But this will only have effect in a VT220+
2207 * emulation.
2208 */
2209 vt52_mode = FALSE;
4eeb7d09 2210 blink_is_real = cfg.blinktext;
32874aea 2211 break;
4eeb7d09 2212#if 0
32874aea 2213 case '^':
2214 /* XXX Enter auto print mode */
2215 break;
2216 case '_':
2217 /* XXX Exit auto print mode */
2218 break;
2219 case ']':
2220 /* XXX Print screen */
2221 break;
4eeb7d09 2222#endif
2223
2224#ifdef VT52_PLUS
2225 case 'E':
2226 /* compatibility(ATARI) */
2227 move(0, 0, 0);
2228 erase_lots(FALSE, FALSE, TRUE);
2229 disptop = 0;
2230 break;
2231 case 'L':
2232 /* compatibility(ATARI) */
2233 if (curs.y <= marg_b)
2234 scroll(curs.y, marg_b, -1, FALSE);
2235 break;
2236 case 'M':
2237 /* compatibility(ATARI) */
2238 if (curs.y <= marg_b)
2239 scroll(curs.y, marg_b, 1, TRUE);
2240 break;
2241 case 'b':
2242 /* compatibility(ATARI) */
2243 termstate = VT52_FG;
2244 break;
2245 case 'c':
2246 /* compatibility(ATARI) */
2247 termstate = VT52_BG;
2248 break;
2249 case 'd':
2250 /* compatibility(ATARI) */
2251 erase_lots(FALSE, TRUE, FALSE);
2252 disptop = 0;
2253 break;
2254 case 'e':
2255 /* compatibility(ATARI) */
2256 cursor_on = TRUE;
2257 break;
2258 case 'f':
2259 /* compatibility(ATARI) */
2260 cursor_on = FALSE;
2261 break;
2262 /* case 'j': Save cursor position - broken on ST */
2263 /* case 'k': Restore cursor position */
2264 case 'l':
2265 /* compatibility(ATARI) */
2266 erase_lots(TRUE, TRUE, TRUE);
2267 curs.x = 0;
2268 wrapnext = FALSE;
2269 fix_cpos;
2270 break;
2271 case 'o':
2272 /* compatibility(ATARI) */
2273 erase_lots(TRUE, TRUE, FALSE);
2274 break;
2275 case 'p':
2276 /* compatibility(ATARI) */
2277 curr_attr |= ATTR_REVERSE;
2278 break;
2279 case 'q':
2280 /* compatibility(ATARI) */
2281 curr_attr &= ~ATTR_REVERSE;
2282 break;
2283 case 'v': /* wrap Autowrap on - Wyse style */
2284 /* compatibility(ATARI) */
2285 wrap = 1;
2286 break;
2287 case 'w': /* Autowrap off */
2288 /* compatibility(ATARI) */
2289 wrap = 0;
2290 break;
2291
2292 case 'R':
2293 /* compatibility(OTHER) */
2294 vt52_bold = FALSE;
2295 curr_attr = ATTR_DEFAULT;
2296 if (use_bce)
2297 erase_char = (' ' |
2298 (curr_attr &
2299 (ATTR_FGMASK | ATTR_BGMASK |
2300 ATTR_BLINK)));
2301 break;
2302 case 'S':
2303 /* compatibility(VI50) */
2304 curr_attr |= ATTR_UNDER;
2305 break;
2306 case 'W':
2307 /* compatibility(VI50) */
2308 curr_attr &= ~ATTR_UNDER;
2309 break;
2310 case 'U':
2311 /* compatibility(VI50) */
2312 vt52_bold = TRUE;
2313 curr_attr |= ATTR_BOLD;
2314 break;
2315 case 'T':
2316 /* compatibility(VI50) */
2317 vt52_bold = FALSE;
2318 curr_attr &= ~ATTR_BOLD;
2319 break;
2320#endif
c9def1b8 2321 }
e14a5a13 2322 break;
32874aea 2323 case VT52_Y1:
2324 termstate = VT52_Y2;
2325 move(curs.x, c - ' ', 0);
e14a5a13 2326 break;
32874aea 2327 case VT52_Y2:
2328 termstate = TOPLEVEL;
2329 move(c - ' ', curs.y, 0);
e14a5a13 2330 break;
4eeb7d09 2331
2332#ifdef VT52_PLUS
2333 case VT52_FG:
2334 termstate = TOPLEVEL;
2335 curr_attr &= ~ATTR_FGMASK;
2336 curr_attr &= ~ATTR_BOLD;
2337 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2338 if ((c & 0x8) || vt52_bold)
2339 curr_attr |= ATTR_BOLD;
2340
2341 if (use_bce)
2342 erase_char = (' ' |
2343 (curr_attr &
2344 (ATTR_FGMASK | ATTR_BGMASK |
2345 ATTR_BLINK)));
2346 break;
2347 case VT52_BG:
2348 termstate = TOPLEVEL;
2349 curr_attr &= ~ATTR_BGMASK;
2350 curr_attr &= ~ATTR_BLINK;
2351 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2352
2353 /* Note: bold background */
2354 if (c & 0x8)
2355 curr_attr |= ATTR_BLINK;
2356
2357 if (use_bce)
2358 erase_char = (' ' |
2359 (curr_attr &
2360 (ATTR_FGMASK | ATTR_BGMASK |
2361 ATTR_BLINK)));
2362 break;
2363#endif
2d466ffd 2364 default: break; /* placate gcc warning about enum use */
e14a5a13 2365 }
4facdf84 2366 if (selstate != NO_SELECTION) {
2367 pos cursplus = curs;
2368 incpos(cursplus);
32874aea 2369 check_selection(curs, cursplus);
4facdf84 2370 }
374330e2 2371 }
c9def1b8 2372 inbuf_head = 0;
374330e2 2373}
2374
4eeb7d09 2375#if 0
374330e2 2376/*
2377 * Compare two lines to determine whether they are sufficiently
2378 * alike to scroll-optimise one to the other. Return the degree of
2379 * similarity.
2380 */
32874aea 2381static int linecmp(unsigned long *a, unsigned long *b)
2382{
374330e2 2383 int i, n;
2384
32874aea 2385 for (i = n = 0; i < cols; i++)
374330e2 2386 n += (*a++ == *b++);
2387 return n;
2388}
4eeb7d09 2389#endif
374330e2 2390
2391/*
2392 * Given a context, update the window. Out of paranoia, we don't
2393 * allow WM_PAINT responses to do scrolling optimisations.
2394 */
32874aea 2395static void do_paint(Context ctx, int may_optimise)
2396{
4eeb7d09 2397 int i, j, our_curs_y;
2398 unsigned long rv, cursor;
4facdf84 2399 pos scrpos;
374330e2 2400 char ch[1024];
4eeb7d09 2401 long cursor_background = ERASE_CHAR;
156686ef 2402 long ticks;
374330e2 2403
156686ef 2404 /*
2405 * Check the visual bell state.
2406 */
2407 if (in_vbell) {
2408 ticks = GetTickCount();
2409 if (ticks - vbell_timeout >= 0)
2410 in_vbell = FALSE;
2411 }
2412
4eeb7d09 2413 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2414
156686ef 2415 /* Depends on:
2416 * screen array, disptop, scrtop,
2417 * selection, rv,
2418 * cfg.blinkpc, blink_is_real, tblinker,
4eeb7d09 2419 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
156686ef 2420 */
4eeb7d09 2421
2422 /* Has the cursor position or type changed ? */
e14a5a13 2423 if (cursor_on) {
32874aea 2424 if (has_focus) {
217dceef 2425 if (blinker || !cfg.blink_cur)
4eeb7d09 2426 cursor = TATTR_ACTCURS;
32874aea 2427 else
2428 cursor = 0;
2429 } else
4eeb7d09 2430 cursor = TATTR_PASCURS;
4e30ff69 2431 if (wrapnext)
4eeb7d09 2432 cursor |= TATTR_RIGHTCURS;
32874aea 2433 } else
156686ef 2434 cursor = 0;
4facdf84 2435 our_curs_y = curs.y - disptop;
374330e2 2436
4eeb7d09 2437 if (dispcurs && (curstype != cursor ||
2438 dispcurs !=
2439 disptext + our_curs_y * (cols + 1) + curs.x)) {
2440 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2441 dispcurs[-1] |= ATTR_INVALID;
2442 if ((*dispcurs & ATTR_WIDE))
2443 dispcurs[1] |= ATTR_INVALID;
2444 *dispcurs |= ATTR_INVALID;
2445 curstype = 0;
2446 }
2447 dispcurs = NULL;
2448
2449 /* The normal screen data */
32874aea 2450 for (i = 0; i < rows; i++) {
4facdf84 2451 unsigned long *ldata;
2452 int lattr;
4eeb7d09 2453 int idx, dirty_line, dirty_run;
2454 unsigned long attr = 0;
2455 int updated_line = 0;
2456 int start = 0;
2457 int ccount = 0;
2458 int last_run_dirty = 0;
2459
4facdf84 2460 scrpos.y = i + disptop;
2461 ldata = lineptr(scrpos.y);
2462 lattr = (ldata[cols] & LATTR_MODE);
4eeb7d09 2463
2464 idx = i * (cols + 1);
2465 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2466 disptext[idx + cols] = ldata[cols];
2467
2468 for (j = 0; j < cols; j++, idx++) {
2469 unsigned long tattr, tchar;
2470 unsigned long *d = ldata + j;
2471 int break_run;
4facdf84 2472 scrpos.x = j;
32874aea 2473
4eeb7d09 2474 tchar = (*d & (CHAR_MASK | CSET_MASK));
2475 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2476 switch (tchar & CSET_MASK) {
2477 case ATTR_ASCII:
2478 tchar = unitab_line[tchar & 0xFF];
2479 break;
2480 case ATTR_LINEDRW:
2481 tchar = unitab_xterm[tchar & 0xFF];
2482 break;
2483 }
2484 tattr |= (tchar & CSET_MASK);
2485 tchar &= CHAR_MASK;
2486
2487 /* Video reversing things */
2488 tattr = (tattr ^ rv
2489 ^ (posle(selstart, scrpos) &&
2490 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2491
2492 /* 'Real' blinking ? */
2493 if (blink_is_real && (tattr & ATTR_BLINK)) {
2494 if (has_focus && tblinker) {
2495 tchar = ' ';
2496 tattr &= ~CSET_MASK;
2497 tattr |= ATTR_ACP;
c9def1b8 2498 }
4eeb7d09 2499 tattr &= ~ATTR_BLINK;
c9def1b8 2500 }
374330e2 2501
4eeb7d09 2502 /* Cursor here ? Save the 'background' */
2503 if (i == our_curs_y && j == curs.x) {
2504 cursor_background = tattr | tchar;
2505 dispcurs = disptext + idx;
2506 }
374330e2 2507
4eeb7d09 2508 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2509 dirty_line = TRUE;
2510
2511 break_run = (tattr != attr || j - start >= sizeof(ch));
2512
2513 /* Special hack for VT100 Linedraw glyphs */
2514 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2515 && tchar <= 0xBD) break_run = TRUE;
2516
2517 if (!dbcs_screenfont && !dirty_line) {
2518 if ((tchar | tattr) == disptext[idx])
2519 break_run = TRUE;
2520 else if (!dirty_run && ccount == 1)
2521 break_run = TRUE;
374330e2 2522 }
4eeb7d09 2523
2524 if (break_run) {
2525 if ((dirty_run || last_run_dirty) && ccount > 0) {
2526 do_text(ctx, start, i, ch, ccount, attr, lattr);
2527 updated_line = 1;
2528 }
2529 start = j;
2530 ccount = 0;
2531 attr = tattr;
2532 if (dbcs_screenfont)
2533 last_run_dirty = dirty_run;
2534 dirty_run = dirty_line;
2535 }
2536
2537 if ((tchar | tattr) != disptext[idx])
2538 dirty_run = TRUE;
2539 ch[ccount++] = (char) tchar;
2540 disptext[idx] = tchar | tattr;
2541
2542 /* If it's a wide char step along to the next one. */
2543 if (tattr & ATTR_WIDE) {
2544 if (++j < cols) {
2545 idx++;
2546 d++;
2547 /* Cursor is here ? Ouch! */
2548 if (i == our_curs_y && j == curs.x) {
2549 cursor_background = *d;
2550 dispcurs = disptext + idx;
2551 }
2552 if (disptext[idx] != *d)
2553 dirty_run = TRUE;
2554 disptext[idx] = *d;
374330e2 2555 }
374330e2 2556 }
4eeb7d09 2557 }
2558 if (dirty_run && ccount > 0) {
2559 do_text(ctx, start, i, ch, ccount, attr, lattr);
2560 updated_line = 1;
2561 }
2562
2563 /* Cursor on this line ? (and changed) */
2564 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2565 ch[0] = (char) (cursor_background & CHAR_MASK);
2566 attr = (cursor_background & ATTR_MASK) | cursor;
2567 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2568 curstype = cursor;
374330e2 2569 }
2570 }
2571}
2572
2573/*
e14a5a13 2574 * Flick the switch that says if blinking things should be shown or hidden.
2575 */
2576
32874aea 2577void term_blink(int flg)
2578{
22dcdc3b 2579 static long last_blink = 0;
2580 static long last_tblink = 0;
e14a5a13 2581 long now, blink_diff;
2582
c9def1b8 2583 now = GetTickCount();
32874aea 2584 blink_diff = now - last_tblink;
c9def1b8 2585
2586 /* Make sure the text blinks no more than 2Hz */
32874aea 2587 if (blink_diff < 0 || blink_diff > 450) {
2588 last_tblink = now;
c9def1b8 2589 tblinker = !tblinker;
2590 }
2591
e14a5a13 2592 if (flg) {
32874aea 2593 blinker = 1;
2594 last_blink = now;
e14a5a13 2595 return;
32874aea 2596 }
e14a5a13 2597
32874aea 2598 blink_diff = now - last_blink;
e14a5a13 2599
22dcdc3b 2600 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
32874aea 2601 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2602 return;
2603
e14a5a13 2604 last_blink = now;
2605 blinker = !blinker;
2606}
2607
2608/*
374330e2 2609 * Invalidate the whole screen so it will be repainted in full.
2610 */
32874aea 2611void term_invalidate(void)
2612{
374330e2 2613 int i;
2614
32874aea 2615 for (i = 0; i < rows * (cols + 1); i++)
374330e2 2616 disptext[i] = ATTR_INVALID;
2617}
2618
2619/*
2620 * Paint the window in response to a WM_PAINT message.
2621 */
32874aea 2622void term_paint(Context ctx, int l, int t, int r, int b)
2623{
374330e2 2624 int i, j, left, top, right, bottom;
2625
2626 left = l / font_width;
2627 right = (r - 1) / font_width;
2628 top = t / font_height;
2629 bottom = (b - 1) / font_height;
32874aea 2630 for (i = top; i <= bottom && i < rows; i++) {
2631 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2632 for (j = left; j <= right && j < cols; j++)
2633 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2634 else
32874aea 2635 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2636 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2637 }
374330e2 2638
e14a5a13 2639 /* This should happen soon enough, also for some reason it sometimes
2640 * fails to actually do anything when re-sizing ... painting the wrong
2641 * window perhaps ?
32874aea 2642 do_paint (ctx, FALSE);
2643 */
374330e2 2644}
2645
2646/*
2647 * Attempt to scroll the scrollback. The second parameter gives the
2648 * position we want to scroll to; the first is +1 to denote that
2649 * this position is relative to the beginning of the scrollback, -1
2650 * to denote it is relative to the end, and 0 to denote that it is
2651 * relative to the current position.
2652 */
32874aea 2653void term_scroll(int rel, int where)
2654{
4facdf84 2655 int sbtop = -count234(scrollback);
374330e2 2656
32874aea 2657 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
374330e2 2658 if (disptop < sbtop)
2659 disptop = sbtop;
4facdf84 2660 if (disptop > 0)
2661 disptop = 0;
374330e2 2662 update_sbar();
2663 term_update();
2664}
2665
4eeb7d09 2666static void clipme(pos top, pos bottom)
32874aea 2667{
4eeb7d09 2668 wchar_t *workbuf;
2669 wchar_t *wbptr; /* where next char goes within workbuf */
32874aea 2670 int wblen = 0; /* workbuf len */
2671 int buflen; /* amount of memory allocated to workbuf */
bc1235d4 2672
4eeb7d09 2673 buflen = 5120; /* Default size */
2674 workbuf = smalloc(buflen * sizeof(wchar_t));
2675 wbptr = workbuf; /* start filling here */
bc1235d4 2676
4facdf84 2677 while (poslt(top, bottom)) {
bc1235d4 2678 int nl = FALSE;
4facdf84 2679 unsigned long *ldata = lineptr(top.y);
260f3dec 2680 pos nlpos;
4facdf84 2681
2682 nlpos.y = top.y;
2683 nlpos.x = cols;
bc1235d4 2684
4eeb7d09 2685 if (!(ldata[cols] & LATTR_WRAPPED)) {
2686 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2687 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2688 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2689 && poslt(top, nlpos))
2690 decpos(nlpos);
4facdf84 2691 if (poslt(nlpos, bottom))
bc1235d4 2692 nl = TRUE;
2693 }
4facdf84 2694 while (poslt(top, bottom) && poslt(top, nlpos)) {
4eeb7d09 2695#if 0
2696 char cbuf[16], *p;
2697 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2698#else
2699 wchar_t cbuf[16], *p;
2700 int uc = (ldata[top.x] & 0xFFFF);
2701 int set, c;
2702
2703 if (uc == UCSWIDE) {
2704 top.x++;
2705 continue;
2706 }
2707
2708 switch (uc & CSET_MASK) {
2709 case ATTR_LINEDRW:
2710 if (!cfg.rawcnp) {
2711 uc = unitab_xterm[uc & 0xFF];
2712 break;
32874aea 2713 }
4eeb7d09 2714 case ATTR_ASCII:
2715 uc = unitab_line[uc & 0xFF];
2716 break;
d3a22f79 2717 }
4eeb7d09 2718 switch (uc & CSET_MASK) {
2719 case ATTR_ACP:
2720 uc = unitab_font[uc & 0xFF];
2721 break;
2722 case ATTR_OEMCP:
2723 uc = unitab_oemcp[uc & 0xFF];
2724 break;
2725 }
2726
2727 set = (uc & CSET_MASK);
2728 c = (uc & CHAR_MASK);
2729 cbuf[0] = uc;
2730 cbuf[1] = 0;
2731
2732 if (DIRECT_FONT(uc)) {
2733 if (c >= ' ' && c != 0x7F) {
2734 unsigned char buf[4];
2735 WCHAR wbuf[4];
2736 int rv;
2737 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2738 buf[0] = c;
2739 buf[1] = (unsigned char) ldata[top.x + 1];
2740 rv = MultiByteToWideChar(font_codepage,
2741 0, buf, 2, wbuf, 4);
2742 top.x++;
2743 } else {
2744 buf[0] = c;
2745 rv = MultiByteToWideChar(font_codepage,
2746 0, buf, 1, wbuf, 4);
2747 }
bc1235d4 2748
4eeb7d09 2749 if (rv > 0) {
2750 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2751 cbuf[rv] = 0;
d3a22f79 2752 }
d3a22f79 2753 }
4eeb7d09 2754 }
2755#endif
2756
2757 for (p = cbuf; *p; p++) {
2758 /* Enough overhead for trailing NL and nul */
2759 if (wblen >= buflen - 16) {
2760 workbuf =
2761 srealloc(workbuf,
2762 sizeof(wchar_t) * (buflen += 100));
2763 wbptr = workbuf + wblen;
2764 }
2765 wblen++;
2766 *wbptr++ = *p;
bc1235d4 2767 }
4facdf84 2768 top.x++;
bc1235d4 2769 }
2770 if (nl) {
2771 int i;
4eeb7d09 2772 for (i = 0; i < sel_nl_sz; i++) {
32874aea 2773 wblen++;
bc1235d4 2774 *wbptr++ = sel_nl[i];
2775 }
2776 }
4facdf84 2777 top.y++;
2778 top.x = 0;
bc1235d4 2779 }
4eeb7d09 2780 wblen++;
2781 *wbptr++ = 0;
32874aea 2782 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2783 if (buflen > 0) /* indicates we allocated this buffer */
bc1235d4 2784 sfree(workbuf);
bc1235d4 2785}
4eeb7d09 2786
32874aea 2787void term_copyall(void)
2788{
4facdf84 2789 pos top;
2790 top.y = -count234(scrollback);
2791 top.x = 0;
4eeb7d09 2792 clipme(top, curs);
2793}
2794
2795/*
2796 * The wordness array is mainly for deciding the disposition of the US-ASCII
2797 * characters.
2798 */
2799static int wordtype(int uc)
2800{
2801 static struct {
2802 int start, end, ctype;
2803 } *wptr, ucs_words[] = {
2804 {
2805 128, 160, 0}, {
2806 161, 191, 1}, {
2807 215, 215, 1}, {
2808 247, 247, 1}, {
2809 0x037e, 0x037e, 1}, /* Greek question mark */
2810 {
2811 0x0387, 0x0387, 1}, /* Greek ano teleia */
2812 {
2813 0x055a, 0x055f, 1}, /* Armenian punctuation */
2814 {
2815 0x0589, 0x0589, 1}, /* Armenian full stop */
2816 {
2817 0x0700, 0x070d, 1}, /* Syriac punctuation */
2818 {
2819 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2820 {
2821 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2822 {
2823 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2824 {
2825 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2826 {
2827 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2828 {
2829 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2830 {
2831 0x2000, 0x200a, 0}, /* Various spaces */
2832 {
2833 0x2070, 0x207f, 2}, /* superscript */
2834 {
2835 0x2080, 0x208f, 2}, /* subscript */
2836 {
2837 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2838 {
2839 0x3000, 0x3000, 0}, /* ideographic space */
2840 {
2841 0x3001, 0x3020, 1}, /* ideographic punctuation */
2842 {
2843 0x303f, 0x309f, 3}, /* Hiragana */
2844 {
2845 0x30a0, 0x30ff, 3}, /* Katakana */
2846 {
2847 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2848 {
2849 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2850 {
2851 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2852 {
2853 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2854 {
2855 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2856 {
2857 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2858 {
2859 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2860 {
2861 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2862 {
2863 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2864 {
2865 0, 0, 0}
2866 };
2867
2868 uc &= (CSET_MASK | CHAR_MASK);
2869
2870 switch (uc & CSET_MASK) {
2871 case ATTR_LINEDRW:
2872 uc = unitab_xterm[uc & 0xFF];
2873 break;
2874 case ATTR_ASCII:
2875 uc = unitab_line[uc & 0xFF];
2876 break;
2877 }
2878 switch (uc & CSET_MASK) {
2879 case ATTR_ACP:
2880 uc = unitab_font[uc & 0xFF];
2881 break;
2882 case ATTR_OEMCP:
2883 uc = unitab_oemcp[uc & 0xFF];
2884 break;
2885 }
2886
2887 if (uc < 0x80)
2888 return wordness[uc];
2889
2890 for (wptr = ucs_words; wptr->start; wptr++) {
2891 if (uc >= wptr->start && uc <= wptr->end)
2892 return wptr->ctype;
2893 }
2894
2895 return 2;
bc1235d4 2896}
2897
374330e2 2898/*
2899 * Spread the selection outwards according to the selection mode.
2900 */
32874aea 2901static pos sel_spread_half(pos p, int dir)
2902{
4facdf84 2903 unsigned long *ldata;
374330e2 2904 short wvalue;
2905
4facdf84 2906 ldata = lineptr(p.y);
374330e2 2907
2908 switch (selmode) {
2909 case SM_CHAR:
2910 /*
2911 * In this mode, every character is a separate unit, except
2912 * for runs of spaces at the end of a non-wrapping line.
2913 */
4eeb7d09 2914 if (!(ldata[cols] & LATTR_WRAPPED)) {
32874aea 2915 unsigned long *q = ldata + cols;
4facdf84 2916 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
374330e2 2917 q--;
32874aea 2918 if (q == ldata + cols)
374330e2 2919 q--;
32874aea 2920 if (p.x >= q - ldata)
2921 p.x = (dir == -1 ? q - ldata : cols - 1);
374330e2 2922 }
2923 break;
2924 case SM_WORD:
2925 /*
2926 * In this mode, the units are maximal runs of characters
2927 * whose `wordness' has the same value.
2928 */
4eeb7d09 2929 wvalue = wordtype(ldata[p.x]);
374330e2 2930 if (dir == +1) {
4eeb7d09 2931 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
2932 p.x++;
374330e2 2933 } else {
4eeb7d09 2934 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
2935 p.x--;
374330e2 2936 }
2937 break;
2938 case SM_LINE:
2939 /*
2940 * In this mode, every line is a unit.
2941 */
4facdf84 2942 p.x = (dir == -1 ? 0 : cols - 1);
374330e2 2943 break;
2944 }
2945 return p;
2946}
2947
32874aea 2948static void sel_spread(void)
2949{
2950 selstart = sel_spread_half(selstart, -1);
4facdf84 2951 decpos(selend);
32874aea 2952 selend = sel_spread_half(selend, +1);
4facdf84 2953 incpos(selend);
374330e2 2954}
2955
32874aea 2956void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
2957 int shift, int ctrl)
2958{
4facdf84 2959 pos selpoint;
2960 unsigned long *ldata;
32874aea 2961
2962 if (y < 0)
2963 y = 0;
2964 if (y >= rows)
2965 y = rows - 1;
2966 if (x < 0) {
2967 if (y > 0) {
2968 x = cols - 1;
2969 y--;
2970 } else
2971 x = 0;
094ed2a6 2972 }
32874aea 2973 if (x >= cols)
2974 x = cols - 1;
37508af4 2975
4facdf84 2976 selpoint.y = y + disptop;
2977 selpoint.x = x;
2978 ldata = lineptr(selpoint.y);
32874aea 2979 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
4facdf84 2980 selpoint.x /= 2;
374330e2 2981
01c034ad 2982 if (xterm_mouse) {
2983 int encstate = 0, r, c;
2984 char abuf[16];
2985 static int is_down = 0;
2986
32874aea 2987 switch (b) {
01c034ad 2988 case MBT_LEFT:
32874aea 2989 encstate = 0x20; /* left button down */
01c034ad 2990 break;
2991 case MBT_MIDDLE:
2992 encstate = 0x21;
2993 break;
2994 case MBT_RIGHT:
2995 encstate = 0x22;
2996 break;
2997 case MBT_WHEEL_UP:
2998 encstate = 0x60;
2999 break;
3000 case MBT_WHEEL_DOWN:
3001 encstate = 0x61;
3002 break;
2d466ffd 3003 default: break; /* placate gcc warning about enum use */
01c034ad 3004 }
32874aea 3005 switch (a) {
01c034ad 3006 case MA_DRAG:
3007 if (xterm_mouse == 1)
3008 return;
3009 encstate += 0x20;
3010 break;
3011 case MA_RELEASE:
3012 encstate = 0x23;
3013 is_down = 0;
3014 break;
3015 case MA_CLICK:
3016 if (is_down == b)
3017 return;
3018 is_down = b;
3019 break;
2d466ffd 3020 default: break; /* placate gcc warning about enum use */
01c034ad 3021 }
3022 if (shift)
3023 encstate += 0x04;
3024 if (ctrl)
3025 encstate += 0x10;
3026 r = y + 33;
3027 c = x + 33;
3028
3029 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3030 ldisc_send(abuf, 6);
3031 return;
3032 }
3033
3034 b = translate_button(b);
3035
3036 if (b == MBT_SELECT && a == MA_CLICK) {
374330e2 3037 deselect();
3038 selstate = ABOUT_TO;
3039 selanchor = selpoint;
3040 selmode = SM_CHAR;
01c034ad 3041 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
374330e2 3042 deselect();
3043 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3044 selstate = DRAGGING;
3045 selstart = selanchor = selpoint;
4facdf84 3046 selend = selstart;
3047 incpos(selend);
374330e2 3048 sel_spread();
01c034ad 3049 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3050 (b == MBT_EXTEND && a != MA_RELEASE)) {
4facdf84 3051 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
374330e2 3052 return;
01c034ad 3053 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
32874aea 3054 if (posdiff(selpoint, selstart) <
3055 posdiff(selend, selstart) / 2) {
4facdf84 3056 selanchor = selend;
3057 decpos(selanchor);
3058 } else {
374330e2 3059 selanchor = selstart;
4facdf84 3060 }
374330e2 3061 selstate = DRAGGING;
3062 }
3063 if (selstate != ABOUT_TO && selstate != DRAGGING)
3064 selanchor = selpoint;
3065 selstate = DRAGGING;
4facdf84 3066 if (poslt(selpoint, selanchor)) {
374330e2 3067 selstart = selpoint;
4facdf84 3068 selend = selanchor;
3069 incpos(selend);
374330e2 3070 } else {
3071 selstart = selanchor;
4facdf84 3072 selend = selpoint;
a4450583 3073 incpos(selend);
374330e2 3074 }
3075 sel_spread();
01c034ad 3076 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
374330e2 3077 if (selstate == DRAGGING) {
3078 /*
3079 * We've completed a selection. We now transfer the
3080 * data to the clipboard.
3081 */
4eeb7d09 3082 clipme(selstart, selend);
374330e2 3083 selstate = SELECTED;
3084 } else
3085 selstate = NO_SELECTION;
32874aea 3086 } else if (b == MBT_PASTE
3087 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
4eeb7d09 3088 wchar_t *data;
374330e2 3089 int len;
3090
4eeb7d09 3091 get_clip(&data, &len);
374330e2 3092 if (data) {
4eeb7d09 3093 wchar_t *p, *q;
c9def1b8 3094
32874aea 3095 if (paste_buffer)
3096 sfree(paste_buffer);
c9def1b8 3097 paste_pos = paste_hold = paste_len = 0;
4eeb7d09 3098 paste_buffer = smalloc(len * sizeof(wchar_t));
c9def1b8 3099
374330e2 3100 p = q = data;
32874aea 3101 while (p < data + len) {
3102 while (p < data + len &&
4eeb7d09 3103 !(p <= data + len - sel_nl_sz &&
374330e2 3104 !memcmp(p, sel_nl, sizeof(sel_nl))))
3105 p++;
14963b8f 3106
3107 {
3108 int i;
32874aea 3109 for (i = 0; i < p - q; i++) {
4eeb7d09 3110 paste_buffer[paste_len++] = q[i];
14963b8f 3111 }
3112 }
3113
4eeb7d09 3114 if (p <= data + len - sel_nl_sz &&
374330e2 3115 !memcmp(p, sel_nl, sizeof(sel_nl))) {
c9def1b8 3116 paste_buffer[paste_len++] = '\r';
4eeb7d09 3117 p += sel_nl_sz;
374330e2 3118 }
3119 q = p;
3120 }
c9def1b8 3121
3122 /* Assume a small paste will be OK in one go. */
32874aea 3123 if (paste_len < 256) {
4eeb7d09 3124 luni_send(paste_buffer, paste_len);
32874aea 3125 if (paste_buffer)
3126 sfree(paste_buffer);
c9def1b8 3127 paste_buffer = 0;
32874aea 3128 paste_pos = paste_hold = paste_len = 0;
c9def1b8 3129 }
374330e2 3130 }
3131 get_clip(NULL, NULL);
3132 }
3133
3134 term_update();
3135}
3136
32874aea 3137void term_nopaste()
3138{
3139 if (paste_len == 0)
3140 return;
c9def1b8 3141 sfree(paste_buffer);
3142 paste_buffer = 0;
3143 paste_len = 0;
3144}
3145
32874aea 3146void term_paste()
3147{
8df7a775 3148 static long last_paste = 0;
c9def1b8 3149 long now, paste_diff;
3150
32874aea 3151 if (paste_len == 0)
3152 return;
c9def1b8 3153
3154 /* Don't wait forever to paste */
32874aea 3155 if (paste_hold) {
3156 now = GetTickCount();
3157 paste_diff = now - last_paste;
3158 if (paste_diff >= 0 && paste_diff < 450)
c9def1b8 3159 return;
3160 }
3161 paste_hold = 0;
3162
32874aea 3163 while (paste_pos < paste_len) {
8df7a775 3164 int n = 0;
3165 while (n + paste_pos < paste_len) {
3166 if (paste_buffer[paste_pos + n++] == '\r')
3167 break;
3168 }
4eeb7d09 3169 luni_send(paste_buffer + paste_pos, n);
8df7a775 3170 paste_pos += n;
c9def1b8 3171
8df7a775 3172 if (paste_pos < paste_len) {
c9def1b8 3173 paste_hold = 1;
3174 return;
3175 }
3176 }
3177 sfree(paste_buffer);
3178 paste_buffer = 0;
3179 paste_len = 0;
3180}
3181
32874aea 3182static void deselect(void)
3183{
374330e2 3184 selstate = NO_SELECTION;
4facdf84 3185 selstart.x = selstart.y = selend.x = selend.y = 0;
374330e2 3186}
3187
32874aea 3188void term_deselect(void)
3189{
374330e2 3190 deselect();
3191 term_update();
3192}
fe50e814 3193
32874aea 3194int term_ldisc(int option)
3195{
3196 if (option == LD_ECHO)
3197 return term_echoing;
3198 if (option == LD_EDIT)
3199 return term_editing;
0965bee0 3200 return FALSE;
3201}
3202
fe50e814 3203/*
3204 * from_backend(), to get data from the backend for the terminal.
3205 */
32874aea 3206void from_backend(int is_stderr, char *data, int len)
3207{
fe50e814 3208 while (len--) {
3209 if (inbuf_head >= INBUF_SIZE)
3210 term_out();
3211 inbuf[inbuf_head++] = *data++;
3212 }
3213}
e1c8e0ed 3214
3215/*
3216 * Log session traffic.
3217 */
32874aea 3218void logtraffic(unsigned char c, int logmode)
3219{
e1c8e0ed 3220 if (cfg.logtype > 0) {
3221 if (cfg.logtype == logmode) {
3222 /* deferred open file from pgm start? */
32874aea 3223 if (!lgfp)
3224 logfopen();
3225 if (lgfp)
3226 fputc(c, lgfp);
3227 }
e1c8e0ed 3228 }
3229}
3230
3231/* open log file append/overwrite mode */
32874aea 3232void logfopen(void)
3233{
e1c8e0ed 3234 char buf[256];
3235 time_t t;
3236 struct tm *tm;
3237 char writemod[4];
3238
3239 if (!cfg.logtype)
3240 return;
32874aea 3241 sprintf(writemod, "wb"); /* default to rewrite */
3242 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
e1c8e0ed 3243 if (lgfp) {
3244 int i;
3245 fclose(lgfp);
3246 i = askappend(cfg.logfilename);
3247 if (i == 1)
3248 writemod[0] = 'a'; /* set append mode */
3249 else if (i == 0) { /* cancelled */
3250 lgfp = NULL;
32874aea 3251 cfg.logtype = 0; /* disable logging */
e1c8e0ed 3252 return;
3253 }
3254 }
3255
3256 lgfp = fopen(cfg.logfilename, writemod);
32874aea 3257 if (lgfp) { /* enter into event log */
e1c8e0ed 3258 sprintf(buf, "%s session log (%s mode) to file : ",
3259 (writemod[0] == 'a') ? "Appending" : "Writing new",
3260 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
32874aea 3261 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
e1c8e0ed 3262 /* Make sure we do not exceed the output buffer size */
32874aea 3263 strncat(buf, cfg.logfilename, 128);
e1c8e0ed 3264 buf[strlen(buf)] = '\0';
3265 logevent(buf);
3266
32874aea 3267 /* --- write header line iinto log file */
3268 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
e1c8e0ed 3269 time(&t);
3270 tm = localtime(&t);
3271 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
32874aea 3272 fputs(buf, lgfp);
3273 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
e1c8e0ed 3274 }
3275}
3276
32874aea 3277void logfclose(void)
3278{
3279 if (lgfp) {
3280 fclose(lgfp);
3281 lgfp = NULL;
3282 }
e1c8e0ed 3283}