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