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