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