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