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