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