Add support for most of the ESC[<low number>t sequences, which xterm
[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 */
32874aea 1958 if (esc_nargs <= 1
1959 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
68f9b3d9 1960 compatibility(VT340TEXT);
5a73255e 1961 request_resize(cols, def(esc_args[0], 24));
32874aea 1962 deselect();
68f9b3d9 1963 } else if (esc_nargs >= 1 &&
1964 esc_args[0] >= 1 &&
1965 esc_args[0] < 24) {
1966 compatibility(OTHER);
1967
1968 switch (esc_args[0]) {
1969 int x, y, len;
1970 char buf[80], *p;
1971 case 1:
1972 set_iconic(FALSE);
1973 break;
1974 case 2:
1975 set_iconic(TRUE);
1976 break;
1977 case 3:
1978 if (esc_nargs >= 3) {
1979 move_window(def(esc_args[1], 0),
1980 def(esc_args[2], 0));
1981 }
1982 break;
1983 case 4:
1984 /* We should resize the window to a given
1985 * size in pixels here, but currently our
1986 * resizing code isn't healthy enough to
1987 * manage it. */
1988 break;
1989 case 5:
1990 set_zorder(TRUE); /* move to top */
1991 break;
1992 case 6:
1993 set_zorder(FALSE); /* move to bottom */
1994 break;
1995 case 7:
1996 refresh_window();
1997 break;
1998 case 8:
1999 if (esc_nargs >= 3) {
2000 request_resize(def(esc_args[1], cfg.width),
2001 def(esc_args[2], cfg.height));
2002 }
2003 break;
2004 case 9:
2005 if (esc_nargs >= 2)
2006 set_zoomed(esc_args[1] ? TRUE : FALSE);
2007 break;
2008 case 11:
2009 ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
2010 4, 0);
2011 break;
2012 case 13:
2013 get_window_pos(&x, &y);
2014 len = sprintf(buf, "\033[3;%d;%dt", x, y);
2015 ldisc_send(buf, len, 0);
2016 break;
2017 case 14:
2018 get_window_pixels(&x, &y);
2019 len = sprintf(buf, "\033[4;%d;%dt", x, y);
2020 ldisc_send(buf, len, 0);
2021 break;
2022 case 18:
2023 len = sprintf(buf, "\033[8;%d;%dt",
2024 cols, rows);
2025 ldisc_send(buf, len, 0);
2026 break;
2027 case 19:
2028 /*
2029 * Hmmm. Strictly speaking we
2030 * should return `the size of the
2031 * screen in characters', but
2032 * that's not easy: (a) window
2033 * furniture being what it is it's
2034 * hard to compute, and (b) in
2035 * resize-font mode maximising the
2036 * window wouldn't change the
2037 * number of characters. *shrug*. I
2038 * think we'll ignore it for the
2039 * moment and see if anyone
2040 * complains, and then ask them
2041 * what they would like it to do.
2042 */
2043 break;
2044 case 20:
2045 p = get_window_title(TRUE);
2046 len = strlen(p);
2047 ldisc_send("\033]L", 3, 0);
2048 ldisc_send(p, len, 0);
2049 ldisc_send("\033\\", 2, 0);
2050 break;
2051 case 21:
2052 p = get_window_title(FALSE);
2053 len = strlen(p);
2054 ldisc_send("\033]l", 3, 0);
2055 ldisc_send(p, len, 0);
2056 ldisc_send("\033\\", 2, 0);
2057 break;
2058 }
32874aea 2059 }
2060 break;
1a837633 2061 case 'S':
2062 compatibility(SCOANSI);
2063 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
2064 fix_cpos;
2065 wrapnext = FALSE;
2066 seen_disp_event = TRUE;
2067 break;
2068 case 'T':
2069 compatibility(SCOANSI);
2070 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
2071 fix_cpos;
2072 wrapnext = FALSE;
2073 seen_disp_event = TRUE;
2074 break;
32874aea 2075 case ANSI('|', '*'):
2076 /* VT420 sequence DECSNLS
2077 * Set number of lines on screen
2078 * VT420 uses VGA like hardware and can support any size in
2079 * reasonable range (24..49 AIUI) with no default specified.
2080 */
2081 compatibility(VT420);
2082 if (esc_nargs == 1 && esc_args[0] > 0) {
5a73255e 2083 request_resize(cols, def(esc_args[0], cfg.height));
32874aea 2084 deselect();
2085 }
2086 break;
2087 case ANSI('|', '$'):
2088 /* VT340/VT420 sequence DECSCPP
2089 * Set number of columns per page
2090 * Docs imply range is only 80 or 132, but I'll allow any.
2091 */
2092 compatibility(VT340TEXT);
2093 if (esc_nargs <= 1) {
5a73255e 2094 request_resize(def(esc_args[0], cfg.width), rows);
32874aea 2095 deselect();
2096 }
2097 break;
2098 case 'X': /* write N spaces w/o moving cursor */
2099 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2100 compatibility(ANSIMIN);
2101 {
2102 int n = def(esc_args[0], 1);
2103 pos cursplus;
2104 unsigned long *p = cpos;
2105 if (n > cols - curs.x)
2106 n = cols - curs.x;
2107 cursplus = curs;
2108 cursplus.x += n;
2109 check_selection(curs, cursplus);
2110 while (n--)
2111 *p++ = erase_char;
2112 seen_disp_event = TRUE;
2113 }
2114 break;
2115 case 'x': /* report terminal characteristics */
2116 compatibility(VT100);
2117 {
2118 char buf[32];
2119 int i = def(esc_args[0], 0);
2120 if (i == 0 || i == 1) {
2121 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2122 buf[2] += i;
760e88b2 2123 ldisc_send(buf, 20, 0);
32874aea 2124 }
2125 }
2126 break;
979f6987 2127 case 'Z': /* BackTab for xterm */
2128 compatibility(OTHER);
2129 {
2130 int i = def(esc_args[0], 1);
2131 pos old_curs = curs;
2132
2133 for(;i>0 && curs.x>0; i--) {
2134 do {
2135 curs.x--;
2136 } while (curs.x >0 && !tabs[curs.x]);
2137 }
2138 fix_cpos;
2139 check_selection(old_curs, curs);
2140 }
2141 break;
32874aea 2142 case ANSI('L', '='):
2143 compatibility(OTHER);
2144 use_bce = (esc_args[0] <= 0);
2145 erase_char = ERASE_CHAR;
2146 if (use_bce)
8f22582c 2147 erase_char = (' ' | ATTR_ASCII |
2148 (curr_attr &
2149 (ATTR_FGMASK | ATTR_BGMASK)));
32874aea 2150 break;
2151 case ANSI('E', '='):
2152 compatibility(OTHER);
2153 blink_is_real = (esc_args[0] >= 1);
2154 break;
2155 case ANSI('p', '"'):
2156 /* Allow the host to make this emulator a 'perfect' VT102.
2157 * This first appeared in the VT220, but we do need to get
2158 * back to PuTTY mode so I won't check it.
e14a5a13 2159 *
4eeb7d09 2160 * The arg in 40..42,50 are a PuTTY extension.
32874aea 2161 * The 2nd arg, 8bit vs 7bit is not checked.
2162 *
2163 * Setting VT102 mode should also change the Fkeys to
2164 * generate PF* codes as a real VT102 has no Fkeys.
2165 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2166 * send nothing.
2167 *
2168 * Note ESC c will NOT change this!
374330e2 2169 */
32874aea 2170
2171 switch (esc_args[0]) {
2172 case 61:
2173 compatibility_level &= ~TM_VTXXX;
2174 compatibility_level |= TM_VT102;
374330e2 2175 break;
32874aea 2176 case 62:
2177 compatibility_level &= ~TM_VTXXX;
2178 compatibility_level |= TM_VT220;
2179 break;
2180
2181 default:
2182 if (esc_args[0] > 60 && esc_args[0] < 70)
2183 compatibility_level |= TM_VTXXX;
2184 break;
2185
2186 case 40:
2187 compatibility_level &= TM_VTXXX;
374330e2 2188 break;
32874aea 2189 case 41:
2190 compatibility_level = TM_PUTTY;
374330e2 2191 break;
32874aea 2192 case 42:
2193 compatibility_level = TM_SCOANSI;
374330e2 2194 break;
32874aea 2195
2196 case ARG_DEFAULT:
2197 compatibility_level = TM_PUTTY;
2198 break;
2199 case 50:
2200 break;
2201 }
2202
2203 /* Change the response to CSI c */
2204 if (esc_args[0] == 50) {
2205 int i;
2206 char lbuf[64];
2207 strcpy(id_string, "\033[?");
2208 for (i = 1; i < esc_nargs; i++) {
2209 if (i != 1)
2210 strcat(id_string, ";");
2211 sprintf(lbuf, "%d", esc_args[i]);
2212 strcat(id_string, lbuf);
2213 }
2214 strcat(id_string, "c");
2215 }
2216#if 0
2217 /* Is this a good idea ?
2218 * Well we should do a soft reset at this point ...
2219 */
2220 if (!has_compat(VT420) && has_compat(VT100)) {
2221 if (reset_132)
5a73255e 2222 request_resize(132, 24);
32874aea 2223 else
5a73255e 2224 request_resize(80, 24);
374330e2 2225 }
32874aea 2226#endif
2227 break;
374330e2 2228 }
374330e2 2229 break;
32874aea 2230 case SEEN_OSC:
2231 osc_w = FALSE;
2232 switch (c) {
2233 case 'P': /* Linux palette sequence */
2234 termstate = SEEN_OSC_P;
2235 osc_strlen = 0;
2236 break;
2237 case 'R': /* Linux palette reset */
2238 palette_reset();
2239 term_invalidate();
2240 termstate = TOPLEVEL;
2241 break;
2242 case 'W': /* word-set */
2243 termstate = SEEN_OSC_W;
2244 osc_w = TRUE;
2245 break;
2246 case '0':
2247 case '1':
2248 case '2':
2249 case '3':
2250 case '4':
2251 case '5':
2252 case '6':
2253 case '7':
2254 case '8':
2255 case '9':
2256 esc_args[0] = 10 * esc_args[0] + c - '0';
2257 break;
2258 case 'L':
2259 /*
2260 * Grotty hack to support xterm and DECterm title
2261 * sequences concurrently.
2262 */
2263 if (esc_args[0] == 2) {
2264 esc_args[0] = 1;
2265 break;
2266 }
2267 /* else fall through */
2268 default:
2269 termstate = OSC_STRING;
2270 osc_strlen = 0;
e14a5a13 2271 }
2272 break;
32874aea 2273 case OSC_STRING:
2274 /*
2275 * This OSC stuff is EVIL. It takes just one character to get into
2276 * sysline mode and it's not initially obvious how to get out.
2277 * So I've added CR and LF as string aborts.
2278 * This shouldn't effect compatibility as I believe embedded
2279 * control characters are supposed to be interpreted (maybe?)
2280 * and they don't display anything useful anyway.
2281 *
2282 * -- RDB
e14a5a13 2283 */
32874aea 2284 if (c == '\n' || c == '\r') {
2285 termstate = TOPLEVEL;
2286 } else if (c == 0234 || c == '\007') {
2287 /*
2288 * These characters terminate the string; ST and BEL
2289 * terminate the sequence and trigger instant
2290 * processing of it, whereas ESC goes back to SEEN_ESC
2291 * mode unless it is followed by \, in which case it is
2292 * synonymous with ST in the first place.
2293 */
2294 do_osc();
2295 termstate = TOPLEVEL;
2296 } else if (c == '\033')
2297 termstate = OSC_MAYBE_ST;
2298 else if (osc_strlen < OSC_STR_MAX)
2299 osc_string[osc_strlen++] = c;
374330e2 2300 break;
32874aea 2301 case SEEN_OSC_P:
374330e2 2302 {
32874aea 2303 int max = (osc_strlen == 0 ? 21 : 16);
2304 int val;
2305 if (c >= '0' && c <= '9')
2306 val = c - '0';
2307 else if (c >= 'A' && c <= 'A' + max - 10)
2308 val = c - 'A' + 10;
2309 else if (c >= 'a' && c <= 'a' + max - 10)
2310 val = c - 'a' + 10;
2d466ffd 2311 else {
32874aea 2312 termstate = TOPLEVEL;
2d466ffd 2313 break;
2314 }
32874aea 2315 osc_string[osc_strlen++] = val;
2316 if (osc_strlen >= 7) {
2317 palette_set(osc_string[0],
2318 osc_string[1] * 16 + osc_string[2],
2319 osc_string[3] * 16 + osc_string[4],
2320 osc_string[5] * 16 + osc_string[6]);
2321 term_invalidate();
2322 termstate = TOPLEVEL;
374330e2 2323 }
2324 }
2325 break;
32874aea 2326 case SEEN_OSC_W:
2327 switch (c) {
2328 case '0':
2329 case '1':
2330 case '2':
2331 case '3':
2332 case '4':
2333 case '5':
2334 case '6':
2335 case '7':
2336 case '8':
2337 case '9':
2338 esc_args[0] = 10 * esc_args[0] + c - '0';
2339 break;
2340 default:
2341 termstate = OSC_STRING;
2342 osc_strlen = 0;
ec55b220 2343 }
32874aea 2344 break;
32874aea 2345 case VT52_ESC:
374330e2 2346 termstate = TOPLEVEL;
32874aea 2347 seen_disp_event = TRUE;
c9def1b8 2348 switch (c) {
32874aea 2349 case 'A':
2350 move(curs.x, curs.y - 1, 1);
2351 break;
2352 case 'B':
2353 move(curs.x, curs.y + 1, 1);
2354 break;
2355 case 'C':
2356 move(curs.x + 1, curs.y, 1);
2357 break;
2358 case 'D':
2359 move(curs.x - 1, curs.y, 1);
2360 break;
4eeb7d09 2361 /*
2362 * From the VT100 Manual
2363 * NOTE: The special graphics characters in the VT100
2364 * are different from those in the VT52
2365 *
2366 * From VT102 manual:
2367 * 137 _ Blank - Same
2368 * 140 ` Reserved - Humm.
2369 * 141 a Solid rectangle - Similar
2370 * 142 b 1/ - Top half of fraction for the
2371 * 143 c 3/ - subscript numbers below.
2372 * 144 d 5/
2373 * 145 e 7/
2374 * 146 f Degrees - Same
2375 * 147 g Plus or minus - Same
2376 * 150 h Right arrow
2377 * 151 i Ellipsis (dots)
2378 * 152 j Divide by
2379 * 153 k Down arrow
2380 * 154 l Bar at scan 0
2381 * 155 m Bar at scan 1
2382 * 156 n Bar at scan 2
2383 * 157 o Bar at scan 3 - Similar
2384 * 160 p Bar at scan 4 - Similar
2385 * 161 q Bar at scan 5 - Similar
2386 * 162 r Bar at scan 6 - Same
2387 * 163 s Bar at scan 7 - Similar
2388 * 164 t Subscript 0
2389 * 165 u Subscript 1
2390 * 166 v Subscript 2
2391 * 167 w Subscript 3
2392 * 170 x Subscript 4
2393 * 171 y Subscript 5
2394 * 172 z Subscript 6
2395 * 173 { Subscript 7
2396 * 174 | Subscript 8
2397 * 175 } Subscript 9
2398 * 176 ~ Paragraph
2399 *
2400 */
32874aea 2401 case 'F':
2402 cset_attr[cset = 0] = ATTR_LINEDRW;
2403 break;
2404 case 'G':
2405 cset_attr[cset = 0] = ATTR_ASCII;
2406 break;
2407 case 'H':
2408 move(0, 0, 0);
2409 break;
2410 case 'I':
2411 if (curs.y == 0)
2412 scroll(0, rows - 1, -1, TRUE);
2413 else if (curs.y > 0)
2414 curs.y--;
2415 fix_cpos;
2416 wrapnext = FALSE;
2417 break;
2418 case 'J':
2419 erase_lots(FALSE, FALSE, TRUE);
4facdf84 2420 disptop = 0;
c9def1b8 2421 break;
32874aea 2422 case 'K':
2423 erase_lots(TRUE, FALSE, TRUE);
2424 break;
4eeb7d09 2425#if 0
32874aea 2426 case 'V':
2427 /* XXX Print cursor line */
2428 break;
2429 case 'W':
2430 /* XXX Start controller mode */
2431 break;
2432 case 'X':
2433 /* XXX Stop controller mode */
2434 break;
4eeb7d09 2435#endif
32874aea 2436 case 'Y':
2437 termstate = VT52_Y1;
2438 break;
2439 case 'Z':
760e88b2 2440 ldisc_send("\033/Z", 3, 0);
32874aea 2441 break;
2442 case '=':
2443 app_keypad_keys = TRUE;
2444 break;
2445 case '>':
2446 app_keypad_keys = FALSE;
2447 break;
2448 case '<':
2449 /* XXX This should switch to VT100 mode not current or default
2450 * VT mode. But this will only have effect in a VT220+
2451 * emulation.
2452 */
2453 vt52_mode = FALSE;
4eeb7d09 2454 blink_is_real = cfg.blinktext;
32874aea 2455 break;
4eeb7d09 2456#if 0
32874aea 2457 case '^':
2458 /* XXX Enter auto print mode */
2459 break;
2460 case '_':
2461 /* XXX Exit auto print mode */
2462 break;
2463 case ']':
2464 /* XXX Print screen */
2465 break;
4eeb7d09 2466#endif
2467
2468#ifdef VT52_PLUS
2469 case 'E':
2470 /* compatibility(ATARI) */
2471 move(0, 0, 0);
2472 erase_lots(FALSE, FALSE, TRUE);
2473 disptop = 0;
2474 break;
2475 case 'L':
2476 /* compatibility(ATARI) */
2477 if (curs.y <= marg_b)
2478 scroll(curs.y, marg_b, -1, FALSE);
2479 break;
2480 case 'M':
2481 /* compatibility(ATARI) */
2482 if (curs.y <= marg_b)
2483 scroll(curs.y, marg_b, 1, TRUE);
2484 break;
2485 case 'b':
2486 /* compatibility(ATARI) */
2487 termstate = VT52_FG;
2488 break;
2489 case 'c':
2490 /* compatibility(ATARI) */
2491 termstate = VT52_BG;
2492 break;
2493 case 'd':
2494 /* compatibility(ATARI) */
2495 erase_lots(FALSE, TRUE, FALSE);
2496 disptop = 0;
2497 break;
2498 case 'e':
2499 /* compatibility(ATARI) */
2500 cursor_on = TRUE;
2501 break;
2502 case 'f':
2503 /* compatibility(ATARI) */
2504 cursor_on = FALSE;
2505 break;
2506 /* case 'j': Save cursor position - broken on ST */
2507 /* case 'k': Restore cursor position */
2508 case 'l':
2509 /* compatibility(ATARI) */
2510 erase_lots(TRUE, TRUE, TRUE);
2511 curs.x = 0;
2512 wrapnext = FALSE;
2513 fix_cpos;
2514 break;
2515 case 'o':
2516 /* compatibility(ATARI) */
2517 erase_lots(TRUE, TRUE, FALSE);
2518 break;
2519 case 'p':
2520 /* compatibility(ATARI) */
2521 curr_attr |= ATTR_REVERSE;
2522 break;
2523 case 'q':
2524 /* compatibility(ATARI) */
2525 curr_attr &= ~ATTR_REVERSE;
2526 break;
2527 case 'v': /* wrap Autowrap on - Wyse style */
2528 /* compatibility(ATARI) */
2529 wrap = 1;
2530 break;
2531 case 'w': /* Autowrap off */
2532 /* compatibility(ATARI) */
2533 wrap = 0;
2534 break;
2535
2536 case 'R':
2537 /* compatibility(OTHER) */
2538 vt52_bold = FALSE;
2539 curr_attr = ATTR_DEFAULT;
2540 if (use_bce)
8f22582c 2541 erase_char = (' ' | ATTR_ASCII |
2542 (curr_attr &
2543 (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2544 break;
2545 case 'S':
2546 /* compatibility(VI50) */
2547 curr_attr |= ATTR_UNDER;
2548 break;
2549 case 'W':
2550 /* compatibility(VI50) */
2551 curr_attr &= ~ATTR_UNDER;
2552 break;
2553 case 'U':
2554 /* compatibility(VI50) */
2555 vt52_bold = TRUE;
2556 curr_attr |= ATTR_BOLD;
2557 break;
2558 case 'T':
2559 /* compatibility(VI50) */
2560 vt52_bold = FALSE;
2561 curr_attr &= ~ATTR_BOLD;
2562 break;
2563#endif
c9def1b8 2564 }
e14a5a13 2565 break;
32874aea 2566 case VT52_Y1:
2567 termstate = VT52_Y2;
2568 move(curs.x, c - ' ', 0);
e14a5a13 2569 break;
32874aea 2570 case VT52_Y2:
2571 termstate = TOPLEVEL;
2572 move(c - ' ', curs.y, 0);
e14a5a13 2573 break;
4eeb7d09 2574
2575#ifdef VT52_PLUS
2576 case VT52_FG:
2577 termstate = TOPLEVEL;
2578 curr_attr &= ~ATTR_FGMASK;
2579 curr_attr &= ~ATTR_BOLD;
2580 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2581 if ((c & 0x8) || vt52_bold)
2582 curr_attr |= ATTR_BOLD;
2583
2584 if (use_bce)
8f22582c 2585 erase_char = (' ' | ATTR_ASCII |
2586 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2587 break;
2588 case VT52_BG:
2589 termstate = TOPLEVEL;
2590 curr_attr &= ~ATTR_BGMASK;
2591 curr_attr &= ~ATTR_BLINK;
2592 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2593
2594 /* Note: bold background */
2595 if (c & 0x8)
2596 curr_attr |= ATTR_BLINK;
2597
2598 if (use_bce)
8f22582c 2599 erase_char = (' ' | ATTR_ASCII |
2600 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2601 break;
2602#endif
2d466ffd 2603 default: break; /* placate gcc warning about enum use */
e14a5a13 2604 }
4facdf84 2605 if (selstate != NO_SELECTION) {
2606 pos cursplus = curs;
2607 incpos(cursplus);
32874aea 2608 check_selection(curs, cursplus);
4facdf84 2609 }
374330e2 2610 }
374330e2 2611}
2612
4eeb7d09 2613#if 0
374330e2 2614/*
2615 * Compare two lines to determine whether they are sufficiently
2616 * alike to scroll-optimise one to the other. Return the degree of
2617 * similarity.
2618 */
32874aea 2619static int linecmp(unsigned long *a, unsigned long *b)
2620{
374330e2 2621 int i, n;
2622
32874aea 2623 for (i = n = 0; i < cols; i++)
374330e2 2624 n += (*a++ == *b++);
2625 return n;
2626}
4eeb7d09 2627#endif
374330e2 2628
2629/*
2630 * Given a context, update the window. Out of paranoia, we don't
2631 * allow WM_PAINT responses to do scrolling optimisations.
2632 */
32874aea 2633static void do_paint(Context ctx, int may_optimise)
2634{
4eeb7d09 2635 int i, j, our_curs_y;
2636 unsigned long rv, cursor;
4facdf84 2637 pos scrpos;
374330e2 2638 char ch[1024];
4eeb7d09 2639 long cursor_background = ERASE_CHAR;
286c6b86 2640 unsigned long ticks;
374330e2 2641
156686ef 2642 /*
2643 * Check the visual bell state.
2644 */
2645 if (in_vbell) {
2646 ticks = GetTickCount();
286c6b86 2647 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
2648 in_vbell = FALSE;
2649 }
156686ef 2650
4eeb7d09 2651 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2652
156686ef 2653 /* Depends on:
2654 * screen array, disptop, scrtop,
2655 * selection, rv,
2656 * cfg.blinkpc, blink_is_real, tblinker,
4eeb7d09 2657 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
156686ef 2658 */
4eeb7d09 2659
2660 /* Has the cursor position or type changed ? */
e14a5a13 2661 if (cursor_on) {
32874aea 2662 if (has_focus) {
217dceef 2663 if (blinker || !cfg.blink_cur)
4eeb7d09 2664 cursor = TATTR_ACTCURS;
32874aea 2665 else
2666 cursor = 0;
2667 } else
4eeb7d09 2668 cursor = TATTR_PASCURS;
4e30ff69 2669 if (wrapnext)
4eeb7d09 2670 cursor |= TATTR_RIGHTCURS;
32874aea 2671 } else
156686ef 2672 cursor = 0;
4facdf84 2673 our_curs_y = curs.y - disptop;
374330e2 2674
4eeb7d09 2675 if (dispcurs && (curstype != cursor ||
2676 dispcurs !=
2677 disptext + our_curs_y * (cols + 1) + curs.x)) {
5a73255e 2678 if (dispcurs > disptext &&
2679 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
4eeb7d09 2680 dispcurs[-1] |= ATTR_INVALID;
5a73255e 2681 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
4eeb7d09 2682 dispcurs[1] |= ATTR_INVALID;
2683 *dispcurs |= ATTR_INVALID;
2684 curstype = 0;
2685 }
2686 dispcurs = NULL;
2687
2688 /* The normal screen data */
32874aea 2689 for (i = 0; i < rows; i++) {
4facdf84 2690 unsigned long *ldata;
2691 int lattr;
6908fed7 2692 int idx, dirty_line, dirty_run, selected;
4eeb7d09 2693 unsigned long attr = 0;
2694 int updated_line = 0;
2695 int start = 0;
2696 int ccount = 0;
2697 int last_run_dirty = 0;
2698
4facdf84 2699 scrpos.y = i + disptop;
2700 ldata = lineptr(scrpos.y);
2701 lattr = (ldata[cols] & LATTR_MODE);
4eeb7d09 2702
2703 idx = i * (cols + 1);
2704 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2705 disptext[idx + cols] = ldata[cols];
2706
2707 for (j = 0; j < cols; j++, idx++) {
2708 unsigned long tattr, tchar;
2709 unsigned long *d = ldata + j;
2710 int break_run;
4facdf84 2711 scrpos.x = j;
32874aea 2712
4eeb7d09 2713 tchar = (*d & (CHAR_MASK | CSET_MASK));
2714 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2715 switch (tchar & CSET_MASK) {
2716 case ATTR_ASCII:
2717 tchar = unitab_line[tchar & 0xFF];
2718 break;
2719 case ATTR_LINEDRW:
2720 tchar = unitab_xterm[tchar & 0xFF];
2721 break;
d3cb5465 2722 case ATTR_SCOACS:
2723 tchar = unitab_scoacs[tchar&0xFF];
2724 break;
4eeb7d09 2725 }
2726 tattr |= (tchar & CSET_MASK);
2727 tchar &= CHAR_MASK;
5a73255e 2728 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2729 tattr |= ATTR_WIDE;
4eeb7d09 2730
2731 /* Video reversing things */
6908fed7 2732 if (seltype == LEXICOGRAPHIC)
2733 selected = posle(selstart, scrpos) && poslt(scrpos, selend);
2734 else
2735 selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
4eeb7d09 2736 tattr = (tattr ^ rv
6908fed7 2737 ^ (selected ? ATTR_REVERSE : 0));
4eeb7d09 2738
2739 /* 'Real' blinking ? */
2740 if (blink_is_real && (tattr & ATTR_BLINK)) {
2741 if (has_focus && tblinker) {
2742 tchar = ' ';
2743 tattr &= ~CSET_MASK;
2744 tattr |= ATTR_ACP;
c9def1b8 2745 }
4eeb7d09 2746 tattr &= ~ATTR_BLINK;
c9def1b8 2747 }
374330e2 2748
5a73255e 2749 /*
2750 * Check the font we'll _probably_ be using to see if
2751 * the character is wide when we don't want it to be.
2752 */
2753 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2754 if ((tattr & ATTR_WIDE) == 0 &&
2755 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2756 tattr |= ATTR_NARROW;
2757 } else if (disptext[idx]&ATTR_NARROW)
2758 tattr |= ATTR_NARROW;
2759
4eeb7d09 2760 /* Cursor here ? Save the 'background' */
2761 if (i == our_curs_y && j == curs.x) {
2762 cursor_background = tattr | tchar;
2763 dispcurs = disptext + idx;
2764 }
374330e2 2765
4eeb7d09 2766 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2767 dirty_line = TRUE;
2768
2769 break_run = (tattr != attr || j - start >= sizeof(ch));
2770
2771 /* Special hack for VT100 Linedraw glyphs */
2772 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2773 && tchar <= 0xBD) break_run = TRUE;
2774
2775 if (!dbcs_screenfont && !dirty_line) {
2776 if ((tchar | tattr) == disptext[idx])
2777 break_run = TRUE;
2778 else if (!dirty_run && ccount == 1)
2779 break_run = TRUE;
374330e2 2780 }
4eeb7d09 2781
2782 if (break_run) {
2783 if ((dirty_run || last_run_dirty) && ccount > 0) {
2784 do_text(ctx, start, i, ch, ccount, attr, lattr);
2785 updated_line = 1;
2786 }
2787 start = j;
2788 ccount = 0;
2789 attr = tattr;
2790 if (dbcs_screenfont)
2791 last_run_dirty = dirty_run;
2792 dirty_run = dirty_line;
2793 }
2794
2795 if ((tchar | tattr) != disptext[idx])
2796 dirty_run = TRUE;
2797 ch[ccount++] = (char) tchar;
2798 disptext[idx] = tchar | tattr;
2799
2800 /* If it's a wide char step along to the next one. */
2801 if (tattr & ATTR_WIDE) {
2802 if (++j < cols) {
2803 idx++;
2804 d++;
2805 /* Cursor is here ? Ouch! */
2806 if (i == our_curs_y && j == curs.x) {
2807 cursor_background = *d;
2808 dispcurs = disptext + idx;
2809 }
2810 if (disptext[idx] != *d)
2811 dirty_run = TRUE;
2812 disptext[idx] = *d;
374330e2 2813 }
374330e2 2814 }
4eeb7d09 2815 }
2816 if (dirty_run && ccount > 0) {
2817 do_text(ctx, start, i, ch, ccount, attr, lattr);
2818 updated_line = 1;
2819 }
2820
2821 /* Cursor on this line ? (and changed) */
2822 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2823 ch[0] = (char) (cursor_background & CHAR_MASK);
2824 attr = (cursor_background & ATTR_MASK) | cursor;
2825 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2826 curstype = cursor;
374330e2 2827 }
2828 }
2829}
2830
2831/*
e14a5a13 2832 * Flick the switch that says if blinking things should be shown or hidden.
2833 */
2834
32874aea 2835void term_blink(int flg)
2836{
22dcdc3b 2837 static long last_blink = 0;
2838 static long last_tblink = 0;
e14a5a13 2839 long now, blink_diff;
2840
c9def1b8 2841 now = GetTickCount();
32874aea 2842 blink_diff = now - last_tblink;
c9def1b8 2843
2844 /* Make sure the text blinks no more than 2Hz */
32874aea 2845 if (blink_diff < 0 || blink_diff > 450) {
2846 last_tblink = now;
c9def1b8 2847 tblinker = !tblinker;
2848 }
2849
e14a5a13 2850 if (flg) {
32874aea 2851 blinker = 1;
2852 last_blink = now;
e14a5a13 2853 return;
32874aea 2854 }
e14a5a13 2855
32874aea 2856 blink_diff = now - last_blink;
e14a5a13 2857
22dcdc3b 2858 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
32874aea 2859 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2860 return;
2861
e14a5a13 2862 last_blink = now;
2863 blinker = !blinker;
2864}
2865
2866/*
374330e2 2867 * Invalidate the whole screen so it will be repainted in full.
2868 */
32874aea 2869void term_invalidate(void)
2870{
374330e2 2871 int i;
2872
32874aea 2873 for (i = 0; i < rows * (cols + 1); i++)
374330e2 2874 disptext[i] = ATTR_INVALID;
2875}
2876
2877/*
2878 * Paint the window in response to a WM_PAINT message.
2879 */
5a73255e 2880void term_paint(Context ctx, int left, int top, int right, int bottom)
32874aea 2881{
5a73255e 2882 int i, j;
2883 if (left < 0) left = 0;
2884 if (top < 0) top = 0;
2885 if (right >= cols) right = cols-1;
2886 if (bottom >= rows) bottom = rows-1;
374330e2 2887
32874aea 2888 for (i = top; i <= bottom && i < rows; i++) {
2889 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2890 for (j = left; j <= right && j < cols; j++)
2891 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2892 else
32874aea 2893 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2894 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2895 }
374330e2 2896
e14a5a13 2897 /* This should happen soon enough, also for some reason it sometimes
2898 * fails to actually do anything when re-sizing ... painting the wrong
2899 * window perhaps ?
32874aea 2900 */
5a73255e 2901 if (alt_pressed)
2902 do_paint (ctx, FALSE);
374330e2 2903}
2904
2905/*
2906 * Attempt to scroll the scrollback. The second parameter gives the
2907 * position we want to scroll to; the first is +1 to denote that
2908 * this position is relative to the beginning of the scrollback, -1
2909 * to denote it is relative to the end, and 0 to denote that it is
2910 * relative to the current position.
2911 */
32874aea 2912void term_scroll(int rel, int where)
2913{
4facdf84 2914 int sbtop = -count234(scrollback);
374330e2 2915
32874aea 2916 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
374330e2 2917 if (disptop < sbtop)
2918 disptop = sbtop;
4facdf84 2919 if (disptop > 0)
2920 disptop = 0;
374330e2 2921 update_sbar();
2922 term_update();
2923}
2924
6908fed7 2925static void clipme(pos top, pos bottom, int rect)
32874aea 2926{
4eeb7d09 2927 wchar_t *workbuf;
2928 wchar_t *wbptr; /* where next char goes within workbuf */
6908fed7 2929 int old_top_x;
32874aea 2930 int wblen = 0; /* workbuf len */
2931 int buflen; /* amount of memory allocated to workbuf */
bc1235d4 2932
4eeb7d09 2933 buflen = 5120; /* Default size */
2934 workbuf = smalloc(buflen * sizeof(wchar_t));
2935 wbptr = workbuf; /* start filling here */
6908fed7 2936 old_top_x = top.x; /* needed for rect==1 */
bc1235d4 2937
4facdf84 2938 while (poslt(top, bottom)) {
bc1235d4 2939 int nl = FALSE;
4facdf84 2940 unsigned long *ldata = lineptr(top.y);
260f3dec 2941 pos nlpos;
4facdf84 2942
6908fed7 2943 /*
2944 * nlpos will point at the maximum position on this line we
2945 * should copy up to. So we start it at the end of the
2946 * line...
2947 */
4facdf84 2948 nlpos.y = top.y;
2949 nlpos.x = cols;
bc1235d4 2950
6908fed7 2951 /*
2952 * ... move it backwards if there's unused space at the end
2953 * of the line (and also set `nl' if this is the case,
2954 * because in normal selection mode this means we need a
2955 * newline at the end)...
2956 */
4eeb7d09 2957 if (!(ldata[cols] & LATTR_WRAPPED)) {
2958 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2959 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2960 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2961 && poslt(top, nlpos))
2962 decpos(nlpos);
4facdf84 2963 if (poslt(nlpos, bottom))
bc1235d4 2964 nl = TRUE;
2965 }
6908fed7 2966
2967 /*
2968 * ... and then clip it to the terminal x coordinate if
2969 * we're doing rectangular selection. (In this case we
2970 * still did the above, so that copying e.g. the right-hand
2971 * column from a table doesn't fill with spaces on the
2972 * right.)
2973 */
2974 if (rect) {
2975 if (nlpos.x > bottom.x)
2976 nlpos.x = bottom.x;
2977 nl = (top.y < bottom.y);
2978 }
2979
4facdf84 2980 while (poslt(top, bottom) && poslt(top, nlpos)) {
4eeb7d09 2981#if 0
2982 char cbuf[16], *p;
2983 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2984#else
2985 wchar_t cbuf[16], *p;
2986 int uc = (ldata[top.x] & 0xFFFF);
2987 int set, c;
2988
2989 if (uc == UCSWIDE) {
2990 top.x++;
2991 continue;
2992 }
2993
2994 switch (uc & CSET_MASK) {
2995 case ATTR_LINEDRW:
2996 if (!cfg.rawcnp) {
2997 uc = unitab_xterm[uc & 0xFF];
2998 break;
32874aea 2999 }
4eeb7d09 3000 case ATTR_ASCII:
3001 uc = unitab_line[uc & 0xFF];
3002 break;
d3cb5465 3003 case ATTR_SCOACS:
3004 uc = unitab_scoacs[uc&0xFF];
3005 break;
d3a22f79 3006 }
4eeb7d09 3007 switch (uc & CSET_MASK) {
3008 case ATTR_ACP:
3009 uc = unitab_font[uc & 0xFF];
3010 break;
3011 case ATTR_OEMCP:
3012 uc = unitab_oemcp[uc & 0xFF];
3013 break;
3014 }
3015
3016 set = (uc & CSET_MASK);
3017 c = (uc & CHAR_MASK);
3018 cbuf[0] = uc;
3019 cbuf[1] = 0;
3020
3021 if (DIRECT_FONT(uc)) {
3022 if (c >= ' ' && c != 0x7F) {
3023 unsigned char buf[4];
3024 WCHAR wbuf[4];
3025 int rv;
3026 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
3027 buf[0] = c;
3028 buf[1] = (unsigned char) ldata[top.x + 1];
3029 rv = MultiByteToWideChar(font_codepage,
3030 0, buf, 2, wbuf, 4);
3031 top.x++;
3032 } else {
3033 buf[0] = c;
3034 rv = MultiByteToWideChar(font_codepage,
3035 0, buf, 1, wbuf, 4);
3036 }
bc1235d4 3037
4eeb7d09 3038 if (rv > 0) {
3039 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3040 cbuf[rv] = 0;
d3a22f79 3041 }
d3a22f79 3042 }
4eeb7d09 3043 }
3044#endif
3045
3046 for (p = cbuf; *p; p++) {
3047 /* Enough overhead for trailing NL and nul */
3048 if (wblen >= buflen - 16) {
3049 workbuf =
3050 srealloc(workbuf,
3051 sizeof(wchar_t) * (buflen += 100));
3052 wbptr = workbuf + wblen;
3053 }
3054 wblen++;
3055 *wbptr++ = *p;
bc1235d4 3056 }
4facdf84 3057 top.x++;
bc1235d4 3058 }
3059 if (nl) {
3060 int i;
4eeb7d09 3061 for (i = 0; i < sel_nl_sz; i++) {
32874aea 3062 wblen++;
bc1235d4 3063 *wbptr++ = sel_nl[i];
3064 }
3065 }
4facdf84 3066 top.y++;
6908fed7 3067 top.x = rect ? old_top_x : 0;
bc1235d4 3068 }
4eeb7d09 3069 wblen++;
3070 *wbptr++ = 0;
32874aea 3071 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3072 if (buflen > 0) /* indicates we allocated this buffer */
bc1235d4 3073 sfree(workbuf);
bc1235d4 3074}
4eeb7d09 3075
32874aea 3076void term_copyall(void)
3077{
4facdf84 3078 pos top;
3079 top.y = -count234(scrollback);
3080 top.x = 0;
6908fed7 3081 clipme(top, curs, 0);
4eeb7d09 3082}
3083
3084/*
3085 * The wordness array is mainly for deciding the disposition of the US-ASCII
3086 * characters.
3087 */
3088static int wordtype(int uc)
3089{
3090 static struct {
3091 int start, end, ctype;
3092 } *wptr, ucs_words[] = {
3093 {
3094 128, 160, 0}, {
3095 161, 191, 1}, {
3096 215, 215, 1}, {
3097 247, 247, 1}, {
3098 0x037e, 0x037e, 1}, /* Greek question mark */
3099 {
3100 0x0387, 0x0387, 1}, /* Greek ano teleia */
3101 {
3102 0x055a, 0x055f, 1}, /* Armenian punctuation */
3103 {
3104 0x0589, 0x0589, 1}, /* Armenian full stop */
3105 {
3106 0x0700, 0x070d, 1}, /* Syriac punctuation */
3107 {
3108 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3109 {
3110 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3111 {
3112 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3113 {
3114 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3115 {
3116 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3117 {
3118 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3119 {
3120 0x2000, 0x200a, 0}, /* Various spaces */
3121 {
3122 0x2070, 0x207f, 2}, /* superscript */
3123 {
3124 0x2080, 0x208f, 2}, /* subscript */
3125 {
3126 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3127 {
3128 0x3000, 0x3000, 0}, /* ideographic space */
3129 {
3130 0x3001, 0x3020, 1}, /* ideographic punctuation */
3131 {
3132 0x303f, 0x309f, 3}, /* Hiragana */
3133 {
3134 0x30a0, 0x30ff, 3}, /* Katakana */
3135 {
3136 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3137 {
3138 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3139 {
3140 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3141 {
3142 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3143 {
3144 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3145 {
3146 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3147 {
3148 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3149 {
3150 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3151 {
3152 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3153 {
3154 0, 0, 0}
3155 };
3156
3157 uc &= (CSET_MASK | CHAR_MASK);
3158
3159 switch (uc & CSET_MASK) {
3160 case ATTR_LINEDRW:
3161 uc = unitab_xterm[uc & 0xFF];
3162 break;
3163 case ATTR_ASCII:
3164 uc = unitab_line[uc & 0xFF];
3165 break;
d3cb5465 3166 case ATTR_SCOACS:
3167 uc = unitab_scoacs[uc&0xFF];
3168 break;
4eeb7d09 3169 }
3170 switch (uc & CSET_MASK) {
3171 case ATTR_ACP:
3172 uc = unitab_font[uc & 0xFF];
3173 break;
3174 case ATTR_OEMCP:
3175 uc = unitab_oemcp[uc & 0xFF];
3176 break;
3177 }
3178
5a73255e 3179 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3180 * fail as there's such a thing as a double width space. :-(
3181 */
3182 if (dbcs_screenfont && font_codepage == line_codepage)
3183 return (uc != ' ');
3184
4eeb7d09 3185 if (uc < 0x80)
3186 return wordness[uc];
3187
3188 for (wptr = ucs_words; wptr->start; wptr++) {
3189 if (uc >= wptr->start && uc <= wptr->end)
3190 return wptr->ctype;
3191 }
3192
3193 return 2;
bc1235d4 3194}
3195
374330e2 3196/*
3197 * Spread the selection outwards according to the selection mode.
3198 */
32874aea 3199static pos sel_spread_half(pos p, int dir)
3200{
4facdf84 3201 unsigned long *ldata;
374330e2 3202 short wvalue;
50ab4088 3203 int topy = -count234(scrollback);
374330e2 3204
4facdf84 3205 ldata = lineptr(p.y);
374330e2 3206
3207 switch (selmode) {
3208 case SM_CHAR:
3209 /*
3210 * In this mode, every character is a separate unit, except
3211 * for runs of spaces at the end of a non-wrapping line.
3212 */
4eeb7d09 3213 if (!(ldata[cols] & LATTR_WRAPPED)) {
32874aea 3214 unsigned long *q = ldata + cols;
4facdf84 3215 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
374330e2 3216 q--;
32874aea 3217 if (q == ldata + cols)
374330e2 3218 q--;
32874aea 3219 if (p.x >= q - ldata)
3220 p.x = (dir == -1 ? q - ldata : cols - 1);
374330e2 3221 }
3222 break;
3223 case SM_WORD:
3224 /*
3225 * In this mode, the units are maximal runs of characters
3226 * whose `wordness' has the same value.
3227 */
4eeb7d09 3228 wvalue = wordtype(ldata[p.x]);
374330e2 3229 if (dir == +1) {
50ab4088 3230 while (1) {
3231 if (p.x < cols-1) {
3232 if (wordtype(ldata[p.x + 1]) == wvalue)
3233 p.x++;
3234 else
3235 break;
3236 } else {
3237 if (ldata[cols] & LATTR_WRAPPED) {
3238 unsigned long *ldata2;
3239 ldata2 = lineptr(p.y+1);
3240 if (wordtype(ldata2[0]) == wvalue) {
3241 p.x = 0;
3242 p.y++;
3243 ldata = ldata2;
3244 } else
3245 break;
3246 } else
3247 break;
3248 }
3249 }
374330e2 3250 } else {
50ab4088 3251 while (1) {
3252 if (p.x > 0) {
3253 if (wordtype(ldata[p.x - 1]) == wvalue)
3254 p.x--;
3255 else
3256 break;
3257 } else {
3258 unsigned long *ldata2;
3259 if (p.y <= topy)
3260 break;
3261 ldata2 = lineptr(p.y-1);
3262 if ((ldata2[cols] & LATTR_WRAPPED) &&
3263 wordtype(ldata2[cols-1]) == wvalue) {
3264 p.x = cols-1;
3265 p.y--;
3266 ldata = ldata2;
3267 } else
3268 break;
3269 }
3270 }
374330e2 3271 }
3272 break;
3273 case SM_LINE:
3274 /*
3275 * In this mode, every line is a unit.
3276 */
4facdf84 3277 p.x = (dir == -1 ? 0 : cols - 1);
374330e2 3278 break;
3279 }
3280 return p;
3281}
3282
32874aea 3283static void sel_spread(void)
3284{
6908fed7 3285 if (seltype == LEXICOGRAPHIC) {
3286 selstart = sel_spread_half(selstart, -1);
3287 decpos(selend);
3288 selend = sel_spread_half(selend, +1);
3289 incpos(selend);
3290 }
374330e2 3291}
3292
568dd02f 3293void term_do_paste(void)
3294{
3295 wchar_t *data;
3296 int len;
3297
3298 get_clip(&data, &len);
3299 if (data) {
3300 wchar_t *p, *q;
3301
3302 if (paste_buffer)
3303 sfree(paste_buffer);
3304 paste_pos = paste_hold = paste_len = 0;
3305 paste_buffer = smalloc(len * sizeof(wchar_t));
3306
3307 p = q = data;
3308 while (p < data + len) {
3309 while (p < data + len &&
3310 !(p <= data + len - sel_nl_sz &&
3311 !memcmp(p, sel_nl, sizeof(sel_nl))))
3312 p++;
3313
3314 {
3315 int i;
3316 for (i = 0; i < p - q; i++) {
3317 paste_buffer[paste_len++] = q[i];
3318 }
3319 }
3320
3321 if (p <= data + len - sel_nl_sz &&
3322 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3323 paste_buffer[paste_len++] = '\r';
3324 p += sel_nl_sz;
3325 }
3326 q = p;
3327 }
3328
3329 /* Assume a small paste will be OK in one go. */
3330 if (paste_len < 256) {
760e88b2 3331 luni_send(paste_buffer, paste_len, 0);
568dd02f 3332 if (paste_buffer)
3333 sfree(paste_buffer);
3334 paste_buffer = 0;
3335 paste_pos = paste_hold = paste_len = 0;
3336 }
3337 }
3338 get_clip(NULL, NULL);
3339}
3340
32874aea 3341void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
6908fed7 3342 int shift, int ctrl, int alt)
32874aea 3343{
4facdf84 3344 pos selpoint;
3345 unsigned long *ldata;
fff61f25 3346 int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
6908fed7 3347 int default_seltype;
32874aea 3348
fff61f25 3349 if (y < 0) {
32874aea 3350 y = 0;
fff61f25 3351 if (a == MA_DRAG && !raw_mouse)
3352 term_scroll(0, -1);
3353 }
3354 if (y >= rows) {
32874aea 3355 y = rows - 1;
fff61f25 3356 if (a == MA_DRAG && !raw_mouse)
3357 term_scroll(0, +1);
3358 }
32874aea 3359 if (x < 0) {
3360 if (y > 0) {
3361 x = cols - 1;
3362 y--;
3363 } else
3364 x = 0;
094ed2a6 3365 }
32874aea 3366 if (x >= cols)
3367 x = cols - 1;
37508af4 3368
4facdf84 3369 selpoint.y = y + disptop;
3370 selpoint.x = x;
3371 ldata = lineptr(selpoint.y);
32874aea 3372 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
4facdf84 3373 selpoint.x /= 2;
374330e2 3374
fff61f25 3375 if (raw_mouse) {
01c034ad 3376 int encstate = 0, r, c;
3377 char abuf[16];
3378 static int is_down = 0;
3379
32874aea 3380 switch (b) {
01c034ad 3381 case MBT_LEFT:
32874aea 3382 encstate = 0x20; /* left button down */
01c034ad 3383 break;
3384 case MBT_MIDDLE:
3385 encstate = 0x21;
3386 break;
3387 case MBT_RIGHT:
3388 encstate = 0x22;
3389 break;
3390 case MBT_WHEEL_UP:
3391 encstate = 0x60;
3392 break;
3393 case MBT_WHEEL_DOWN:
3394 encstate = 0x61;
3395 break;
2d466ffd 3396 default: break; /* placate gcc warning about enum use */
01c034ad 3397 }
32874aea 3398 switch (a) {
01c034ad 3399 case MA_DRAG:
3400 if (xterm_mouse == 1)
3401 return;
3402 encstate += 0x20;
3403 break;
3404 case MA_RELEASE:
3405 encstate = 0x23;
3406 is_down = 0;
3407 break;
3408 case MA_CLICK:
3409 if (is_down == b)
3410 return;
3411 is_down = b;
3412 break;
2d466ffd 3413 default: break; /* placate gcc warning about enum use */
01c034ad 3414 }
3415 if (shift)
3416 encstate += 0x04;
3417 if (ctrl)
3418 encstate += 0x10;
3419 r = y + 33;
3420 c = x + 33;
3421
3422 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
760e88b2 3423 ldisc_send(abuf, 6, 0);
01c034ad 3424 return;
3425 }
3426
3427 b = translate_button(b);
3428
6908fed7 3429 /*
3430 * Set the selection type (rectangular or normal) at the start
3431 * of a selection attempt, from the state of Alt.
3432 */
3433 if (!alt ^ !cfg.rect_select)
3434 default_seltype = RECTANGULAR;
3435 else
3436 default_seltype = LEXICOGRAPHIC;
3437
3438 if (selstate == NO_SELECTION) {
3439 seltype = default_seltype;
3440 }
3441
01c034ad 3442 if (b == MBT_SELECT && a == MA_CLICK) {
374330e2 3443 deselect();
3444 selstate = ABOUT_TO;
6908fed7 3445 seltype = default_seltype;
374330e2 3446 selanchor = selpoint;
3447 selmode = SM_CHAR;
01c034ad 3448 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
374330e2 3449 deselect();
3450 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3451 selstate = DRAGGING;
3452 selstart = selanchor = selpoint;
4facdf84 3453 selend = selstart;
3454 incpos(selend);
374330e2 3455 sel_spread();
01c034ad 3456 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3457 (b == MBT_EXTEND && a != MA_RELEASE)) {
4facdf84 3458 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
374330e2 3459 return;
01c034ad 3460 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
6908fed7 3461 if (seltype == LEXICOGRAPHIC) {
3462 /*
3463 * For normal selection, we extend by moving
3464 * whichever end of the current selection is closer
3465 * to the mouse.
3466 */
3467 if (posdiff(selpoint, selstart) <
3468 posdiff(selend, selstart) / 2) {
3469 selanchor = selend;
3470 decpos(selanchor);
3471 } else {
3472 selanchor = selstart;
3473 }
4facdf84 3474 } else {
6908fed7 3475 /*
3476 * For rectangular selection, we have a choice of
3477 * _four_ places to put selanchor and selpoint: the
3478 * four corners of the selection.
3479 */
3480 if (2*selpoint.x < selstart.x + selend.x)
3481 selanchor.x = selend.x-1;
3482 else
3483 selanchor.x = selstart.x;
3484
3485 if (2*selpoint.y < selstart.y + selend.y)
3486 selanchor.y = selend.y;
3487 else
3488 selanchor.y = selstart.y;
4facdf84 3489 }
374330e2 3490 selstate = DRAGGING;
3491 }
3492 if (selstate != ABOUT_TO && selstate != DRAGGING)
3493 selanchor = selpoint;
3494 selstate = DRAGGING;
6908fed7 3495 if (seltype == LEXICOGRAPHIC) {
3496 /*
3497 * For normal selection, we set (selstart,selend) to
3498 * (selpoint,selanchor) in some order.
3499 */
3500 if (poslt(selpoint, selanchor)) {
3501 selstart = selpoint;
3502 selend = selanchor;
3503 incpos(selend);
3504 } else {
3505 selstart = selanchor;
3506 selend = selpoint;
3507 incpos(selend);
3508 }
374330e2 3509 } else {
6908fed7 3510 /*
3511 * For rectangular selection, we may need to
3512 * interchange x and y coordinates (if the user has
3513 * dragged in the -x and +y directions, or vice versa).
3514 */
3515 selstart.x = min(selanchor.x, selpoint.x);
3516 selend.x = 1+max(selanchor.x, selpoint.x);
3517 selstart.y = min(selanchor.y, selpoint.y);
3518 selend.y = max(selanchor.y, selpoint.y);
374330e2 3519 }
3520 sel_spread();
01c034ad 3521 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
374330e2 3522 if (selstate == DRAGGING) {
3523 /*
3524 * We've completed a selection. We now transfer the
3525 * data to the clipboard.
3526 */
6908fed7 3527 clipme(selstart, selend, (seltype == RECTANGULAR));
374330e2 3528 selstate = SELECTED;
3529 } else
3530 selstate = NO_SELECTION;
32874aea 3531 } else if (b == MBT_PASTE
3532 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
568dd02f 3533 term_do_paste();
374330e2 3534 }
3535
3536 term_update();
3537}
3538
32874aea 3539void term_nopaste()
3540{
3541 if (paste_len == 0)
3542 return;
c9def1b8 3543 sfree(paste_buffer);
3544 paste_buffer = 0;
3545 paste_len = 0;
3546}
3547
32874aea 3548void term_paste()
3549{
8df7a775 3550 static long last_paste = 0;
c9def1b8 3551 long now, paste_diff;
3552
32874aea 3553 if (paste_len == 0)
3554 return;
c9def1b8 3555
3556 /* Don't wait forever to paste */
32874aea 3557 if (paste_hold) {
3558 now = GetTickCount();
3559 paste_diff = now - last_paste;
3560 if (paste_diff >= 0 && paste_diff < 450)
c9def1b8 3561 return;
3562 }
3563 paste_hold = 0;
3564
32874aea 3565 while (paste_pos < paste_len) {
8df7a775 3566 int n = 0;
3567 while (n + paste_pos < paste_len) {
3568 if (paste_buffer[paste_pos + n++] == '\r')
3569 break;
3570 }
760e88b2 3571 luni_send(paste_buffer + paste_pos, n, 0);
8df7a775 3572 paste_pos += n;
c9def1b8 3573
8df7a775 3574 if (paste_pos < paste_len) {
c9def1b8 3575 paste_hold = 1;
3576 return;
3577 }
3578 }
3579 sfree(paste_buffer);
3580 paste_buffer = 0;
3581 paste_len = 0;
3582}
3583
32874aea 3584static void deselect(void)
3585{
374330e2 3586 selstate = NO_SELECTION;
4facdf84 3587 selstart.x = selstart.y = selend.x = selend.y = 0;
374330e2 3588}
3589
32874aea 3590void term_deselect(void)
3591{
374330e2 3592 deselect();
3593 term_update();
3594}
fe50e814 3595
32874aea 3596int term_ldisc(int option)
3597{
3598 if (option == LD_ECHO)
3599 return term_echoing;
3600 if (option == LD_EDIT)
3601 return term_editing;
0965bee0 3602 return FALSE;
3603}
3604
fe50e814 3605/*
3606 * from_backend(), to get data from the backend for the terminal.
3607 */
5471d09a 3608int from_backend(int is_stderr, char *data, int len)
32874aea 3609{
a748a096 3610 bufchain_add(&inbuf, data, len);
5471d09a 3611
3612 /*
a748a096 3613 * term_out() always completely empties inbuf. Therefore,
3614 * there's no reason at all to return anything other than zero
3615 * from this function, because there _can't_ be a question of
3616 * the remote side needing to wait until term_out() has cleared
3617 * a backlog.
3618 *
5471d09a 3619 * This is a slightly suboptimal way to deal with SSH2 - in
3620 * principle, the window mechanism would allow us to continue
3621 * to accept data on forwarded ports and X connections even
3622 * while the terminal processing was going slowly - but we
3623 * can't do the 100% right thing without moving the terminal
3624 * processing into a separate thread, and that might hurt
3625 * portability. So we manage stdout buffering the old SSH1 way:
3626 * if the terminal processing goes slowly, the whole SSH
3627 * connection stops accepting data until it's ready.
a748a096 3628 *
5471d09a 3629 * In practice, I can't imagine this causing serious trouble.
3630 */
3631 return 0;
fe50e814 3632}
e1c8e0ed 3633
3634/*
3635 * Log session traffic.
3636 */
32874aea 3637void logtraffic(unsigned char c, int logmode)
3638{
e1c8e0ed 3639 if (cfg.logtype > 0) {
3640 if (cfg.logtype == logmode) {
3641 /* deferred open file from pgm start? */
32874aea 3642 if (!lgfp)
3643 logfopen();
3644 if (lgfp)
3645 fputc(c, lgfp);
3646 }
e1c8e0ed 3647 }
3648}
3649
66ee282a 3650void settimstr(char *ta, int no_sec);
3651char *subslfcode(char *dest, char *src, char *dstrt);
3652char *stpncpy(char *dst, const char *src, size_t maxlen);
3653char timdatbuf[20];
3654char currlogfilename[FILENAME_MAX];
3655
e1c8e0ed 3656/* open log file append/overwrite mode */
32874aea 3657void logfopen(void)
3658{
e1c8e0ed 3659 char buf[256];
3660 time_t t;
66ee282a 3661 struct tm tm;
e1c8e0ed 3662 char writemod[4];
3663
3664 if (!cfg.logtype)
3665 return;
32874aea 3666 sprintf(writemod, "wb"); /* default to rewrite */
66ee282a 3667
3668 time(&t);
3669 tm = *localtime(&t);
3670
3671 /* substitute special codes in file name */
3672 xlatlognam(currlogfilename,cfg.logfilename,cfg.host, &tm);
3673
3674 lgfp = fopen(currlogfilename, "r"); /* file already present? */
e1c8e0ed 3675 if (lgfp) {
3676 int i;
3677 fclose(lgfp);
66ee282a 3678 i = askappend(currlogfilename);
e1c8e0ed 3679 if (i == 1)
3680 writemod[0] = 'a'; /* set append mode */
3681 else if (i == 0) { /* cancelled */
3682 lgfp = NULL;
32874aea 3683 cfg.logtype = 0; /* disable logging */
e1c8e0ed 3684 return;
3685 }
3686 }
3687
66ee282a 3688 lgfp = fopen(currlogfilename, writemod);
32874aea 3689 if (lgfp) { /* enter into event log */
e1c8e0ed 3690 sprintf(buf, "%s session log (%s mode) to file : ",
3691 (writemod[0] == 'a') ? "Appending" : "Writing new",
3692 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
32874aea 3693 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
e1c8e0ed 3694 /* Make sure we do not exceed the output buffer size */
66ee282a 3695 strncat(buf, currlogfilename, 128);
e1c8e0ed 3696 buf[strlen(buf)] = '\0';
3697 logevent(buf);
3698
66ee282a 3699 /* --- write header line into log file */
32874aea 3700 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
66ee282a 3701 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
32874aea 3702 fputs(buf, lgfp);
3703 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
e1c8e0ed 3704 }
3705}
3706
32874aea 3707void logfclose(void)
3708{
3709 if (lgfp) {
3710 fclose(lgfp);
3711 lgfp = NULL;
3712 }
e1c8e0ed 3713}
66ee282a 3714
3715/*
3716 * translate format codes into time/date strings
3717 * and insert them into log file name
3718 *
3719 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3720 */
3721static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm) {
3722 char buf[10], *bufp;
3723 int size;
3724 char *ds = d; /* save start pos. */
3725 int len = FILENAME_MAX-1;
3726
3727 while (*s) {
3728 /* Let (bufp, len) be the string to append. */
3729 bufp = buf; /* don't usually override this */
3730 if (*s == '&') {
3731 char c;
3732 s++;
3733 if (*s) switch (c = *s++, tolower(c)) {
3734 case 'y':
3735 size = strftime(buf, sizeof(buf), "%Y", tm);
3736 break;
3737 case 'm':
3738 size = strftime(buf, sizeof(buf), "%m", tm);
3739 break;
3740 case 'd':
3741 size = strftime(buf, sizeof(buf), "%d", tm);
3742 break;
3743 case 't':
3744 size = strftime(buf, sizeof(buf), "%H%M%S", tm);
3745 break;
3746 case 'h':
3747 bufp = hostname;
3748 size = strlen(bufp);
3749 break;
3750 default:
3751 buf[0] = '&';
3752 size = 1;
3753 if (c != '&')
3754 buf[size++] = c;
3755 }
3756 } else {
3757 buf[0] = *s++;
3758 size = 1;
3759 }
3760 if (size > len)
3761 size = len;
3762 memcpy(d, bufp, size);
3763 d += size;
3764 len -= size;
3765 }
3766 *d = '\0';
3767}