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