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