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