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