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