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