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