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